chrome-devtools-frontend 1.0.1604514 → 1.0.1605219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHORS +1 -0
- package/front_end/Tests.js +23 -7
- package/front_end/core/host/AidaClient.ts +114 -63
- package/front_end/core/host/AidaGcaTranslation.ts +6 -0
- package/front_end/core/host/GcaClient.ts +112 -0
- package/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/host/host.ts +2 -0
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/AutofillModel.ts +3 -2
- package/front_end/core/sdk/CSSModel.ts +3 -4
- package/front_end/core/sdk/CSSProperty.ts +1 -1
- package/front_end/core/sdk/ConsoleModel.ts +15 -13
- package/front_end/core/sdk/CookieModel.ts +5 -4
- package/front_end/core/sdk/DOMDebuggerModel.ts +20 -14
- package/front_end/core/sdk/DebuggerModel.ts +21 -36
- package/front_end/core/sdk/EventBreakpointsModel.ts +16 -6
- package/front_end/core/sdk/IsolateManager.ts +12 -7
- package/front_end/entrypoints/main/MainImpl.ts +0 -4
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +31 -17
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +1 -1
- package/front_end/models/emulation/DeviceModeModel.ts +21 -26
- package/front_end/models/trace/insights/LCPDiscovery.ts +14 -6
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +2 -17
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +36 -20
- package/front_end/panels/ai_assistance/components/chatMessage.css +5 -1
- package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -0
- package/front_end/panels/application/IndexedDBViews.ts +0 -9
- package/front_end/panels/application/WebMCPTreeElement.ts +35 -0
- package/front_end/panels/application/WebMCPView.ts +118 -0
- package/front_end/panels/application/application.ts +4 -0
- package/front_end/panels/application/webMCPView.css +65 -0
- package/front_end/panels/common/aiCodeCompletionSummaryToolbar.css +1 -1
- package/front_end/panels/console/consoleView.css +4 -0
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +23 -2
- package/front_end/panels/elements/StylesSidebarPane.ts +85 -20
- package/front_end/panels/elements/elementsPanel.css +14 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +3 -3
- package/front_end/panels/recorder/components/recordingView.css +0 -6
- package/front_end/panels/sensors/SensorsView.ts +28 -59
- package/front_end/panels/timeline/EventsTimelineTreeView.ts +2 -2
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +35 -36
- package/front_end/panels/timeline/TimelineDetailsView.ts +47 -41
- package/front_end/panels/timeline/TimelineTreeView.ts +13 -13
- package/front_end/panels/timeline/components/LiveMetricsView.ts +839 -783
- package/front_end/panels/timeline/thirdPartyTreeView.css +2 -2
- package/front_end/ui/components/buttons/button.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +0 -11
- package/front_end/ui/legacy/InspectorDrawerView.ts +206 -0
- package/front_end/ui/legacy/InspectorView.ts +41 -91
- package/front_end/ui/legacy/ViewManager.ts +14 -5
- package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +5 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -1
- package/package.json +1 -1
|
@@ -342,12 +342,13 @@ export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, ta
|
|
|
342
342
|
class="chat-message answer ${input.isLastMessage ? 'is-last-message' : ''}"
|
|
343
343
|
jslog=${VisualLogging.section('answer')}
|
|
344
344
|
>
|
|
345
|
-
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
345
|
+
${aiAssistanceV2 ? Lit.nothing : html`
|
|
346
|
+
<div class="message-info">
|
|
347
|
+
<devtools-icon name=${icon}></devtools-icon>
|
|
348
|
+
<div class="message-name">
|
|
349
|
+
<h2>${AiAssistanceModel.AiUtils.isGeminiBranding() ? lockedString(UIStringsNotTranslate.gemini) : lockedString(UIStringsNotTranslate.ai)}</h2>
|
|
350
|
+
</div>
|
|
351
|
+
</div>`}
|
|
351
352
|
${aiAssistanceV2 ? renderWalkthroughUI(input, steps) : Lit.nothing}
|
|
352
353
|
${Lit.Directives.repeat(
|
|
353
354
|
message.parts,
|
|
@@ -519,11 +520,14 @@ function renderWalkthroughSidebarButton(
|
|
|
519
520
|
// want to change it visually at the end once everything has stopped
|
|
520
521
|
// loading.
|
|
521
522
|
const variant = hasOneStepWithWidget && !input.isLoading ? Buttons.Button.Variant.TONAL : Buttons.Button.Variant.TEXT;
|
|
523
|
+
const icon = AiAssistanceModel.AiUtils.getIconName();
|
|
522
524
|
|
|
523
525
|
// clang-format off
|
|
524
526
|
return html`
|
|
525
|
-
<div class="walkthrough-toggle-container">
|
|
526
|
-
${input.isLoading ?
|
|
527
|
+
<div class="walkthrough-toggle-container ${hasOneStepWithWidget ? 'has-widgets' : ''}">
|
|
528
|
+
${input.isLoading ?
|
|
529
|
+
html`<devtools-spinner></devtools-spinner>` :
|
|
530
|
+
html`<devtools-icon name=${icon}></devtools-icon>`}
|
|
527
531
|
<devtools-button
|
|
528
532
|
.variant=${variant}
|
|
529
533
|
.size=${Buttons.Button.Size.SMALL}
|
|
@@ -540,8 +544,8 @@ function renderWalkthroughSidebarButton(
|
|
|
540
544
|
walkthrough.onOpen(message as ModelChatMessage);
|
|
541
545
|
}
|
|
542
546
|
}}
|
|
543
|
-
|
|
544
|
-
${title}<devtools-icon class="chevron" .name=${'chevron-right'}></devtools-icon>
|
|
547
|
+
>
|
|
548
|
+
${title}<devtools-icon class="chevron" .name=${isExpanded ? 'cross' : 'chevron-right'}></devtools-icon>
|
|
545
549
|
</devtools-button>
|
|
546
550
|
</div>
|
|
547
551
|
`;
|
|
@@ -1236,7 +1240,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1236
1240
|
};
|
|
1237
1241
|
|
|
1238
1242
|
#suggestionsResizeObserver = new ResizeObserver(() => this.#handleSuggestionsScrollOrResize());
|
|
1239
|
-
#suggestionsEvaluateLayoutThrottler = new Common.Throttler.Throttler(
|
|
1243
|
+
#suggestionsEvaluateLayoutThrottler = new Common.Throttler.Throttler(100);
|
|
1240
1244
|
|
|
1241
1245
|
#feedbackValue = '';
|
|
1242
1246
|
#currentRating: Host.AidaClient.Rating|undefined;
|
|
@@ -1246,6 +1250,8 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1246
1250
|
#view: View;
|
|
1247
1251
|
#viewOutput: ViewOutput = {};
|
|
1248
1252
|
|
|
1253
|
+
#isObservingSuggestions = false;
|
|
1254
|
+
|
|
1249
1255
|
constructor(element?: HTMLElement, view?: View) {
|
|
1250
1256
|
super(element);
|
|
1251
1257
|
this.#view = view ?? DEFAULT_VIEW;
|
|
@@ -1255,10 +1261,6 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1255
1261
|
super.wasShown();
|
|
1256
1262
|
void this.performUpdate();
|
|
1257
1263
|
this.#evaluateSuggestionsLayout();
|
|
1258
|
-
|
|
1259
|
-
if (this.#viewOutput.suggestionsScrollContainer) {
|
|
1260
|
-
this.#suggestionsResizeObserver.observe(this.#viewOutput.suggestionsScrollContainer);
|
|
1261
|
-
}
|
|
1262
1264
|
}
|
|
1263
1265
|
|
|
1264
1266
|
override performUpdate(): Promise<void>|void {
|
|
@@ -1297,6 +1299,11 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1297
1299
|
walkthrough: this.walkthrough,
|
|
1298
1300
|
},
|
|
1299
1301
|
this.#viewOutput, this.contentElement);
|
|
1302
|
+
|
|
1303
|
+
if (this.#viewOutput.suggestionsScrollContainer && !this.#isObservingSuggestions) {
|
|
1304
|
+
this.#suggestionsResizeObserver.observe(this.#viewOutput.suggestionsScrollContainer);
|
|
1305
|
+
this.#isObservingSuggestions = true;
|
|
1306
|
+
}
|
|
1300
1307
|
}
|
|
1301
1308
|
|
|
1302
1309
|
#handleInputChange(value: string): void {
|
|
@@ -1320,6 +1327,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1320
1327
|
const shouldShowRightButton = suggestionsScrollContainer.scrollLeft +
|
|
1321
1328
|
(suggestionsScrollContainer as HTMLElement).offsetWidth + SCROLL_ROUNDING_OFFSET <
|
|
1322
1329
|
suggestionsScrollContainer.scrollWidth;
|
|
1330
|
+
|
|
1323
1331
|
leftScrollButtonContainer.classList.toggle('hidden', !shouldShowLeftButton);
|
|
1324
1332
|
rightScrollButtonContainer.classList.toggle('hidden', !shouldShowRightButton);
|
|
1325
1333
|
};
|
|
@@ -1327,6 +1335,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1327
1335
|
override willHide(): void {
|
|
1328
1336
|
super.willHide();
|
|
1329
1337
|
this.#suggestionsResizeObserver.disconnect();
|
|
1338
|
+
this.#isObservingSuggestions = false;
|
|
1330
1339
|
}
|
|
1331
1340
|
|
|
1332
1341
|
#handleSuggestionsScrollOrResize(): void {
|
|
@@ -1410,8 +1419,8 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
|
|
|
1410
1419
|
|
|
1411
1420
|
const thirdPartyTree = new Timeline.ThirdPartyTreeView.ThirdPartyTreeViewWidget();
|
|
1412
1421
|
const mapper = new Trace.EntityMapper.EntityMapper(parsedTrace);
|
|
1413
|
-
thirdPartyTree.
|
|
1414
|
-
thirdPartyTree.
|
|
1422
|
+
thirdPartyTree.model = {selectedEvents: eventsArray, parsedTrace, entityMapper: mapper};
|
|
1423
|
+
thirdPartyTree.activeSelection = Timeline.TimelineSelection.selectionFromRangeMicroSeconds(bounds.min, bounds.max);
|
|
1415
1424
|
thirdPartyTree.refreshTree(true);
|
|
1416
1425
|
|
|
1417
1426
|
// clang-format off
|
|
@@ -1423,9 +1432,16 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
|
|
|
1423
1432
|
events,
|
|
1424
1433
|
startTime: Trace.Helpers.Timing.microToMilli(bounds.min),
|
|
1425
1434
|
endTime: Trace.Helpers.Timing.microToMilli(bounds.max),
|
|
1426
|
-
thirdPartyTreeTemplate: html
|
|
1427
|
-
|
|
1428
|
-
|
|
1435
|
+
thirdPartyTreeTemplate: html`${
|
|
1436
|
+
widget(Timeline.ThirdPartyTreeView.ThirdPartyTreeViewWidget, {
|
|
1437
|
+
maxRows: 10,
|
|
1438
|
+
model: {
|
|
1439
|
+
selectedEvents: thirdPartyTree.selectedEvents() ?? null,
|
|
1440
|
+
parsedTrace,
|
|
1441
|
+
entityMapper: thirdPartyTree.entityMapper(),
|
|
1442
|
+
},
|
|
1443
|
+
activeSelection: { bounds },
|
|
1444
|
+
})}`,
|
|
1429
1445
|
} as TimelineComponents.TimelineRangeSummaryView.TimelineRangeSummaryViewData,
|
|
1430
1446
|
})}
|
|
1431
1447
|
></devtools-widget>`;
|
|
@@ -344,9 +344,13 @@
|
|
|
344
344
|
|
|
345
345
|
.walkthrough-toggle-container {
|
|
346
346
|
display: flex;
|
|
347
|
-
gap: var(--sys-size-
|
|
347
|
+
gap: var(--sys-size-2);
|
|
348
348
|
align-items: center;
|
|
349
349
|
|
|
350
|
+
&.has-widgets {
|
|
351
|
+
gap: var(--sys-size-6);
|
|
352
|
+
}
|
|
353
|
+
|
|
350
354
|
.chevron {
|
|
351
355
|
color: var(--sys-color-primary);
|
|
352
356
|
width: var(--sys-size-8);
|
|
@@ -92,6 +92,7 @@ import {SharedStorageTreeElement} from './SharedStorageTreeElement.js';
|
|
|
92
92
|
import {StorageBucketsTreeParentElement} from './StorageBucketsTreeElement.js';
|
|
93
93
|
import {StorageView} from './StorageView.js';
|
|
94
94
|
import {TrustTokensTreeElement} from './TrustTokensTreeElement.js';
|
|
95
|
+
import {WebMCPTreeElement} from './WebMCPTreeElement.js';
|
|
95
96
|
|
|
96
97
|
const UIStrings = {
|
|
97
98
|
/**
|
|
@@ -346,6 +347,7 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
346
347
|
periodicBackgroundSyncTreeElement: BackgroundServiceTreeElement;
|
|
347
348
|
pushMessagingTreeElement: BackgroundServiceTreeElement;
|
|
348
349
|
reportingApiTreeElement: ReportingApiTreeElement;
|
|
350
|
+
webMcpTreeElement?: WebMCPTreeElement;
|
|
349
351
|
deviceBoundSessionsRootTreeElement: DeviceBoundSessionsRootTreeElement|undefined;
|
|
350
352
|
deviceBoundSessionsModel: DeviceBoundSessionsModel|undefined;
|
|
351
353
|
preloadingSummaryTreeElement: PreloadingSummaryTreeElement|undefined;
|
|
@@ -390,6 +392,10 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
390
392
|
this.applicationTreeElement.appendChild(this.serviceWorkersTreeElement);
|
|
391
393
|
const clearStorageTreeElement = new ClearStorageTreeElement(panel);
|
|
392
394
|
this.applicationTreeElement.appendChild(clearStorageTreeElement);
|
|
395
|
+
if (Root.Runtime.hostConfig.devToolsWebMCPSupport?.enabled) {
|
|
396
|
+
this.webMcpTreeElement = new WebMCPTreeElement(panel);
|
|
397
|
+
this.applicationTreeElement.appendChild(this.webMcpTreeElement);
|
|
398
|
+
}
|
|
393
399
|
|
|
394
400
|
const storageSectionTitle = i18nString(UIStrings.storage);
|
|
395
401
|
const storageTreeElement = this.addSidebarSection(storageSectionTitle, 'storage');
|
|
@@ -321,27 +321,19 @@ export class IDBDataView extends UI.View.SimpleView {
|
|
|
321
321
|
const keyPath = this.isIndex && this.index ? this.index.keyPath : this.objectStore.keyPath;
|
|
322
322
|
|
|
323
323
|
const columns: DataGrid.DataGrid.ColumnDescriptor[] = [];
|
|
324
|
-
|
|
325
|
-
// Create column defaults so that we avoid repetition below.
|
|
326
|
-
const columnDefaults: Partial<DataGrid.DataGrid.ColumnDescriptor> = {
|
|
327
|
-
sortable: false,
|
|
328
|
-
};
|
|
329
324
|
columns.push({
|
|
330
|
-
...columnDefaults,
|
|
331
325
|
id: 'number',
|
|
332
326
|
title: '#' as Platform.UIString.LocalizedString,
|
|
333
327
|
sortable: false,
|
|
334
328
|
width: '50px',
|
|
335
329
|
});
|
|
336
330
|
columns.push({
|
|
337
|
-
...columnDefaults,
|
|
338
331
|
id: 'key',
|
|
339
332
|
titleDOMFragment: this.keyColumnHeaderFragment(i18nString(UIStrings.keyString), keyPath),
|
|
340
333
|
sortable: false,
|
|
341
334
|
});
|
|
342
335
|
if (this.isIndex) {
|
|
343
336
|
columns.push({
|
|
344
|
-
...columnDefaults,
|
|
345
337
|
id: 'primary-key',
|
|
346
338
|
titleDOMFragment: this.keyColumnHeaderFragment(i18nString(UIStrings.primaryKey), this.objectStore.keyPath),
|
|
347
339
|
sortable: false,
|
|
@@ -349,7 +341,6 @@ export class IDBDataView extends UI.View.SimpleView {
|
|
|
349
341
|
}
|
|
350
342
|
const title = i18nString(UIStrings.valueString);
|
|
351
343
|
columns.push({
|
|
352
|
-
...columnDefaults,
|
|
353
344
|
id: 'value',
|
|
354
345
|
title,
|
|
355
346
|
sortable: false,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Host from '../../core/host/host.js';
|
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
7
|
+
import {createIcon} from '../../ui/kit/kit.js';
|
|
8
|
+
|
|
9
|
+
import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
|
|
10
|
+
import type {ResourcesPanel} from './ResourcesPanel.js';
|
|
11
|
+
import {WebMCPView} from './WebMCPView.js';
|
|
12
|
+
|
|
13
|
+
export class WebMCPTreeElement extends ApplicationPanelTreeElement {
|
|
14
|
+
#view?: WebMCPView;
|
|
15
|
+
|
|
16
|
+
constructor(storagePanel: ResourcesPanel) {
|
|
17
|
+
super(storagePanel, 'WebMCP', false, 'web-mcp');
|
|
18
|
+
const icon = createIcon('document');
|
|
19
|
+
this.setLeadingIcons([icon]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override get itemURL(): Platform.DevToolsPath.UrlString {
|
|
23
|
+
return 'webMcp://' as Platform.DevToolsPath.UrlString;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override onselect(selectedByUser?: boolean): boolean {
|
|
27
|
+
super.onselect(selectedByUser);
|
|
28
|
+
if (!this.#view) {
|
|
29
|
+
this.#view = new WebMCPView();
|
|
30
|
+
}
|
|
31
|
+
this.showView(this.#view);
|
|
32
|
+
Host.userMetrics.panelShown('web-mcp');
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import '../../ui/components/icon_button/icon_button.js';
|
|
6
|
+
import '../../ui/components/lists/lists.js';
|
|
7
|
+
import '../../ui/legacy/legacy.js';
|
|
8
|
+
|
|
9
|
+
import * as i18n from '../../core/i18n/i18n.js';
|
|
10
|
+
import * as SDK from '../../core/sdk/sdk.js';
|
|
11
|
+
import type * as Protocol from '../../generated/protocol.js';
|
|
12
|
+
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
|
+
import {
|
|
14
|
+
html,
|
|
15
|
+
render,
|
|
16
|
+
} from '../../ui/lit/lit.js';
|
|
17
|
+
|
|
18
|
+
import webMCPViewStyles from './webMCPView.css.js';
|
|
19
|
+
|
|
20
|
+
const UIStrings = {
|
|
21
|
+
/**
|
|
22
|
+
* @description Text for the header of the tool registry section
|
|
23
|
+
*/
|
|
24
|
+
toolRegistry: 'Available Tools',
|
|
25
|
+
/**
|
|
26
|
+
* @description Title of text to display when no tools are registered
|
|
27
|
+
*/
|
|
28
|
+
noToolsPlaceholderTitle: 'Available `WebMCP` Tools',
|
|
29
|
+
/**
|
|
30
|
+
* @description Text to display when no tools are registered
|
|
31
|
+
*/
|
|
32
|
+
noToolsPlaceholder:
|
|
33
|
+
'Registered `WebMCP` tools for this page will appear here. No tools have been registered or detected yet.',
|
|
34
|
+
/**
|
|
35
|
+
* @description Title of text to display when no calls have been made
|
|
36
|
+
*/
|
|
37
|
+
noCallsPlaceholderTitle: 'Tool Activity',
|
|
38
|
+
/**
|
|
39
|
+
* @description Text to display when no calls have been made
|
|
40
|
+
*/
|
|
41
|
+
noCallsPlaceholder: 'Start interacting with your `WebMCP` agent to see real-time tool calls and executions here.'
|
|
42
|
+
} as const;
|
|
43
|
+
const str_ = i18n.i18n.registerUIStrings('panels/application/WebMCPView.ts', UIStrings);
|
|
44
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
45
|
+
|
|
46
|
+
export interface ViewInput {
|
|
47
|
+
tools: Protocol.WebMCP.Tool[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type View = (input: ViewInput, output: object, target: HTMLElement) => void;
|
|
51
|
+
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
52
|
+
const tools = input.tools;
|
|
53
|
+
// clang-format off
|
|
54
|
+
render(html`
|
|
55
|
+
<style>${webMCPViewStyles}</style>
|
|
56
|
+
<devtools-split-view class="webmcp-view" direction="row" sidebar-position="second" name="webmcp-split-view">
|
|
57
|
+
<div slot="main" class="call-log">
|
|
58
|
+
${UI.Widget.widget(UI.EmptyWidget.EmptyWidget, {header: i18nString(UIStrings.noCallsPlaceholderTitle),
|
|
59
|
+
text: i18nString(UIStrings.noCallsPlaceholder)})}
|
|
60
|
+
</div>
|
|
61
|
+
<div slot="sidebar" class="tool-list">
|
|
62
|
+
<div class="section-title">${i18nString(UIStrings.toolRegistry)}</div>
|
|
63
|
+
${tools.length === 0 ? html`
|
|
64
|
+
${UI.Widget.widget(UI.EmptyWidget.EmptyWidget, {header: i18nString(UIStrings.noToolsPlaceholderTitle),
|
|
65
|
+
text: i18nString(UIStrings.noToolsPlaceholder)})}
|
|
66
|
+
` : html`
|
|
67
|
+
<devtools-list>
|
|
68
|
+
${tools.map(tool => html`
|
|
69
|
+
<div class="tool-item">
|
|
70
|
+
<div class="tool-name-container">
|
|
71
|
+
<div class="tool-name source-code">${tool.name}</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="tool-description">${tool.description}</div>
|
|
74
|
+
</div>
|
|
75
|
+
`)}
|
|
76
|
+
</devtools-list>
|
|
77
|
+
`}
|
|
78
|
+
</div>
|
|
79
|
+
</devtools-split-view>
|
|
80
|
+
`, target);
|
|
81
|
+
// clang-format on
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export class WebMCPView extends UI.Widget.VBox {
|
|
85
|
+
readonly #view: View;
|
|
86
|
+
|
|
87
|
+
constructor(target?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
88
|
+
super();
|
|
89
|
+
this.#view = view;
|
|
90
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.WebMCPModel.WebMCPModel, {
|
|
91
|
+
modelAdded: (model: SDK.WebMCPModel.WebMCPModel) => this.#webMCPModelAdded(model),
|
|
92
|
+
modelRemoved: (model: SDK.WebMCPModel.WebMCPModel) => this.#webMCPModelRemoved(model),
|
|
93
|
+
});
|
|
94
|
+
this.requestUpdate();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#webMCPModelAdded(model: SDK.WebMCPModel.WebMCPModel): void {
|
|
98
|
+
model.addEventListener(SDK.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
|
|
99
|
+
model.addEventListener(SDK.WebMCPModel.Events.TOOLS_REMOVED, this.requestUpdate, this);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#webMCPModelRemoved(model: SDK.WebMCPModel.WebMCPModel): void {
|
|
103
|
+
model.removeEventListener(SDK.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
|
|
104
|
+
model.removeEventListener(SDK.WebMCPModel.Events.TOOLS_REMOVED, this.requestUpdate, this);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
override performUpdate(): void {
|
|
108
|
+
const tools = SDK.TargetManager.TargetManager.instance()
|
|
109
|
+
.models(SDK.WebMCPModel.WebMCPModel)
|
|
110
|
+
.flatMap(m => m.tools.toArray())
|
|
111
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
112
|
+
this.#view(
|
|
113
|
+
{
|
|
114
|
+
tools,
|
|
115
|
+
},
|
|
116
|
+
{}, this.contentElement);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -40,6 +40,8 @@ import * as StorageBucketsTreeElement from './StorageBucketsTreeElement.js';
|
|
|
40
40
|
import * as StorageItemsToolbar from './StorageItemsToolbar.js';
|
|
41
41
|
import * as StorageView from './StorageView.js';
|
|
42
42
|
import * as TrustTokensTreeElement from './TrustTokensTreeElement.js';
|
|
43
|
+
import * as WebMCPTreeElement from './WebMCPTreeElement.js';
|
|
44
|
+
import * as WebMCPView from './WebMCPView.js';
|
|
43
45
|
|
|
44
46
|
export {
|
|
45
47
|
ApplicationPanelSidebar,
|
|
@@ -80,4 +82,6 @@ export {
|
|
|
80
82
|
StorageItemsToolbar,
|
|
81
83
|
StorageView,
|
|
82
84
|
TrustTokensTreeElement,
|
|
85
|
+
WebMCPTreeElement,
|
|
86
|
+
WebMCPView,
|
|
83
87
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The Chromium Authors
|
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
@scope to (devtools-widget > *) {
|
|
8
|
+
.webmcp-view {
|
|
9
|
+
height: 100%;
|
|
10
|
+
width: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.call-log,
|
|
14
|
+
.tool-list {
|
|
15
|
+
height: 100%;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
overflow: auto;
|
|
19
|
+
padding: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.empty-view-scroller {
|
|
23
|
+
flex: auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.section-title {
|
|
27
|
+
background-color: var(--sys-color-surface1);
|
|
28
|
+
padding: 0 5px;
|
|
29
|
+
line-height: 22px;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
align-items: center;
|
|
32
|
+
flex: none;
|
|
33
|
+
color: var(--sys-color-on-surface);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
devtools-list {
|
|
37
|
+
flex: 1 1 auto;
|
|
38
|
+
margin: var(--sys-size-4);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.tool-item {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
padding: 8px 0;
|
|
45
|
+
gap: 4px;
|
|
46
|
+
width: 100%;
|
|
47
|
+
border-bottom: 1px solid var(--sys-color-divider);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tool-name-container {
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: space-between;
|
|
53
|
+
align-items: center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tool-name.source-code {
|
|
57
|
+
color: var(--sys-color-token-string);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.tool-description {
|
|
61
|
+
color: var(--sys-color-on-surface);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
}
|
|
@@ -16,6 +16,18 @@ export class StylesAiCodeCompletionProvider {
|
|
|
16
16
|
#aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
|
|
17
17
|
#aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
|
|
18
18
|
|
|
19
|
+
getCompletionHint?: () => string | null;
|
|
20
|
+
setAiAutoCompletion?: (args: {
|
|
21
|
+
text: string,
|
|
22
|
+
from: number,
|
|
23
|
+
startTime: number,
|
|
24
|
+
onImpression: (rpcGlobalId: Host.AidaClient.RpcGlobalId, latency: number, sampleId?: number) => void,
|
|
25
|
+
clearCachedRequest: () => void,
|
|
26
|
+
citations: Host.AidaClient.Citation[],
|
|
27
|
+
rpcGlobalId?: Host.AidaClient.RpcGlobalId,
|
|
28
|
+
sampleId?: number,
|
|
29
|
+
}|null) => void;
|
|
30
|
+
|
|
19
31
|
#boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
|
|
20
32
|
|
|
21
33
|
private constructor(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig) {
|
|
@@ -123,7 +135,7 @@ export class StylesAiCodeCompletionProvider {
|
|
|
123
135
|
return;
|
|
124
136
|
}
|
|
125
137
|
|
|
126
|
-
this
|
|
138
|
+
this.setAiAutoCompletion?.({
|
|
127
139
|
text: currentPropertyString + aidaSuggestion.suggestionText,
|
|
128
140
|
from: cursorPosition,
|
|
129
141
|
rpcGlobalId: aidaSuggestion.rpcGlobalId,
|
|
@@ -131,6 +143,7 @@ export class StylesAiCodeCompletionProvider {
|
|
|
131
143
|
startTime,
|
|
132
144
|
clearCachedRequest: this.clearCache.bind(this),
|
|
133
145
|
onImpression: this.#aiCodeCompletion.registerUserImpression.bind(this.#aiCodeCompletion),
|
|
146
|
+
citations: aidaSuggestion.citations,
|
|
134
147
|
});
|
|
135
148
|
}
|
|
136
149
|
|
|
@@ -206,7 +219,7 @@ export class StylesAiCodeCompletionProvider {
|
|
|
206
219
|
return null;
|
|
207
220
|
}
|
|
208
221
|
|
|
209
|
-
const completionHint = this
|
|
222
|
+
const completionHint = this.getCompletionHint?.();
|
|
210
223
|
if (!completionHint) {
|
|
211
224
|
return response.generatedSamples[0];
|
|
212
225
|
}
|
|
@@ -216,4 +229,12 @@ export class StylesAiCodeCompletionProvider {
|
|
|
216
229
|
clearCache(): void {
|
|
217
230
|
this.#aiCodeCompletion?.clearCachedRequest();
|
|
218
231
|
}
|
|
232
|
+
|
|
233
|
+
onSuggestionAccepted(
|
|
234
|
+
citations: Host.AidaClient.Citation[], rpcGlobalId?: Host.AidaClient.RpcGlobalId, sampleId?: number): void {
|
|
235
|
+
this.#aiCodeCompletionConfig?.onSuggestionAccepted(citations);
|
|
236
|
+
if (rpcGlobalId) {
|
|
237
|
+
this.#aiCodeCompletion?.registerUserAcceptance(rpcGlobalId, sampleId);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
219
240
|
}
|