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.
- package/.agents/skills/verification/SKILL.md +5 -1
- package/front_end/core/common/MapWithDefault.ts +2 -2
- package/front_end/core/common/Object.ts +9 -6
- package/front_end/models/ai_assistance/agents/README.md +77 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +6 -4
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +13 -3
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +2 -1
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +4 -3
- package/front_end/panels/elements/StylesSidebarPane.ts +3 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +13 -4
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +1 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +9 -17
- package/front_end/panels/protocol_monitor/JSONEditor.ts +30 -4
- package/front_end/panels/recorder/models/RecordingPlayer.ts +1 -1
- package/front_end/panels/settings/AISettingsTab.ts +14 -3
- package/front_end/services/puppeteer/PuppeteerConnection.ts +1 -1
- package/front_end/ui/kit/icons/Icon.ts +1 -0
- package/front_end/ui/legacy/TextPrompt.ts +1 -1
- package/front_end/ui/legacy/Treeoutline.ts +3 -2
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +4 -3
- package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
- package/package.json +2 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
139
|
+
this.__events.removeEventListener(eventType, listener, thisObject);
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
hasEventListeners(eventType: keyof Events): boolean {
|
|
140
|
-
return this
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
347
|
-
|
|
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.
|
|
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
|
|
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
|
|
140
|
-
readonly savedChildren
|
|
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
|
|
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
|
-
|
|
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:
|
|
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'
|
|
688
|
-
|
|
689
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
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.
|
|
107
|
+
"version": "1.0.1613625"
|
|
108
108
|
}
|