chrome-devtools-frontend 1.0.1603822 → 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.
Files changed (89) hide show
  1. package/AUTHORS +1 -0
  2. package/front_end/Tests.js +23 -7
  3. package/front_end/core/host/AidaClient.ts +153 -525
  4. package/front_end/core/host/AidaClientTypes.ts +470 -0
  5. package/front_end/core/host/AidaGcaTranslation.ts +231 -122
  6. package/front_end/core/host/GcaClient.ts +112 -0
  7. package/front_end/core/host/GcaTypes.ts +107 -155
  8. package/front_end/core/host/UserMetrics.ts +0 -1
  9. package/front_end/core/host/host.ts +2 -0
  10. package/front_end/core/protocol_client/DevToolsCDPConnection.ts +1 -1
  11. package/front_end/core/root/ExperimentNames.ts +0 -1
  12. package/front_end/core/root/Runtime.ts +5 -0
  13. package/front_end/core/sdk/AutofillModel.ts +3 -2
  14. package/front_end/core/sdk/CSSModel.ts +3 -4
  15. package/front_end/core/sdk/CSSProperty.ts +1 -1
  16. package/front_end/core/sdk/ConsoleModel.ts +15 -13
  17. package/front_end/core/sdk/CookieModel.ts +5 -4
  18. package/front_end/core/sdk/DOMDebuggerModel.ts +20 -14
  19. package/front_end/core/sdk/DOMModel.ts +24 -22
  20. package/front_end/core/sdk/DebuggerModel.ts +21 -36
  21. package/front_end/core/sdk/EmulationModel.ts +21 -23
  22. package/front_end/core/sdk/EventBreakpointsModel.ts +16 -6
  23. package/front_end/core/sdk/HeapProfilerModel.ts +4 -7
  24. package/front_end/core/sdk/IsolateManager.ts +12 -7
  25. package/front_end/core/sdk/NetworkManager.ts +35 -28
  26. package/front_end/core/sdk/OverlayModel.ts +10 -10
  27. package/front_end/core/sdk/PreloadingModel.ts +3 -4
  28. package/front_end/core/sdk/ServiceWorkerManager.ts +2 -3
  29. package/front_end/core/sdk/SourceMapScopesInfo.ts +93 -63
  30. package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +1 -3
  31. package/front_end/entrypoints/heap_snapshot_worker/AllocationProfile.ts +22 -48
  32. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +1 -1
  33. package/front_end/entrypoints/main/MainImpl.ts +0 -4
  34. package/front_end/generated/InspectorBackendCommands.ts +4 -0
  35. package/front_end/generated/SupportedCSSProperties.js +68 -2
  36. package/front_end/generated/protocol-mapping.d.ts +7 -0
  37. package/front_end/generated/protocol-proxy-api.d.ts +14 -0
  38. package/front_end/generated/protocol.ts +22 -0
  39. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +31 -17
  40. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +1 -1
  41. package/front_end/models/bindings/TempFile.ts +1 -4
  42. package/front_end/models/emulation/DeviceModeModel.ts +21 -26
  43. package/front_end/models/extensions/ExtensionAPI.ts +1 -1
  44. package/front_end/models/heap_snapshot_model/HeapSnapshotModel.ts +34 -47
  45. package/front_end/models/javascript_metadata/NativeFunctions.js +6 -6
  46. package/front_end/models/trace/insights/LCPDiscovery.ts +14 -6
  47. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +85 -31
  48. package/front_end/panels/ai_assistance/components/ChatMessage.ts +50 -40
  49. package/front_end/panels/ai_assistance/components/ChatView.ts +3 -2
  50. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +35 -7
  51. package/front_end/panels/ai_assistance/components/chatMessage.css +5 -1
  52. package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -0
  53. package/front_end/panels/application/IndexedDBViews.ts +0 -9
  54. package/front_end/panels/application/WebMCPTreeElement.ts +35 -0
  55. package/front_end/panels/application/WebMCPView.ts +118 -0
  56. package/front_end/panels/application/application.ts +4 -0
  57. package/front_end/panels/application/webMCPView.css +65 -0
  58. package/front_end/panels/common/aiCodeCompletionSummaryToolbar.css +1 -1
  59. package/front_end/panels/console/consoleView.css +4 -0
  60. package/front_end/panels/elements/ElementsTreeElement.ts +91 -13
  61. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +23 -2
  62. package/front_end/panels/elements/StylesSidebarPane.ts +85 -20
  63. package/front_end/panels/elements/elementsPanel.css +14 -0
  64. package/front_end/panels/elements/elementsTreeOutline.css +18 -0
  65. package/front_end/panels/network/NetworkLogViewColumns.ts +3 -3
  66. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +15 -19
  67. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +4 -2
  68. package/front_end/panels/profiler/HeapSnapshotProxy.ts +12 -16
  69. package/front_end/panels/profiler/HeapSnapshotView.ts +3 -10
  70. package/front_end/panels/profiler/ProfilesPanel.ts +5 -7
  71. package/front_end/panels/recorder/components/recordingView.css +0 -6
  72. package/front_end/panels/sensors/SensorsView.ts +28 -61
  73. package/front_end/panels/timeline/EventsTimelineTreeView.ts +2 -2
  74. package/front_end/panels/timeline/ThirdPartyTreeView.ts +35 -36
  75. package/front_end/panels/timeline/TimelineDetailsView.ts +47 -41
  76. package/front_end/panels/timeline/TimelinePanel.ts +1 -3
  77. package/front_end/panels/timeline/TimelineTreeView.ts +13 -13
  78. package/front_end/panels/timeline/components/LiveMetricsView.ts +852 -824
  79. package/front_end/panels/timeline/components/liveMetricsView.css +4 -0
  80. package/front_end/panels/timeline/thirdPartyTreeView.css +2 -2
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/ui/components/buttons/button.css +2 -2
  83. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +0 -11
  84. package/front_end/ui/legacy/InspectorDrawerView.ts +206 -0
  85. package/front_end/ui/legacy/InspectorView.ts +41 -91
  86. package/front_end/ui/legacy/ViewManager.ts +14 -5
  87. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +5 -0
  88. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -1
  89. package/package.json +1 -1
