chrome-devtools-frontend 1.0.1625854 → 1.0.1626840

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 (62) hide show
  1. package/front_end/core/host/AidaClient.ts +1 -1
  2. package/front_end/core/host/AidaClientTypes.ts +2 -0
  3. package/front_end/core/host/AidaGcaTranslation.ts +2 -2
  4. package/front_end/core/host/UserMetrics.ts +11 -6
  5. package/front_end/core/root/ExperimentNames.ts +1 -1
  6. package/front_end/core/sdk/CSSMetadata.ts +60 -55
  7. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +11 -2
  8. package/front_end/core/sdk/HeapProfilerModel.ts +1 -11
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -1
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -1
  11. package/front_end/entrypoints/main/MainImpl.ts +4 -29
  12. package/front_end/generated/InspectorBackendCommands.ts +3 -2
  13. package/front_end/generated/protocol-mapping.d.ts +5 -0
  14. package/front_end/generated/protocol-proxy-api.d.ts +4 -0
  15. package/front_end/generated/protocol.ts +21 -0
  16. package/front_end/models/ai_assistance/StorageItem.ts +16 -0
  17. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
  18. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +11 -5
  19. package/front_end/models/ai_assistance/agents/StorageAgent.ts +82 -0
  20. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +9 -37
  22. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +26 -3
  23. package/front_end/models/javascript_metadata/NativeFunctions.js +0 -4
  24. package/front_end/models/lighthouse/RunTypes.ts +1 -1
  25. package/front_end/models/web_mcp/WebMCPModel.ts +11 -2
  26. package/front_end/models/workspace/UISourceCode.ts +0 -1
  27. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +6 -1
  28. package/front_end/panels/ai_assistance/components/ChatMessage.ts +4 -3
  29. package/front_end/panels/application/WebMCPView.ts +74 -16
  30. package/front_end/panels/application/webMCPView.css +13 -2
  31. package/front_end/panels/common/AiCodeCompletionTeaser.ts +9 -0
  32. package/front_end/panels/elements/StandaloneStylesContainer.ts +3 -0
  33. package/front_end/panels/elements/StylePropertyTreeElement.ts +35 -2
  34. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -1
  35. package/front_end/panels/elements/StylesContainer.ts +1 -0
  36. package/front_end/panels/elements/StylesSidebarPane.ts +10 -4
  37. package/front_end/panels/emulation/DeviceModeToolbar.ts +7 -3
  38. package/front_end/panels/lighthouse/LighthouseController.ts +18 -0
  39. package/front_end/panels/profiler/profiler-meta.ts +0 -71
  40. package/front_end/panels/profiler/profiler.ts +0 -2
  41. package/front_end/panels/sources/ProfilePlugin.ts +12 -65
  42. package/front_end/panels/sources/ScopeChainSidebarPane.ts +5 -1
  43. package/front_end/panels/sources/UISourceCodeFrame.ts +1 -2
  44. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +179 -145
  45. package/front_end/panels/timeline/timelineTreeView.css +2 -2
  46. package/front_end/third_party/chromium/README.chromium +1 -1
  47. package/front_end/third_party/lighthouse/README.chromium +2 -2
  48. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +4213 -4208
  49. package/front_end/third_party/lighthouse/locales/en-US.json +26 -8
  50. package/front_end/third_party/lighthouse/locales/en-XL.json +26 -8
  51. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
  52. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +7 -1
  53. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +8 -1
  54. package/front_end/ui/components/tooltips/Tooltip.ts +3 -3
  55. package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +0 -56
  56. package/front_end/ui/legacy/components/perf_ui/perf_ui-meta.ts +0 -35
  57. package/front_end/ui/legacy/components/perf_ui/perf_ui.ts +0 -2
  58. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  59. package/package.json +1 -1
  60. package/front_end/panels/profiler/LiveHeapProfileView.ts +0 -405
  61. package/front_end/panels/profiler/liveHeapProfile.css +0 -22
  62. package/front_end/ui/legacy/components/perf_ui/LiveHeapProfile.ts +0 -113
@@ -14,6 +14,7 @@ import * as NetworkAgent from './agents/NetworkAgent.js';
14
14
  import * as PatchAgent from './agents/PatchAgent.js';
15
15
  import * as PerformanceAgent from './agents/PerformanceAgent.js';
16
16
  import * as PerformanceAnnotationsAgent from './agents/PerformanceAnnotationsAgent.js';
17
+ import * as StorageAgent from './agents/StorageAgent.js';
17
18
  import * as StylingAgent from './agents/StylingAgent.js';
18
19
  import * as AiConversation from './AiConversation.js';
