chrome-devtools-frontend 1.0.1642845 → 1.0.1642899
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/SECURITY.md +1 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
- package/front_end/core/sdk/CSSRule.ts +1 -0
- package/front_end/core/sdk/DebuggerModel.ts +5 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
- package/front_end/models/ai_assistance/AiAgent2.ts +80 -16
- package/front_end/models/ai_assistance/AiConversation.ts +3 -2
- package/front_end/models/ai_assistance/README.md +8 -0
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +50 -35
- package/front_end/models/ai_assistance/agents/AiAgent.ts +16 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +195 -147
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -305
- package/front_end/models/ai_assistance/ai_assistance.ts +8 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
- package/front_end/models/ai_assistance/skills/styling.md +36 -2
- package/front_end/models/ai_assistance/tools/GetStyles.ts +137 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +55 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +34 -0
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
- package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
- package/front_end/models/stack_trace/StackTrace.ts +4 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
- package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
- package/front_end/models/stack_trace/Trie.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +19 -15
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
- package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
- package/front_end/panels/common/ExtensionServer.ts +26 -15
- package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
- package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
- package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
- package/front_end/panels/network/NetworkLogView.ts +3 -0
- package/front_end/panels/network/networkLogView.css +0 -15
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
- package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
- package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: styling
|
|
3
3
|
description: Helping with CSS and styling
|
|
4
|
-
allowed-tools:
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- getStyles
|
|
5
6
|
---
|
|
6
|
-
You are a CSS
|
|
7
|
+
You are a CSS/DOM/HTML debugging assistant.
|
|
8
|
+
You always suggest considering the best web development practices and the newest platform features such as view transitions.
|
|
9
|
+
The user selected a DOM element in the browser's DevTools and sends a query about the page or the selected DOM element.
|
|
10
|
+
First, examine the provided context, then use the getStyles function to gather additional context and resolve the user request.
|
|
11
|
+
|
|
12
|
+
# Considerations
|
|
13
|
+
|
|
14
|
+
* Meticulously investigate all potential causes for the observed behavior before moving on. Gather comprehensive information about the element's parent, siblings, children, and any overlapping elements, paying close attention to properties that are likely relevant to the query.
|
|
15
|
+
* Be aware of the different node types (element, text, comment, document fragment, etc.) and their properties. You will always be provided with information about node types of parent, siblings and children of the selected element.
|
|
16
|
+
* Avoid making assumptions without sufficient evidence, and always seek further clarification if needed.
|
|
17
|
+
* Always explore multiple possible explanations for the observed behavior before settling on a conclusion.
|
|
18
|
+
* When presenting solutions, clearly distinguish between the primary cause and contributing factors.
|
|
19
|
+
* Please answer only if you are sure about the answer. Otherwise, explain why you're not able to answer.
|
|
20
|
+
* When answering, always consider MULTIPLE possible solutions.
|
|
21
|
+
* When answering, remember to consider CSS concepts such as the CSS cascade, explicit and implicit stacking contexts and various CSS layout types.
|
|
22
|
+
* Use the getStyles function available to you to investigate and fulfill the user request.
|
|
23
|
+
* ALWAYS OUTPUT a list of follow-up queries at the end of your text response. The format is SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]. Make sure that the array and the `SUGGESTIONS: ` text is in the same line.
|
|
24
|
+
* Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Don't add repeated information, and keep the whole answer short.
|
|
25
|
+
* **CRITICAL** When answering questions about positioning or layout, ALWAYS inspect `position`, `display` and all other related properties. You MUST provide a specific list of CSS property names when calling getStyles. Do not use generic values like "all" or "*".
|
|
26
|
+
* **CRITICAL** You are a CSS/DOM/HTML debugging assistant. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, religion, race, politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such questions.
|
|
27
|
+
|
|
28
|
+
## Response Structure
|
|
29
|
+
|
|
30
|
+
If the user asks a question that requires an investigation of a problem, use this structure:
|
|
31
|
+
- If available, point out the root cause(s) of the problem.
|
|
32
|
+
- Example: "**Root Cause**: The page is slow because of [reason]."
|
|
33
|
+
- Example: "**Root Causes**:"
|
|
34
|
+
- [Reason 1]
|
|
35
|
+
- [Reason 2]
|
|
36
|
+
- if applicable, list actionable solution suggestion(s) in order of impact:
|
|
37
|
+
- Example: "**Suggestion**: [Suggestion 1]
|
|
38
|
+
- Example: "**Suggestions**:"
|
|
39
|
+
- [Suggestion 1]
|
|
40
|
+
- [Suggestion 2]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Host from '../../../core/host/host.js';
|
|
6
|
+
import * as SDK from '../../../core/sdk/sdk.js';
|
|
7
|
+
import type * as Protocol from '../../../generated/protocol.js';
|
|
8
|
+
import type {ComputedStyleAiWidget, FunctionCallHandlerResult} from '../agents/AiAgent.js';
|
|
9
|
+
import {DOMNodeContext} from '../contexts/DOMNodeContext.js';
|
|
10
|
+
import {debugLog} from '../debug.js';
|
|
11
|
+
|
|
12
|
+
import {type Tool, type ToolArgs, type ToolContext, ToolName} from './Tool.js';
|
|
13
|
+
|
|
14
|
+
export interface GetStylesArgs extends ToolArgs {
|
|
15
|
+
elements: number[];
|
|
16
|
+
styleProperties: string[];
|
|
17
|
+
explanation: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class GetStylesTool implements Tool<GetStylesArgs, unknown> {
|
|
21
|
+
readonly name = ToolName.GET_STYLES;
|
|
22
|
+
readonly description =
|
|
23
|
+
`Get computed and source styles for one or multiple elements on the inspected page for multiple elements at once by uid.
|
|
24
|
+
|
|
25
|
+
**CRITICAL** An element uid is a number, not a selector.
|
|
26
|
+
**CRITICAL** Use selectors to refer to elements in the text output. Do not use uids.
|
|
27
|
+
**CRITICAL** Always provide the explanation argument to explain what and why you query.
|
|
28
|
+
**CRITICAL** You MUST provide a specific list of CSS property names. Do not use generic values like "all" or "*".`;
|
|
29
|
+
|
|
30
|
+
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetStylesArgs> = {
|
|
31
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
32
|
+
description: '',
|
|
33
|
+
nullable: false,
|
|
34
|
+
properties: {
|
|
35
|
+
explanation: {
|
|
36
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
37
|
+
description: 'Explain why you want to get styles',
|
|
38
|
+
nullable: false,
|
|
39
|
+
},
|
|
40
|
+
elements: {
|
|
41
|
+
type: Host.AidaClient.ParametersTypes.ARRAY,
|
|
42
|
+
description: 'A list of element uids to get data for. These are numbers, not selectors.',
|
|
43
|
+
items: {type: Host.AidaClient.ParametersTypes.INTEGER, description: 'An element uid.'},
|
|
44
|
+
nullable: false,
|
|
45
|
+
},
|
|
46
|
+
styleProperties: {
|
|
47
|
+
type: Host.AidaClient.ParametersTypes.ARRAY,
|
|
48
|
+
description:
|
|
49
|
+
'One or more specific CSS style property names to fetch. Generic values like "all" or "*" are not supported.',
|
|
50
|
+
nullable: false,
|
|
51
|
+
items: {
|
|
52
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
53
|
+
description: 'A CSS style property name to retrieve. For example, \'background-color\'.'
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['explanation', 'elements', 'styleProperties']
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
displayInfoFromArgs(params: GetStylesArgs): {
|
|
61
|
+
title: string,
|
|
62
|
+
thought: string,
|
|
63
|
+
action: string,
|
|
64
|
+
} {
|
|
65
|
+
return {
|
|
66
|
+
title: 'Reading computed and source styles',
|
|
67
|
+
thought: params.explanation,
|
|
68
|
+
action: `getStyles(${JSON.stringify(params.elements)}, ${JSON.stringify(params.styleProperties)})`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async handler(params: GetStylesArgs, context: ToolContext): Promise<FunctionCallHandlerResult<unknown>> {
|
|
73
|
+
const widgets: ComputedStyleAiWidget[] = [];
|
|
74
|
+
const result:
|
|
75
|
+
Record<string, {computed: Record<string, string|undefined>, authored: Record<string, string|undefined>}> = {};
|
|
76
|
+
|
|
77
|
+
const activeContext = context.conversationContext;
|
|
78
|
+
if (!activeContext || !(activeContext instanceof DOMNodeContext)) {
|
|
79
|
+
return {error: 'Error: Could not find the currently selected element.'};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const selectedNode = activeContext.getItem();
|
|
83
|
+
if (!selectedNode) {
|
|
84
|
+
return {error: 'Error: Could not find the currently selected element.'};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const uid of params.elements) {
|
|
88
|
+
result[uid] = {computed: {}, authored: {}};
|
|
89
|
+
debugLog(`Action to execute: uid=${uid}`);
|
|
90
|
+
const node =
|
|
91
|
+
new SDK.DOMModel.DeferredDOMNode(selectedNode.domModel().target(), uid as Protocol.DOM.BackendNodeId);
|
|
92
|
+
const resolved = await node.resolvePromise();
|
|
93
|
+
if (!resolved) {
|
|
94
|
+
return {error: 'Error: Could not find the element with uid=' + uid};
|
|
95
|
+
}
|
|
96
|
+
const newContext = new DOMNodeContext(resolved);
|
|
97
|
+
if (activeContext.getOrigin() !== newContext.getOrigin()) {
|
|
98
|
+
return {error: 'Error: Node does not belong to the current origin.'};
|
|
99
|
+
}
|
|
100
|
+
const styles = await resolved.domModel().cssModel().getComputedStyle(resolved.id);
|
|
101
|
+
if (!styles) {
|
|
102
|
+
return {error: 'Error: Could not get computed styles.'};
|
|
103
|
+
}
|
|
104
|
+
const matchedStyles = await resolved.domModel().cssModel().getMatchedStyles(resolved.id);
|
|
105
|
+
if (!matchedStyles) {
|
|
106
|
+
return {error: 'Error: Could not get authored styles.'};
|
|
107
|
+
}
|
|
108
|
+
widgets.push({
|
|
109
|
+
name: 'COMPUTED_STYLES',
|
|
110
|
+
data: {
|
|
111
|
+
computedStyles: styles,
|
|
112
|
+
backendNodeId: node.backendNodeId(),
|
|
113
|
+
matchedCascade: matchedStyles,
|
|
114
|
+
properties: params.styleProperties,
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
for (const prop of params.styleProperties) {
|
|
118
|
+
result[uid].computed[prop] = styles.get(prop);
|
|
119
|
+
}
|
|
120
|
+
for (const style of matchedStyles.nodeStyles()) {
|
|
121
|
+
for (const property of style.allProperties()) {
|
|
122
|
+
if (!params.styleProperties.includes(property.name)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const state = matchedStyles.propertyState(property);
|
|
126
|
+
if (state === SDK.CSSMatchedStyles.PropertyState.ACTIVE) {
|
|
127
|
+
result[uid].authored[property.name] = property.value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
result: JSON.stringify(result, null, 2),
|
|
134
|
+
widgets,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 type * as Host from '../../../core/host/host.js';
|
|
6
|
+
import type {ConversationContext, FunctionCallHandlerResult,} from '../agents/AiAgent.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Context provided to the tool's handler execution.
|
|
10
|
+
*/
|
|
11
|
+
export interface ToolContext {
|
|
12
|
+
conversationContext: ConversationContext<unknown>|null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Base argument type for AI Tools.
|
|
17
|
+
*/
|
|
18
|
+
export type ToolArgs = Record<string, unknown>;
|
|
19
|
+
|
|
20
|
+
export const enum ToolName {
|
|
21
|
+
GET_STYLES = 'getStyles',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Non-generic metadata interface for a Tool.
|
|
26
|
+
* Used for storing and retrieving tools generically without type-erasure concerns.
|
|
27
|
+
*/
|
|
28
|
+
export interface BaseTool {
|
|
29
|
+
readonly name: ToolName;
|
|
30
|
+
readonly description: string;
|
|
31
|
+
/**
|
|
32
|
+
* JSON schema representing the parameters this tool accepts.
|
|
33
|
+
*/
|
|
34
|
+
readonly parameters: Host.AidaClient.FunctionObjectParam<string|number|symbol>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Main generic interface for defining a Tool.
|
|
39
|
+
* Binds the parameter schema properties and the handler implementation to a strict `Args` contract.
|
|
40
|
+
*
|
|
41
|
+
* @template Args - The expected object type for tool arguments. Must be an object type.
|
|
42
|
+
* @template ReturnType - The type of data returned by the handler function.
|
|
43
|
+
*/
|
|
44
|
+
export interface Tool<Args extends ToolArgs = ToolArgs, ReturnType = unknown, > extends BaseTool {
|
|
45
|
+
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof Args>;
|
|
46
|
+
readonly displayInfoFromArgs?: (
|
|
47
|
+
args: Args,
|
|
48
|
+
) => {
|
|
49
|
+
title?: string, thought?: string, action?: string, suggestions?: [string, ...string[]],
|
|
50
|
+
};
|
|
51
|
+
handler(
|
|
52
|
+
args: Args,
|
|
53
|
+
context: ToolContext,
|
|
54
|
+
): Promise<FunctionCallHandlerResult<ReturnType>>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 {GetStylesTool} from './GetStyles.js';
|
|
6
|
+
import {type Tool, ToolName} from './Tool.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Plain object registry containing concrete instantiated tools.
|
|
10
|
+
* Keep this type concrete (no type-erasure) to preserve exact tool types.
|
|
11
|
+
*/
|
|
12
|
+
export const TOOLS = {
|
|
13
|
+
[ToolName.GET_STYLES]: new GetStylesTool(),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Registry class for registering and querying AI Assistance Tools.
|
|
18
|
+
*/
|
|
19
|
+
export class ToolRegistry {
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves a tool by its literal name with 100% type safety.
|
|
22
|
+
*
|
|
23
|
+
* @template K - A key from the `TOOLS` registry.
|
|
24
|
+
* @returns The concrete class type of the requested tool.
|
|
25
|
+
*/
|
|
26
|
+
static get<K extends keyof typeof TOOLS>(name: K): typeof TOOLS[K];
|
|
27
|
+
/**
|
|
28
|
+
* Fallback retrieval signature for general or runtime string lookups.
|
|
29
|
+
*/
|
|
30
|
+
static get(name: string): Tool|undefined;
|
|
31
|
+
static get(name: string): Tool|undefined {
|
|
32
|
+
return Object.prototype.hasOwnProperty.call(TOOLS, name) ? TOOLS[name as keyof typeof TOOLS] as Tool : undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -77,6 +77,11 @@ export interface ReportJSON {
|
|
|
77
77
|
audits: Record<string, AuditResultJSON>;
|
|
78
78
|
categories: Record<CategoryId, CategoryJSON>;
|
|
79
79
|
categoryGroups: Record<string, GroupJSON>;
|
|
80
|
+
/**
|
|
81
|
+
* Identifies if the report was imported from a file (untrusted).
|
|
82
|
+
* Used to disable page-touching AI assistance tools for security.
|
|
83
|
+
*/
|
|
84
|
+
isImported?: boolean;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
export type DetailsJSON =
|
|
@@ -64,17 +64,22 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
64
64
|
super();
|
|
65
65
|
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
66
66
|
targetManager.observeTargets(this, {scoped: true});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
targetManager.addEventListener(
|
|
71
|
-
SDK.TargetManager.Events.AVAILABLE_TARGETS_CHANGED, this.#onAvailableTargetsChanged, this);
|
|
67
|
+
targetManager.addModelListener(
|
|
68
|
+
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.PrimaryPageChanged,
|
|
69
|
+
this.#onPrimaryPageChanged, this);
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
#
|
|
72
|
+
#onPrimaryPageChanged(
|
|
73
|
+
event: Common.EventTarget.EventTargetEvent<
|
|
74
|
+
{frame: SDK.ResourceTreeModel.ResourceTreeFrame, type: SDK.ResourceTreeModel.PrimaryPageChangeType}>): void {
|
|
75
75
|
const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
76
|
-
if (primaryTarget
|
|
77
|
-
|
|
76
|
+
if (!primaryTarget) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (primaryTarget !== this.#target || event.data.type === SDK.ResourceTreeModel.PrimaryPageChangeType.ACTIVATION) {
|
|
80
|
+
// Primary target changed or prerender activated. Switch to it and reset metrics.
|
|
81
|
+
this.#clearMetrics();
|
|
82
|
+
this.#sendStatusUpdate();
|
|
78
83
|
void this.#switchToTarget(primaryTarget);
|
|
79
84
|
}
|
|
80
85
|
}
|
|
@@ -381,11 +386,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
381
386
|
break;
|
|
382
387
|
}
|
|
383
388
|
case 'reset': {
|
|
384
|
-
this.#
|
|
385
|
-
this.#clsValue = undefined;
|
|
386
|
-
this.#inpValue = undefined;
|
|
387
|
-
this.#interactions.clear();
|
|
388
|
-
this.#layoutShifts = [];
|
|
389
|
+
this.#clearMetrics();
|
|
389
390
|
break;
|
|
390
391
|
}
|
|
391
392
|
}
|
|
@@ -393,6 +394,15 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
393
394
|
this.#sendStatusUpdate();
|
|
394
395
|
}
|
|
395
396
|
|
|
397
|
+
#clearMetrics(): void {
|
|
398
|
+
this.#lcpValue = undefined;
|
|
399
|
+
this.#clsValue = undefined;
|
|
400
|
+
this.#inpValue = undefined;
|
|
401
|
+
this.#interactions.clear();
|
|
402
|
+
this.#interactionsByGroupId.clear();
|
|
403
|
+
this.#layoutShifts = [];
|
|
404
|
+
}
|
|
405
|
+
|
|
396
406
|
#isPrimaryFrameExecutionContext(executionContextId: Protocol.Runtime.ExecutionContextId): boolean {
|
|
397
407
|
if (!this.#target) {
|
|
398
408
|
return false;
|
|
@@ -460,6 +470,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
460
470
|
|
|
461
471
|
clearInteractions(): void {
|
|
462
472
|
this.#interactions.clear();
|
|
473
|
+
this.#interactionsByGroupId.clear();
|
|
463
474
|
this.#sendStatusUpdate();
|
|
464
475
|
}
|
|
465
476
|
|
|
@@ -142,12 +142,12 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
|
|
|
142
142
|
functionName,
|
|
143
143
|
lineNumber,
|
|
144
144
|
columnNumber,
|
|
145
|
+
isWasm,
|
|
145
146
|
parsedFrameInfo: {
|
|
146
147
|
isAsync,
|
|
147
148
|
isConstructor,
|
|
148
149
|
isEval,
|
|
149
150
|
evalOrigin,
|
|
150
|
-
isWasm,
|
|
151
151
|
wasmModuleName,
|
|
152
152
|
wasmFunctionIndex,
|
|
153
153
|
typeName,
|
|
@@ -181,7 +181,7 @@ export function parseMessage(stack: string): string {
|
|
|
181
181
|
export function augmentRawFramesWithScriptIds(
|
|
182
182
|
rawFrames: RawFrame[], protocolStackTrace: Protocol.Runtime.StackTrace): void {
|
|
183
183
|
function augmentFrame(rawFrame: RawFrame): void {
|
|
184
|
-
const isWasm = rawFrame.
|
|
184
|
+
const isWasm = rawFrame.isWasm;
|
|
185
185
|
const protocolFrame = protocolStackTrace.callFrames.find(frame => {
|
|
186
186
|
if (isWasm) {
|
|
187
187
|
// The parser parses Wasm offsets into the `columnNumber` field. The `lineNumber` is always -1.
|
|
@@ -44,6 +44,10 @@ export interface Frame {
|
|
|
44
44
|
* of the containing function.
|
|
45
45
|
*/
|
|
46
46
|
readonly rawName?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the corresponding raw frame is JS or WASM.
|
|
49
|
+
*/
|
|
50
|
+
readonly isWasm?: boolean;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
export interface ParsedErrorStackFrame extends Frame {
|
|
@@ -51,7 +55,6 @@ export interface ParsedErrorStackFrame extends Frame {
|
|
|
51
55
|
readonly isConstructor?: boolean;
|
|
52
56
|
readonly isEval?: boolean;
|
|
53
57
|
readonly evalOrigin?: ParsedErrorStackFrame;
|
|
54
|
-
readonly isWasm?: boolean;
|
|
55
58
|
readonly wasmModuleName?: string;
|
|
56
59
|
readonly wasmFunctionIndex?: number;
|
|
57
60
|
readonly typeName?: string;
|
|
@@ -88,10 +88,12 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
|
|
|
88
88
|
|
|
89
89
|
readonly missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo;
|
|
90
90
|
readonly rawName?: string;
|
|
91
|
+
readonly isWasm?: boolean;
|
|
91
92
|
|
|
92
93
|
constructor(
|
|
93
94
|
url: string|undefined, uiSourceCode: Workspace.UISourceCode.UISourceCode|undefined, name: string|undefined,
|
|
94
|
-
line: number, column: number, missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo, rawName?: string
|
|
95
|
+
line: number, column: number, missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo, rawName?: string,
|
|
96
|
+
isWasm?: boolean) {
|
|
95
97
|
this.url = url;
|
|
96
98
|
this.uiSourceCode = uiSourceCode;
|
|
97
99
|
this.name = name;
|
|
@@ -99,6 +101,7 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
|
|
|
99
101
|
this.column = column;
|
|
100
102
|
this.missingDebugInfo = missingDebugInfo;
|
|
101
103
|
this.rawName = rawName;
|
|
104
|
+
this.isWasm = isWasm;
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -192,7 +195,7 @@ export class ParsedErrorStackFrameImpl implements StackTrace.StackTrace.ParsedEr
|
|
|
192
195
|
return this.#evalOrigin;
|
|
193
196
|
}
|
|
194
197
|
get isWasm(): boolean|undefined {
|
|
195
|
-
return this.#
|
|
198
|
+
return this.#frame.isWasm;
|
|
196
199
|
}
|
|
197
200
|
get wasmModuleName(): string|undefined {
|
|
198
201
|
return this.#parsedFrameInfo?.wasmModuleName;
|
|
@@ -284,6 +287,10 @@ export class DebuggableFrameImpl implements StackTrace.StackTrace.DebuggableFram
|
|
|
284
287
|
return this.#frame.rawName;
|
|
285
288
|
}
|
|
286
289
|
|
|
290
|
+
get isWasm(): boolean|undefined {
|
|
291
|
+
return this.#frame.isWasm;
|
|
292
|
+
}
|
|
293
|
+
|
|
287
294
|
get sdkFrame(): SDK.DebuggerModel.CallFrame {
|
|
288
295
|
return this.#sdkFrame;
|
|
289
296
|
}
|
|
@@ -48,8 +48,14 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
48
48
|
|
|
49
49
|
async createFromProtocolRuntime(stackTrace: Protocol.Runtime.StackTrace, rawFramesToUIFrames: TranslateRawFrames):
|
|
50
50
|
Promise<StackTrace.StackTrace.StackTrace> {
|
|
51
|
+
const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
|
|
52
|
+
const syncFrames = stackTrace.callFrames.map((frame): RawFrame => {
|
|
53
|
+
const isWasm = debuggerModel?.isWasm(frame.scriptId) ?? false;
|
|
54
|
+
return {...frame, isWasm};
|
|
55
|
+
});
|
|
56
|
+
|
|
51
57
|
const [syncFragment, asyncFragments] = await Promise.all([
|
|
52
|
-
this.#createFragment(
|
|
58
|
+
this.#createFragment(syncFrames, rawFramesToUIFrames),
|
|
53
59
|
this.#createAsyncFragments(stackTrace, rawFramesToUIFrames),
|
|
54
60
|
]);
|
|
55
61
|
|
|
@@ -126,6 +132,7 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
126
132
|
functionName: frame.functionName,
|
|
127
133
|
lineNumber: frame.location().lineNumber,
|
|
128
134
|
columnNumber: frame.location().columnNumber,
|
|
135
|
+
isWasm: frame.script.isWasm(),
|
|
129
136
|
})),
|
|
130
137
|
rawFramesToUIFrames);
|
|
131
138
|
return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
|
|
@@ -144,8 +151,14 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
144
151
|
continue;
|
|
145
152
|
}
|
|
146
153
|
const model = StackTraceModel.#modelForTarget(target);
|
|
154
|
+
const targetDebuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
|
|
155
|
+
const asyncFrames = asyncStackTrace.callFrames.map((frame): RawFrame => {
|
|
156
|
+
const isWasm = targetDebuggerModel?.isWasm(frame.scriptId) ?? false;
|
|
157
|
+
return {...frame, isWasm};
|
|
158
|
+
});
|
|
159
|
+
|
|
147
160
|
const asyncFragmentPromise =
|
|
148
|
-
model.#createFragment(
|
|
161
|
+
model.#createFragment(asyncFrames, rawFramesToUIFrames)
|
|
149
162
|
.then(fragment => new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
|
|
150
163
|
asyncFragments.push(asyncFragmentPromise);
|
|
151
164
|
}
|
|
@@ -201,7 +214,7 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
201
214
|
node.frames = uiFrames[i++].map(
|
|
202
215
|
frame => new FrameImpl(
|
|
203
216
|
frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column, frame.missingDebugInfo,
|
|
204
|
-
node.rawFrame.functionName));
|
|
217
|
+
node.rawFrame.functionName, node.rawFrame.isWasm));
|
|
205
218
|
|
|
206
219
|
if (node.parsedFrameInfo?.evalOrigin) {
|
|
207
220
|
node.evalOrigin = evalOrigins[evalI++];
|
|
@@ -243,7 +256,7 @@ async function translateEvalOrigin(
|
|
|
243
256
|
const frames = uiFrames[0].map(
|
|
244
257
|
frame => new FrameImpl(
|
|
245
258
|
frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column, frame.missingDebugInfo,
|
|
246
|
-
rawFrame.functionName));
|
|
259
|
+
rawFrame.functionName, rawFrame.isWasm));
|
|
247
260
|
|
|
248
261
|
let parentEvalOrigin: EvalOrigin|undefined;
|
|
249
262
|
if (rawFrame.parsedFrameInfo?.evalOrigin) {
|
|
@@ -11,7 +11,6 @@ export interface ParsedFrameInfo {
|
|
|
11
11
|
readonly isConstructor?: boolean;
|
|
12
12
|
readonly isEval?: boolean;
|
|
13
13
|
readonly evalOrigin?: RawFrame;
|
|
14
|
-
readonly isWasm?: boolean;
|
|
15
14
|
readonly wasmModuleName?: string;
|
|
16
15
|
readonly wasmFunctionIndex?: number;
|
|
17
16
|
readonly typeName?: string;
|
|
@@ -30,6 +29,7 @@ export interface RawFrame {
|
|
|
30
29
|
readonly columnNumber: number;
|
|
31
30
|
|
|
32
31
|
readonly parsedFrameInfo?: ParsedFrameInfo;
|
|
32
|
+
readonly isWasm?: boolean;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -362,23 +362,22 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
|
|
|
362
362
|
const context = conversation?.selectedContext;
|
|
363
363
|
|
|
364
364
|
if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
} else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
|
|
365
|
+
const focus = context.getItem();
|
|
366
|
+
return new PerformanceAgentMarkdownRenderer(focus.parsedTrace.data.Meta.mainFrameId, focus.lookupEvent.bind(focus));
|
|
367
|
+
}
|
|
368
|
+
if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
|
|
371
369
|
// Handle historical conversations (can't linkify anything).
|
|
372
370
|
return new PerformanceAgentMarkdownRenderer();
|
|
373
|
-
}
|
|
374
|
-
|
|
371
|
+
}
|
|
372
|
+
if (Greendev.Prototypes.instance().isEnabled('emulationCapabilities') &&
|
|
375
373
|
conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING &&
|
|
376
374
|
SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel)) {
|
|
377
375
|
const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
|
|
378
376
|
const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
379
377
|
const mainFrameId = resourceTreeModel?.mainFrame?.id;
|
|
380
378
|
return new StylingAgentMarkdownRenderer(mainFrameId);
|
|
381
|
-
}
|
|
379
|
+
}
|
|
380
|
+
if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY) {
|
|
382
381
|
const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
|
|
383
382
|
const mainDocumentURL = domModel?.existingDocument()?.documentURL;
|
|
384
383
|
return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
|
|
@@ -570,11 +569,11 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
|
|
|
570
569
|
// clang-format on
|
|
571
570
|
}
|
|
572
571
|
|
|
573
|
-
function
|
|
572
|
+
function createDOMNodeContext(node: SDK.DOMModel.DOMNode|null): AiAssistanceModel.DOMNodeContext.DOMNodeContext|null {
|
|
574
573
|
if (!node) {
|
|
575
574
|
return null;
|
|
576
575
|
}
|
|
577
|
-
return new AiAssistanceModel.
|
|
576
|
+
return new AiAssistanceModel.DOMNodeContext.DOMNodeContext(node);
|
|
578
577
|
}
|
|
579
578
|
|
|
580
579
|
function createFileContext(file: Workspace.UISourceCode.UISourceCode|null): AiAssistanceModel.FileAgent.FileContext|
|
|
@@ -668,7 +667,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
668
667
|
#conversation?: AiAssistanceModel.AiConversation.AiConversation;
|
|
669
668
|
|
|
670
669
|
#selectedFile: AiAssistanceModel.FileAgent.FileContext|null = null;
|
|
671
|
-
#selectedElement: AiAssistanceModel.
|
|
670
|
+
#selectedElement: AiAssistanceModel.DOMNodeContext.DOMNodeContext|null = null;
|
|
672
671
|
#selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
|
|
673
672
|
#selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
|
|
674
673
|
|
|
@@ -1117,7 +1116,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1117
1116
|
this.#viewOutput.chatView?.focusTextInput();
|
|
1118
1117
|
void this.#handleAidaAvailabilityChange();
|
|
1119
1118
|
this.#selectedElement =
|
|
1120
|
-
|
|
1119
|
+
createDOMNodeContext(selectedElementFilter(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)));
|
|
1121
1120
|
this.#selectedRequest =
|
|
1122
1121
|
createRequestContext(UI.Context.Context.instance().flavor(SDK.NetworkRequest.NetworkRequest));
|
|
1123
1122
|
this.#selectedPerformanceTrace =
|
|
@@ -1225,7 +1224,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1225
1224
|
return;
|
|
1226
1225
|
}
|
|
1227
1226
|
|
|
1228
|
-
this.#selectedElement =
|
|
1227
|
+
this.#selectedElement = createDOMNodeContext(selectedElementFilter(ev.data));
|
|
1229
1228
|
this.#updateConversationState(this.#conversation);
|
|
1230
1229
|
};
|
|
1231
1230
|
|
|
@@ -1576,6 +1575,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1576
1575
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.FILE;
|
|
1577
1576
|
break;
|
|
1578
1577
|
}
|
|
1578
|
+
case 'ai-assistance.storage-floating-button': {
|
|
1579
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceOpenedFromStoragePanelFloatingButton);
|
|
1580
|
+
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STORAGE;
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1579
1583
|
}
|
|
1580
1584
|
|
|
1581
1585
|
if (!targetConversationType) {
|
|
@@ -1727,7 +1731,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1727
1731
|
#handleConversationContextChange = (data: unknown): void => {
|
|
1728
1732
|
if (data instanceof AiAssistanceModel.FileAgent.FileContext) {
|
|
1729
1733
|
this.#selectedFile = data;
|
|
1730
|
-
} else if (data instanceof AiAssistanceModel.
|
|
1734
|
+
} else if (data instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
|
|
1731
1735
|
this.#selectedElement = data;
|
|
1732
1736
|
} else if (data instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
|
|
1733
1737
|
this.#selectedRequest = data;
|
|
@@ -297,3 +297,19 @@ UI.ActionRegistration.registerActionExtension({
|
|
|
297
297
|
},
|
|
298
298
|
condition: config => isFileAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
299
299
|
});
|
|
300
|
+
|
|
301
|
+
UI.ActionRegistration.registerActionExtension({
|
|
302
|
+
actionId: 'ai-assistance.storage-floating-button',
|
|
303
|
+
contextTypes(): [] {
|
|
304
|
+
return [];
|
|
305
|
+
},
|
|
306
|
+
category: UI.ActionRegistration.ActionCategory.GLOBAL,
|
|
307
|
+
title: i18nAiBrandedString(UIStrings.debugWithGemini, UIStrings.debugWithAi),
|
|
308
|
+
configurableBindings: false,
|
|
309
|
+
async loadActionDelegate() {
|
|
310
|
+
const AiAssistance = await loadAiAssistanceModule();
|
|
311
|
+
return new AiAssistance.ActionDelegate();
|
|
312
|
+
},
|
|
313
|
+
condition: config =>
|
|
314
|
+
isStorageAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
315
|
+
});
|
|
@@ -174,7 +174,7 @@ function getContextRemoveLabel(context: AiAssistanceModel.AiAgent.ConversationCo
|
|
|
174
174
|
if (context instanceof AiAssistanceModel.FileAgent.FileContext) {
|
|
175
175
|
return lockedString(UIStringsNotTranslate.removeContextFile);
|
|
176
176
|
}
|
|
177
|
-
if (context instanceof AiAssistanceModel.
|
|
177
|
+
if (context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
|
|
178
178
|
return lockedString(UIStringsNotTranslate.removeContextElement);
|
|
179
179
|
}
|
|
180
180
|
if (context instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
|
|
@@ -336,7 +336,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
336
336
|
})}
|
|
337
337
|
>
|
|
338
338
|
${
|
|
339
|
-
input.context instanceof AiAssistanceModel.
|
|
339
|
+
input.context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext ?
|
|
340
340
|
html`
|
|
341
341
|
<devtools-widget
|
|
342
342
|
class="title"
|
|
@@ -205,6 +205,10 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
|
|
|
205
205
|
UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, storageItem);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
protected override isAiButtonEnabled(): boolean {
|
|
209
|
+
return UI.ActionRegistry.ActionRegistry.instance().hasAction('ai-assistance.storage-floating-button');
|
|
210
|
+
}
|
|
211
|
+
|
|
208
212
|
protected removeItem(key: string): void {
|
|
209
213
|
this.domStorage?.removeItem(key);
|
|
210
214
|
}
|