chrome-devtools-frontend 1.0.1613465 → 1.0.1613625

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.
@@ -25,9 +25,13 @@ description: MANDATORY: Activate this skill ANY TIME you need to build the proje
25
25
  - `npm run lint` will execute ESLint and StyleLint. It will report any violations and automatically fix them where possible.
26
26
  - To run the linter on a specific file or directory, you can run `npm run lint -- <PATH>` where `PATH` is a path to a file or directory. This will also automatically fix violations where possible.
27
27
 
28
+ ## Presubmit
29
+
30
+ - `git cl presubmit -u` will check if the current change is ready for upload. It will also format and lint the change.
31
+
28
32
  ## Best practices
29
33
 
30
34
  - Run tests often to verify your changes.
31
35
  - Prefer using a fast build, if it exists, to keep the feedback loop shorter.
32
36
  - Periodically compile with TypeScript to check for type errors.
33
- - Run linting at the end of your code changes.
37
+ - Run `git cl presubmit -u` at the end of your code changes.
@@ -8,7 +8,7 @@
8
8
  * TODO: Once the proposal is merged, just replace `MapWithDefault` with `Map` and remove it.
9
9
  **/
10
10
  export class MapWithDefault<K, V> extends Map<K, V> {
11
- getOrInsert(key: K, defaultValue: V): V {
11
+ override getOrInsert(key: K, defaultValue: V): V {
12
12
  if (!this.has(key)) {
13
13
  this.set(key, defaultValue);
14
14
  }
@@ -16,7 +16,7 @@ export class MapWithDefault<K, V> extends Map<K, V> {
16
16
  return this.get(key) as V;
17
17
  }
18
18
 
19
- getOrInsertComputed(key: K, callbackFunction: (key: K) => V): V {
19
+ override getOrInsertComputed(key: K, callbackFunction: (key: K) => V): V {
20
20
  if (!this.has(key)) {
21
21
  this.set(key, callbackFunction(key));
22
22
  }
@@ -119,31 +119,34 @@ export class ObjectWrapper<Events> implements EventTarget<Events> {
119
119
  export function eventMixin<Events, Base extends Platform.Constructor.Constructor<object>>(base: Base) {
120
120
  console.assert(base !== HTMLElement);
121
121
  return class EventHandling extends base implements EventTarget<Events> {
122
- #events = new ObjectWrapper<Events>();
122
+ // Note that the weird name is due to TSC disallowing private/protected fields in
123
+ // anonmous exported classes. We use a `__` prefix to prevent clashes with `base`.
124
+ // eslint-disable-next-line @devtools/no-underscored-properties, @typescript-eslint/naming-convention
125
+ __events = new ObjectWrapper<Events>();
123
126
 
124
127
  addEventListener<T extends keyof Events>(
125
128
  eventType: T, listener: (arg0: EventTargetEvent<Events[T]>) => void,
126
129
  thisObject?: Object): EventDescriptor<Events, T> {
127
- return this.#events.addEventListener(eventType, listener, thisObject);
130
+ return this.__events.addEventListener(eventType, listener, thisObject);
128
131
  }
129
132
 
130
133
  once<T extends keyof Events>(eventType: T): Promise<Events[T]> {
131
- return this.#events.once(eventType);
134
+ return this.__events.once(eventType);
132
135
  }
133
136
 
134
137
  removeEventListener<T extends keyof Events>(
135
138
  eventType: T, listener: (arg0: EventTargetEvent<Events[T]>) => void, thisObject?: Object): void {
136
- this.#events.removeEventListener(eventType, listener, thisObject);
139
+ this.__events.removeEventListener(eventType, listener, thisObject);
137
140
  }
138
141
 
139
142
  hasEventListeners(eventType: keyof Events): boolean {
140
- return this.#events.hasEventListeners(eventType);
143
+ return this.__events.hasEventListeners(eventType);
141
144
  }
142
145
 
143
146
  dispatchEventToListeners<T extends keyof Events>(
144
147
  eventType: Platform.TypeScriptUtilities.NoUnion<T>,
145
148
  ...eventData: EventPayloadToRestParameters<Events, T>): void {
146
- this.#events.dispatchEventToListeners(eventType, ...eventData);
149
+ this.__events.dispatchEventToListeners(eventType, ...eventData);
147
150
  }
148
151
  };
149
152
  }
@@ -0,0 +1,77 @@
1
+ # AI Agents
2
+
3
+ This directory contains the implementations of various AI agents used in the AI Assistance panel in Chrome DevTools.
4
+
5
+ ## Performance Agent
6
+
7
+ The `PerformanceAgent` analyzes performance traces. This documentation details the specific data provided to the agent and the data it can retrieve via functions.
8
+
9
+ ### Initial Data Provided to the Agent
10
+
11
+ When a conversation starts or the context changes, the agent is provided with a set of "facts" and query enhancements. This data forms the agent's base knowledge about the trace.
12
+
13
+ #### Trace Facts (Initial Context)
14
+
15
+ Facts are text-based data summaries injected into the conversation. The agent receives:
16
+
17
+ - **Data Schema Descriptions**: Text explaining the format of call frame and network data to help the agent interpret subsequent tool outputs.
18
+ - **Environment State**: Flags indicating if the trace is a fresh recording or loaded from an external file.
19
+ - **Trace Summary Data**: A text summary containing key performance metrics (LCP, INP, CLS) and basic trace metadata.
20
+ - **Critical Requests Data**: A text list of the most critical network requests identified in the trace.
21
+ - **Main Thread Activity Data**: A bottom-up aggregated summary of where time was spent on the main thread.
22
+ - **Longest Tasks Data**: A list of the longest tasks found on the main thread.
23
+ - **Third Party Activity Data**: A summary of time spent on third-party scripts.
24
+
25
+ #### Context Selection (Additional initial context)
26
+
27
+ The agent receives additional initial context based on the user's context selection. The context can be a Trace, an Insight, or a specific Event:
28
+
29
+ - **Trace Context**: No additional context is added beyond the base trace facts.
30
+ - **Insight Context**: The agent receives the name and key of the specific performance insight selected.
31
+ - **Event Context**: The agent receives the serialized details of the specific trace event selected.
32
+
33
+
34
+ ### Data Retrieval Functions (Tools)
35
+
36
+ The agent can request additional data by calling functions. Here is the data the agent receives from each function:
37
+
38
+ #### `getInsightDetails`
39
+ - **Arguments**: `insightSetId` (string), `insightName` (string)
40
+ - **Data Returned to Agent**: A detailed text representation of the specific insight (e.g., LCP breakdown components, render-blocking resource URLs).
41
+
42
+ #### `getEventByKey`
43
+ - **Arguments**: `eventKey` (string)
44
+ - **Data Returned to Agent**: The full JSON representation of the specific trace event.
45
+
46
+ #### `getMainThreadTrackSummary`
47
+ - **Arguments**: `min` (integer, optional), `max` (integer, optional)
48
+ - **Data Returned to Agent**: A comprehensive text summary of main thread activity *within the specified bounds*.
49
+ - **Note**: This differs from the initial "Main Thread Activity Data" fact as it is scoped to the provided time range and includes significantly more data: a **top-down tree**, a **bottom-up tree**, a **third-parties summary**, and a list of **related insights** for events in that range.
50
+
51
+ #### `getNetworkTrackSummary`
52
+ - **Arguments**: `min` (integer, optional), `max` (integer, optional)
53
+ - **Data Returned to Agent**: A text summary of network requests and activity within the bounds.
54
+
55
+ #### `getDetailedCallTree`
56
+ - **Arguments**: `eventKey` (string)
57
+ - **Data Returned to Agent**: A detailed call tree representation for the specified event.
58
+
59
+ #### `addElementAnnotation`
60
+ - **Arguments**: `elementId` (string), `annotationMessage` (string)
61
+ - **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function also modifies the DevTools UI by adding a visual annotation).
62
+
63
+ #### `addNetworkRequestAnnotation`
64
+ - **Arguments**: `eventKey` (string), `annotationMessage` (string)
65
+ - **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function also modifies the DevTools UI by adding a visual annotation).
66
+
67
+ #### `getFunctionCode`
68
+ - **Arguments**: `scriptUrl` (string), `line` (integer), `column` (integer)
69
+ - **Data Returned to Agent**: The source code lines for the function, annotated with performance cost per line if available.
70
+
71
+ #### `getResourceContent`
72
+ - **Arguments**: `url` (string)
73
+ - **Data Returned to Agent**: The full text content of the specified resource (e.g., script file).
74
+
75
+ #### `selectEventByKey`
76
+ - **Arguments**: `eventKey` (string)
77
+ - **Data Returned to Agent**: Confirmation of success or an error message. (Note: This function also modifies the DevTools UI by selecting the event in the flamechart).
@@ -17,13 +17,15 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
17
17
  readonly previousCallbacks = new Set<number>();
18
18
  readonly worker: PlatformApi.HostRuntime.Worker;
19
19
  interval?: number;
20
+ readonly workerUrl?: string;
20
21
 
21
- constructor(eventHandler: (arg0: string, arg1: string) => void) {
22
+ constructor(eventHandler: (arg0: string, arg1: string) => void, workerUrl?: string) {
22
23
  super();
23
24
  this.eventHandler = eventHandler;
25
+ this.workerUrl = workerUrl;
24
26
  this.worker = Platform.HostRuntime.HOST_RUNTIME.createWorker(
25
- new URL('../../entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.js', import.meta.url)
26
- .toString());
27
+ workerUrl ?? import.meta.resolve('../../entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.js'),
28
+ );
27
29
  this.worker.onmessage = this.messageReceived.bind(this);
28
30
  }
29
31
 
@@ -236,7 +238,7 @@ export class HeapSnapshotLoaderProxy extends HeapSnapshotProxyObject implements
236
238
 
237
239
  async close(): Promise<void> {
238
240
  await this.callMethodPromise('close');
239
- const secondWorker = new HeapSnapshotWorkerProxy(() => {});
241
+ const secondWorker = new HeapSnapshotWorkerProxy(() => {}, this.worker.workerUrl);
240
242
  const channel = new MessageChannel();
241
243
  await secondWorker.setupForSecondaryInit(channel.port2);
242
244
  const snapshotProxy = await this.callFactoryMethodPromise('buildSnapshot', HeapSnapshotProxy, [channel.port1]);
@@ -729,7 +729,7 @@ export function renderStep({step, isLoading, markdownRenderer, isLast}: {
729
729
  // clang-format off
730
730
  return html`
731
731
  <details class=${stepClasses}
732
- jslog=${VisualLogging.section('step')}
732
+ jslog=${VisualLogging.expand('step').track({click: true})}
733
733
  .open=${Boolean(step.requestApproval)}>
734
734
  <summary>
735
735
  <div class="summary">
@@ -755,6 +755,7 @@ interface WidgetMakerResponse {
755
755
  customRevealTitle?: Platform.UIString.LocalizedString;
756
756
  // Can be null if the widget is only used to add the Reveal CTA.
757
757
  title: Lit.LitTemplate|Platform.UIString.LocalizedString|null;
758
+ jslogContext?: string;
758
759
  }
759
760
 
760
761
  const nodeCache = new Map<Protocol.DOM.BackendNodeId, SDK.DOMModel.DOMNode>();
@@ -823,6 +824,7 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
823
824
  </span>
824
825
  </span>`,
825
826
  // clang-format on
827
+ jslogContext: 'computed-styles',
826
828
  };
827
829
  }
828
830
 
@@ -836,6 +838,7 @@ async function makeCoreWebVitalsWidget(widgetData: CoreVitalsAiWidget): Promise<
836
838
  renderedWidget,
837
839
  revealable: new TimelineUtils.Helpers.RevealableCoreVitals(widgetData.data.insightSetKey),
838
840
  title: lockedString(UIStringsNotTranslate.coreVitals),
841
+ jslogContext: 'core-web-vitals',
839
842
  };
840
843
  }
841
844
 
@@ -872,6 +875,7 @@ async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): P
872
875
  node: domNodeForId,
873
876
  })}
