chrome-devtools-frontend 1.0.1646714 → 1.0.1649421
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/extension-api/ExtensionAPI.d.ts +26 -0
- package/front_end/core/common/Debouncer.ts +9 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
- package/front_end/core/sdk/CSSMetadata.ts +1 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
- package/front_end/generated/InspectorBackendCommands.ts +3 -2
- package/front_end/generated/SupportedCSSProperties.js +533 -19
- package/front_end/generated/protocol.ts +26 -4
- package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
- package/front_end/models/extensions/ExtensionAPI.ts +47 -21
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +39 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
- package/front_end/panels/application/CookieItemsView.ts +55 -6
- package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
- package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
- package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
- package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
- package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
- package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
- package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
- package/front_end/panels/timeline/timeline.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
- package/front_end/ui/components/buttons/floatingButton.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
- package/front_end/ui/legacy/TextPrompt.ts +24 -18
- package/front_end/ui/legacy/Treeoutline.ts +31 -4
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- 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,
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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,
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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 {
|
|
@@ -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
|
-
|
|
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,
|
|
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
|
}
|