@@ -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
+ }
@@ -49,7 +49,7 @@
49
49
  border-left: var(--sys-size-1) solid var(--sys-color-divider);
50
50
  }
51
51
 
52
- @media (width < 545px) {
52
+ @container (width < 545px) {
53
53
  &.has-disclaimer.has-recitation-notice {
54
54
  height: 46px;
55
55
  flex-direction: column;
@@ -633,6 +633,10 @@
633
633
  }
634
634
  }
635
635
 
636
+ .ai-code-completion-summary-toolbar-container {
637
+ container-type: inline-size;
638
+ }
639
+
636
640
  @media (forced-colors: active) {
637
641
  .console-message-expand-icon,
638
642
  .console-warning-level .expand-group-icon {
@@ -78,9 +78,25 @@ const {animateOn} = UI.UIUtils;
78
78
 
79
79
  const UIStrings = {
80
80
  /**
81
- * @description Title for Ad adorner. This iframe is marked as advertisement frame.
81
+ * @description Title for Ad adorner. This element is marked as advertisement element.
82
82
  */
83
- thisFrameWasIdentifiedAsAnAd: 'This frame was identified as an ad frame',
83
+ thisElementWasIdentifiedAsAnAd: 'This element was identified as an ad',
84
+ /**
85
+ * @description Title of a section in the Ad adorner tooltip. Lists the ad script(s) responsible for generating this element.
86
+ */
87
+ creatorAdScriptAncestry: 'Creator ad script ancestry',
88
+ /**
89
+ * @description Title of a section in the Ad adorner tooltip. The filter list rule that flagged the root script in 'Creator ad script ancestry' as an ad.
90
+ */
91
+ rootScriptFilterListRule: 'Root script filter list rule',
92
+ /**
93
+ * @description Title of a section in the Ad adorner tooltip. The filter list rule that flagged the element's current resource.
94
+ */
95
+ filterListRule: 'Filter list rule',
96
+ /**
97
+ * @description Title of a section in the Ad adorner tooltip. This element was identified as an ad, but no provenance data is available.
98
+ */
99
+ noProvenanceAvailable: 'No provenance data is available',
84
100
  /**
85
101
  * @description A context menu item in the Elements panel. Force is used as a verb, indicating intention to make the state change.
86
102
  */
@@ -399,7 +415,10 @@ export interface ViewInput {
399
415
  gridAdornerActive: boolean;
400
416
  popoverAdornerActive: boolean;
401
417
 
402
- showAdAdorner: boolean;
418
+ adProvenance?: Protocol.Network.AdProvenance;
419
+ target?: SDK.Target.Target;
420
+ adTooltipId: string;
421
+
403
422
  showContainerAdorner: boolean;
404
423
  containerType?: string;
405
424
  showFlexAdorner: boolean;
@@ -851,8 +870,66 @@ function renderTag(
851
870
  attributes.map(attr => html` ${renderAttribute(attr, updateRecord, false, node)}`)}&gt;</span>\u200B`;
852
871
  }
853
872
 
873
+ function maybeRenderAdAdorner(input: ViewInput): Lit.TemplateResult|typeof nothing {
874
+ if (!input.adProvenance) {
875
+ return nothing;
876
+ }
877
+
878
+ // clang-format off
879
+ return html`
880
+ <devtools-adorner
881
+ aria-describedby=${input.adTooltipId}
882
+ aria-label=${i18nString(UIStrings.thisElementWasIdentifiedAsAnAd)}
883
+ .name=${ElementsComponents.AdornerManager.RegisteredAdorners.AD}
884
+ jslog=${VisualLogging.adorner(ElementsComponents.AdornerManager.RegisteredAdorners.AD)}
885
+ ${adornerRef()}>
886
+ <span>${ElementsComponents.AdornerManager.RegisteredAdorners.AD}</span>
887
+ </devtools-adorner>
888
+
889
+ <!--
890
+ Prevent the copy event from bubbling up to the Elements tree outline. Otherwise, DevTools
891
+ copies the underlying DOM node's HTML instead of the user's highlighted text.
892
+ -->
893
+ <devtools-tooltip id=${input.adTooltipId} variant=rich @copy=${(e: Event) => e.stopPropagation()}>
894
+ <div class="ad-provenance-tooltip">
895
+ ${input.adProvenance.filterlistRule ? html`
896
+ <div class="ad-provenance-tooltip-title">${i18nString(UIStrings.filterListRule)}</div>
897
+ <div class="ad-provenance-tooltip-content">${input.adProvenance.filterlistRule}</div>
898
+ ` : nothing}
899
+
900
+ ${input.adProvenance.adScriptAncestry && input.target ? html`
901
+ <div class="ad-provenance-tooltip-title">${i18nString(UIStrings.creatorAdScriptAncestry)}</div>
902
+ <div class="ad-provenance-tooltip-content">
903
+ ${input.adProvenance.adScriptAncestry.ancestryChain.map(script => html`
904
+ <div>
905
+ ${UI.Widget.widget(Components.Linkifier.ScriptLocationLink, {
906
+ target: input.target,
907
+ scriptId: script.scriptId,
908
+ options: { jslogContext: 'ad-script' },
909
+ })}
910
+ </div>
911
+ `)}
912
+ </div>
913
+
914
+ ${input.adProvenance.adScriptAncestry.rootScriptFilterlistRule ? html`
915
+ <div class="ad-provenance-tooltip-title">${i18nString(UIStrings.rootScriptFilterListRule)}</div>
916
+ <div class="ad-provenance-tooltip-content">
917
+ ${input.adProvenance.adScriptAncestry.rootScriptFilterlistRule}
918
+ </div>
919
+ ` : nothing}
920
+ ` : nothing}
921
+
922
+ ${!input.adProvenance.adScriptAncestry && !input.adProvenance.filterlistRule ? html`
923
+ <div class="ad-provenance-tooltip-title">${i18nString(UIStrings.noProvenanceAvailable)}</div>
924
+ ` : nothing}
925
+ </div>
926
+ </devtools-tooltip>
927
+ `;
928
+ // clang-format on
929
+ }
930
+
854
931
  export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
855
- const hasAdorners = input.showAdAdorner || input.showContainerAdorner || input.showFlexAdorner ||
932
+ const hasAdorners = !!input.adProvenance || input.showContainerAdorner || input.showFlexAdorner ||
856
933
  input.showGridAdorner || input.showGridLanesAdorner || input.showMediaAdorner || input.showPopoverAdorner ||
857
934
  input.showTopLayerAdorner || input.showViewSourceAdorner || input.showScrollAdorner ||
858
935
  input.showScrollSnapAdorner || input.showSlotAdorner || input.showStartingStyleAdorner;
@@ -888,13 +965,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
888
965
  </div>` : nothing}
889
966
  </div>
890
967
  ${hasAdorners ? html`<div class="adorner-container ${!hasAdorners ? 'hidden' : ''}">
891
- ${input.showAdAdorner ? html`<devtools-adorner
892
- aria-label=${i18nString(UIStrings.thisFrameWasIdentifiedAsAnAd)}
893
- .name=${ElementsComponents.AdornerManager.RegisteredAdorners.AD}
894
- jslog=${VisualLogging.adorner(ElementsComponents.AdornerManager.RegisteredAdorners.AD)}
895
- ${adornerRef()}>
896
- <span>${ElementsComponents.AdornerManager.RegisteredAdorners.AD}</span>
897
- </devtools-adorner>` : nothing}
968
+ ${maybeRenderAdAdorner(input)}
898
969
  ${input.showViewSourceAdorner ? html`<devtools-adorner
899
970
  .name=${ElementsComponents.AdornerManager.RegisteredAdorners.VIEW_SOURCE}
900
971
  jslog=${VisualLogging.adorner(ElementsComponents.AdornerManager.RegisteredAdorners.VIEW_SOURCE)}
@@ -1102,6 +1173,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1102
1173
  #descendantDecorations: Decoration[] = [];
1103
1174
  #decorationsTooltip = '';
1104
1175
 
1176
+ static #adTooltipIdCounter = 0;
1177
+ #adTooltipId = `ad-tooltip-${++ElementsTreeElement.#adTooltipIdCounter}`;
1178
+
1105
1179
  #updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null = null;
1106
1180
 
1107
1181
  // Used to add the content to TreeElement's title element.
@@ -1230,7 +1304,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1230
1304
  onExpand: () => this.expand(),
1231
1305
 
1232
1306
  containerAdornerActive: this.#containerAdornerActive,
1233
- showAdAdorner: this.nodeInternal.isAdRelatedNode(),
1307
+ adProvenance: this.nodeInternal.adProvenance(),
1308
+ adTooltipId: this.#adTooltipId,
1309
+ target: this.nodeInternal.domModel().target(),
1234
1310
  showContainerAdorner: Boolean(this.#layout?.containerType) && !this.isClosingTag(),
1235
1311
  containerType: this.#layout?.containerType,
1236
1312
  showFlexAdorner: Boolean(this.#layout?.isFlex) && !this.isClosingTag(),
@@ -1565,7 +1641,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1565
1641
  onHighlightSearchResults: () => {},
1566
1642
  onExpand: () => {},
1567
1643
  containerAdornerActive: false,
1568
- showAdAdorner: false,
1644
+ adProvenance: undefined,
1645
+ target: undefined,
1646
+ adTooltipId: '',
1569
1647
  showContainerAdorner: false,
1570
1648
  containerType: this.#layout?.containerType,
1571
1649
  showFlexAdorner: false,
@@ -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.#aiCodeCompletionConfig?.setAiAutoCompletion?.({
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.#aiCodeCompletionConfig?.getCompletionHint?.();
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
  }
@@ -169,6 +169,10 @@ const HIGHLIGHTABLE_PROPERTIES = [
169
169
  {mode: 'flexibility', properties: ['flex', 'flex-basis', 'flex-grow', 'flex-shrink']},
170
170
  ];
171
171
 
172
+ const DISCLAIMER_TOOLTIP_ID = 'styles-ai-code-completion-disclaimer-tooltip';
173
+ const SPINNER_TOOLTIP_ID = 'styles-ai-code-completion-spinner-tooltip';
174
+ const CITATIONS_TOOLTIP_ID = 'styles-ai-code-completion-citations-tooltip';
175
+
172
176
  export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventTypes, typeof ElementsSidebarPane>(
173
177
  ElementsSidebarPane) implements StylesContainer {
174
178
  private matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles|null = null;
@@ -214,6 +218,11 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
214
218
  #updateAbortController?: AbortController;
215
219
  #updateComputedStylesAbortController?: AbortController;
216
220
 
221
+ aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
222
+ aiCodeCompletionProvider?: StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider;
223
+ #aiCodeCompletionSummaryToolbarContainer?: HTMLElement;
224
+ #aiCodeCompletionSummaryToolbar?: PanelsCommon.AiCodeCompletionSummaryToolbar;
225
+
217
226
  constructor(computedStyleModel: ComputedStyle.ComputedStyleModel.ComputedStyleModel) {
218
227
  super(computedStyleModel, {delegatesFocus: true});
219
228
  this.setMinimumSize(96, 26);
@@ -259,6 +268,26 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
259
268
  this.#scheduleResetUpdateIfNotEditing();
260
269
  }
261
270
  });
271
+
272
+ const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
273
+ if (AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesEnabled(devtoolsLocale.locale)) {
274
+ this.aiCodeCompletionConfig = {
275
+ completionContext: {},
276
+ generationContext: {},
277
+ onFeatureEnabled: () => {
278
+ this.#createAiCodeCompletionSummaryToolbar();
279
+ },
280
+ onFeatureDisabled: () => {
281
+ this.#cleanupAiCodeCompletion();
282
+ },
283
+ onSuggestionAccepted: this.#onAiCodeCompletionSuggestionAccepted.bind(this),
284
+ onRequestTriggered: this.#onAiCodeCompletionRequestTriggered.bind(this),
285
+ onResponseReceived: this.#onAiCodeCompletionResponseReceived.bind(this),
286
+ panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
287
+ };
288
+ this.aiCodeCompletionProvider =
289
+ StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider.createInstance(this.aiCodeCompletionConfig);
290
+ }
262
291
  }
263
292
 
264
293
  get webCustomData(): WebCustomData|undefined {
@@ -1528,6 +1557,45 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1528
1557
 
1529
1558
  return button;
1530
1559
  }
1560
+
1561
+ #cleanupAiCodeCompletion(): void {
1562
+ this.#aiCodeCompletionSummaryToolbarContainer?.remove();
1563
+ this.#aiCodeCompletionSummaryToolbarContainer = undefined;
1564
+ this.#aiCodeCompletionSummaryToolbar = undefined;
1565
+ }
1566
+
1567
+ #createAiCodeCompletionSummaryToolbar(): void {
1568
+ if (this.#aiCodeCompletionSummaryToolbar) {
1569
+ return;
1570
+ }
1571
+ this.#aiCodeCompletionSummaryToolbar = new PanelsCommon.AiCodeCompletionSummaryToolbar({
1572
+ citationsTooltipId: CITATIONS_TOOLTIP_ID,
1573
+ disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
1574
+ spinnerTooltipId: SPINNER_TOOLTIP_ID,
1575
+ panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
1576
+ });
1577
+ const containingPane = this.contentElement.enclosingNodeOrSelfWithClass('style-panes-wrapper') as HTMLElement;
1578
+ this.#aiCodeCompletionSummaryToolbarContainer =
1579
+ containingPane.createChild('div', 'ai-code-completion-summary-toolbar-container');
1580
+ this.#aiCodeCompletionSummaryToolbarContainer.role = 'toolbar';
1581
+ this.#aiCodeCompletionSummaryToolbar.show(this.#aiCodeCompletionSummaryToolbarContainer, undefined, true);
1582
+ }
1583
+
1584
+ #onAiCodeCompletionSuggestionAccepted(citations: Host.AidaClient.Citation[]): void {
1585
+ if (!this.#aiCodeCompletionSummaryToolbar || citations.length === 0) {
1586
+ return;
1587
+ }
1588
+ const citationsUri = citations.map(citation => citation.uri).filter((uri): uri is string => Boolean(uri));
1589
+ this.#aiCodeCompletionSummaryToolbar.updateCitations(citationsUri);
1590
+ }
1591
+
1592
+ #onAiCodeCompletionRequestTriggered(): void {
1593
+ this.#aiCodeCompletionSummaryToolbar?.setLoading(true);
1594
+ }
1595
+
1596
+ #onAiCodeCompletionResponseReceived(): void {
1597
+ this.#aiCodeCompletionSummaryToolbar?.setLoading(false);
1598
+ }
1531
1599
  }
