chrome-devtools-frontend 1.0.1646714 → 1.0.1650035

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 (48) hide show
  1. package/extension-api/ExtensionAPI.d.ts +26 -0
  2. package/front_end/core/common/Debouncer.ts +9 -1
  3. package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
  4. package/front_end/core/sdk/CSSMetadata.ts +20 -1
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
  6. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +15 -0
  7. package/front_end/generated/Deprecation.ts +8 -0
  8. package/front_end/generated/InspectorBackendCommands.ts +3 -2
  9. package/front_end/generated/SupportedCSSProperties.js +533 -19
  10. package/front_end/generated/protocol.ts +26 -4
  11. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  12. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
  13. package/front_end/models/extensions/ExtensionAPI.ts +47 -21
  14. package/front_end/models/javascript_metadata/NativeFunctions.js +7 -3
  15. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +39 -1
  16. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  17. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
  18. package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
  19. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
  20. package/front_end/panels/application/CookieItemsView.ts +55 -6
  21. package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
  22. package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
  23. package/front_end/panels/console/ConsolePinPane.ts +1 -0
  24. package/front_end/panels/elements/ElementsTreeElement.ts +97 -57
  25. package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
  26. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
  27. package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
  28. package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
  29. package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
  30. package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
  31. package/front_end/panels/profiler/HeapSnapshotView.ts +6 -0
  32. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
  33. package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
  34. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
  35. package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
  36. package/front_end/panels/timeline/components/components.ts +2 -0
  37. package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
  38. package/front_end/panels/timeline/timeline.ts +2 -0
  39. package/front_end/third_party/chromium/README.chromium +1 -1
  40. package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
  41. package/front_end/ui/components/buttons/floatingButton.css +2 -2
  42. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
  43. package/front_end/ui/legacy/TextPrompt.ts +24 -18
  44. package/front_end/ui/legacy/Treeoutline.ts +31 -4
  45. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
  46. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +10 -10
  47. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  48. package/package.json +1 -1
@@ -2918,6 +2918,28 @@ export namespace CSS {
2918
2918
  specificity?: Specificity;
2919
2919
  }
2920
2920
 
2921
+ /**
2922
+ * Contribution of an individual simple selector to specificity.
2923
+ */
2924
+ export interface SpecificityComponent {
2925
+ /**
2926
+ * The simple selector text that contributes to specificity.
2927
+ */
2928
+ text: string;
2929
+ /**
2930
+ * The a component contribution.
2931
+ */
2932
+ a: integer;
2933
+ /**
2934
+ * The b component contribution.
2935
+ */
2936
+ b: integer;
2937
+ /**
2938
+ * The c component contribution.
2939
+ */
2940
+ c: integer;
2941
+ }
2942
+
2921
2943
  /**
2922
2944
  * Specificity:
2923
2945
  * https://drafts.csswg.org/selectors/#specificity-rules
@@ -2936,6 +2958,10 @@ export namespace CSS {
2936
2958
  * The c component, which represents the number of type selectors and pseudo-elements.
2937
2959
  */
2938
2960
  c: integer;
2961
+ /**
2962
+ * Per-simple-selector contributions used to explain this specificity.
2963
+ */
2964
+ components?: SpecificityComponent[];
2939
2965
  }
2940
2966
 
