diff --git a/playwright/github-byot-ai.spec.ts b/playwright/github-byot-ai.spec.ts index 55868a0..a9f2bef 100644 --- a/playwright/github-byot-ai.spec.ts +++ b/playwright/github-byot-ai.spec.ts @@ -15,6 +15,7 @@ import { } from './helpers/app-test-helpers.js' import { getAllWorkspaceRecords, + openStoredWorkspaceContextById, seedLocalWorkspaceContexts, } from './github-pr-drawer/github-pr-drawer.helpers.js' @@ -400,6 +401,79 @@ test('Local workspace can be renamed from Workspaces drawer', async ({ page }) = ) }) +test('chat stays usable after opening a Local workspace with PAT connected', async ({ + page, +}) => { + const localWorkspaceId = 'local_chat_issue_128' + let streamRequestBody: ChatRequestBody | undefined + + await page.route('https://models.github.ai/inference/chat/completions', async route => { + streamRequestBody = route.request().postDataJSON() as ChatRequestBody + + await route.fulfill({ + status: 200, + contentType: 'text/event-stream', + body: [ + 'data: {"choices":[{"delta":{"content":"Local workspace chat works"}}]}', + '', + 'data: [DONE]', + '', + ].join('\n'), + }) + }) + + await waitForAppReady(page) + + await seedLocalWorkspaceContexts(page, [ + { + id: localWorkspaceId, + repo: '', + workspaceScope: 'local', + head: 'feat/local-chat-issue-128', + prTitle: 'Issue 128 local workspace', + prContextState: 'inactive', + tabs: [ + { + id: 'component', + path: 'src/component.tsx', + language: 'tsx', + role: 'component', + content: 'export const App = () =>
local chat issue 128
', + order: 0, + source: 'workspace', + dirty: false, + }, + ], + activeTabId: 'component', + }, + ]) + + await connectByotWithSingleRepo(page) + await openStoredWorkspaceContextById(page, localWorkspaceId, { + repositoryFilter: '__local__', + }) + await ensureWorkspacesDrawerClosed(page) + + await ensureAiChatDrawerOpen(page) + + await page.getByLabel('Ask AI assistant').fill('Confirm local workspace chat context.') + await page.getByRole('button', { name: 'Send' }).click() + + await expect(page.getByText('Local workspace chat works')).toBeVisible() + await expect( + page.getByText('Select a writable repository before starting chat.', { exact: true }), + ).toHaveCount(0) + + const repositorySystemMessage = streamRequestBody?.messages?.find( + (message: ChatRequestMessage) => + message.role === 'system' && + message.content?.includes('Selected repository context'), + ) + expect(repositorySystemMessage?.content).toContain( + 'Repository: knightedcodemonkey/develop', + ) +}) + test('BYOT controls render with default app entry', async ({ page }) => { await waitForAppReady(page, appEntryPath) diff --git a/src/app.js b/src/app.js index 3f1a7be..3ca41ae 100644 --- a/src/app.js +++ b/src/app.js @@ -607,6 +607,25 @@ const getCurrentSelectedRepositoryFullName = () => { return '' } +const getActiveWorkspaceDisplayLabel = workspace => { + const workspaceIdFromArg = toNonEmptyWorkspaceText(workspace?.id) + const activeWorkspaceId = toNonEmptyWorkspaceText(activeWorkspaceRecordId) + if ( + workspaceIdFromArg && + activeWorkspaceId && + workspaceIdFromArg !== activeWorkspaceId + ) { + return '' + } + + return ( + toNonEmptyWorkspaceText(activeWorkspacePersistedPrTitle) || + toNonEmptyWorkspaceText(activeWorkspacePersistedHeadBranch) || + toNonEmptyWorkspaceText(githubPrTitle?.value) || + toNonEmptyWorkspaceText(githubPrHeadBranch?.value) + ) +} + workspaceContextStatusController = createWorkspaceContextStatusController({ statusNode: workspaceContextStatus, toNonEmptyWorkspaceText, @@ -1138,6 +1157,7 @@ const githubWorkflows = createGitHubWorkflowsSetup({ workspace: { workspaceStorage, getActiveWorkspaceRecordId: () => activeWorkspaceRecordId, + getActiveWorkspaceDisplayLabel, setActiveWorkspaceRecordId, setActiveWorkspaceCreatedAt: value => (activeWorkspaceCreatedAt = value), buildWorkspaceRecordSnapshot, diff --git a/src/modules/app-core/github-workflows-setup.js b/src/modules/app-core/github-workflows-setup.js index b4b8fa2..e6f9cc6 100644 --- a/src/modules/app-core/github-workflows-setup.js +++ b/src/modules/app-core/github-workflows-setup.js @@ -23,6 +23,7 @@ const createGitHubWorkflowsSetup = ({ setActiveWorkspaceRecordId: workspace.setActiveWorkspaceRecordId, setActiveWorkspaceCreatedAt: workspace.setActiveWorkspaceCreatedAt, buildWorkspaceRecordSnapshot: workspace.buildWorkspaceRecordSnapshot, + getActiveWorkspaceDisplayLabel: workspace.getActiveWorkspaceDisplayLabel, listLocalContextRecords: workspace.listLocalContextRecords, refreshLocalContextOptions: workspace.refreshLocalContextOptions, applyWorkspaceRecord: workspace.applyWorkspaceRecord, diff --git a/src/modules/app-core/github-workflows.js b/src/modules/app-core/github-workflows.js index ec55a0f..5884dc6 100644 --- a/src/modules/app-core/github-workflows.js +++ b/src/modules/app-core/github-workflows.js @@ -50,6 +50,7 @@ const initializeGitHubWorkflows = ({ workspacesRemove, workspaceStorage, getActiveWorkspaceRecordId, + getActiveWorkspaceDisplayLabel, setActiveWorkspaceRecordId, setActiveWorkspaceCreatedAt, buildWorkspaceRecordSnapshot, @@ -430,6 +431,10 @@ const initializeGitHubWorkflows = ({ openButton: workspacesOpen, renameButton: workspacesRename, removeButton: workspacesRemove, + getActiveWorkspaceDisplayLabel: workspace => + typeof getActiveWorkspaceDisplayLabel === 'function' + ? toSafeWorkspaceText(getActiveWorkspaceDisplayLabel(workspace)) + : '', getRepositoryFilterOptions: () => getCurrentWritableRepositories().map(repository => ({ value: repository.fullName, diff --git a/src/modules/app-core/workspace-record-applied-handler.js b/src/modules/app-core/workspace-record-applied-handler.js index 210b25d..f69c820 100644 --- a/src/modules/app-core/workspace-record-applied-handler.js +++ b/src/modules/app-core/workspace-record-applied-handler.js @@ -21,7 +21,6 @@ const createWorkspaceRecordAppliedHandler = ({ byotControls?.setSelectedRepository(nextWorkspaceRepositoryFullName) } else { setWorkspaceRepositoryFullName('') - byotControls?.clearSelectedRepositoryPreference?.() } const state = diff --git a/src/modules/workspace/workspaces-drawer/drawer.js b/src/modules/workspace/workspaces-drawer/drawer.js index 08f985e..e574121 100644 --- a/src/modules/workspace/workspaces-drawer/drawer.js +++ b/src/modules/workspace/workspaces-drawer/drawer.js @@ -54,6 +54,7 @@ export const createWorkspacesDrawer = ({ renameButton, removeButton, getDrawerSide, + getActiveWorkspaceDisplayLabel, getRepositoryFilterOptions, getSelectedRepositoryFilter, onRepositoryFilterChange, @@ -242,10 +243,19 @@ export const createWorkspacesDrawer = ({ placeholder.selected = !filteredEntries.some(entry => entry.id === selectedId) selectInput.append(placeholder) + const activeWorkspaceId = + typeof getActiveWorkspaceId === 'function' ? toSafeText(getActiveWorkspaceId()) : '' + for (const entry of filteredEntries) { const option = document.createElement('option') option.value = toSafeText(entry.id) - option.textContent = toWorkspaceLabel(entry) + const activeWorkspaceDisplayLabel = + option.value && + option.value === activeWorkspaceId && + typeof getActiveWorkspaceDisplayLabel === 'function' + ? toSafeText(getActiveWorkspaceDisplayLabel(entry)) + : '' + option.textContent = activeWorkspaceDisplayLabel || toWorkspaceLabel(entry) option.selected = option.value === selectedId selectInput.append(option) }