1532
1600
 
1533
1601
  export const enum Events {
@@ -1757,8 +1825,9 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1757
1825
  private treeElement: StylePropertyTreeElement;
1758
1826
  private isEditingName: boolean;
1759
1827
  private readonly cssVariables: string[];
1760
- aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
1761
1828
  aiCodeCompletionProvider?: StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider;
1829
+ private activeAiSuggestionInfo?:
1830
+ {citations: Host.AidaClient.Citation[], rpcGlobalId?: Host.AidaClient.RpcGlobalId, sampleId?: number};
1762
1831
 
1763
1832
  #debouncedTriggerAiCodeCompletion = Common.Debouncer.debounce(() => {
1764
1833
  void this.triggerAiCodeCompletion();
@@ -1822,25 +1891,13 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1822
1891
  }
1823
1892
  }
1824
1893
 
1825
- const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
1826
- if (AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesEnabled(devtoolsLocale.locale)) {
1827
- this.aiCodeCompletionConfig = {
1828
- completionContext: {},
1829
- generationContext: {},
1830
- onFeatureEnabled: () => {},
1831
- onFeatureDisabled: () => {},
1832
- onSuggestionAccepted: () => {},
1833
- onRequestTriggered: () => {},
1834
- onResponseReceived: () => {},
1835
- panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
1836
- getCompletionHint: this.getCompletionHint.bind(this),
1837
- getCurrentText: () => {
1838
- return this.text();
1839
- },
1840
- setAiAutoCompletion: this.setAiAutoCompletion.bind(this),
1841
- };
1842
- this.aiCodeCompletionProvider =
1843
- StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider.createInstance(this.aiCodeCompletionConfig);
1894
+ const stylesContainer = this.treeElement.stylesContainer();
1895
+ if (stylesContainer instanceof StylesSidebarPane) {
1896
+ this.aiCodeCompletionProvider = stylesContainer.aiCodeCompletionProvider;
1897
+ if (this.aiCodeCompletionProvider) {
1898
+ this.aiCodeCompletionProvider.getCompletionHint = this.getCompletionHint.bind(this);
1899
+ this.aiCodeCompletionProvider.setAiAutoCompletion = this.setAiAutoCompletion.bind(this);
1900
+ }
1844
1901
  }
1845
1902
  }