2941
2967
  /**
@@ -11136,10 +11162,6 @@ export namespace Network {
11136
11162
  export const enum CookieExemptionReason {
11137
11163
  None = 'None',
11138
11164
  UserSetting = 'UserSetting',
11139
- TPCDMetadata = 'TPCDMetadata',
11140
- TPCDDeprecationTrial = 'TPCDDeprecationTrial',
11141
- TopLevelTPCDDeprecationTrial = 'TopLevelTPCDDeprecationTrial',
11142
- TPCDHeuristics = 'TPCDHeuristics',
11143
11165
  EnterprisePolicy = 'EnterprisePolicy',
11144
11166
  StorageAccess = 'StorageAccess',
11145
11167
  TopLevelStorageAccess = 'TopLevelStorageAccess',
@@ -338,6 +338,13 @@ export interface NetworkRequestsListAiWidget {
338
338
  requests: SDK.NetworkRequest.NetworkRequest[],
339
339
  };
340
340
  }
341
+ export interface NetworkTrackAiWidget {
342
+ name: 'NETWORK_TRACK';
343
+ data: {
344
+ parsedTrace: Trace.TraceModel.ParsedTrace,
345
+ bounds: Trace.Types.Timing.TraceWindowMicro,
346
+ };
347
+ }
341
348
 
342
349
  export interface LighthouseReportAiWidget {
343
350
  name: 'LIGHTHOUSE_REPORT';
@@ -376,7 +383,7 @@ export interface SourceCodeAiWidget {
376
383
  export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|
377
384
  PerformanceTraceAiWidget|PerfInsightAiWidget|TimelineRangeSummaryAiWidget|BottomUpTreeAiWidget|SourceFileAiWidget|
378
385
  LighthouseReportAiWidget|TimelineEventSummaryAiWidget|NetworkRequestGeneralHeadersAiWidget|SourceCodeAiWidget|
379
- SourceFilesListAiWidget|NetworkRequestsListAiWidget;
386
+ SourceFilesListAiWidget|NetworkRequestsListAiWidget|NetworkTrackAiWidget;
380
387
 
381
388
  export type FunctionCallHandlerResult<Result> = {
382
389
  requiresApproval: true,
@@ -1232,6 +1232,13 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1232
1232
  this.#cacheFunctionResult(focus, key, summary);
1233
1233
  return {
1234
1234
  result: {summary},
1235
+ widgets: [{
1236
+ name: 'NETWORK_TRACK',
1237
+ data: {
1238
+ parsedTrace,
1239
+ bounds,
1240
+ },
1241
+ }],
1235
1242
  };
1236
1243
  },
1237
1244
 
@@ -607,19 +607,29 @@ self.injectedExtensionAPI = function(
607
607
  }
608
608
 
609
609
  (Network.prototype as Pick<APIImpl.Network, 'getHAR'|'addRequestHeaders'>) = {
610
- getHAR: function(this: PublicAPI.Chrome.DevTools.Network, callback?: (harLog: Object) => unknown): void {
611
- function callbackWrapper(response: unknown): void {
612
- const result =
613
- response as ({entries: Array<HAR.Log.EntryDTO&{__proto__?: APIImpl.Request, _requestId?: number}>});
614
- const entries = (result?.entries) || [];
615
- for (let i = 0; i < entries.length; ++i) {
616
- entries[i].__proto__ = new (Constructor(Request))(entries[i]._requestId as number);
617
- delete entries[i]._requestId;
618
- }
619
- callback?.(result);
620
- }
621
- extensionServer.sendRequest({command: PrivateAPI.Commands.GetHAR}, callback && callbackWrapper);
622
- },
610
+ getHAR: function(this: PublicAPI.Chrome.DevTools.Network, _callback?: (harLog: object) => unknown): Promise<object>|
611
+ void {
612
+ const {callback: callbackArg, promise, resolve, reject} = callbackOrPromise<object>(arguments);
613
+
614
+ function callbackWrapper(response: unknown): void {
615
+ if (checkErrorAndReject(response, reject)) {
616
+ return;
617
+ }
618
+
619
+ const result =
620
+ response as ({entries: Array<HAR.Log.EntryDTO&{__proto__?: APIImpl.Request, _requestId?: number}>});
621
+ const entries = (result?.entries) || [];
622
+ for (let i = 0; i < entries.length; ++i) {
623
+ entries[i].__proto__ = new (Constructor(Request))(entries[i]._requestId as number);
624
+ delete entries[i]._requestId;
625
+ }
626
+ resolve?.(result);
627
+ callbackArg?.(result);
628
+ }
629
+ extensionServer.sendRequest({command: PrivateAPI.Commands.GetHAR}, callbackWrapper);
630
+
631
+ return promise;
632
+ } as PublicAPI.Chrome.DevTools.Network['getHAR'],
623
633
 
624
634
  addRequestHeaders: function(headers: Record<string, string>): void {
625
635
  extensionServer.sendRequest(
@@ -631,15 +641,31 @@ self.injectedExtensionAPI = function(
631
641
  this._id = id;
632
642
  }
633
643
 
644
+ interface GetContentPromiseSignature {
645
+ content: string;
646
+ encoding: string;
647
+ }
648
+ type GetContentCallbackSignature = [content: string, encoding: string];
634
649
  (RequestImpl.prototype as Pick<APIImpl.Request, 'getContent'>) = {
635
- getContent: function(this: APIImpl.Request, callback?: (content: string, encoding: string) => unknown): void {
636
- function callbackWrapper(response: unknown): void {
637
- const {content, encoding} = response as {content: string, encoding: string};
638
- callback?.(content, encoding);
639
- }
640
- extensionServer.sendRequest(
641
- {command: PrivateAPI.Commands.GetRequestContent, id: this._id}, callback && callbackWrapper);
642
- },
650
+ getContent: function(this: APIImpl.Request, _callback?: (...args: GetContentCallbackSignature) => unknown):
651
+ Promise<GetContentPromiseSignature>|
652
+ void {
653
+ const {callback: callbackArg, promise, resolve, reject} =
654
+ callbackOrPromise<GetContentPromiseSignature, GetContentCallbackSignature>(arguments);
655
+
656
+ function callbackWrapper(response: unknown): void {
657
+ if (checkErrorAndReject(response, reject)) {
658
+ return;
659
+ }
660
+
661
+ const {content, encoding} = response as {content: string, encoding: string};
662
+ resolve?.({content, encoding});
663
+ callbackArg?.(content, encoding);
664
+ }
665
+ extensionServer.sendRequest({command: PrivateAPI.Commands.GetRequestContent, id: this._id}, callbackWrapper);
666
+
667
+ return promise;
668
+ } as PublicAPI.Chrome.DevTools.Request['getContent'],
643
669
  };
644
670
 
645
671
  function Panels(this: APIImpl.Panels): void {
@@ -1430,7 +1430,7 @@ export const NativeFunctions = [
1430
1430
  },
1431
1431
  {
1432
1432
  name: "add",
1433
- signatures: [["sub_apps_to_add"]],
1433
+ signatures: [["install_paths"]],
1434
1434
  receivers: ["SubApps"]
1435
1435
  },
1436
1436
  {
@@ -2005,7 +2005,7 @@ export const NativeFunctions = [
2005
2005
  },
2006
2006
  {
2007
2007
  name: "remove",
2008
- signatures: [["app_ids"]],
2008
+ signatures: [["manifest_ids"]],
2009
2009
  receivers: ["SubApps"]
2010
2010
  },
2011
2011
  {
@@ -6975,7 +6975,7 @@ export const NativeFunctions = [
6975
6975
  },
6976
6976
  {
6977
6977
  name: "install",
6978
- signatures: [["?install_url","?manifest_id"]],
6978
+ signatures: [["?install_url","?manifest_id"],["params"]],
6979
6979
  receivers: ["Navigator"]
6980
6980
  },
6981
6981
  {
@@ -8398,6 +8398,10 @@ export const NativeFunctions = [
8398
8398
  name: "Profiler",
8399
8399
  signatures: [["options"]]
8400
8400
  },
8401
+ {
8402
+ name: "getPathData",
8403
+ signatures: [["?settings"]]
8404
+ },
8401
8405
  {
8402
8406
  name: "ByteLengthQueuingStrategy",
8403
8407
  signatures: [["init"]]
@@ -18,6 +18,7 @@ import type * as LHModel from '../../models/lighthouse/lighthouse.js';
18
18
  import type * as Trace from '../../models/trace/trace.js';
19
19
  import * as Workspace from '../../models/workspace/workspace.js';
20
20
  import * as Buttons from '../../ui/components/buttons/buttons.js';
21
+ import type * as MarkdownView from '../../ui/components/markdown_view/markdown_view.js';
21
22
  import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
22
23
  import * as UIHelpers from '../../ui/helpers/helpers.js';
23
24
  import * as UI from '../../ui/legacy/legacy.js';
@@ -30,6 +31,10 @@ import * as TimelinePanel from '../timeline/timeline.js';
30
31
 
31
32
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
32
33
  import {AccessibilityAgentMarkdownRenderer} from './components/AccessibilityAgentMarkdownRenderer.js';
34
+ import {
35
+ AIv2MarkdownRenderer,
36
+ type AIv2MarkdownRendererOptions,
37
+ } from './components/AIv2MarkdownRenderer.js';
33
38
  import {
34
39
  type AnswerPart,
35
40
  ChatMessageEntity,
@@ -358,8 +363,41 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
358
363
  }
359
364
  }
360
365
 
366
+ function createV2MarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiConversation):
367
+ AIv2MarkdownRenderer {
368
+ const options: AIv2MarkdownRendererOptions = {};
369
+ const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
370
+ const domModel = primaryTarget?.model(SDK.DOMModel.DOMModel);
371
+ const resourceTreeModel = primaryTarget?.model(SDK.ResourceTreeModel.ResourceTreeModel);
372
+ const context = conversation?.selectedContext;
373
+
374
+ if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
375
+ const focus = context.getItem();
376
+ options.mainFrameId = focus.parsedTrace.data.Meta.mainFrameId;
377
+ options.lookupTraceEvent = focus.lookupEvent.bind(focus);
378
+ } else {
379
+ if (domModel) {
380
+ options.mainDocumentURL = domModel.existingDocument()?.documentURL;
381
+ }
382
+ if (resourceTreeModel) {
383
+ options.mainFrameId = resourceTreeModel.mainFrame?.id;
384
+ }
385
+ }
386
+ return new AIv2MarkdownRenderer(options);
387
+ }
388
+
361
389
  function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiConversation):
362
- MarkdownRendererWithCodeBlock {
390
+ MarkdownView.MarkdownView.MarkdownInsightRenderer {
391
+ if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE &&
392
+ conversation.isReadOnly) {
393
+ // Handle historical conversations (can't linkify anything).
394
+ return new PerformanceAgentMarkdownRenderer();
395
+ }
396
+
397
+ if (Root.Runtime.hostConfig.devToolsAiV2Architecture?.enabled && conversation && !conversation.isReadOnly) {
398
+ return createV2MarkdownRenderer(conversation);
399
+ }
400
+
363
401
  const context = conversation?.selectedContext;
364
402
 
365
403
  if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
@@ -5,6 +5,7 @@
5
5
  export * from './AiAssistancePanel.js';
6
6
  export * from './components/ChatView.js';
7
7
  export * from './components/AccessibilityAgentMarkdownRenderer.js';
8
+ export * from './components/AIv2MarkdownRenderer.js';
8
9
  export * as ChatInput from './components/ChatInput.js';
9
10
  export * from './components/MarkdownRendererWithCodeBlock.js';
10
11
  export * from './SelectWorkspaceDialog.js';
@@ -0,0 +1,228 @@
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 Common from '../../../core/common/common.js';
6
+ import * as Platform from '../../../core/platform/platform.js';
7
+ import * as SDK from '../../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../../generated/protocol.js';
9
+ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
10
+ import * as Logs from '../../../models/logs/logs.js';
11
+ import * as Trace from '../../../models/trace/trace.js';
12
+ import type * as Marked from '../../../third_party/marked/marked.js';
13
+ import * as MarkdownView from '../../../ui/components/markdown_view/markdown_view.js';
14
+ import * as Lit from '../../../ui/lit/lit.js';
15
+ import * as PanelsCommon from '../../common/common.js';
16
+
17
+ const {html} = Lit.StaticHtml;
18
+ const {until} = Lit.Directives;
19
+
20
+ export interface AIv2MarkdownRendererOptions {
21
+ mainFrameId?: string;
22
+ mainDocumentURL?: Platform.DevToolsPath.UrlString;
23
+ lookupTraceEvent?: (key: string) => Trace.Types.Events.Event | null;
24
+ }
25
+
26
+ type ParsedLink = {
27
+ type: 'path',
28
+ path: string,
29
+ }|{
30
+ type: 'node',
31
+ nodeId: Protocol.DOM.BackendNodeId,
32
+ };
33
+
34
+ /**
35
+ * AIv2MarkdownRenderer is currently duplicated from the agent-specific renderers
36
+ * as part of the migration to the V2 architecture. It will eventually become
37
+ * the only markdown renderer used by AI assistance.
38
+ */
39
+ export class AIv2MarkdownRenderer extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
40
+ constructor(private readonly options: AIv2MarkdownRendererOptions = {}) {
41
+ super();
42
+ }
43
+
44
+ #isSameOrigin(node: SDK.DOMModel.DOMNode): boolean {
45
+ if (!this.options.mainDocumentURL) {
46
+ return true;
47
+ }
48
+ const nodeDocumentURL = node.ownerDocument?.documentURL ?? '' as Platform.DevToolsPath.UrlString;
49
+ return AiAssistanceModel.AiUtils.isSameOrigin(this.options.mainDocumentURL, nodeDocumentURL);
50
+ }
51
+
52
+ #revealableLink(revealable: unknown, label: string): Lit.LitTemplate {
53
+ return html`<devtools-link @click=${(e: Event) => {
54
+ e.preventDefault();
55
+ e.stopPropagation();
56
+ void Common.Revealer.reveal(revealable);
57
+ }}>${Platform.StringUtilities.trimEndWithMaxLength(label, 100)}</devtools-link>`;
58
+ }
59
+
60
+ #renderLink(href: string, text: string): Lit.LitTemplate|null {
61
+ const devtoolsLink = this.#renderDevToolsLink(href, text);
62
+ if (devtoolsLink) {
63
+ return devtoolsLink;
64
+ }
65
+
66
+ if (href.startsWith('#')) {
67
+ const parsed = this.#parseLink(href);
68
+ if (parsed) {
69
+ const resultPromise =
70
+ parsed.type === 'path' ? this.#linkifyPath(parsed.path, text) : this.#linkifyNode(parsed.nodeId, text);
71
+
72
+ return html`<span>${until(resultPromise.then(node => node || text), text)}</span>`;
73
+ }
74
+
75
+ if (this.options.lookupTraceEvent) {
76
+ const event = this.options.lookupTraceEvent(href.slice(1));
77
+ if (event) {
78
+ let label = text;
79
+ let title = '';
80
+ if (Trace.Types.Events.isSyntheticNetworkRequest(event)) {
81
+ title = event.args.data.url;
82
+ } else {
83
+ label += ` (${event.name})`;
84
+ }
85
+
86
+ // eslint-disable-next-line @devtools/no-a-tags-in-lit
87
+ return html`<a href="#" draggable=false .title=${title} @click=${(e: Event) => {
88
+ e.stopPropagation();
89
+ void Common.Revealer.reveal(new SDK.TraceObject.RevealableEvent(event));
90
+ }}>${label}</a>`;
91
+ }
92
+ }
93
+ }
94
+
95
+ return null;
96
+ }
97
+
98
+ #renderDevToolsLink(
99
+ href: string,
100
+ fallbackText: string,
101
+ ): Lit.LitTemplate|null {
102
+ if (href.startsWith('#req-')) {
103
+ const request = Logs.NetworkLog.NetworkLog.instance().requests().find(
104
+ req => req.requestId() === href.substring(5),
105
+ );
106
+
107
+ if (request) {
108
+ return this.#revealableLink(request, request.url());
109
+ }
110
+ return html`${fallbackText}`;
111
+ }
112
+ if (href.startsWith('#file-')) {
113
+ const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getUISourceCodes().find(
114
+ file => AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.uiSourceCodeId.get(file) ===
115
+ Number(href.substring(6)));
116
+
117
+ if (file) {
118
+ return this.#revealableLink(file, file.name());
119
+ }
120
+ return html`${fallbackText}`;
121
+ }
122
+ return null;
123
+ }
124
+
125
+ #parseLink(href: string): ParsedLink|null {
126
+ if (href.startsWith('#path-')) {
127
+ return {type: 'path', path: href.replace('#path-', '')};
128
+ }
129
+ if (href.startsWith('#1,HTML')) {
130
+ return {type: 'path', path: href.slice(1)};
131
+ }
132
+
133
+ let nodeIdStr = '';
134
+ if (href.startsWith('#node-')) {
135
+ nodeIdStr = href.replace('#node-', '');
136
+ } else if (href.startsWith('#')) {
137
+ nodeIdStr = href.slice(1);
138
+ }
139
+
140
+ if (nodeIdStr.trim() !== '') {
141
+ const nodeId = Number(nodeIdStr);
142
+ if (Number.isInteger(nodeId)) {
143
+ return {type: 'node', nodeId: nodeId as Protocol.DOM.BackendNodeId};
144
+ }
145
+ }
146
+
147
+ return null;
148
+ }
149
+
150
+ async #linkifyNode(backendNodeId: Protocol.DOM.BackendNodeId, label: string): Promise<Lit.LitTemplate|undefined> {
151
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
152
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
153
+ if (!domModel) {
154
+ return undefined;
155
+ }
156
+ const domNodesMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([backendNodeId]));
157
+ const node = domNodesMap?.get(backendNodeId);
158
+ if (!node) {
159
+ return;
160
+ }
161
+
162
+ if (this.options.mainFrameId && node.frameId() !== this.options.mainFrameId) {
163
+ return;
164
+ }
165
+
166
+ if (!this.#isSameOrigin(node)) {
167
+ return;
168
+ }
169
+
170
+ const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
171
+ return linkedNode;
172
+ }
173
+
174
+ async #linkifyPath(path: string, label: string): Promise<Lit.LitTemplate|undefined> {
175
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
176
+ const domModel = target?.model(SDK.DOMModel.DOMModel);
177
+ if (!domModel) {
178
+ return undefined;
179
+ }
180
+ const nodeId = await domModel.pushNodeByPathToFrontend(path);
181
+ if (!nodeId) {
182
+ return;
183
+ }
184
+ const node = domModel.nodeForId(nodeId);
185
+ if (!node) {
186
+ return;
187
+ }
188
+ if (!this.#isSameOrigin(node)) {
189
+ return;
190
+ }
191
+ const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
192
+ return linkedNode;
193
+ }
194
+
195
+ override templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
196
+ if (token.type === 'link') {
197
+ const link = this.#renderLink(token.href, token.text);
198
+ if (link) {
199
+ return link;
200
+ }
201
+ }
202
+
203
+ if (token.type === 'code') {
204
+ const lines = (token.text).split('\n');
205
+ if (lines[0]?.trim() === 'css') {
206
+ token.lang = 'css';
207
+ token.text = lines.slice(1).join('\n');
208
+ }
209
+ }
210
+
211
+ if (token.type === 'codespan') {
212
+ // LLM likes outputting the link inside a codespan block.
213
+ // Remove the codespan and render the link directly
214
+ const matches = token.text.match(/^\[(.*)\]\((.+)\)$/);
215
+ if (matches?.[2]) {
216
+ const link = this.#renderLink(
217
+ matches[2],
218
+ matches[1],
219
+ );
220
+ if (link) {
221
+ return link;
222
+ }
223
+ }
224
+ }
225
+
226
+ return super.templateForToken(token);
227
+ }
228
+ }
@@ -14,8 +14,8 @@ import * as SDK from '../../../core/sdk/sdk.js';
14
14
  import type * as Protocol from '../../../generated/protocol.js';