19
20
  import * as AiHistoryStorage from './AiHistoryStorage.js';
@@ -33,6 +34,7 @@ import * as Injected from './injected.js';
33
34
  import * as AICallTree from './performance/AICallTree.js';
34
35
  import * as AIContext from './performance/AIContext.js';
35
36
  import * as AIQueries from './performance/AIQueries.js';
37
+ import * as StorageItem from './StorageItem.js';
36
38
 
37
39
  export {
38
40
  AccessibilityAgent,
@@ -64,6 +66,8 @@ export {
64
66
  PerformanceAnnotationsAgent,
65
67
  PerformanceInsightFormatter,
66
68
  PerformanceTraceFormatter,
69
+ StorageAgent,
70
+ StorageItem,
67
71
  StylingAgent,
68
72
  UnitFormatters,
69
73
  };
@@ -332,33 +332,21 @@ IMPORTANT: Never show eventKey to the user.
332
332
 
333
333
  The following are markdown block(s) of code that ran in the page, each representing a separate function. <FUNCTION_START> and <FUNCTION_END> marks the exact function declaration, and everything outside that is provided for additional context. Comments at the end of each line indicate the runtime performance cost of that code. Do not show the user the function markers or the additional context.
334
334
 
335
- Here are 3 relevant functions:
335
+ Here is the first line of 3 relevant functions:
336
336
 
337
- (anonymous) @ https://s.yimg.com/aaq/prebid/prebid-2.0.js:12:53984. With added context, chunk is from 9:0 to 18:0
337
+ (anonymous) @ https://s.yimg.com/aaq/prebid/prebid-2.0.js:12:53984
338
338
  ```
339
- // context ...
340
-
341
339
  () => { /* some code from https://s.yimg.com/aaq/prebid/prebid-2.0.js... */ }
342
-
343
- // context ...
344
340
  ```
345
341
 
346
- (anonymous) @ https://s.yimg.com/aaq/f10d509c/d1irmdsmbztlvx.js:0:43042. With added context, chunk is from 0:0 to 6:0
342
+ (anonymous) @ https://s.yimg.com/aaq/f10d509c/d1irmdsmbztlvx.js:0:43042
347
343
  ```
348
- // context ...
349
-
350
344
  () => { /* some code from https://s.yimg.com/aaq/f10d509c/d1irmdsmbztlvx.js... */ }
351
-
352
- // context ...
353
345
  ```
354
346
 
355
- (anonymous) @ https://s.yimg.com/aaq/prebid/prebid-2.0.js:12:15854. With added context, chunk is from 9:0 to 18:0
347
+ (anonymous) @ https://s.yimg.com/aaq/prebid/prebid-2.0.js:12:15854
356
348
  ```
357
- // context ...
358
-
359
349
  () => { /* some code from https://s.yimg.com/aaq/prebid/prebid-2.0.js... */ }
360
-
361
- // context ...
362
350
  ```
363
351
 
364
352
  # Bottom-up main thread summary
@@ -1019,42 +1007,26 @@ IMPORTANT: Never show eventKey to the user.
1019
1007
 
1020
1008
  The following are markdown block(s) of code that ran in the page, each representing a separate function. <FUNCTION_START> and <FUNCTION_END> marks the exact function declaration, and everything outside that is provided for additional context. Comments at the end of each line indicate the runtime performance cost of that code. Do not show the user the function markers or the additional context.
1021
1009
 
1022
- Here are 4 relevant functions:
1010
+ Here is the first line of 4 relevant functions:
1023
1011
 
1024
- (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:0. With added context, chunk is from 0:0 to 6:0
1012
+ (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:0
1025
1013
  ```
1026
- // context ...
1027
-
1028
1014
  () => { /* some code from https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js... */ }
1029
-
1030
- // context ...
1031
1015
  ```
1032
1016
 
1033
- (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:231. With added context, chunk is from 0:0 to 6:0
1017
+ (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:231
1034
1018
  ```
1035
- // context ...
1036
-
1037
1019
  () => { /* some code from https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js... */ }
1038
-
1039
- // context ...
1040
1020
  ```
1041
1021
 
1042
- (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:292. With added context, chunk is from 0:0 to 6:0
1022
+ (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:292
1043
1023
  ```
1044
- // context ...
1045
-
1046
1024
  () => { /* some code from https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js... */ }
1047
-
1048
- // context ...
1049
1025
  ```
1050
1026
 
1051
- (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:155034. With added context, chunk is from 0:0 to 6:0
1027
+ (anonymous) @ https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js:0:155034
1052
1028
  ```
1053
- // context ...
1054
-
1055
1029
  () => { /* some code from https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js... */ }
1056
-
1057
- // context ...
1058
1030
  ```
1059
1031
  === end content
1060
1032
 
@@ -901,6 +901,29 @@ The order of headers corresponds to an internal fixed list. If a header is not p
901
901
  return parts.join('\n');
902
902
  }
903
903
 
904
+ /**
905
+ * Formats only the first line of the function code to save space in summaries.
906
+ * The agent can use this information (url, line, column) to get the whole function source.
907
+ */
908
+ #formatFunctionCodeSummary(code: SourceMapScopes.FunctionCodeResolver.FunctionCode): string {
909
+ this.#formattedFunctionCodes.add(this.#functionCodeToKey(code));
910
+
911
+ const {startLine, startColumn} = code.range;
912
+ const name = code.functionBounds.name || '(anonymous)';
913
+ const url = code.functionBounds.uiSourceCode.url();
914
+
915
+ const lines = code.code.split('\n');
916
+ const firstLine = lines[0] || '';
917
+
918
+ const parts = [];
919
+ parts.push(`${name} @ ${url}:${startLine}:${startColumn}`);
920
+ parts.push('```');
921
+ parts.push(firstLine);
922
+ parts.push('```');
923
+
924
+ return parts.join('\n');
925
+ }
926
+
904
927
  /**
905
928
  * Appends the code of each call frame's function, but only if the function was not
906
929
  * serialized previously.
@@ -917,7 +940,7 @@ The order of headers corresponds to an internal fixed list. If a header is not p
917
940
  resolveFunctionCode(frame.url as Platform.DevToolsPath.UrlString, frame.lineNumber, frame.columnNumber)));
918
941
  for (const code of functionCodes) {
919
942
  if (code && !this.#hasFormattedFunctionCode(code)) {
920
- functionCodeStrings.push(this.#formatFunctionCode(code));
943
+ functionCodeStrings.push(this.#formatFunctionCodeSummary(code));
921
944
  }
922
945
  }
923
946
 
@@ -927,8 +950,8 @@ The order of headers corresponds to an internal fixed list. If a header is not p
927
950
 
928
951
  return '\n' + [
929
952
  this.#getFormattedFunctionCodeExplainer(),
930
- functionCodeStrings.length > 1 ? `Here are ${functionCodeStrings.length} relevant functions:` :
931
- `Here is a relevant function:`,
953
+ functionCodeStrings.length > 1 ? `Here is the first line of ${functionCodeStrings.length} relevant functions:` :
954
+ `Here is the first line of a relevant function:`,
932
955
  ...functionCodeStrings,
933
956
  ].join('\n\n');
934
957
  }
@@ -7960,10 +7960,6 @@ export const NativeFunctions = [
7960
7960
  name: "strokeTextCluster",
7961
7961
  signatures: [["textCluster","x","y","?options"]]
7962
7962
  },
7963
- {
7964
- name: "transferToGPUTexture",
7965
- signatures: [["options"]]
7966
- },
7967
7963
  {
7968
7964
  name: "beginLayer",
7969
7965
  signatures: [["?options"]]
@@ -5,7 +5,7 @@ import type * as Common from '../../core/common/common.js';
5
5
  import type * as Host from '../../core/host/host.js';
6
6
  import type * as Platform from '../../core/platform/platform.js';
7
7
 
8
- export type CategoryId = 'performance'|'accessibility'|'best-practices'|'seo';
8
+ export type CategoryId = 'performance'|'accessibility'|'best-practices'|'seo'|'agentic-browsing';
9
9
  export type RunMode = 'navigation'|'timespan'|'snapshot';
10
10
 
11
11
  export interface Preset {
@@ -147,12 +147,17 @@ export class Tool {
147
147
  new SDK.DOMModel.DeferredDOMNode(target, this.#protocolTool.backendNodeId);
148
148
  }
149
149
 
150
- async invoke(input: unknown): Promise<Protocol.WebMCP.InvokeToolResponse|undefined> {
151
- return await this.#target.deref()?.webMCPAgent().invoke_invokeTool({
150
+ async invoke(input: unknown): Promise<string|undefined> {
151
+ const target = this.#target.deref();
152
+ const response = await target?.webMCPAgent().invoke_invokeTool({
152
153
  toolName: this.name,
153
154
  frameId: this.#protocolTool.frameId,
154
155
  input,
155
156
  });
157
+ if (!response || response.getError()) {
158
+ return undefined;
159
+ }
160
+ return response.invocationId;
156
161
  }
157
162
  }
158
163
  export interface EventTypes {
@@ -190,6 +195,10 @@ export class WebMCPModel extends SDK.SDKModel.SDKModel<EventTypes> implements Pr
190
195
  return [...this.#calls.values()];
191
196
  }
192
197
 
198
+ toolCallForId(invocationId: string): Call|undefined {
199
+ return this.#calls.get(invocationId);
200
+ }
201
+
193
202
  clearCalls(): void {
194
203
  this.#calls.clear();
195
204
  }
@@ -719,7 +719,6 @@ export class UISourceCodeMetadata {
719
719
 
720
720
  export const enum DecoratorType {
721
721
  PERFORMANCE = 'performance',
722
- MEMORY = 'memory',
723
722
  COVERAGE = 'coverage',
724
723
  }
725
724
 
@@ -115,9 +115,14 @@ function isFileAgentFeatureAvailable(config?: Root.Runtime.HostConfig): boolean
115
115
  return (config?.aidaAvailability?.enabled && (config?.devToolsAiAssistanceFileAgent?.enabled)) === true;
116
116
  }
117
117
 
118
+ function isStorageAgentFeatureAvailable(config?: Root.Runtime.HostConfig): boolean {
119
+ return (config?.aidaAvailability?.enabled && (config?.devToolsAiAssistanceStorageAgent?.enabled)) === true;
120
+ }
121
+
118
122
  function isAnyFeatureAvailable(config?: Root.Runtime.HostConfig): boolean {
119
123
  return isStylingAgentFeatureAvailable(config) || isNetworkAgentFeatureAvailable(config) ||
120
- isPerformanceAgentFeatureAvailable(config) || isFileAgentFeatureAvailable(config);
124
+ isPerformanceAgentFeatureAvailable(config) || isFileAgentFeatureAvailable(config) ||
125
+ isStorageAgentFeatureAvailable(config);
121
126
  }
122
127
 
123
128
  UI.ViewManager.registerViewExtension({
@@ -1330,17 +1330,18 @@ function renderNetworkRequestPreview(networkRequest: NonNullable<DomTreeAiWidget
1330
1330
  const resourceType = Common.ResourceType.resourceTypes[networkRequest.resourceType];
1331
1331
  const {iconName, color} = PanelUtils.iconDataForResourceType(resourceType);
1332
1332
 
1333
+ const imageUrl = networkRequest.imageContent?.asImagePreviewUrl() ?? networkRequest.url;
1334
+
1333
1335
  return html`
1334
1336
  <div class="network-request-preview">
1335
1337
  <div class="network-request-header">
1336
1338
  <div class="network-request-icon">
1337
1339
  ${
1338
- resourceType.isImage() ? html`<img src=${networkRequest.imageUrl ?? networkRequest.url} alt=${filename} />` :
1340
+ resourceType.isImage() ? html`<img src=${imageUrl} alt=${filename} />` :
1339
1341
  html`<devtools-icon name=${iconName} style=${Lit.Directives.styleMap({
1340
1342
  color: color ?? ''
1341
1343
  })}></devtools-icon>`}
1342
- </div>
1343
- <div class="network-request-details">
1344
+ </div> <div class="network-request-details">
1344
1345
  <div class="network-request-name" title=${networkRequest.url}>${filename}</div>
1345
1346
  <div class="network-request-size">${size}</div>
1346
1347
  </div>
@@ -191,6 +191,10 @@ const UIStrings = {
191
191
  * @description Notice to display when a tool has been unregistered
192
192
  */
193
193
  toolUnregisteredNotice: 'This tool has been unregistered',
194
+ /**
195
+ * @description Text preceding a nested error in a stack trace
196
+ */
197
+ causedBy: 'Caused by:',
194
198
  } as const;
195
199
  const str_ = i18n.i18n.registerUIStrings('panels/application/WebMCPView.ts', UIStrings);
196
200
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -219,6 +223,11 @@ export interface FilterMenuButtons {
219
223
  toolTypes: FilterMenuButton;
220
224
  statusTypes: FilterMenuButton;
221
225
  }
226
+ export const enum TabId {
227
+ DETAILS = 'webmcp.tool-details',
228
+ INPUT = 'webmcp.call-inputs',
229
+ OUTPUT = 'webmcp.call-outputs',
230
+ }
222
231
  export interface SelectedTool {
223
232
  tool: WebMCP.WebMCPModel.Tool;
224
233
  parameters?: Record<string, unknown>;
@@ -229,7 +238,8 @@ export interface ViewInput {
229
238
  onToolSelect: (tool: WebMCP.WebMCPModel.Tool|null) => void;
230
239
  onRevealTool: (tool: WebMCP.WebMCPModel.Tool, parameters?: Record<string, unknown>) => void;
231
240
  selectedCall: WebMCP.WebMCPModel.Call|null;
232
- onCallSelect: (call: WebMCP.WebMCPModel.Call|null) => void;
241
+ selectedTab?: TabId;
242
+ onCallSelect: (call: WebMCP.WebMCPModel.Call|null, tabId?: TabId) => void;
233
243
  filters: FilterState;
234
244
  filterButtons: FilterMenuButtons;
235
245
  onClearLogClick: () => void;
@@ -508,7 +518,10 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
508
518
  }, {jslogContext: 'webmcp.cancel-call'});
509
519
  }
510
520
  }}>
511
- <td>
521
+ <td @click=${(e: Event) => {
522
+ e.stopPropagation();
523
+ input.onCallSelect(call, TabId.DETAILS);
524
+ }}>
512
525
  <div class="name-cell">
513
526
  <span>${call.tool.name}</span>
514
527
  <button class="run-tool-action-button"
@@ -524,7 +537,10 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
524
537
  </button>
525
538
  </div>
526
539
  </td>
527
- <td>
540
+ <td @click=${(e: Event) => {
541
+ e.stopPropagation();
542
+ input.onCallSelect(call, TabId.OUTPUT);
543
+ }}>
528
544
  <div class="status-cell">
529
545
  ${iconName(call) ? html`<devtools-icon class="small" name=${iconName(call)}></devtools-icon>`
530
546
  : ''}
@@ -532,8 +548,14 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
532
548
  </div>
533
549
  </td>
534
550
  ${!input.selectedCall ? html`
535
- <td>${call.input}</td>
536
- <td>${call.result?.output ? JSON.stringify(call.result.output)
551
+ <td @click=${(e: Event) => {
552
+ e.stopPropagation();
553
+ input.onCallSelect(call, TabId.INPUT);
554
+ }}>${call.input}</td>
555
+ <td @click=${(e: Event) => {
556
+ e.stopPropagation();
557
+ input.onCallSelect(call, TabId.OUTPUT);
558
+ }}>${call.result?.output ? JSON.stringify(call.result.output)
537
559
  : call.result?.errorText ?? ''}</td>
538
560
  ` : nothing}
539
561
  </tr>
@@ -552,17 +574,20 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
552
574
  @click=${() => input.onCallSelect(null)}
553
575
  ></devtools-button>
554
576
  <devtools-widget
555
- id="webmcp.tool-details"
577
+ id=${TabId.DETAILS}
578
+ ?selected=${input.selectedTab === TabId.DETAILS}
556
579
  title=${i18nString(UIStrings.toolDetails)}
557
580
  ${widget(ToolDetailsWidget, {tool: input.selectedCall?.tool, isUnregistered: input.selectedCall ? !input.tools.includes(input.selectedCall.tool) : false})}>
558
581
  </devtools-widget>
559
582
  <devtools-widget
560
- id="webmcp.call-inputs"
583
+ id=${TabId.INPUT}
584
+ ?selected=${input.selectedTab === TabId.INPUT}
561
585
  title=${i18nString(UIStrings.input)}
562
586
  ${widget(PayloadWidget, parsePayload(input.selectedCall?.input))}>
563
587
  </devtools-widget>
564
588
  <devtools-widget
565
- id="webmcp.call-outputs"
589
+ id=${TabId.OUTPUT}
590
+ ?selected=${input.selectedTab === TabId.OUTPUT}
566
591
  title=${i18nString(UIStrings.output)}
567
592
  ${widget(PayloadWidget, {
568
593
  valueObject: input.selectedCall?.result?.output,
@@ -697,6 +722,8 @@ export class WebMCPView extends UI.Widget.VBox {
697
722
  readonly #view: View;
698
723
  #selectedTool: SelectedTool|null = null;
699
724
  #selectedCall: WebMCP.WebMCPModel.Call|null = null;
725
+ #selectedTab: TabId|undefined = undefined;
726
+ #lastDevToolsInvocationId: string|null = null;
700
727
 
701
728
  #filterState: FilterState = {
702
729
  text: '',
@@ -799,17 +826,26 @@ export class WebMCPView extends UI.Widget.VBox {
799
826
  #webMCPModelAdded(model: WebMCP.WebMCPModel.WebMCPModel): void {
800
827
  model.addEventListener(WebMCP.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
801
828
  model.addEventListener(WebMCP.WebMCPModel.Events.TOOLS_REMOVED, this.#toolsRemoved, this);
802
- model.addEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.requestUpdate, this);
829
+ model.addEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.#toolInvoked, this);
803
830
  model.addEventListener(WebMCP.WebMCPModel.Events.TOOL_RESPONDED, this.requestUpdate, this);
804
831
  }
805
832
 
806
833
  #webMCPModelRemoved(model: WebMCP.WebMCPModel.WebMCPModel): void {
807
834
  model.removeEventListener(WebMCP.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
808
835
  model.removeEventListener(WebMCP.WebMCPModel.Events.TOOLS_REMOVED, this.#toolsRemoved, this);
809
- model.removeEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.requestUpdate, this);
836
+ model.removeEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.#toolInvoked, this);
810
837
  model.removeEventListener(WebMCP.WebMCPModel.Events.TOOL_RESPONDED, this.requestUpdate, this);
811
838
  }
812
839
 
840
+ #toolInvoked(event: Common.EventTarget.EventTargetEvent<WebMCP.WebMCPModel.Call>): void {
841
+ const call = event.data;
842
+ if (call.invocationId === this.#lastDevToolsInvocationId) {
843
+ this.#selectedCall = call;
844
+ this.#lastDevToolsInvocationId = null;
845
+ }
846
+ this.requestUpdate();
847
+ }
848
+
813
849
  #toolsRemoved(event: Common.EventTarget.EventTargetEvent<readonly WebMCP.WebMCPModel.Tool[]>): void {
814
850
  if (this.#selectedTool && event.data.includes(this.#selectedTool.tool)) {
815
851
  this.#selectedTool = null;
@@ -861,8 +897,16 @@ export class WebMCPView extends UI.Widget.VBox {
861
897
  this.requestUpdate();
862
898
  },
863
899
  selectedCall: this.#selectedCall,
864
- onCallSelect: call => {
865
- this.#selectedCall = call;
900
+ selectedTab: this.#selectedTab,
901
+ onCallSelect: (call, tabId) => {
902
+ if (call === null) {
903
+ this.#selectedCall = null;
904
+ } else if (this.#selectedCall === null) {
905
+ this.#selectedCall = call;
906
+ this.#selectedTab = tabId;
907
+ } else {
908
+ this.#selectedCall = call;
909
+ }
866
910
  this.requestUpdate();
867
911
  },
868
912
  toolCalls: filteredCalls,
@@ -870,9 +914,20 @@ export class WebMCPView extends UI.Widget.VBox {
870
914
  filterButtons: this.#filterButtons,
871
915
  onClearLogClick: this.#handleClearLogClick,
872
916
  onFilterChange: this.#handleFilterChange,
873
- onRunTool: event => {
917
+ onRunTool: async event => {
874
918
  if (this.#selectedTool) {
875
- void this.#selectedTool.tool.invoke(event.data.parameters || {});
919
+ this.#selectedTool.parameters = event.data.parameters || {};
920
+ this.#lastDevToolsInvocationId = await this.#selectedTool.tool.invoke(this.#selectedTool.parameters) ?? null;
921
+ if (this.#lastDevToolsInvocationId) {
922
+ const models = SDK.TargetManager.TargetManager.instance().models(WebMCP.WebMCPModel.WebMCPModel);
923
+ const call =
924
+ models.flatMap(model => model.toolCalls).find(c => c.invocationId === this.#lastDevToolsInvocationId);
925
+ if (call) {
926
+ this.#selectedCall = call;
927
+ this.#lastDevToolsInvocationId = null;
928
+ }
929
+ }
930
+ this.requestUpdate();
876
931
  }
877
932
  },
878
933
  onPaste: async () => {
@@ -891,6 +946,7 @@ export class WebMCPView extends UI.Widget.VBox {
891
946
  },
892
947
  };
893
948
  this.#view(input, {}, this.contentElement);
949
+ this.#selectedTab = undefined;
894
950
  }
895
951
  }
896
952
  export interface PayloadViewInput {
@@ -972,7 +1028,9 @@ export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, ta
972
1028
  <div class="payload-value source-code error-text">
973
1029
  ${details.frames.length === 0 && details.description ? html`<span>${details.description}\n</span>` : nothing}
974
1030
  <div>${details.frames.map(renderFrame)}</div>
975
- ${details.cause ? html`\nCaused by:\n${createException(details.cause, linkifier)}` : nothing}</div>`;
1031
+ ${
1032
+ details.cause ? html`\n${i18nString(UIStrings.causedBy)}\n${createException(details.cause, linkifier)}` :
1033
+ nothing}</div>`;
976
1034
  };
977
1035
 
978
1036
  render(
@@ -1124,7 +1182,7 @@ const TOOL_DETAILS_VIEW = (input: ToolDetailsViewInput, output: undefined, targe
1124
1182
  ></devtools-button>
1125
1183
  </div>` : origin ? html`
1126
1184
  <div class="label">Origin</div>
1127
- <div class="value">
1185
+ <div class="value stack-trace">
1128
1186
  ${widget(Components.JSPresentationUtils.StackTracePreviewContent,
1129
1187
  {stackTrace: origin, options: { expandable: true}})}
1130
1188
  </div>` : nothing}
@@ -88,6 +88,14 @@
88
88
  display: flex;
89
89
  gap: var(--sys-size-5);
90
90
  align-items: center;
91
+ min-width: 0;
92
+ }
93
+
94
+ .name-cell > span {
95
+ white-space: nowrap;
96
+ overflow: hidden;
97
+ text-overflow: ellipsis;
98
+ min-width: 0;
91
99
  }
92
100
 
93
101
  .run-tool-action-button {
@@ -141,8 +149,11 @@
141
149
  color: var(--sys-color-on-surface);
142
150
  overflow-wrap: anywhere;
143
151
 
144
- &:has(> .stack-preview-container) {
145
- padding: var(--sys-size-4) 0;
152
+ &.stack-trace {
153
+ display: flex;
154
+ padding: 0;
155
+ margin-top: calc(-1 * (var(--sys-size-1) + var(--sys-size-2)));
156
+ margin-left: calc(-1 * var(--sys-size-3));
146
157
  }
147
158
 
148
159
  &.tool-origin-container {
@@ -9,6 +9,7 @@ import * as Host from '../../core/host/host.js';
9
9
  import * as i18n from '../../core/i18n/i18n.js';
10
10
  import * as Root from '../../core/root/root.js';
11
11
  import * as AIAssistance from '../../models/ai_assistance/ai_assistance.js';
12
+ import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
12
13
  import * as AiCodeGeneration from '../../models/ai_code_generation/ai_code_generation.js';
13
14
  import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
14
15
  import * as UI from '../../ui/legacy/legacy.js';
@@ -163,6 +164,7 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
163
164
 
164
165
  interface AiCodeCompletionTeaserConfig {
165
166
  onDetach: () => void;
167
+ panel?: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
166
168
  }
167
169
 
168
170
  export class AiCodeCompletionTeaser extends UI.Widget.Widget {
@@ -172,6 +174,7 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
172
174
  #boundOnAidaAvailabilityChange: () => Promise<void>;
173
175
  #boundOnAiCodeCompletionSettingChanged: () => void;
174
176
  #onDetach: () => void;
177
+ #panel?: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
175
178
 
176
179
  // Whether the user completed first run experience dialog or not.
177
180
  #aiCodeCompletionFreCompletedSetting =
@@ -186,6 +189,7 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
186
189
  super();
187
190
  this.markAsExternallyManaged();
188
191
  this.#onDetach = config.onDetach;
192
+ this.#panel = config.panel;
189
193
  this.#view = view ?? DEFAULT_VIEW;
190
194
  this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
191
195
  this.#boundOnAiCodeCompletionSettingChanged = this.#onAiCodeCompletionSettingChanged.bind(this);
@@ -283,6 +287,11 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
283
287
 
284
288
  if (result) {
285
289
  this.#aiCodeCompletionFreCompletedSetting.set(true);
290
+ if (this.#panel === AiCodeCompletion.AiCodeCompletion.ContextFlavor.CONSOLE) {
291
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionFreCompletedFromConsole);
292
+ } else if (this.#panel === AiCodeCompletion.AiCodeCompletion.ContextFlavor.SOURCES) {
293
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionFreCompletedFromSources);
294
+ }
286
295
  this.detach();
287
296
  } else {
288
297
  this.requestUpdate();
@@ -314,6 +314,9 @@ export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<E
314
314
  jumpToFontPaletteDefinition(_paletteName: string): void {
315
315
  }
316
316
 
317
+ jumpToCounterStyleDefinition(_counterStyleName: string): void {
318
+ }
319
+
317
320
  jumpToDeclaration(_valueSource: SDK.CSSMatchedStyles.CSSValueSource): void {
318
321
  }
319
322
 
@@ -1156,10 +1156,19 @@ export class LinkableNameRenderer extends rendererBase(SDK.CSSPropertyParserMatc
1156
1156
  return {
1157
1157
  jslogContext: 'css-font-palette',
1158
1158
  metric: null,
1159
- ruleBlock: '@font-*',
1159
+ ruleBlock: '', // Not used
1160
1160
  isDefined: Boolean(this.#matchedStyles.atRules().find(
1161
1161
  ar => ar.type() === 'font-palette-values' && ar.name()?.text === match.text)),
1162
1162
  };
1163
+ case SDK.CSSPropertyParserMatchers.LinkableNameProperties.LIST_STYLE:
1164
+ case SDK.CSSPropertyParserMatchers.LinkableNameProperties.LIST_STYLE_TYPE:
1165
+ return {
1166
+ jslogContext: 'css-list-style-type',
1167
+ metric: null,
1168
+ ruleBlock: '', // Not used
1169
+ isDefined: Boolean(this.#matchedStyles.atRules().find(
1170
+ ar => ar.type() === 'counter-style' && ar.name()?.text === match.text)),
1171
+ };
1163
1172
  case SDK.CSSPropertyParserMatchers.LinkableNameProperties.POSITION_TRY:
1164
1173
  case SDK.CSSPropertyParserMatchers.LinkableNameProperties.POSITION_TRY_FALLBACKS:
1165
1174
  return {
@@ -1182,6 +1191,10 @@ export class LinkableNameRenderer extends rendererBase(SDK.CSSPropertyParserMatc
1182
1191
  metric && Host.userMetrics.swatchActivated(metric);
1183
1192
  if (match.propertyName === SDK.CSSPropertyParserMatchers.LinkableNameProperties.FONT_PALETTE) {
1184
1193
  this.#stylesContainer.jumpToFontPaletteDefinition(match.text);
1194
+ } else if (
1195
+ match.propertyName === SDK.CSSPropertyParserMatchers.LinkableNameProperties.LIST_STYLE ||
1196
+ match.propertyName === SDK.CSSPropertyParserMatchers.LinkableNameProperties.LIST_STYLE_TYPE) {
1197
+ this.#stylesContainer.jumpToCounterStyleDefinition(match.text);
1185
1198
  } else {
1186
1199
  this.#stylesContainer.jumpToSectionBlock(`${ruleBlock} ${match.text}`);
1187
1200
  }
@@ -3635,6 +3648,9 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
3635
3648
  return;
3636
3649
  }
3637
3650
  this.#clearGhostTextInValue();
3651
+ if (this.value === text) {
3652
+ return;
3653
+ }
3638
3654
  if (this.value) {
3639
3655
  // If there is an existing value, and user is editing the name field
3640
3656
  // which leads to a new value suggestion, then the previous value should no
@@ -3648,7 +3664,24 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
3648
3664
  }
3649
3665
 
3650
3666
  #clearGhostTextInValue(): void {
3651
- this.listItemElement.querySelector('.ghost-value-prediction')?.remove();
3667
+ const ghostTextElement = this.listItemElement.querySelector('.ghost-value-prediction');
3668
+ if (!ghostTextElement) {
3669
+ return;
3670
+ }
3671
+ ghostTextElement.remove();
3672
+ // If there's no value, there's nothing to restore
3673
+ if (!this.value) {
3674
+ return;
3675
+ }
3676
+ // Restore original classes
3677
+ if (this.property.parsedOk) {
3678
+ this.listItemElement.classList.remove('not-parsed-ok', 'invalid-property-value');
3679
+ } else {
3680
+ const invalidPropertyValue = SDK.CSSMetadata.cssMetadata().isCSSPropertyName(this.property.name);
3681
+ if (!invalidPropertyValue) {
3682
+ this.listItemElement.classList.remove('invalid-property-value');
3683
+ }
3684
+ }
3652
3685
  }
3653
3686
  }
3654
3687
 
@@ -166,7 +166,7 @@ export class StylesAiCodeCompletionProvider {
166
166
  }|null> {
167
167
  this.#aiCodeCompletionConfig?.onRequestTriggered();
168
168
  // Registering AiCodeCompletionRequestTriggered metric even if the request is served from cache
169
- Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionRequestTriggered);
169
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionRequestTriggeredFromStyles);
170
170
 
171
171
  try {
172
172
  const completionResponse = await this.#aiCodeCompletion?.completeCode(
@@ -52,6 +52,7 @@ export interface StylesContainer {
52
52
  jumpToProperty(propertyName: string, sectionName?: string, blockName?: string): boolean;
53
53
  jumpToSectionBlock(section: string): void;
54
54
  jumpToFontPaletteDefinition(paletteName: string): void;
55
+ jumpToCounterStyleDefinition(counterStyleName: string): void;
55
56
  jumpToDeclaration(valueSource: SDK.CSSMatchedStyles.CSSValueSource): void;
56
57
  setActiveProperty(treeElement: StylePropertyTreeElement|null): void;
57
58
  addStyleUpdateListener(listener: () => void): void;