874
877
  ></devtools-widget>`,
878
+ jslogContext: 'standalone-styles',
875
879
  };
876
880
  }
877
881
 
@@ -894,6 +898,7 @@ async function makeLcpBreakdownWidget(widgetData: LcpBreakdownAiWidget): Promise
894
898
  renderedWidget,
895
899
  revealable: new TimelineUtils.Helpers.RevealableInsight(insight),
896
900
  title: lockedString(UIStringsNotTranslate.lcpBreakdown),
901
+ jslogContext: 'lcp-breakdown',
897
902
  };
898
903
  }
899
904
 
@@ -922,7 +927,8 @@ async function makeBottomUpTimelineTreeWidget(widgetData: BottomUpTreeAiWidget):
922
927
  return {
923
928
  renderedWidget,
924
929
  revealable: new TimelineUtils.Helpers.RevealableBottomUpProfile(widgetData.data.bounds),
925
- title: lockedString(UIStringsNotTranslate.bottomUpTree)
930
+ title: lockedString(UIStringsNotTranslate.bottomUpTree),
931
+ jslogContext: 'bottom-up',
926
932
  };
927
933
  }
928
934
 
@@ -947,6 +953,7 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
947
953
  <devtools-button class="widget-reveal-button"
948
954
  .variant=${Buttons.Button.Variant.TEXT}
949
955
  .accessibleLabel=${lockedString(UIStringsNotTranslate.reveal)}
956
+ .jslogContext=${'reveal'}
950
957
  @click=${onReveal}
951
958
  >
952
959
  ${response.customRevealTitle ?? lockedString(UIStringsNotTranslate.reveal)}
@@ -956,7 +963,7 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
956
963
 
957
964
  // clang-format off
958
965
  return html`