15
15
  import type {
16
16
  AiWidget, BottomUpTreeAiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, LighthouseReportAiWidget,
17
- NetworkRequestGeneralHeadersAiWidget, NetworkRequestsListAiWidget, PerfInsightAiWidget, PerformanceTraceAiWidget,
18
- SourceCodeAiWidget, SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget,
17
+ NetworkRequestGeneralHeadersAiWidget, NetworkRequestsListAiWidget, NetworkTrackAiWidget, PerfInsightAiWidget,
18
+ PerformanceTraceAiWidget, SourceCodeAiWidget, SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget,
19
19
  TimelineEventSummaryAiWidget, TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
20
20
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
21
21
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
@@ -231,6 +231,10 @@ const UIStringsNotTranslate = {
231
231
  * @description Accessible label for the reveal button in the performance summary widget.
232
232
  */
233
233
  revealPerformanceSummary: 'Reveal performance summary',
234
+ /**
235
+ * @description Accessible label for the reveal button in the network track widget.
236
+ */
237
+ revealNetworkActivity: 'Reveal network activity',
234
238
  /**
235
239
  * @description Accessible label for the reveal button in the bottom up thread activity widget.
236
240
  */
@@ -295,6 +299,10 @@ const UIStringsNotTranslate = {
295
299
  * @description Title for the performance summary widget.
296
300
  */
297
301
  performanceSummary: 'Performance summary',
302
+ /**
303
+ * @description Title for the network activity summary widget.
304
+ */
305
+ networkActivitySummary: 'Network activity',
298
306
  /**
299
307
  * @description The title of the button that allows exporting the conversation for agents.
300
308
  */
@@ -1658,6 +1666,8 @@ export function getWidgetSignature(widget: AiWidget): string {
1658
1666
  return `${widget.name}:${widget.data.track}:${widget.data.bounds.min}-${widget.data.bounds.max}`;
1659
1667
  case 'BOTTOM_UP_TREE':
1660
1668
  return `${widget.name}:${widget.data.bounds.min}-${widget.data.bounds.max}`;
1669
+ case 'NETWORK_TRACK':
1670
+ return `${widget.name}:${widget.data.bounds.min}-${widget.data.bounds.max}`;
1661
1671
  case 'SOURCE_FILE':
1662
1672
  return `${widget.name}:${widget.data.uiSourceCode.url()}`;
1663
1673
  case 'SOURCE_FILES_LIST':
@@ -1755,6 +1765,9 @@ async function renderWidgets(
1755
1765
  case 'BOTTOM_UP_TREE':
1756
1766
  response = await makeBottomUpTimelineTreeWidget(widgetData);
1757
1767
  break;
1768
+ case 'NETWORK_TRACK':
1769
+ response = await makeNetworkTrackWidget(widgetData);
1770
+ break;
1758
1771
  case 'SOURCE_FILE':
1759
1772
  response = await makeSourceFileWidget(widgetData);
1760
1773
  break;
@@ -2316,6 +2329,30 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
2316
2329
  };
2317
2330
  }
2318
2331
 
2332
+ async function makeNetworkTrackWidget(widgetData: NetworkTrackAiWidget): Promise<WidgetMakerResponse|null> {
2333
+ const {parsedTrace, bounds} = widgetData.data;
2334
+ const dataProvider = new Timeline.TimelineFlameChartNetworkDataProvider.TimelineFlameChartNetworkDataProvider();
2335
+
2336
+ // clang-format off
2337
+ const template = html`
2338
+ <devtools-performance-agent-network-track
2339
+ .data=${{
2340
+ parsedTrace,
2341
+ bounds,
2342
+ dataProvider,
2343
+ } as TimelineComponents.NetworkTrackWidget.NetworkTrackWidgetData}
2344
+ ></devtools-performance-agent-network-track>`;
2345
+ // clang-format on
2346
+
2347
+ return {
2348
+ renderedWidget: template,
2349
+ revealable: new TimelineUtils.Helpers.RevealableTimeRange(bounds),
2350
+ accessibleRevealLabel: lockedString(UIStringsNotTranslate.revealNetworkActivity),
2351
+ title: lockedString(UIStringsNotTranslate.networkActivitySummary),
2352
+ jslogContext: 'network-track-widget',
2353
+ };
2354
+ }
2355
+
2319
2356
  async function makeLighthouseReportWidget(widgetData: LighthouseReportAiWidget): Promise<WidgetMakerResponse|null> {
2320
2357
  const reportEl =
2321
2358
  Lighthouse.LighthouseReportRenderer.LighthouseReportRenderer.renderLighthouseScores(widgetData.data.report);
@@ -1797,6 +1797,8 @@ export class DOMStorageTreeElement extends ApplicationPanelTreeElement {
1797
1797
  super.onselect(selectedByUser);
1798
1798
  UI.UIUserMetrics.UIUserMetrics.instance().panelShown('dom-storage');
1799
1799
  this.resourcesPanel.showDOMStorage(this.domStorage);
1800
+ const storageItem = this.#getStorageItem();
1801
+ UI.Context.Context.instance().setFlavor(AiAssistance.StorageItem.StorageItem, storageItem);
1800
1802
  return false;
1801
1803
  }
1802
1804
 
@@ -1977,6 +1979,8 @@ export class CookieTreeElement extends ApplicationPanelTreeElement {
1977
1979
  this.resourcesPanel.showCookies(this.target, this.#cookieDomain);
1978
1980
  UI.UIUserMetrics.UIUserMetrics.instance().panelShown(
1979
1981
  Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.cookies]);
1982
+ const storageItem = this.#getStorageItem();
1983
+ UI.Context.Context.instance().setFlavor(AiAssistance.StorageItem.StorageItem, storageItem);
1980
1984
  return false;
1981
1985
  }
1982
1986
  }