chrome-devtools-frontend 1.0.1597624 → 1.0.1598808

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 (79) hide show
  1. package/docs/ui_engineering.md +6 -6
  2. package/front_end/core/host/AidaClient.ts +2 -0
  3. package/front_end/models/ai_assistance/AiConversation.ts +16 -14
  4. package/front_end/models/ai_assistance/agents/AiAgent.ts +14 -4
  5. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +109 -0
  6. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +30 -1
  7. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  8. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +6 -9
  9. package/front_end/panels/ai_assistance/PatchWidget.ts +1 -1
  10. package/front_end/panels/ai_assistance/components/ChatMessage.ts +53 -29
  11. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
  12. package/front_end/panels/application/AppManifestView.ts +2 -2
  13. package/front_end/panels/application/CookieItemsView.ts +9 -15
  14. package/front_end/panels/application/DeviceBoundSessionsView.ts +3 -5
  15. package/front_end/panels/application/FrameDetailsView.ts +2 -2
  16. package/front_end/panels/application/ReportingApiView.ts +2 -2
  17. package/front_end/panels/application/preloading/PreloadingView.ts +18 -3
  18. package/front_end/panels/changes/ChangesView.ts +7 -11
  19. package/front_end/panels/coverage/CoverageView.ts +4 -4
  20. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +3 -3
  21. package/front_end/panels/elements/ElementsTreeOutline.ts +34 -8
  22. package/front_end/panels/elements/StylePropertiesSection.ts +129 -1
  23. package/front_end/panels/elements/StylePropertyHighlighter.ts +3 -0
  24. package/front_end/panels/elements/stylesSidebarPane.css +34 -0
  25. package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +3 -5
  26. package/front_end/panels/layer_viewer/LayerDetailsView.ts +2 -1
  27. package/front_end/panels/network/RequestConditionsDrawer.ts +4 -4
  28. package/front_end/panels/network/RequestCookiesView.ts +2 -2
  29. package/front_end/panels/network/RequestHeadersView.ts +4 -4
  30. package/front_end/panels/network/RequestResponseView.ts +2 -2
  31. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +2 -2
  32. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +3 -3
  33. package/front_end/panels/recorder/components/StepView.ts +2 -1
  34. package/front_end/panels/search/SearchView.ts +2 -2
  35. package/front_end/panels/timeline/TimelineDetailsView.ts +8 -6
  36. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -7
  37. package/front_end/panels/timeline/components/insights/CharacterSet.ts +2 -6
  38. package/front_end/panels/timeline/components/insights/EventRef.ts +2 -2
  39. package/front_end/panels/webauthn/WebauthnPane.ts +2 -2
  40. package/front_end/third_party/chromium/README.chromium +1 -1
  41. package/front_end/third_party/puppeteer/README.chromium +2 -2
  42. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Realm.js +1 -1
  43. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Realm.js.map +1 -1
  44. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts +4 -0
  45. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts.map +1 -1
  46. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js +13 -0
  47. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js.map +1 -1
  48. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  49. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  50. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  51. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  52. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/mitt/mitt.js +2 -2
  56. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/parsel-js/parsel-js.js +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/third_party/rxjs/rxjs.js +446 -446
  58. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +166 -167
  59. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js +2 -2
  60. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js.map +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts +4 -0
  62. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js +12 -0
  64. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js.map +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  66. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  67. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/third_party/mitt/mitt.js +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/third_party/parsel-js/parsel-js.js +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/third_party/rxjs/rxjs.js +97 -97
  73. package/front_end/third_party/puppeteer/package/package.json +1 -1
  74. package/front_end/third_party/puppeteer/package/src/bidi/Realm.ts +2 -2
  75. package/front_end/third_party/puppeteer/package/src/bidi/util.ts +17 -0
  76. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  77. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  78. package/front_end/ui/legacy/Widget.ts +42 -6
  79. package/package.json +1 -1