959
- <div class=${classes}>
966
+ <div class=${classes} jslog=${ifDefined(response.jslogContext ? VisualLogging.section(response.jslogContext) : undefined)}>
960
967
  ${response.title ? html`
961
968
  <div class="widget-header">
962
969
  <h3 class="widget-name">${response.title}</h3>
@@ -986,6 +993,7 @@ async function makePerformanceTraceWidget(widgetData: PerformanceTraceAiWidget):
986
993
  title: null,
987
994
  revealable: new Timeline.TimelinePanel.ParsedTraceRevealable(widgetData.data.parsedTrace),
988
995
  customRevealTitle: lockedString(UIStringsNotTranslate.revealTrace),
996
+ jslogContext: 'performance-trace',
989
997
  };
990
998
  }
991
999
 
@@ -1045,6 +1053,7 @@ async function makeDomTreeWidget(widgetData: DomTreeAiWidget): Promise<WidgetMak
1045
1053
  renderedWidget,
1046
1054
  revealable: new SDK.DOMModel.DeferredDOMNode(root.domModel().target(), root.backendNodeId()),
1047
1055
  title: lockedString(UIStringsNotTranslate.lcpElement),
1056
+ jslogContext: 'dom-snapshot',
1048
1057
  };
1049
1058
  }
1050
1059
 
@@ -1607,5 +1616,6 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
1607
1616
  renderedWidget: template,
1608
1617
  revealable: new TimelineUtils.Helpers.RevealableTimeRange(bounds),
1609
1618
  title: lockedString(UIStringsNotTranslate.performanceSummary),
1619
+ jslogContext: 'timeline-range-summary',
1610
1620
  };
1611
1621
  }
@@ -10,6 +10,7 @@ import * as Buttons from '../../../ui/components/buttons/buttons.js';
10
10
  import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
11
11
  import * as UI from '../../../ui/legacy/legacy.js';
12
12
  import * as Lit from '../../../ui/lit/lit.js';
13
+ import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
13
14
 
14
15
  import styles from './exportForAgentsDialog.css.js';
15
16
 
@@ -85,7 +86,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
85
86
 
86
87
  render(html`
87
88
  <style>${styles}</style>
88
- <div class="export-for-agents-dialog">
89
+ <div class="export-for-agents-dialog" jslog=${VisualLogging.dialog('ai-export-for-agents')}>
89
90
  <header>
90
91
  <h1 id="export-for-agents-dialog-title" tabindex="-1">
91
92
  ${i18nString(UIStrings.exportForAgents)}
@@ -9,6 +9,7 @@ import * as Input from '../../../ui/components/input/input.js';
9
9
  import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
10
10
  import * as UI from '../../../ui/legacy/legacy.js';
11
11
  import * as Lit from '../../../ui/lit/lit.js';
12
+ import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
12
13
 
13
14
  import chatMessageStyles from './chatMessage.css.js';
14
15
  import {type ModelChatMessage, renderStep, type Step, titleForStep} from './ChatMessage.js';
@@ -119,14 +120,14 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate,
119
120
 
120
121
  // clang-format off
121
122
  return html`
122
- <div class="inline-wrapper" ?data-open=${input.isExpanded}>
123
+ <div class="inline-wrapper" ?data-open=${input.isExpanded} jslog=${VisualLogging.section('walkthrough-container')}>
123
124
  <span class="inline-icon">
124
125
  ${input.isLoading ?
125
126
  html`<devtools-spinner aria-label=${lockedString(UIStrings.inProgress)}></devtools-spinner>` :
126
127
  html`<devtools-icon name=${icon}></devtools-icon>`
127
128
  }
128
129
  </span>
129
- <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle}>
130
+ <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle} jslog=${VisualLogging.expand('walkthrough').track({click: true})}>
130
131
  <summary ?data-has-widgets=${!input.isLoading && hasWidgets}>
131
132
  <span class="walkthrough-inline-title">
132
133
  ${input.isExpanded ?
@@ -151,7 +152,7 @@ function renderSidebarWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate
151
152
 
152
153
  // clang-format off
153
154
  return html`
154
- <div class="walkthrough-view">
155
+ <div class="walkthrough-view" jslog=${VisualLogging.section('walkthrough-container')}>
155
156
  <div class="walkthrough-header">
156
157
  <h2 class="walkthrough-title">${i18nString(UIStrings.title)}</h2>
157
158
  <devtools-button
@@ -2290,7 +2290,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2290
2290
  do {
2291
2291
  if (cursor.name === ':') {
2292
2292
  name = suggestionText.slice(node.from, cursor.from);
2293
- value = suggestionText.slice(cursor.to + 1, node.to);
2293
+ value = suggestionText.slice(cursor.to, node.to);
2294
2294
  }
2295
2295
  } while (cursor.nextSibling());
2296
2296
  }