1846
1903
 
@@ -2192,11 +2249,13 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2192
2249
  startTime: number,
2193
2250
  onImpression: (rpcGlobalId: Host.AidaClient.RpcGlobalId, latency: number, sampleId?: number) => void,
2194
2251
  clearCachedRequest: () => void,
2252
+ citations: Host.AidaClient.Citation[],
2195
2253
  rpcGlobalId?: Host.AidaClient.RpcGlobalId,
2196
2254
  sampleId?: number,
2197
2255
  }|null): void {
2198
2256
  if (!args) {
2199
2257
  this.treeElement.section().activeAiSuggestion = undefined;
2258
+ this.activeAiSuggestionInfo = undefined;
2200
2259
  return;
2201
2260
  }
2202
2261
 
@@ -2207,6 +2266,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2207
2266
  clearCachedRequest: args.clearCachedRequest,
2208
2267
  cssProperty: this.treeElement.property,
2209
2268
  };
2269
+ this.activeAiSuggestionInfo = {citations: args.citations, rpcGlobalId: args.rpcGlobalId, sampleId: args.sampleId};
2210
2270
  const latency = performance.now() - args.startTime;
2211
2271
  if (args.rpcGlobalId) {
2212
2272
  args.onImpression(args.rpcGlobalId, latency, args.sampleId);
@@ -2305,6 +2365,11 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2305
2365
 
2306
2366
  async commitAiSuggestion(): Promise<void> {
2307
2367
  await this.treeElement.section().commitActiveAiSuggestion();
2368
+ if (this.activeAiSuggestionInfo) {
2369
+ this.aiCodeCompletionProvider?.onSuggestionAccepted(
2370
+ this.activeAiSuggestionInfo.citations, this.activeAiSuggestionInfo.rpcGlobalId,
2371
+ this.activeAiSuggestionInfo.sampleId);
2372
+ }
2308
2373
  // Clear state and return
2309
2374
  this.setAiAutoCompletion(null);
2310
2375
  }
@@ -47,6 +47,20 @@
47
47
  border-bottom: 1px solid var(--sys-color-divider);
48
48
  }