@@ -44,7 +44,7 @@ To embed another presenter (`UI.Widget`) in the lit-html template, use `widget(<
44
44
 
45
45
  This will instantiate a `Widget` class with the web component as its `element` and, optionally, will set the properties provided in the second parameter. The widget won’t be re-instantiated on the subsequent template renders, but the properties would be updated. For this to work, the widget needs to accept `HTMLElement` as a sole constructor parameter and properties need to be public members or setters.
46
46
 
47
- For backwards compatibility, the first argument to `widgetConfig` can also be a factory function: `widget(element => new MyWidget(foo, bar, element))`. Similar to the class constructor version, `element` is the actual `<devtools-widget>` so the following two invocations of `widgetConfig` are equivalent: `widget(MyWidget)` and `widget(element => new MyWidget(element))`.
47
+ For backwards compatibility, the first argument to `widget` can also be a factory function: `widget(element => new MyWidget(foo, bar, element))`. Similar to the class constructor version, `element` is the actual `<devtools-widget>` so the following two invocations of `widget` are equivalent: `widget(MyWidget)` and `widget(element => new MyWidget(element))`.
48
48
 
49
49
  ## Styling
50
50
  To prevent style conflicts in widgets without relying on shadow DOM, we use the CSS [`@scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope) at-rule for style encapsulation. This ensures that styles defined for a widget do not leak out and affect other components.
@@ -93,9 +93,9 @@ In this example, the `.title` style will apply within the parent widget but will
93
93
  ## Examples
94
94
 
95
95
  ```html
96
- <devtools-widget .widgetConfig=${widgetConfig(ElementsPanel)}>
96
+ <devtools-widget ${widget(ElementsPanel)}>
97
97
  <devtools-split-view>
98
- <devtools-widget slot="main" .widgetConfig=${widgetConfig(ElementsTree)}></devtools-widget>
98
+ <devtools-widget slot="main" ${widget(ElementsTree)}></devtools-widget>
99
99
  <devtools-tab-pane slot="sidebar">
100
100
  ${widget(StylesPane, {element: input.element})}
101
101
  ${widget(ComputedPane, {element: input.element})}
@@ -1169,12 +1169,12 @@ export const DEFAULT_VIEW = (input, _output, target) => {
1169
1169
  <div>
1170
1170
  <devtools-split-view direction=${this.vertical ? 'column' : 'row'} sidebar-position="first"
1171
1171
  sidebar-initial-size="200">
1172
- <devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(SidebarPanel,
1172
+ <devtools-widget slot="sidebar" ${widget(SidebarPanel,
1173
1173
  {minimumSize: {width: 100, height: 25}})}></devtools-widget>
1174
1174
  <devtools-split-view direction="column" sidebar-position="second" slot="main"
1175
1175
  direction="row" sidebar-position="$this.dockedLeft ? 'second' : 'first'}">
1176
- <devtools-widget slot="main" .widgetConfig=${widgetConfig(UI.Widget.EmptyWidget)}></devtools-widget>
1177
- <devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(DetailsView)}></devtools-widget>
1176
+ <devtools-widget slot="main" ${widget(UI.Widget.EmptyWidget)}></devtools-widget>
1177
+ <devtools-widget slot="sidebar" ${widget(DetailsView)}></devtools-widget>
1178
1178
  </devtools-split-view>
1179
1179
  </devtools-split-view>
1180
1180
  </div>`,