@@ -2350,6 +2350,8 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2350
2350
  }
2351
2351
 
2352
2352
  if (suggestionForCurrentPrompt !== textAfterAccept) {
2353
+ // Explicitly set the query range as it is cleared during `acceptAutoComplete`
2354
+ this.queryRange = new TextUtils.TextRange.TextRange(0, 0, 0, textAfterAccept.length);
2353
2355
  // Re-apply the ghost text for the remainder
2354
2356
  this.applySuggestion({text: suggestionForCurrentPrompt}, true);
2355
2357
  }
@@ -217,6 +217,7 @@ export class DeviceModeToolbar {
217
217
  private widthInput: EmulationComponents.DeviceSizeInputElement.SizeInputElement;
218
218
  private heightInput: EmulationComponents.DeviceSizeInputElement.SizeInputElement;
219
219
  private deviceScaleItem!: UI.Toolbar.ToolbarComboBox;
220
+ private deviceScaleItems: UI.Toolbar.ToolbarItem[] = [];
220
221
  private deviceSelectItem!: UI.Toolbar.ToolbarComboBox;
221
222
  private scaleItem!: UI.Toolbar.ToolbarComboBox;
222
223
  private uaItem!: UI.Toolbar.ToolbarComboBox;
@@ -341,10 +342,14 @@ export class DeviceModeToolbar {
341
342
  this.deviceScaleItem = new UI.Toolbar.ToolbarComboBox(
342
343
  this.onDeviceScaleChange.bind(this), i18nString(UIStrings.devicePixelRatio), 'dark-text', 'device-pixel-ratio');
343
344
  this.deviceScaleItem.turnShrinkable();
344
- this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
345
345
  const deviceScaleSpan = uiI18n.getFormatLocalizedString(str_, UIStrings.dpr, {PH1: this.deviceScaleItem.element});
346
- mainToolbar.append(...deviceScaleSpan.childNodes);
347
- mainToolbar.appendToolbarItem(this.deviceScaleItem);
346
+ for (const node of Array.from(deviceScaleSpan.childNodes)) {
347
+ const item = node === this.deviceScaleItem.element ? this.deviceScaleItem :
348
+ new UI.Toolbar.ToolbarText(node.textContent || '');
349
+ item.setVisible(this.showDeviceScaleFactorSetting.get());
350
+ this.deviceScaleItems.push(item);
351
+ mainToolbar.appendToolbarItem(item);
352
+ }
348
353
  mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
349
354
  this.uaItem = new UI.Toolbar.ToolbarComboBox(
350
355
  this.onUAChange.bind(this), i18nString(UIStrings.deviceType), 'dark-text', 'device-type');
@@ -758,7 +763,11 @@ export class DeviceModeToolbar {
758
763
 
759
764
  private updateDeviceScaleFactorVisibility(): void {
760
765
  if (this.deviceScaleItem) {
761
- this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
766
+ const visible = this.showDeviceScaleFactorSetting.get();
767
+ this.deviceScaleItem.setVisible(visible);
768
+ for (const item of this.deviceScaleItems) {
769
+ item.setVisible(visible);
770
+ }
762
771
  }
763
772
  }
764
773
 
@@ -300,7 +300,7 @@ export class HeapSnapshotSortableDataGrid extends Common.ObjectWrapper
300
300
  let field1 = nodeA[sortFields.fieldName1];
301
301
  // @ts-expect-error
302
302
  let field2 = nodeB[sortFields.fieldName1];
303
- let result: number|(0 | 1 | -1) = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
303
+ let result: number = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
304
304
  if (!sortFields.ascending1) {
305
305
  result = -result;
306
306
  }
@@ -136,31 +136,23 @@ export class HeapSnapshotGridNode extends
136
136
  Common.ObjectWrapper.eventMixin<HeapSnapshotGridNode.EventTypes, typeof HeapSnapshotGridNodeBase>(
137
137
  HeapSnapshotGridNodeBase) {
138
138
  dataGridInternal: HeapSnapshotSortableDataGrid;
139
- instanceCount: number;
140
- readonly savedChildren: Map<number, HeapSnapshotGridNode>;
139
+ instanceCount = 0;
140
+ readonly savedChildren = new Map<number, HeapSnapshotGridNode>();
141
+ /**
142
+ * List of position ranges for all visible nodes: [startPos1, endPos1),...,[startPosN, endPosN)
143
+ * Position is an item position in the provider.
144
+ */
141
145
  retrievedChildrenRanges: Array<{
142
146
  from: number,
143
147
  to: number,
144
- }>;
145
- providerObject: HeapSnapshotModel.ChildrenProvider.ChildrenProvider|null;
146
- reachableFromWindow: boolean;
148
+ }> = [];
149
+ providerObject: HeapSnapshotModel.ChildrenProvider.ChildrenProvider|null = null;
150
+ reachableFromWindow = false;
147
151
  populated?: boolean;
148
152
 
149
153
  constructor(tree: HeapSnapshotSortableDataGrid, hasChildren: boolean) {
150
154
  super(null, hasChildren);
151
155
  this.dataGridInternal = tree;
152
- this.instanceCount = 0;
153
-
154
- this.savedChildren = new Map();
155
-
156
- /**
157
- * List of position ranges for all visible nodes: [startPos1, endPos1),...,[startPosN, endPosN)
158
- * Position is an item position in the provider.
159
- */
160
- this.retrievedChildrenRanges = [];
161
-
162
- this.providerObject = null;
163
- this.reachableFromWindow = false;
164
156
  }
165
157
 
166
158
  get name(): string|undefined {
@@ -64,6 +64,7 @@ export const enum ParameterType {
64
64
  BOOLEAN = 'boolean',
65
65
  ARRAY = 'array',
66
66
  OBJECT = 'object',
67
+ UNKNOWN = 'unknown',
67
68
  }
68
69
 
69
70
  interface BaseParameter {
@@ -100,7 +101,13 @@ interface ObjectParameter extends BaseParameter {
100
101
  value?: Parameter[];
101
102
  }
102
103
 
103
- export type Parameter = ArrayParameter|NumberParameter|StringParameter|BooleanParameter|ObjectParameter;
104
+ interface UnknownParameter extends BaseParameter {
105
+ type: ParameterType.UNKNOWN;
106
+ value?: string;
107
+ }
108
+
109
+ export type Parameter =
110
+ ArrayParameter|NumberParameter|StringParameter|BooleanParameter|ObjectParameter|UnknownParameter;
104
111
 
105
112
  export interface Command {
106
113
  command: string;
@@ -128,6 +135,8 @@ interface ViewInput {
128
135
  onParameterKeydown: (event: KeyboardEvent) => void;
129
136
  onParameterKeyBlur: (event: Event) => void;
130
137
  onParameterValueBlur: (event: Event) => void;
138
+ displayTargetSelector?: boolean;
139
+ displayCommandInput?: boolean;
131
140
  }
132
141
 
133
142
  export type View = (input: ViewInput, output: object, target: HTMLElement) => void;
@@ -149,6 +158,7 @@ const defaultValueByType = new Map<string, string|number|boolean>([
149
158
  ['string', ''],
150
159
  ['number', 0],
151
160
  ['boolean', false],
161
+ ['unknown', ''],
152
162
  ]);
153
163
 
154
164
  const DUMMY_DATA = 'dummy';
@@ -176,6 +186,8 @@ export class JSONEditor extends Common.ObjectWrapper.eventMixin<EventTypes, type
176
186
  #targetId?: string;
177
187
  #hintPopoverHelper?: UI.PopoverHelper.PopoverHelper;
178
188
  #view: View;
189
+ displayTargetSelector = true;
190
+ displayCommandInput = true;
179
191
 
180
192
  constructor(element: HTMLElement, view = DEFAULT_VIEW) {
181
193
  super(element, {useShadowDom: true});
@@ -240,6 +252,10 @@ export class JSONEditor extends Common.ObjectWrapper.eventMixin<EventTypes, type
240
252
  }
241
253
  }
242
254
 
255
+ set commandToDisplay(command: string) {
256
+ this.displayCommand(command, {});
257
+ }
258
+
243
259
  get targetId(): string|undefined {
244
260
  return this.#targetId;
245
261
  }
@@ -312,6 +328,13 @@ export class JSONEditor extends Common.ObjectWrapper.eventMixin<EventTypes, type
312
328
  }
313
329
  return nestedArrayParameters.length === 0 ? [] : nestedArrayParameters;
314
330
  }
331
+ case ParameterType.UNKNOWN: {
332
+ try {
333
+ return JSON.parse(parameter.value as string);
334
+ } catch {
335
+ return parameter.value;
336
+ }
337
+ }
315
338
  default: {
316
339
  return parameter.value;
317
340
  }
@@ -387,7 +410,7 @@ export class JSONEditor extends Common.ObjectWrapper.eventMixin<EventTypes, type
387
410
  typeRef: schema?.typeRef,
388
411
  value,
389
412
  description,
390
- } as Parameter;
413
+ } as unknown as Parameter;
391
414
  }