49
49
 
50
+ .style-panes-wrapper > div:has(+ .ai-code-completion-summary-toolbar-container) {
51
+ border-bottom: 0;
52
+ }
53
+
54
+ .style-panes-wrapper .ai-code-completion-summary-toolbar-container {
55
+ container-type: inline-size;
56
+ flex-shrink: 0;
57
+ overflow: hidden;
58
+ position: fixed;
59
+ bottom: 0;
60
+ background-color: var(--sys-color-cdt-base-container);
61
+ width: 100%;
62
+ }
63
+
50
64
  #elements-content:not(.elements-wrap) > div {
51
65
  display: inline-block;
52
66
  min-width: 100%;
@@ -384,3 +384,21 @@ li.hovered:not(.always-parent) + ol.children:not(.shadow-root) {
384
384
  background-position: bottom;
385
385
  padding-bottom: 1px;
386
386
  }
387
+
388
+ /* Ad Provenance Tooltip */
389
+ .ad-provenance-tooltip {
390
+ user-select: text;
391
+ }
392
+
393
+ .ad-provenance-tooltip-title {
394
+ color: var(--sys-color-on-surface-subtle);
395
+ margin-top: 4px;
396
+ }
397
+
398
+ .ad-provenance-tooltip-title:first-child {
399
+ margin-top: 0;
400
+ }
401
+
402
+ .ad-provenance-tooltip-content {
403
+ padding-left: 16px;
404
+ }
@@ -236,13 +236,13 @@ export class NetworkLogViewColumns {
236
236
  const title = columnConfig.title instanceof Function ? columnConfig.title() : columnConfig.title;
237
237
  return {
238
238
  id: columnConfig.id,
239
- title,
239
+ title: title as Common.UIString.LocalizedString,
240
240
  sortable: columnConfig.sortable,
241
241
  align: columnConfig.align,
242
242
  nonSelectable: columnConfig.nonSelectable,
243
243
  weight: columnConfig.weight,
244
244
  allowInSortByEvenWhenHidden: columnConfig.allowInSortByEvenWhenHidden,
245
- } as DataGrid.DataGrid.ColumnDescriptor;
245
+ };
246
246
  }
247
247
 
248
248
  wasShown(): void {
@@ -1251,7 +1251,7 @@ export interface Descriptor {
1251
1251
  hideableGroup: string|null;
1252
1252
  nonSelectable: boolean;
1253
1253
  sortable: boolean;
1254
- align?: string|null;
1254
+ align?: DataGrid.DataGrid.Align|null;
1255
1255
  isResponseHeader: boolean;
1256
1256
  isRequestHeader: boolean;
1257
1257
  sortingFunction: (arg0: NetworkNode, arg1: NetworkNode) => number | undefined;