@@ -137,6 +137,8 @@ export enum ClientFeature {
137
137
  CHROME_CONTEXT_SELECTION_AGENT = 25,
138
138
  // Chrome Accessibility Agent
139
139
  CHROME_ACCESSIBILITY_AGENT = 26,
140
+ // Chrome AI Assistance Conversation Summary Agent.
141
+ CHROME_CONVERSATION_SUMMARY_AGENT = 27,
140
142
 
141
143
  // Removed features (for reference).
142
144
  // Chrome AI Assistance Performance Insights Agent.
@@ -265,21 +265,23 @@ export class AiConversation {
265
265
  id: this.id,
266
266
  history: this.history
267
267
  .map(item => {
268
- if (item.type === ResponseType.CONTEXT_CHANGE) {
269
- return null;
268
+ switch (item.type) {
269
+ case ResponseType.CONTEXT_CHANGE: {
270
+ return null;
271
+ }
272
+ case ResponseType.USER_QUERY: {
273
+ return {...item, imageInput: undefined};
274
+ }
275
+ case ResponseType.SIDE_EFFECT: {
276
+ return {...item, confirm: undefined};
277
+ }
278
+ case ResponseType.CONTEXT:
279
+ case ResponseType.ACTION: {
280
+ return {...item, widgets: undefined};
281
+ }
282
+ default:
283
+ return item;
270
284
  }
271
-
272
- if (item.type === ResponseType.USER_QUERY) {
273
- return {...item, imageInput: undefined};
274
- }
275
- // Remove the `confirm()`-function because `structuredClone()` throws on functions
276
- if (item.type === ResponseType.SIDE_EFFECT) {
277
- return {...item, confirm: undefined};
278
- }
279
- if (item.type === ResponseType.CONTEXT && item.widgets) {
280
- return {...item, widgets: undefined};
281
- }
282
- return item;
283
285
  })
284
286
  .filter(history => !!history),
285
287
  type: this.#type,
@@ -249,8 +249,15 @@ export interface StylePropertiesAiWidget {
249
249
  };
250
250
  }
251
251
 
252
+ export interface DomTreeAiWidget {
253
+ name: 'DOM_TREE';
254
+ data: {
255
+ root: SDK.DOMModel.DOMNodeSnapshot,
256
+ };
257
+ }
258
+
252
259
  // This type will grow as we add more widgets.
253
- export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget;
260
+ export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget;
254
261
 
255
262
  export type FunctionCallHandlerResult<Result> = {
256
263
  requiresApproval: true,
@@ -689,10 +696,12 @@ export abstract class AiAgent<T> {
689
696
 
690
697
  return;
691
698
  }
699
+
692
700
  query = {
693
701
  functionResponse: {
694
702
  name: functionCall.name,
695
- response: result,
703
+ // Widgets are not sent back to the LLM
704
+ response: {...result, widgets: undefined},
696
705
  },
697
706
  };
698
707
  request = this.buildRequest(query, Host.AidaClient.Role.ROLE_UNSPECIFIED);
@@ -720,7 +729,8 @@ export abstract class AiAgent<T> {
720
729
  options?: FunctionHandlerOptions&{explanation?: string},
721
730
  ): AsyncGenerator<FunctionCallResponseData, {
722
731
  result: unknown,
723
- }|{context: ConversationContext<unknown>, description: string}> {
732
+ widgets?: AiWidget[],
733
+ }|{context: ConversationContext<unknown>, description: string, widgets?: AiWidget[]}> {
724
734
  const call = this.#functionDeclarations.get(name);
725
735
  if (!call) {
726
736
  throw new Error(`Function ${name} is not found.`);
@@ -837,7 +847,7 @@ export abstract class AiAgent<T> {
837
847
  return result;
838
848
  }
839
849
 
840
- return result as {result: unknown};
850
+ return result as {result: unknown, widgets?: AiWidget[]};
841
851
  }
842
852
 
843
853
  async *
@@ -0,0 +1,109 @@
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 * as Root from '../../../core/root/root.js';
7
+
8
+ import {AiAgent, type ContextResponse, ConversationContext, type RequestOptions, ResponseType} from './AiAgent.js';
9
+
10
+ const preamble =
11
+ `You are an expert technical assistant specializing in summarizing debugging conversations from Chrome DevTools.
12
+ You will receive a markdown-formatted transcript of a conversation between a user and a DevTools AI assistant.
13
+ Your goal is to produce a succinct, structured summary that a local AI agent in the user's IDE can use to apply code fixes.
14
+
15
+ Focus on extracting:
16
+ 1. **Core Issue:** The primary problem or question the user was investigating.
17
+ 2. **Diagnostic Findings:** Key technical details discovered (e.g., specific functions, bottlenecks, error messages, URLs, or CSS properties).
18
+ 3. **Proposed Solution:** The specific changes or optimizations recommended by the DevTools assistant.
19
+ 4. **Actionable Steps:** A clear, step-by-step list of instructions for the IDE agent to implement the fix.
20
+
21
+ Maintain a professional, technical, and extremely concise tone. Avoid conversational filler or introductory/concluding remarks.
22
+ The output must be structured markdown.`;
23
+
24
+ export class ConversationSummaryContext extends ConversationContext<string> {
25
+ #conversation: string;
26
+ constructor(conversation: string) {
27
+ super();
28
+ this.#conversation = conversation;
29
+ }
30
+
31
+ getOrigin(): string {
32
+ return 'devtools://ai-assistance';
33
+ }
34
+
35
+ getItem(): string {
36
+ return this.#conversation;
37
+ }
38
+
39
+ override getTitle(): string {
40
+ return 'Conversation';
41
+ }
42
+ }
43
+
44
+ /**
45
+ * An agent that takes a full conversation between a user and an agent in markdown
46
+ * format and produces a succinct summary of the conversation.
47
+ *
48
+ * This summary is designed to be read by a local agent in the user's IDE and it
49
+ * will be used to help apply fixes to the user's local codebase based on the
50
+ * debugging information the devtools agent found.
51
+ *
52
+ * This agent is not intended to be used directly by users in the AI Assistance
53
+ * panel when chatting with DevTools AI.
54
+ */
55
+ export class ConversationSummaryAgent extends AiAgent<string> {
56
+ override preamble = preamble;
57
+
58
+ get clientFeature(): Host.AidaClient.ClientFeature {
59
+ return Host.AidaClient.ClientFeature.CHROME_CONVERSATION_SUMMARY_AGENT;
60
+ }
61
+
62
+ get userTier(): string|undefined {
63
+ // TODO(b/491772868): tidy up userTier & feature flags in the backend.
64
+ return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
65
+ }
66
+
67
+ get options(): RequestOptions {
68
+ // TODO(b/491772868): tidy up userTier & feature flags in the backend.
69
+ const temperature = Root.Runtime.hostConfig.devToolsFreestyler?.temperature;
70
+ const modelId = Root.Runtime.hostConfig.devToolsFreestyler?.modelId;
71
+
72
+ return {
73
+ temperature,
74
+ modelId,
75
+ };
76
+ }
77
+
78
+ async * handleContextDetails(context: ConversationContext<string>|null): AsyncGenerator<ContextResponse, void, void> {
79
+ if (!context) {
80
+ return;
81
+ }
82
+
83
+ yield {
84
+ type: ResponseType.CONTEXT,
85
+ title: 'Summarizing conversation',
86
+ details: [
87
+ {
88
+ title: 'Conversation transcript',
89
+ text: context.getItem(),
90
+ },
91
+ ],
92
+ };
93
+ }
94
+
95
+ override async enhanceQuery(query: string, context: ConversationContext<string>|null): Promise<string> {
96
+ const conversation = context ? context.getItem() : query;
97
+ return `Summarize the following conversation:\n\n${conversation}`;
98
+ }
99
+
100
+ async summarizeConversation(conversation: string): Promise<string> {
101
+ const context = new ConversationSummaryContext(conversation);
102
+ const response = await Array.fromAsync(this.run('', {selected: context}));
103
+ const lastResponse = response.at(-1);
104
+ if (lastResponse && lastResponse.type === ResponseType.ANSWER && lastResponse.complete === true) {
105
+ return lastResponse.text.trim();
106
+ }
107
+ throw new Error('Failed to summarize conversation');
108
+ }
109
+ }
@@ -8,6 +8,7 @@ import * as i18n from '../../../core/i18n/i18n.js';
8
8
  import * as Platform from '../../../core/platform/platform.js';
9
9
  import * as Root from '../../../core/root/root.js';
10
10
  import * as SDK from '../../../core/sdk/sdk.js';
11
+ import type * as Protocol from '../../../generated/protocol.js';
11
12
  import * as Tracing from '../../../services/tracing/tracing.js';
12
13
  import * as Annotations from '../../annotations/annotations.js';
13
14
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
@@ -700,6 +701,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
700
701
  #declareFunctions(context: PerformanceTraceContext): void {
701
702
  const focus = context.getItem();
702
703
  const {parsedTrace} = focus;
704
+ const processedNodeIds = new Set<Protocol.DOM.BackendNodeId>();
703
705
 
704
706
  this.declareFunction<{insightSetId: string, insightName: string}, {details: string}>('getInsightDetails', {
705
707
  description:
@@ -749,9 +751,36 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
749
751
 
750
752
  const details = new PerformanceInsightFormatter(focus, insight).formatInsight();
751
753
 
754
+ const widgets: AiWidget[] = [];
755
+ if (params.insightName === 'LCPDiscovery' || params.insightName === 'LCPBreakdown') {
756
+ const lcpMetric = Trace.Insights.Common.getLCP(insightSet);
757
+ const lcpEvent = lcpMetric?.event;
758
+ if (lcpEvent && Trace.Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
759
+ const nodeId = lcpEvent.args.data?.nodeId;
760
+ // We want to show only one DOM tree widget per walkthrough per node.
761
+ // We do want to show the widget for the same node again, if it's within a new walkthrough.
762
+ if (nodeId && !processedNodeIds.has(nodeId)) {
763
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
764
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
765
+ if (domModel) {
766
+ const nodeMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([nodeId]));
767
+ const node = nodeMap?.get(nodeId);
768
+ if (node) {
769
+ const snapshot = await node.takeSnapshot();
770
+ widgets.push({
771
+ name: 'DOM_TREE',
772
+ data: {root: snapshot},
773
+ });
774
+ processedNodeIds.add(nodeId);
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+
752
781
  const key = `getInsightDetails('${params.insightSetId}', '${params.insightName}')`;
753
782
  this.#cacheFunctionResult(focus, key, details);
754
- return {result: {details}};
783
+ return {result: {details}, widgets};
755
784
  },
756
785
  });
757
786
 
@@ -7,6 +7,7 @@ import * as AccessibilityAgent from './agents/AccessibilityAgent.js';
7
7
  import * as AiAgent from './agents/AiAgent.js';
8
8
  import * as BreakpointDebuggerAgent from './agents/BreakpointDebuggerAgent.js';
9
9
  import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
10
+ import * as ConversationSummaryAgent from './agents/ConversationSummaryAgent.js';
10
11
  import * as FileAgent from './agents/FileAgent.js';
11
12
  import * as NetworkAgent from './agents/NetworkAgent.js';
12
13
  import * as PatchAgent from './agents/PatchAgent.js';
@@ -47,6 +48,7 @@ export {
47
48
  ChangeManager,
48
49
  ContextSelectionAgent,
49
50
  ConversationHandler,
51
+ ConversationSummaryAgent,
50
52
  Debug,
51
53
  EvaluateAction,
52
54
  ExtensionScope,
@@ -50,6 +50,7 @@ import {
50
50
  import {isAiAssistancePatchingEnabled} from './PatchWidget.js';
51
51
 
52
52
  const {html} = Lit;
53
+ const {widget} = UI.Widget;
53
54
 
54
55
  const AI_ASSISTANCE_SEND_FEEDBACK = 'https://crbug.com/364805393' as Platform.DevToolsPath.UrlString;
55
56
  const AI_ASSISTANCE_HELP =
@@ -465,16 +466,12 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
465
466
  ></devtools-ai-chat-view>`;
466
467
  }
467
468
  case ViewState.EXPLORE_VIEW:
468
- return html`<devtools-widget
469
- class="fill-panel"
470
- .widgetConfig=${UI.Widget.widgetConfig(ExploreWidget)}
471
- ></devtools-widget>`;
469
+ return html`<devtools-widget class="fill-panel" ${widget(ExploreWidget)}>
470
+ </devtools-widget>`;
472
471
 
473
472
  case ViewState.DISABLED_VIEW:
474
- return html`<devtools-widget
475
- class="fill-panel"
476
- .widgetConfig=${UI.Widget.widgetConfig(DisabledWidget, input.props)}
477
- ></devtools-widget>`;
473
+ return html`<devtools-widget class="fill-panel" ${widget(DisabledWidget, input.props)}>
474
+ </devtools-widget>`;
478
475
  }
479
476
  }
480
477
 
@@ -510,7 +507,7 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
510
507
  </div>
511
508
  <div slot="sidebar" class="sidebar-view">
512
509
  ${shouldShowWalkthrough ? html`
513
- <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(WalkthroughView, {
510
+ <devtools-widget ${widget(WalkthroughView, {
514
511
  message: input.props.walkthrough.activeMessage,
515
512
  isLoading: input.props.isLoading && walkthroughIsForLastMessage,
516
513
  markdownRenderer: input.props.markdownRenderer,
@@ -280,7 +280,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
280
280
 
281
281
  // clang-format off
282
282
  return html`<devtools-widget class="copy-to-prompt"
283
- .widgetConfig=${UI.Widget.widgetConfig(PanelCommon.CopyChangesToPrompt, {
283
+ ${widget(PanelCommon.CopyChangesToPrompt, {
284
284
  workspaceDiff: input.workspaceDiff,
285
285
  patchAgentCSSChange: changedCode,
286
286
  })}></devtools-widget>`;
@@ -13,7 +13,7 @@ import * as Root from '../../../core/root/root.js';
13
13
  import * as SDK from '../../../core/sdk/sdk.js';
14
14
  import type * as Protocol from '../../../generated/protocol.js';
15
15
  import type {
16
- AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, StylePropertiesAiWidget} from
16
+ AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, StylePropertiesAiWidget} from
17
17
  '../../../models/ai_assistance/agents/AiAgent.js';
18
18
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
19
19
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
@@ -676,33 +676,29 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
676
676
  }
677
677
  const styles = new ComputedStyle.ComputedStyleModel.ComputedStyle(domNodeForId, widgetData.data.computedStyles);
678
678
 
679
- const widgetConfig = UI.Widget.widgetConfig(Elements.ComputedStyleWidget.ComputedStyleWidget, {
680
- nodeStyle: styles,
681
- matchedStyles: widgetData.data.matchedCascade,
682
- // This disables showing the nested traces and detailed information in the widget.
683
- propertyTraces: null,
684
- allowUserControl: false,
685
- filterText: new RegExp(widgetData.data.properties.join('|'), 'i')
686
- });
687
-
688
679
  // clang-format off
689
- const widget = html`<devtools-widget class="computed-styles-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
680
+ const renderedWidget = html`<devtools-widget
681
+ class="computed-styles-widget" ${widget(Elements.ComputedStyleWidget.ComputedStyleWidget, {
682
+ nodeStyle: styles,
683
+ matchedStyles: widgetData.data.matchedCascade,
684
+ // This disables showing the nested traces and detailed information in the widget.
685
+ propertyTraces: null,
686
+ allowUserControl: false,
687
+ filterText: new RegExp(widgetData.data.properties.join('|'), 'i')
688
+ })}></devtools-widget>`;
690
689
  // clang-format on
691
690
 
692
- return {renderedWidget: widget, revealable: new Elements.ElementsPanel.NodeComputedStyles(domNodeForId)};
691
+ return {renderedWidget, revealable: new Elements.ElementsPanel.NodeComputedStyles(domNodeForId)};
693
692
  }
694
693
 
695
694
  async function makeCoreVitalsWidget(widgetData: CoreVitalsAiWidget): Promise<WidgetMakerResponse|null> {
696
- const widgetConfig = UI.Widget.widgetConfig(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data});
697
-
698
695
  // clang-format off
699
- const widget = html`<devtools-widget class="core-vitals-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
696
+ const renderedWidget = html`<devtools-widget
697
+ class="core-vitals-widget" ${widget(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data})}>
698
+ </devtools-widget>`;
700
699
  // clang-format on
701
700
 
702
- return {
703
- renderedWidget: widget,
704
- revealable: new TimelineUtils.Helpers.RevealableCoreVitals(widgetData.data.insightSetKey)
705
- };
701
+ return {renderedWidget, revealable: new TimelineUtils.Helpers.RevealableCoreVitals(widgetData.data.insightSetKey)};
706
702
  }
707
703
 
708
704
  async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): Promise<WidgetMakerResponse|null> {
@@ -711,19 +707,17 @@ async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): P
711
707
  return null;
712
708
  }
713
709
 
714
- const widgetConfig = UI.Widget.widgetConfig(Elements.StandaloneStylesContainer.StandaloneStylesContainer, {
715
- domNode: domNodeForId,
716
- filter: widgetData.data.selector ? new RegExp(widgetData.data.selector) : null,
717
- });
718
-
719
710
  // clang-format off
720
- const widget = html`<devtools-widget
721
- class="styling-preview-widget"
722
- .widgetConfig=${widgetConfig}
723
- ></devtools-widget>`;
711
+ const renderedWidget = html`<devtools-widget
712
+ class="styling-preview-widget"
713
+ ${widget(Elements.StandaloneStylesContainer.StandaloneStylesContainer, {
714
+ domNode: domNodeForId,
715
+ filter: widgetData.data.selector ? new RegExp(widgetData.data.selector) : null,
716
+ })}>
717
+ </devtools-widget>`;
724
718
  // clang-format on
725
719
 
726
- return {renderedWidget: widget, revealable: domNodeForId};
720
+ return {renderedWidget, revealable: domNodeForId};
727
721
  }
728
722
 
729
723
  function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
@@ -756,6 +750,34 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
756
750
  // clang-format on
757
751
  }
758
752
 
753
+ async function makeDomTreeWidget(widgetData: DomTreeAiWidget): Promise<WidgetMakerResponse|null> {
754
+ const root = widgetData.data.root;
755
+ if (!(root instanceof SDK.DOMModel.DOMNodeSnapshot)) {
756
+ return null;
757
+ }
758
+
759
+ const widgetConfig = UI.Widget.widgetConfig(Elements.ElementsTreeOutline.DOMTreeWidget, {
760
+ maxTreeDepth: 2,
761
+ enableContextMenu: false,
762
+ showComments: false,
763
+ showAIButton: false,
764
+ disableEdits: true,
765
+ expandRoot: true,
766
+ rootDOMNode: root,
767
+ visibleWidth: 400,
768
+ wrap: true,
769
+ });
770
+
771
+ // clang-format off
772
+ const widget = html`<devtools-widget class="dom-tree-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
773
+ // clang-format on
774
+
775
+ return {
776
+ renderedWidget: widget,
777
+ revealable: new SDK.DOMModel.DeferredDOMNode(root.domModel().target(), root.backendNodeId()),
778
+ };
779
+ }
780
+
759
781
  /**
760
782
  * Renders AI-defined UI widgets.
761
783
  * When a ModelChatMessage contains a WidgetPart, or a Step has widgets,
@@ -786,6 +808,8 @@ async function renderWidgets(
786
808
  response = await makeCoreVitalsWidget(widgetData);
787
809
  } else if (widgetData.name === 'STYLE_PROPERTIES') {
788
810
  response = await makeStylePropertiesWidget(widgetData);
811
+ } else if (widgetData.name === 'DOM_TREE') {
812
+ response = await makeDomTreeWidget(widgetData);
789
813
  }
790
814
  return renderWidgetResponse(response);
791
815
  }));
@@ -169,7 +169,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
169
169
  </div>
170
170
  </div>
171
171
  `}
172
- <devtools-widget class=${inputWidgetClasses} .widgetConfig=${UI.Widget.widgetConfig(ChatInput, {
172
+ <devtools-widget class=${inputWidgetClasses} ${widget(ChatInput, {
173
173
  isLoading: input.isLoading,
174
174
  blockedByCrossOrigin: input.blockedByCrossOrigin,
175
175
  isTextInputDisabled: input.isTextInputDisabled,
@@ -22,7 +22,7 @@ import * as ApplicationComponents from './components/components.js';
22
22
 
23
23
  const {styleMap, classMap, ref} = Directives;
24
24
  const {linkifyURL} = Components.Linkifier.Linkifier;
25
- const {widget, widgetConfig} = UI.Widget;
25
+ const {widget} = UI.Widget;
26
26
 
27
27
  const UIStrings = {
28
28
  /**
@@ -719,7 +719,7 @@ function renderProtocolHandlers(data: ProtocolHandlersSectionData, output: ViewO
719
719
  // clang-format off
720
720
  return html`${renderSectionHeader(i18nString(UIStrings.protocolHandlers), output)}
721
721
  <div class="report-row">
722
- <devtools-widget .widgetConfig=${widgetConfig(
722
+ <devtools-widget ${widget(
723
723
  ApplicationComponents.ProtocolHandlersView.ProtocolHandlersView,
724
724
  {protocolHandlers: data.protocolHandlers, manifestLink: data.manifestLink})}
725
725
  ${ref(setFocusOnSection(i18nString(UIStrings.protocolHandlers), output))}>
@@ -82,6 +82,7 @@ const UIStrings = {
82
82
  const str_ = i18n.i18n.registerUIStrings('panels/application/CookieItemsView.ts', UIStrings);
83
83
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
84
84
  const {Size} = Geometry;
85
+ const {widget} = UI.Widget;
85
86
 
86
87
  interface CookiePreviewWidgetInput {
87
88
  cookie: SDK.Cookie.Cookie|null;
@@ -184,10 +185,8 @@ type View = (input: CookieItemsViewInput, output: CookieItemsViewOutput, target:
184
185
  export const DEFAULT_VIEW: View = (input, output, target) => {
185
186
  // clang-format off
186
187
  render(html`<style>${cookieItemsViewStyles}</style>
187
- <devtools-widget class="storage-view"
188
- .widgetConfig=${UI.Widget.widgetConfig(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}>
189
- <devtools-widget
190
- .widgetConfig=${UI.Widget.widgetConfig(StorageItemsToolbar, {
188
+ <devtools-widget class="storage-view" ${widget(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}>
189
+ <devtools-widget ${widget(StorageItemsToolbar, {
191
190
  onDeleteSelectedCallback: input.onDeleteSelectedItems,
192
191
  onDeleteAllCallback: input.onDeleteAllItems,
193
192
  onRefreshCallback: input.onRefreshItems,
@@ -196,10 +195,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
196
195
  ${UI.Widget.widgetRef(StorageItemsToolbar, toolbar => { output.toolbar = toolbar; })}
197
196
  ></devtools-widget>
198
197
  <devtools-split-view sidebar-position="second" name="cookie-items-split-view-state">
199
- <devtools-widget
200
- slot="main"
201
- .widgetConfig=${UI.Widget.widgetConfig(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}>
202
- <devtools-widget slot="main" .widgetConfig=${UI.Widget.widgetConfig(CookieTable.CookiesTable.CookiesTable, {
198
+ <devtools-widget slot="main" ${widget(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}>
199
+ <devtools-widget slot="main" ${widget(CookieTable.CookiesTable.CookiesTable, {
203
200
  cookieDomain: input.cookieDomain,
204
201
  cookiesData: input.cookiesData,
205
202
  saveCallback: input.onSaveCookie,
@@ -210,15 +207,12 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
210
207
  })}
211
208
  ></devtools-widget>
212
209
  </devtools-widget>
213
- <devtools-widget
214
- slot="sidebar"
215
- .widgetConfig=${UI.Widget.widgetConfig(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}
210
+ <devtools-widget slot="sidebar" ${widget(UI.Widget.VBox, {minimumSize: new Size(0, 50)})}
216
211
  jslog=${VisualLogging.pane('preview').track({resize: true})}>
217
212
  ${input.selectedCookie ?
218
- html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(CookiePreviewWidget, {
219
- cookie: input.selectedCookie
220
- })}></devtools-widget>` :
221
- html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(UI.EmptyWidget.EmptyWidget, {
213
+ html`<devtools-widget ${widget(CookiePreviewWidget, {cookie: input.selectedCookie})}>
214
+ </devtools-widget>` :
215
+ html`<devtools-widget ${widget(UI.EmptyWidget.EmptyWidget, {
222
216
  header: i18nString(UIStrings.noCookieSelected),
223
217
  text: i18nString(UIStrings.selectACookieToPreviewItsValue)
224
218
  })}></devtools-widget>`}
@@ -19,7 +19,7 @@ import {
19
19
  type SessionAndEvents
20
20
  } from './DeviceBoundSessionsModel.js';
21
21
  import deviceBoundSessionsViewStyles from './deviceBoundSessionsView.css.js';
22
- const {widget, widgetConfig} = UI.Widget;
22
+ const {widget} = UI.Widget;
23
23
 
24
24
  const UIStrings = {
25
25
  /**
@@ -583,10 +583,8 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
583
583
  <style>${UI.inspectorCommonStyles}</style>
584
584
  <style>${deviceBoundSessionsViewStyles}</style>
585
585
  ${toolbarHtml}
586
- <devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget, {
587
- header: defaultTitle,
588
- text: defaultDescription
589
- })} jslog=${VisualLogging.pane('device-bound-sessions-empty')}></devtools-widget>
586
+ <devtools-widget ${widget(UI.EmptyWidget.EmptyWidget, {header: defaultTitle, text: defaultDescription})} jslog=${
587
+ VisualLogging.pane('device-bound-sessions-empty')}></devtools-widget>
590
588
  `,
591
589
  target);
592
590
  return;
@@ -30,7 +30,7 @@ import * as ApplicationComponents from './components/components.js';
30
30
  import frameDetailsReportViewStyles from './frameDetailsReportView.css.js';
31
31
  import {OriginTrialTreeView} from './OriginTrialTreeView.js';
32
32
 
33
- const {widget, widgetConfig} = UI.Widget;
33
+ const {widget} = UI.Widget;
34
34
 
35
35
  const UIStrings = {
36
36
  /**
@@ -327,7 +327,7 @@ function renderOriginTrial(trials: Protocol.Page.OriginTrial[]|null): LitTemplat
327
327
  </devtools-link>
328
328
  </span>
329
329
  </devtools-report-section>
330
- <devtools-widget class="span-cols" .widgetConfig=${widgetConfig(OriginTrialTreeView, {data})}>
330
+ <devtools-widget class="span-cols" ${widget(OriginTrialTreeView, {data})}>
331
331
  </devtools-widget>
332
332
  <devtools-report-divider></devtools-report-divider>`;
333
333
  // clang-format on
@@ -13,7 +13,7 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
13
13
 
14
14
  import * as ApplicationComponents from './components/components.js';
15
15
 
16
- const {widget, widgetConfig} = UI.Widget;
16
+ const {widget} = UI.Widget;
17
17
 
18
18
  const UIStrings = {
19
19
  /**
@@ -97,7 +97,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLEl
97
97
  } else {
98
98
  // clang-format off
99
99
  render(html`
100
- <devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget, {
100
+ <devtools-widget ${widget(UI.EmptyWidget.EmptyWidget, {
101
101
  header: i18nString(UIStrings.noReportOrEndpoint),
102
102
  text: i18nString(UIStrings.reportingApiDescription),
103
103
  link: REPORTING_API_EXPLANATION_URL,