392
415
 
393
416
  #convertPrimitiveParameter(key: string, value: unknown, schema?: Parameter): Parameter {
@@ -967,6 +990,8 @@ export class JSONEditor extends Common.ObjectWrapper.eventMixin<EventTypes, type
967
990
  computeDropdownValues: (parameter: Parameter) => {
968
991
  return this.#computeDropdownValues(parameter);
969
992
  },
993
+ displayTargetSelector: this.displayTargetSelector,
994
+ displayCommandInput: this.displayCommandInput,
970
995
  };
971
996
  const viewOutput = {};
972
997
  this.#view(viewInput, viewOutput, this.contentElement);
@@ -1229,7 +1254,8 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
1229
1254
  render(html`
1230
1255
  <div class="wrapper" @keydown=${input.onKeydown} jslog=${VisualLogging.pane('command-editor').track({resize: true})}>
1231
1256
  <div class="editor-wrapper">
1232
- ${renderTargetSelectorRow(input)}
1257
+ ${input.displayTargetSelector !== false ? renderTargetSelectorRow(input) : nothing}
1258
+ ${input.displayCommandInput !== false ? html`
1233
1259
  <div class="row attribute padded">
1234
1260
  <div class="command">command<span class="separator">:</span></div>
1235
1261
  <devtools-suggestion-input
@@ -1241,7 +1267,7 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
1241
1267
  @blur=${input.onCommandInputBlur}
1242
1268
  class=${classMap({'json-input': true})}
1243
1269
  ></devtools-suggestion-input>
1244
- </div>
1270
+ </div>` : nothing}
1245
1271
  ${input.parameters.length ? html`
1246
1272
  <div class="row attribute padded">
1247
1273
  <div>parameters<span class="separator">:</span></div>
@@ -133,7 +133,7 @@ export class RecordingPlayer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
133
133
  throw new Error('could not find main page!');
134
134
  }
