[AI Assistance] Add better integration with Workspace

Bug: 393268664
Change-Id: I1a1acc1e1b2db365cc94c8f23cebb0913edfd259
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6280747
Reviewed-by: Ergün Erdoğmuş <ergunsh@chromium.org>
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
This commit is contained in:
Alex Rudenko
2025-02-19 03:13:41 -08:00
committed by Devtools-frontend LUCI CQ
parent f9f582e701
commit 9748d8ef61
2 changed files with 83 additions and 0 deletions
@@ -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');
});
});
});
@@ -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<void> => {
@@ -1001,6 +1022,9 @@ export class AiAssistancePanel extends UI.Panel.Panel {
}
async #onApplyToWorkspace(): Promise<void> {
if (!isAiAssistancePatchingEnabled()) {
return;
}
const changeSummary = this.#getChangeSummary();
if (!changeSummary) {
throw new Error('Change summary does not exist');