diff --git a/front_end/panels/ai_assistance/AiAssistancePanel.test.ts b/front_end/panels/ai_assistance/AiAssistancePanel.test.ts index 137c2c96ef..0bf6378a2b 100644 --- a/front_end/panels/ai_assistance/AiAssistancePanel.test.ts +++ b/front_end/panels/ai_assistance/AiAssistancePanel.test.ts @@ -18,6 +18,7 @@ import {createTarget, registerNoopActions, updateHostConfig} from '../../testing import {expectCall} from '../../testing/ExpectStubCall.js'; import {describeWithMockConnection} from '../../testing/MockConnection.js'; import {createNetworkPanelForMockConnection} from '../../testing/NetworkHelpers.js'; +import {createFileSystemUISourceCode} from '../../testing/UISourceCodeHelpers.js'; import * as UI from '../../ui/legacy/legacy.js'; import * as Elements from '../elements/elements.js'; import * as Network from '../network/network.js'; @@ -1116,4 +1117,62 @@ describeWithMockConnection('AI Assistance Panel', () => { ]); }); }); + + describe('workspace', () => { + function createTestFilesystem(fileSystemPath: string) { + const {project, uiSourceCode} = createFileSystemUISourceCode({ + url: Platform.DevToolsPath.urlString`file:///example.html`, + mimeType: 'text/html', + content: 'content', + fileSystemPath, + }); + return {project, uiSourceCode}; + } + + it('does not report a workspace project if disabled', async () => { + createTestFilesystem('file://test'); + updateHostConfig({ + devToolsFreestyler: { + enabled: true, + patching: false, + }, + }); + const { + initialViewInput, + } = await createAiAssistancePanel(); + assert.strictEqual(initialViewInput.projectName, ''); + }); + + it('reports a current workspace project', async () => { + createTestFilesystem('file://test'); + updateHostConfig({ + devToolsFreestyler: { + enabled: true, + patching: true, + }, + }); + const { + initialViewInput, + } = await createAiAssistancePanel(); + assert.strictEqual(initialViewInput.projectName, 'test'); + }); + + it('reports an updated project', async () => { + const {project} = createTestFilesystem('file://test'); + updateHostConfig({ + devToolsFreestyler: { + enabled: true, + patching: true, + }, + }); + const {initialViewInput, expectViewUpdate} = await createAiAssistancePanel(); + assert.strictEqual(initialViewInput.projectName, 'test'); + + const updatedViewInput = await expectViewUpdate(() => { + Workspace.Workspace.WorkspaceImpl.instance().removeProject(project); + createTestFilesystem('file://test2'); + }); + assert.strictEqual(updatedViewInput.projectName, 'test2'); + }); + }); }); diff --git a/front_end/panels/ai_assistance/AiAssistancePanel.ts b/front_end/panels/ai_assistance/AiAssistancePanel.ts index 2dca4c88f5..2aeb51034a 100644 --- a/front_end/panels/ai_assistance/AiAssistancePanel.ts +++ b/front_end/panels/ai_assistance/AiAssistancePanel.ts @@ -322,6 +322,7 @@ export class AiAssistancePanel extends UI.Panel.Panel { #patchSuggestion?: string; #patchSuggestionLoading?: boolean; #imageInput: string = ''; + #workspace = Workspace.Workspace.WorkspaceImpl.instance(); constructor(private view: View = defaultView, {aidaClient, aidaAvailability, syncInfo}: { aidaClient: Host.AidaClient.AidaClient, @@ -345,6 +346,10 @@ export class AiAssistancePanel extends UI.Panel.Panel { return new Conversation(item.type, item.history, item.id, true); }); + this.#selectProject(); + } + + #selectProject(): void { if (isAiAssistancePatchingEnabled()) { // TODO: this is temporary code that should be replaced with workflow selection flow. // For now it picks the first Workspace project that is not Snippets. @@ -356,11 +361,16 @@ export class AiAssistancePanel extends UI.Panel.Panel { continue; } this.#project = project; + this.requestUpdate(); break; } } } + #onProjectAddedOrRemoved(): void { + this.#selectProject(); + } + #getChatUiState(): ChatViewState { const blockedByAge = Root.Runtime.hostConfig.aidaAvailability?.blockedByAge === true; return (this.#aiAssistanceEnabledSetting?.getIfNotDisabled() && !blockedByAge) ? ChatViewState.CHAT_VIEW : @@ -536,6 +546,11 @@ export class AiAssistancePanel extends UI.Panel.Panel { SDK.TargetManager.TargetManager.instance().addModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrRemoved, this.#handleDOMNodeAttrChange, this); Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistancePanelOpened); + + if (isAiAssistancePatchingEnabled()) { + this.#workspace.addEventListener(Workspace.Workspace.Events.ProjectAdded, this.#onProjectAddedOrRemoved, this); + this.#workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this.#onProjectAddedOrRemoved, this); + } } override willHide(): void { @@ -572,6 +587,12 @@ export class AiAssistancePanel extends UI.Panel.Panel { this.#handleDOMNodeAttrChange, this, ); + + if (isAiAssistancePatchingEnabled()) { + this.#workspace.removeEventListener(Workspace.Workspace.Events.ProjectAdded, this.#onProjectAddedOrRemoved, this); + this.#workspace.removeEventListener( + Workspace.Workspace.Events.ProjectRemoved, this.#onProjectAddedOrRemoved, this); + } } #handleAidaAvailabilityChange = async(): Promise => { @@ -1001,6 +1022,9 @@ export class AiAssistancePanel extends UI.Panel.Panel { } async #onApplyToWorkspace(): Promise { + if (!isAiAssistancePatchingEnabled()) { + return; + } const changeSummary = this.#getChangeSummary(); if (!changeSummary) { throw new Error('Change summary does not exist');