135
135
 
136
- browser.on('targetdiscovered', (targetInfo: Protocol.Target.TargetInfo) => {
136
+ browser.on('targetdiscovered', (targetInfo: ReturnType<puppeteer.Target['_getTargetInfo']>) => {
137
137
  // Pop-ups opened by the main target won't be auto-attached. Therefore,
138
138
  // we need to create a session for them explicitly. We user openedId
139
139
  // and type to classify a target as requiring a session.
@@ -684,9 +684,20 @@ export class AISettingsTab extends UI.Widget.VBox {
684
684
  .createSetting('console-insights-skip-reminder', true, Common.Settings.SettingStorageType.SESSION)
685
685
  .set(true);
686
686
  }
687
- } else if (setting.name === 'ai-assistance-enabled' && !setting.get()) {
688
- // If the "AI Assistance" is toggled off, we remove all the history entries related to the feature.
689
- void AiAssistanceModel.AiHistoryStorage.AiHistoryStorage.instance().deleteAll();
687
+ } else if (setting.name === 'ai-assistance-enabled') {
688
+ if (!setting.get()) {
689
+ // If the "AI Assistance" is toggled off, we remove all the history entries related to the feature.
690
+ void AiAssistanceModel.AiHistoryStorage.AiHistoryStorage.instance().deleteAll();
691
+ }
692
+
693
+ if (Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled && setting.get()) {
694
+ // If the user turns on ai-assistance whilst on the V2 experiment, they
695
+ // do not need to see the opt-in change management dialog. This dialog
696
+ // exists to inform users who opted-in to "V1" that in "V2" there are
697
+ // some data access changes. But if a user opts-in when on "V2", they
698
+ // do not need to see that dialog.
699
+ Common.Settings.Settings.instance().moduleSetting('ai-assistance-v2-opt-in-change-dialog-seen').set(true);
700
+ }
690
701
  }
691
702
  this.requestUpdate();
692
703
  }
@@ -91,7 +91,7 @@ export class PuppeteerConnectionHelper {
91
91
  undefined /* process */,
92
92
  undefined /* closeCallback */,
93
93
  undefined /* targetFilterCallback */,
94
- target => isPageTargetCallback((target as puppeteer.Target)._getTargetInfo()),
94
+ target => isPageTargetCallback((target as puppeteer.Target)._getTargetInfo() as Protocol.Target.TargetInfo),
95
95
  false /* waitForInitiallyDiscoveredTargets */,
96
96
  );
97
97
 
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable @devtools/no-imperative-dom-api */
5
5
 
6
+ // @ts-expect-error tsc 6 can't find type declarations for this file.
6
7
  import '../../../Images/Images.js';
7
8
 
8
9
  import iconStyles from './icon.css.js';
@@ -251,7 +251,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
251
251
  private proxyElementDisplay: string;
252
252
  private autocompletionTimeout: number;
253
253
  #title: string;
254
- private queryRange: TextUtils.TextRange.TextRange|null;
254
+ protected queryRange: TextUtils.TextRange.TextRange|null;
255
255
  private previousText: string;
256
256
  private currentSuggestion: Suggestion|null;
257
257
  private completionRequestId: number;
@@ -1932,7 +1932,7 @@ export namespace TreeViewElement {
1932
1932
  }
1933
1933
  }
1934
1934
 
1935
- export const ifExpanded = Lit.Directive.directive(class extends Lit.Directive.Directive {
1935
+ class IfExpandedDirective extends Lit.Directive.Directive {
1936
1936
  #partInfo: {type: Lit.Directive.PartType, startNode: Node};
1937
1937
  constructor(partInfo: Lit.Directive.PartInfo) {
1938
1938
  if (partInfo.type !== Lit.Directive.PartType.CHILD) {
@@ -1969,7 +1969,8 @@ export const ifExpanded = Lit.Directive.directive(class extends Lit.Directive.Di
1969
1969
  }
1970
1970
  return node.expanded;
1971
1971
  }
1972
- });
1972
+ }
1973
+ export const ifExpanded = Lit.Directive.directive(IfExpandedDirective);
1973
1974
 
1974
1975
  export class TreeElementWrapper extends HTMLElement {
1975
1976
  #treeElement?: TreeElement;
@@ -50,7 +50,7 @@ const DUMMY_COLUMN_ID = 'dummy'; // SortableDataGrid.create requires at least o
50
50
  * @attribute striped If true, the data grid will have striped rows.
51
51
  * @attribute displayName
52
52
  */
53
- class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate {
53
+ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate {
54
54
  static readonly observedAttributes = ['striped', 'name', 'inline', 'resize'];
55
55
 
56
56
  #dataGrid = SortableDataGrid.create([DUMMY_COLUMN_ID], [], '') as SortableDataGrid<DataGridElementNode>;
@@ -609,7 +609,7 @@ const INTERNAL_TOKEN: DataGridInternalToken = {
609
609
  token: 'DataGridInternalToken'
610
610
  };
611
611
 
612
- export const ifExpanded = Lit.Directive.directive(class extends Lit.Directive.Directive {
612
+ class IfExpandedDirective extends Lit.Directive.Directive {
613
613
  #partInfo: {type: Lit.Directive.PartType, startNode: Node};
614
614
  constructor(partInfo: Lit.Directive.PartInfo) {
615
615
  if (partInfo.type !== Lit.Directive.PartType.CHILD) {
@@ -639,4 +639,5 @@ export const ifExpanded = Lit.Directive.directive(class extends Lit.Directive.Di
639
639
  }
640
640
  return node.expanded;
641
641
  }
642
- });
642
+ }
643
+ export const ifExpanded = Lit.Directive.directive(IfExpandedDirective);
@@ -1050,6 +1050,7 @@ export const knownContextValues = new Set([
1050
1050
  'copy-visible-styled-selection',
1051
1051
  'copy-watch-expression-value',
1052
1052
  'copy-xpath',
1053
+ 'core-web-vitals',
1053
1054
  'corner-block-end-shape',
1054
1055
  'corner-block-start-shape',
1055
1056
  'corner-bottom-left-shape',
@@ -1363,6 +1364,7 @@ export const knownContextValues = new Set([
1363
1364
  'dom-node-inserted-into-document',
1364
1365
  'dom-node-removed',
1365
1366
  'dom-node-removed-from-document',
1367
+ 'dom-snapshot',
1366
1368
  'dom-subtree-modified',
1367
1369
  'dom-window.close',
1368
1370
  'dom-word-wrap',
@@ -2248,6 +2250,7 @@ export const knownContextValues = new Set([
2248
2250
  'layout',
2249
2251
  'layout-count',
2250
2252
  'layout-shifts',
2253
+ 'lcp-breakdown',
2251
2254
  'learn-more',
2252
2255
  'learn-more.ai-annotations',
2253
2256
  'learn-more.ai-assistance',
@@ -2975,6 +2978,7 @@ export const knownContextValues = new Set([
2975
2978
  'performance-full-default',
2976
2979
  'performance-insights',
2977
2980
  'performance-insights-default',
2981
+ 'performance-trace',
2978
2982
  'performance.history-item',
2979
2983
  'performance.monitor',
2980
2984
  'performance.sidebar-insights-category-select',
@@ -3962,6 +3966,7 @@ export const knownContextValues = new Set([
3962
3966
  'timeline-overview',
3963
3967
  'timeline-persisted-main-flamechart-track-config',
3964
3968
  'timeline-persisted-network-flamechart-track-config',
3969
+ 'timeline-range-summary',
3965
3970
  'timeline-save-as-gz',
3966
3971
  'timeline-scope',
3967
3972
  'timeline-settings-pane',
@@ -4313,6 +4318,8 @@ export const knownContextValues = new Set([
4313
4318
  'vw',
4314
4319
  'waiting',
4315
4320
  'waiting-entry-inspect',
4321
+ 'walkthrough',
4322
+ 'walkthrough-container',
4316
4323
  'warning',
4317
4324
  'wasm',
4318
4325
  'wasm-auto-stepping',
package/package.json CHANGED
@@ -89,7 +89,7 @@
89
89
  "svgo": "3.3.2",
90
90
  "terser": "5.44.1",
91
91
  "ts-lit-plugin": "2.0.2",
92
- "typescript": "5.9.3",
92
+ "typescript": "6.0.2",
93
93
  "typescript-eslint": "8.58.0",
94
94
  "uuid": "13.0.0",
95
95
  "webidl2": "24.5.0",
@@ -104,5 +104,5 @@
104
104
  "flat-cache": "6.1.12"
105
105
  }
106
106
  },
107
- "version": "1.0.1613465"
107
+ "version": "1.0.1613625"
108
108
  }