chrome-devtools-frontend 1.0.1601661 → 1.0.1602543
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/version-control/SKILL.md +71 -0
- package/AUTHORS +1 -0
- package/front_end/core/common/Settings.ts +1 -871
- package/front_end/core/common/VersionController.ts +880 -0
- package/front_end/core/common/common.ts +2 -0
- package/front_end/core/host/InspectorFrontendHostStub.ts +0 -5
- package/front_end/core/platform/HostRuntime.ts +9 -3
- package/front_end/core/protocol_client/InspectorBackend.ts +8 -0
- package/front_end/core/root/Runtime.ts +5 -8
- package/front_end/core/sdk/DOMModel.ts +5 -4
- package/front_end/core/sdk/Target.ts +5 -0
- package/front_end/core/sdk/WebMCPModel.ts +98 -0
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +13 -19
- package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -1
- package/front_end/entrypoints/main/GlobalAiButton.ts +1 -1
- package/front_end/generated/InspectorBackendCommands.ts +6 -5
- package/front_end/generated/protocol.ts +36 -3
- package/front_end/models/ai_assistance/AiConversation.ts +3 -3
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +217 -9
- package/front_end/models/ai_assistance/agents/AiAgent.ts +14 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +19 -4
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +7 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -2
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -23
- package/front_end/models/heap_snapshot_model/HeapSnapshotModel.ts +7 -7
- package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
- package/front_end/models/lighthouse/RunTypes.ts +3 -1
- package/front_end/models/live-metrics/LiveMetrics.ts +51 -31
- package/front_end/panels/accessibility/ARIAAttributesView.ts +2 -2
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
- package/front_end/panels/ai_assistance/components/ChatInput.ts +3 -2
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +84 -27
- package/front_end/panels/ai_assistance/components/chatMessage.css +31 -2
- package/front_end/panels/application/AppManifestView.ts +2 -2
- package/front_end/panels/application/BackgroundServiceView.ts +9 -9
- package/front_end/panels/application/FrameDetailsView.ts +4 -13
- package/front_end/panels/application/IndexedDBViews.ts +1 -1
- package/front_end/panels/application/KeyValueStorageItemsView.ts +4 -4
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +3 -3
- package/front_end/panels/application/components/PermissionsPolicySection.ts +1 -1
- package/front_end/panels/application/components/permissionsPolicySection.css +54 -52
- package/front_end/panels/application/frameDetailsReportView.css +13 -1
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +2 -1
- package/front_end/panels/console_counters/WarningErrorCounter.ts +7 -14
- package/front_end/panels/elements/CSSValueTraceView.ts +9 -1
- package/front_end/panels/elements/NodeStackTraceWidget.ts +2 -1
- package/front_end/panels/elements/PropertiesWidget.ts +14 -13
- package/front_end/panels/elements/PropertyRenderer.ts +10 -9
- package/front_end/panels/elements/StandaloneStylesContainer.ts +15 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +22 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +34 -25
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +5 -0
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +2 -2
- package/front_end/panels/lighthouse/LighthouseController.ts +4 -1
- package/front_end/panels/media/EventDisplayTable.ts +3 -2
- package/front_end/panels/network/EventSourceMessagesView.ts +2 -2
- package/front_end/panels/network/RequestPayloadView.ts +2 -2
- package/front_end/panels/network/ResourceChunkView.ts +1 -1
- package/front_end/panels/profiler/HeapDetachedElementsView.ts +9 -4
- package/front_end/panels/profiler/HeapProfileView.ts +8 -4
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +40 -33
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +1 -1
- package/front_end/panels/profiler/HeapSnapshotProxy.ts +1 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +12 -18
- package/front_end/panels/profiler/LiveHeapProfileView.ts +3 -4
- package/front_end/panels/profiler/ProfileHeader.ts +5 -11
- package/front_end/panels/profiler/ProfileView.ts +1 -1
- package/front_end/panels/recorder/RecorderController.ts +4 -3
- package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -1
- package/front_end/panels/recorder/components/RecordingView.ts +2 -2
- package/front_end/panels/recorder/models/RecordingPlayer.ts +3 -0
- package/front_end/panels/security/SecurityPanel.ts +3 -3
- package/front_end/panels/sensors/SensorsView.ts +149 -125
- package/front_end/panels/timeline/EventsTimelineTreeView.ts +2 -2
- package/front_end/panels/timeline/TimelineDetailsView.ts +6 -37
- package/front_end/panels/timeline/TimelineTreeView.ts +19 -17
- package/front_end/panels/timeline/TimelineUIUtils.ts +0 -118
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +2 -2
- package/front_end/panels/timeline/components/TimelineRangeSummaryView.ts +162 -9
- package/front_end/panels/timeline/components/timelineRangeSummaryView.css +22 -8
- package/front_end/panels/webauthn/WebauthnPane.ts +52 -0
- package/front_end/panels/whats_new/ReleaseNoteText.ts +10 -10
- package/front_end/panels/whats_new/resources/WNDT.md +6 -6
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +4 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +4 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +8 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/legacy/Toolbar.ts +25 -3
- package/front_end/ui/legacy/Treeoutline.ts +7 -3
- package/front_end/ui/legacy/UIUtils.ts +13 -11
- package/front_end/ui/legacy/Widget.ts +40 -40
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -1
- package/front_end/ui/legacy/components/data_grid/SortableDataGrid.ts +2 -2
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +39 -23
- package/front_end/ui/legacy/components/perf_ui/GCActionDelegate.ts +20 -4
- package/front_end/ui/legacy/treeoutline.css +4 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/package.json +1 -1
|
@@ -54,7 +54,7 @@ export interface AiConversationOptions {
|
|
|
54
54
|
performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>;
|
|
55
55
|
onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
|
|
56
56
|
networkTimeCalculator?: NetworkTimeCalculator.NetworkTransferTimeCalculator;
|
|
57
|
-
lighthouseRecording?: () => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
57
|
+
lighthouseRecording?: (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export class AiConversation {
|
|
@@ -91,7 +91,7 @@ export class AiConversation {
|
|
|
91
91
|
#contexts: Array<ConversationContext<unknown>> = [];
|
|
92
92
|
|
|
93
93
|
#performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>;
|
|
94
|
-
#lighthouseRecording?: () => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
94
|
+
#lighthouseRecording?: (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
95
95
|
#onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
|
|
96
96
|
#networkTimeCalculator?: NetworkTimeCalculator.NetworkTransferTimeCalculator;
|
|
97
97
|
|
|
@@ -334,9 +334,9 @@ export class AiConversation {
|
|
|
334
334
|
sessionId: this.id,
|
|
335
335
|
changeManager: this.#changeManager,
|
|
336
336
|
performanceRecordAndReload: this.#performanceRecordAndReload,
|
|
337
|
-
lighthouseRecording: this.#lighthouseRecording,
|
|
338
337
|
onInspectElement: this.#onInspectElement,
|
|
339
338
|
networkTimeCalculator: this.#networkTimeCalculator,
|
|
339
|
+
lighthouseRecording: this.#lighthouseRecording,
|
|
340
340
|
allowedOrigin: this.allowedOrigin,
|
|
341
341
|
history,
|
|
342
342
|
};
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
import * as Host from '../../../core/host/host.js';
|
|
6
6
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
7
7
|
import * as Root from '../../../core/root/root.js';
|
|
8
|
+
import * as SDK from '../../../core/sdk/sdk.js';
|
|
8
9
|
import type * as LHModel from '../../lighthouse/lighthouse.js';
|
|
9
10
|
import {LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
|
|
10
11
|
import {debugLog} from '../debug.js';
|
|
11
12
|
|
|
12
13
|
import {
|
|
14
|
+
type AgentOptions,
|
|
13
15
|
AiAgent,
|
|
14
16
|
type ContextDetail,
|
|
15
17
|
type ContextResponse,
|
|
@@ -23,20 +25,30 @@ import {
|
|
|
23
25
|
* TESTERS. Otherwise, a server-side preamble is used (see
|
|
24
26
|
* chrome_preambles.gcl). Sync local changes with the server-side.
|
|
25
27
|
*/
|
|
26
|
-
const preamble = `You are an accessibility expert agent.
|
|
28
|
+
const preamble = `You are an accessibility expert agent integrated into Chrome DevTools.
|
|
29
|
+
Your role is to help users understand and fix accessibility issues found in Lighthouse reports.
|
|
27
30
|
|
|
28
|
-
#
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
# Style Guidelines
|
|
32
|
+
* **Concise and Direct**: Use short sentences and bullet points. Avoid paragraphs and long explanations.
|
|
33
|
+
* **Structured**: Organize your findings by problem, root cause, and next steps, but do NOT use those literal words as headings.
|
|
34
|
+
* **No Internal Identifiers**: NEVER show Lighthouse paths (e.g., "1,HTML,1,BODY...") to the user. Refer to elements by their tag name, classes, or IDs.
|
|
35
|
+
* **Managing Volume**: If the report contains many issues, provide a brief summary of the top 2-3 most critical ones. Tell the user that there are more issues and invite them to ask for more details or to explore a specific area.
|
|
36
|
+
|
|
37
|
+
# Workflow
|
|
38
|
+
1. **Identify**: Find the most critical accessibility issues in the Lighthouse report.
|
|
39
|
+
2. **Investigate**: For any element identified as failing, you **MUST** call \`getStyles\` or \`getElementAccessibilityDetails\` first to confirm its current state and gather details.
|
|
40
|
+
3. **Analyze**: Use the live data from your tools to determine the exact root cause.
|
|
41
|
+
4. **Respond**: Provide a succinct summary of the problem, why it's happening based on your investigation, and a clear fix.
|
|
32
42
|
|
|
33
43
|
# Capabilities
|
|
34
|
-
*
|
|
35
|
-
*
|
|
44
|
+
* \`getLighthouseAudits\`: Get detailed audit data.
|
|
45
|
+
* \`runAccessibilityAudits\`: Trigger new accessibility snapshot audits.
|
|
46
|
+
* \`getStyles\`: Get computed styles for an element by its path.
|
|
47
|
+
* \`getElementAccessibilityDetails\`: Get A11y properties for an element by its path.
|
|
36
48
|
|
|
37
49
|
# Constraints
|
|
38
|
-
*
|
|
39
|
-
* **CRITICAL
|
|
50
|
+
* **CRITICAL**: ALWAYS call a tool before providing an answer if an element path is available.
|
|
51
|
+
* **CRITICAL**: You are an accessibility agent. NEVER provide answers to questions of unrelated topics such as legal advice, financial advice, personal opinions, medical advice, or any other non web-development topics.
|
|
40
52
|
`;
|
|
41
53
|
|
|
42
54
|
export class AccessibilityContext extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
|
|
@@ -71,6 +83,13 @@ export class AccessibilityContext extends ConversationContext<LHModel.ReporterTy
|
|
|
71
83
|
export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON> {
|
|
72
84
|
readonly preamble = preamble;
|
|
73
85
|
readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_ACCESSIBILITY_AGENT;
|
|
86
|
+
readonly #lighthouseRecording?:
|
|
87
|
+
(overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
88
|
+
|
|
89
|
+
constructor(opts: AgentOptions) {
|
|
90
|
+
super(opts);
|
|
91
|
+
this.#lighthouseRecording = opts.lighthouseRecording;
|
|
92
|
+
}
|
|
74
93
|
|
|
75
94
|
get userTier(): string|undefined {
|
|
76
95
|
return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
|
|
@@ -100,7 +119,64 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
|
|
|
100
119
|
};
|
|
101
120
|
}
|
|
102
121
|
|
|
122
|
+
async #resolvePathToNode(path: string): Promise<SDK.DOMModel.DOMNode|null> {
|
|
123
|
+
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
124
|
+
if (!target) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const domModel = target.model(SDK.DOMModel.DOMModel);
|
|
128
|
+
if (!domModel) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const nodeId = await domModel.pushNodeByPathToFrontend(path);
|
|
132
|
+
if (!nodeId) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return domModel.nodeForId(nodeId);
|
|
136
|
+
}
|
|
137
|
+
|
|
103
138
|
#declareFunctions(): void {
|
|
139
|
+
this.declareFunction<{explanation: string}, {audits: string}>('runAccessibilityAudits', {
|
|
140
|
+
description:
|
|
141
|
+
'Triggers new Lighthouse accessibility audits in snapshot mode. Use this if the user has made changes to the page and you want to re-evaluate the accessibility audits.',
|
|
142
|
+
parameters: {
|
|
143
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
144
|
+
description: '',
|
|
145
|
+
nullable: false,
|
|
146
|
+
properties: {
|
|
147
|
+
explanation: {
|
|
148
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
149
|
+
description: 'Explain why you want to run new audits.',
|
|
150
|
+
nullable: false,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
required: ['explanation'],
|
|
154
|
+
},
|
|
155
|
+
displayInfoFromArgs: params => {
|
|
156
|
+
return {
|
|
157
|
+
title: i18n.i18n.lockedString('Running accessibility audits…'),
|
|
158
|
+
thought: params.explanation,
|
|
159
|
+
action: 'runAccessibilityAudits()'
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
handler: async params => {
|
|
163
|
+
debugLog('Function call: runAccessibilityAudits', params);
|
|
164
|
+
if (!this.#lighthouseRecording) {
|
|
165
|
+
return {error: 'Lighthouse recording is not available.'};
|
|
166
|
+
}
|
|
167
|
+
const report = await this.#lighthouseRecording({
|
|
168
|
+
mode: 'snapshot',
|
|
169
|
+
categoryIds: ['accessibility'],
|
|
170
|
+
isAIControlled: true,
|
|
171
|
+
});
|
|
172
|
+
if (!report) {
|
|
173
|
+
return {error: 'Failed to run accessibility audits.'};
|
|
174
|
+
}
|
|
175
|
+
const audits = new LighthouseFormatter().audits(report, 'accessibility');
|
|
176
|
+
return {result: {audits}};
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
104
180
|
this.declareFunction<{categoryId: LHModel.RunTypes.CategoryId}, {audits: string}>('getLighthouseAudits', {
|
|
105
181
|
description:
|
|
106
182
|
'Returns the audits for a specific Lighthouse category. Use this to get more information about the performance, accessibility, best-practices, or seo audits.',
|
|
@@ -134,6 +210,138 @@ export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON
|
|
|
134
210
|
return {result: {audits}};
|
|
135
211
|
}
|
|
136
212
|
});
|
|
213
|
+
|
|
214
|
+
this.declareFunction<{
|
|
215
|
+
path: string,
|
|
216
|
+
styleProperties: string[],
|
|
217
|
+
explanation: string,
|
|
218
|
+
}>('getStyles', {
|
|
219
|
+
description: 'Get computed styles for an element on the inspected page by its Lighthouse path.',
|
|
220
|
+
parameters: {
|
|
221
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
222
|
+
description: '',
|
|
223
|
+
nullable: false,
|
|
224
|
+
properties: {
|
|
225
|
+
explanation: {
|
|
226
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
227
|
+
description: 'Explain why you want to get styles.',
|
|
228
|
+
nullable: false,
|
|
229
|
+
},
|
|
230
|
+
path: {
|
|
231
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
232
|
+
description:
|
|
233
|
+
'The Lighthouse path of the element (e.g., "1,HTML,1,BODY,2,DIV"). Find this in the report data.',
|
|
234
|
+
nullable: false,
|
|
235
|
+
},
|
|
236
|
+
styleProperties: {
|
|
237
|
+
type: Host.AidaClient.ParametersTypes.ARRAY,
|
|
238
|
+
description: 'One or more CSS style property names to fetch.',
|
|
239
|
+
nullable: false,
|
|
240
|
+
items: {
|
|
241
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
242
|
+
description: 'A CSS style property name to retrieve. For example, \'background-color\'.'
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ['explanation', 'path', 'styleProperties']
|
|
247
|
+
},
|
|
248
|
+
displayInfoFromArgs: params => {
|
|
249
|
+
return {
|
|
250
|
+
title: 'Reading computed styles',
|
|
251
|
+
thought: params.explanation,
|
|
252
|
+
action: `getStyles('${params.path}', ${JSON.stringify(params.styleProperties)})`,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
handler: async params => {
|
|
256
|
+
debugLog('Function call: getStyles', params);
|
|
257
|
+
const node = await this.#resolvePathToNode(params.path);
|
|
258
|
+
if (!node) {
|
|
259
|
+
return {error: `Could not find the element with path: ${params.path}`};
|
|
260
|
+
}
|
|
261
|
+
const styles = await node.domModel().cssModel().getComputedStyle(node.id);
|
|
262
|
+
if (!styles) {
|
|
263
|
+
return {error: 'Could not get computed styles.'};
|
|
264
|
+
}
|
|
265
|
+
const result: Record<string, string|undefined> = {};
|
|
266
|
+
for (const prop of params.styleProperties) {
|
|
267
|
+
result[prop] = styles.get(prop);
|
|
268
|
+
}
|
|
269
|
+
return {result: JSON.stringify(result, null, 2)};
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
this.declareFunction<{
|
|
274
|
+
path: string,
|
|
275
|
+
explanation: string,
|
|
276
|
+
}>('getElementAccessibilityDetails', {
|
|
277
|
+
description:
|
|
278
|
+
'Get detailed accessibility information for an element on the inspected page by its Lighthouse path.',
|
|
279
|
+
parameters: {
|
|
280
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
281
|
+
description: '',
|
|
282
|
+
nullable: false,
|
|
283
|
+
properties: {
|
|
284
|
+
explanation: {
|
|
285
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
286
|
+
description: 'Explain why you want to get accessibility details.',
|
|
287
|
+
nullable: false,
|
|
288
|
+
},
|
|
289
|
+
path: {
|
|
290
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
291
|
+
description:
|
|
292
|
+
'The Lighthouse path of the element (e.g., "1,HTML,1,BODY,2,DIV"). Find this in the report data.',
|
|
293
|
+
nullable: false,
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
required: ['explanation', 'path']
|
|
297
|
+
},
|
|
298
|
+
displayInfoFromArgs: params => {
|
|
299
|
+
return {
|
|
300
|
+
title: 'Reading accessibility details',
|
|
301
|
+
thought: params.explanation,
|
|
302
|
+
action: `getElementAccessibilityDetails('${params.path}')`,
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
handler: async params => {
|
|
306
|
+
debugLog('Function call: getElementAccessibilityDetails', params);
|
|
307
|
+
const node = await this.#resolvePathToNode(params.path);
|
|
308
|
+
if (!node) {
|
|
309
|
+
return {error: `Could not find the element with path: ${params.path}`};
|
|
310
|
+
}
|
|
311
|
+
const accessibilityModel = node.domModel().target().model(SDK.AccessibilityModel.AccessibilityModel);
|
|
312
|
+
if (!accessibilityModel) {
|
|
313
|
+
return {error: 'Accessibility model not found.'};
|
|
314
|
+
}
|
|
315
|
+
await accessibilityModel.requestAndLoadSubTreeToNode(node);
|
|
316
|
+
const axNode = accessibilityModel.axNodeForDOMNode(node);
|
|
317
|
+
if (!axNode) {
|
|
318
|
+
return {error: 'Could not find accessibility node for the element.'};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const result = {
|
|
322
|
+
role: axNode.role()?.value,
|
|
323
|
+
name: axNode.name()?.value,
|
|
324
|
+
nameSource: axNode.name()?.sources?.[0]?.type,
|
|
325
|
+
properties: {
|
|
326
|
+
focusable: node.getAttribute('tabindex') !== undefined || axNode.role()?.value === 'button' ||
|
|
327
|
+
axNode.role()?.value === 'link',
|
|
328
|
+
hidden: axNode.ignored(),
|
|
329
|
+
},
|
|
330
|
+
ariaAttributes: node.attributes()
|
|
331
|
+
.filter(attr => attr.name.startsWith('aria-') || attr.name === 'role')
|
|
332
|
+
.reduce(
|
|
333
|
+
(acc, attr) => {
|
|
334
|
+
acc[attr.name] = attr.value;
|
|
335
|
+
return acc;
|
|
336
|
+
},
|
|
337
|
+
{} as Record<string, string>),
|
|
338
|
+
isIgnored: axNode.ignored(),
|
|
339
|
+
ignoredReasons: axNode.ignoredReasons(),
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
return {result: JSON.stringify(result, null, 2)};
|
|
343
|
+
},
|
|
344
|
+
});
|
|
137
345
|
}
|
|
138
346
|
|
|
139
347
|
/**
|
|
@@ -7,6 +7,7 @@ import * as Root from '../../../core/root/root.js';
|
|
|
7
7
|
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
8
8
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
9
9
|
import * as Greendev from '../../greendev/greendev.js';
|
|
10
|
+
import type * as LHModel from '../../lighthouse/lighthouse.js';
|
|
10
11
|
import type * as Trace from '../../trace/trace.js';
|
|
11
12
|
import {debugLog, isStructuredLogEnabled} from '../debug.js';
|
|
12
13
|
|
|
@@ -151,6 +152,7 @@ export interface AgentOptions {
|
|
|
151
152
|
onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
|
|
152
153
|
history?: Host.AidaClient.Content[];
|
|
153
154
|
allowedOrigin?: () => string | undefined;
|
|
155
|
+
lighthouseRecording?: (overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
export interface ParsedAnswer {
|
|
@@ -277,9 +279,20 @@ export interface LcpBreakdownAiWidget {
|
|
|
277
279
|
};
|
|
278
280
|
}
|
|
279
281
|
|
|
282
|
+
export interface TimelineRangeSummaryAiWidget {
|
|
283
|
+
name: 'TIMELINE_RANGE_SUMMARY';
|
|
284
|
+
data: {
|
|
285
|
+
bounds: Trace.Types.Timing.TraceWindowMicro,
|
|
286
|
+
parsedTrace: Trace.TraceModel.ParsedTrace,
|
|
287
|
+
// We can use this component for other tracks summaries later, so
|
|
288
|
+
// we include the track in the data.
|
|
289
|
+
track: 'main',
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
280
293
|
// This type will grow as we add more widgets.
|
|
281
294
|
export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget|
|
|
282
|
-
PerformanceTraceAiWidget|LcpBreakdownAiWidget;
|
|
295
|
+
PerformanceTraceAiWidget|LcpBreakdownAiWidget|TimelineRangeSummaryAiWidget;
|
|
283
296
|
|
|
284
297
|
export type FunctionCallHandlerResult<Result> = {
|
|
285
298
|
requiresApproval: true,
|
|
@@ -82,14 +82,14 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
82
82
|
readonly #performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>;
|
|
83
83
|
readonly #onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
|
|
84
84
|
readonly #networkTimeCalculator?: NetworkTimeCalculator.NetworkTransferTimeCalculator;
|
|
85
|
-
readonly #lighthouseRecording?:
|
|
85
|
+
readonly #lighthouseRecording?:
|
|
86
|
+
(overrides?: LHModel.RunTypes.RunOverrides) => Promise<LHModel.ReporterTypes.ReportJSON|null>;
|
|
86
87
|
#allowedOrigin: () => string | undefined;
|
|
87
88
|
|
|
88
89
|
constructor(opts: AgentOptions&{
|
|
89
90
|
performanceRecordAndReload?: () => Promise<Trace.TraceModel.ParsedTrace>,
|
|
90
91
|
onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>,
|
|
91
92
|
networkTimeCalculator?: NetworkTimeCalculator.NetworkTransferTimeCalculator,
|
|
92
|
-
lighthouseRecording?: () => Promise<LHModel.ReporterTypes.ReportJSON|null>,
|
|
93
93
|
}) {
|
|
94
94
|
super(opts);
|
|
95
95
|
this.#performanceRecordAndReload = opts.performanceRecordAndReload;
|
|
@@ -119,7 +119,16 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
119
119
|
|
|
120
120
|
let hasCrossOriginRequest = false;
|
|
121
121
|
for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
|
|
122
|
-
|
|
122
|
+
const requestOrigin = new URL(request.documentURL).origin;
|
|
123
|
+
/**
|
|
124
|
+
* NOTE: this origin check does not ensure that all the requests are
|
|
125
|
+
* from the same origin as the target page. Instead, it ensures that
|
|
126
|
+
* the document that loaded the request is the same as the target
|
|
127
|
+
* page. This ensures that we limit the scope to all requests fetched
|
|
128
|
+
* during the loading of the target page, and do not leak URLs from
|
|
129
|
+
* other pages.
|
|
130
|
+
*/
|
|
131
|
+
if (origin && requestOrigin !== origin) {
|
|
123
132
|
hasCrossOriginRequest = true;
|
|
124
133
|
continue;
|
|
125
134
|
}
|
|
@@ -170,8 +179,14 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
170
179
|
};
|
|
171
180
|
},
|
|
172
181
|
handler: async ({id}) => {
|
|
182
|
+
const origin = this.#allowedOrigin();
|
|
173
183
|
const request = Logs.NetworkLog.NetworkLog.instance().requests().find(req => {
|
|
174
|
-
|
|
184
|
+
if (req.requestId() !== id) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const requestOrigin = new URL(req.documentURL).origin;
|
|
189
|
+
return !origin || requestOrigin === origin;
|
|
175
190
|
});
|
|
176
191
|
|
|
177
192
|
if (request) {
|
|
@@ -95,8 +95,14 @@ export class RequestContext extends ConversationContext<SDK.NetworkRequest.Netwo
|
|
|
95
95
|
this.#calculator = calculator;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Note: this is not the literal origin of the network request. This origin
|
|
100
|
+
* is used to determine when we should force the user to start a new AI
|
|
101
|
+
* conversation when the context changes. We allow a single AI conversation to
|
|
102
|
+
* inspect all network requests that were made for that given target URL.
|
|
103
|
+
*/
|
|
98
104
|
override getOrigin(): string {
|
|
99
|
-
return
|
|
105
|
+
return this.#request.documentURL;
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
override getItem(): SDK.NetworkRequest.NetworkRequest {
|
|
@@ -942,7 +942,17 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
|
942
942
|
|
|
943
943
|
const key = `getMainThreadTrackSummary({min: ${bounds.min}, max: ${bounds.max}})`;
|
|
944
944
|
this.#cacheFunctionResult(focus, key, summary);
|
|
945
|
-
return {
|
|
945
|
+
return {
|
|
946
|
+
result: {summary},
|
|
947
|
+
widgets: [{
|
|
948
|
+
name: 'TIMELINE_RANGE_SUMMARY',
|
|
949
|
+
data: {
|
|
950
|
+
parsedTrace,
|
|
951
|
+
bounds,
|
|
952
|
+
track: 'main',
|
|
953
|
+
},
|
|
954
|
+
}],
|
|
955
|
+
};
|
|
946
956
|
},
|
|
947
957
|
|
|
948
958
|
});
|
|
@@ -999,7 +1009,9 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
|
999
1009
|
|
|
1000
1010
|
const key = `getNetworkTrackSummary({min: ${bounds.min}, max: ${bounds.max}})`;
|
|
1001
1011
|
this.#cacheFunctionResult(focus, key, summary);
|
|
1002
|
-
return {
|
|
1012
|
+
return {
|
|
1013
|
+
result: {summary},
|
|
1014
|
+
};
|
|
1003
1015
|
},
|
|
1004
1016
|
|
|
1005
1017
|
});
|
|
@@ -20,7 +20,6 @@ import {AI_ASSISTANCE_CSS_CLASS_NAME, FREESTYLER_WORLD_NAME} from '../injected.j
|
|
|
20
20
|
import {
|
|
21
21
|
type AgentOptions as BaseAgentOptions,
|
|
22
22
|
AiAgent,
|
|
23
|
-
type AnswerResponse,
|
|
24
23
|
type ComputedStyleAiWidget,
|
|
25
24
|
type ContextResponse,
|
|
26
25
|
ConversationContext,
|
|
@@ -1052,28 +1051,6 @@ const data = {
|
|
|
1052
1051
|
this.#currentTurnId++;
|
|
1053
1052
|
}
|
|
1054
1053
|
|
|
1055
|
-
protected override async finalizeAnswer(answer: AnswerResponse): Promise<AnswerResponse> {
|
|
1056
|
-
if (!Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled) {
|
|
1057
|
-
return answer;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
const changedNodeIds = this.#changes.getChangedNodesForGroupId(this.sessionId, this.#currentTurnId);
|
|
1061
|
-
if (changedNodeIds.length === 0) {
|
|
1062
|
-
return answer;
|
|
1063
|
-
}
|
|
1064
|
-
answer.widgets = [
|
|
1065
|
-
...(answer.widgets ?? []),
|
|
1066
|
-
...changedNodeIds.map(id => ({
|
|
1067
|
-
name: 'STYLE_PROPERTIES' as const,
|
|
1068
|
-
data: {
|
|
1069
|
-
backendNodeId: id,
|
|
1070
|
-
selector: AI_ASSISTANCE_FILTER_REGEX,
|
|
1071
|
-
},
|
|
1072
|
-
})),
|
|
1073
|
-
];
|
|
1074
|
-
return answer;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
1054
|
override async enhanceQuery(
|
|
1078
1055
|
query: string, selectedElement: ConversationContext<SDK.DOMModel.DOMNode>|null,
|
|
1079
1056
|
multimodalInputType?: MultimodalInputType): Promise<string> {
|
|
@@ -108,13 +108,13 @@ export class Edge {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
export
|
|
112
|
-
count
|
|
113
|
-
distance
|
|
114
|
-
self
|
|
115
|
-
maxRet
|
|
116
|
-
name
|
|
117
|
-
idxs
|
|
111
|
+
export interface AggregatedInfo {
|
|
112
|
+
count: number;
|
|
113
|
+
distance: number;
|
|
114
|
+
self: number;
|
|
115
|
+
maxRet: number;
|
|
116
|
+
name: string;
|
|
117
|
+
idxs: number[];
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export class AggregateForDiff {
|
|
@@ -6904,6 +6904,10 @@ export const NativeFunctions = [
|
|
|
6904
6904
|
name: "MouseEvent",
|
|
6905
6905
|
signatures: [["type","?eventInitDict"]]
|
|
6906
6906
|
},
|
|
6907
|
+
{
|
|
6908
|
+
name: "PageHideEvent",
|
|
6909
|
+
signatures: [["type","?eventInitDict"]]
|
|
6910
|
+
},
|
|
6907
6911
|
{
|
|
6908
6912
|
name: "PageTransitionEvent",
|
|
6909
6913
|
signatures: [["type","?eventInitDict"]]
|
|
@@ -6,6 +6,7 @@ import type * as Host from '../../core/host/host.js';
|
|
|
6
6
|
import type * as Platform from '../../core/platform/platform.js';
|
|
7
7
|
|
|
8
8
|
export type CategoryId = 'performance'|'accessibility'|'best-practices'|'seo';
|
|
9
|
+
export type RunMode = 'navigation'|'timespan'|'snapshot';
|
|
9
10
|
|
|
10
11
|
export interface Preset {
|
|
11
12
|
setting: Common.Settings.Setting<boolean>;
|
|
@@ -33,10 +34,11 @@ export interface Flags {
|
|
|
33
34
|
disableStorageReset?: boolean;
|
|
34
35
|
throttlingMethod?: string;
|
|
35
36
|
formFactor?: string|boolean;
|
|
36
|
-
mode?:
|
|
37
|
+
mode?: RunMode;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface RunOverrides {
|
|
40
41
|
categoryIds?: CategoryId[];
|
|
42
|
+
mode?: RunMode;
|
|
41
43
|
isAIControlled?: boolean;
|
|
42
44
|
}
|
|
@@ -62,29 +62,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
62
62
|
|
|
63
63
|
private constructor() {
|
|
64
64
|
super();
|
|
65
|
-
|
|
66
|
-
targetManager.observeTargets(this, {scoped: true});
|
|
67
|
-
// Listen for target info changes to detect prerender activation.
|
|
68
|
-
// Scoped observers don't receive events when a prerendered target becomes
|
|
69
|
-
// primary because setScopeTarget() isn't called during that transition.
|
|
70
|
-
targetManager.addEventListener(
|
|
71
|
-
SDK.TargetManager.Events.AVAILABLE_TARGETS_CHANGED, this.#onAvailableTargetsChanged, this);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
#onAvailableTargetsChanged(): void {
|
|
75
|
-
const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
76
|
-
if (primaryTarget && primaryTarget !== this.#target) {
|
|
77
|
-
// Primary target changed (e.g., prerender activation). Switch to it.
|
|
78
|
-
void this.#switchToTarget(primaryTarget);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async #switchToTarget(newTarget: SDK.Target.Target): Promise<void> {
|
|
83
|
-
if (this.#target) {
|
|
84
|
-
await this.disable();
|
|
85
|
-
}
|
|
86
|
-
this.#target = newTarget;
|
|
87
|
-
await this.enable();
|
|
65
|
+
SDK.TargetManager.TargetManager.instance().observeTargets(this);
|
|
88
66
|
}
|
|
89
67
|
|
|
90
68
|
static instance(opts: {forceNew?: boolean} = {forceNew: false}): LiveMetrics {
|
|
@@ -393,6 +371,31 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
393
371
|
this.#sendStatusUpdate();
|
|
394
372
|
}
|
|
395
373
|
|
|
374
|
+
async #getFrameForExecutionContextId(executionContextId: Protocol.Runtime.ExecutionContextId):
|
|
375
|
+
Promise<SDK.ResourceTreeModel.ResourceTreeFrame|null> {
|
|
376
|
+
if (!this.#target) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const runtimeModel = this.#target.model(SDK.RuntimeModel.RuntimeModel);
|
|
381
|
+
if (!runtimeModel) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const executionContext = runtimeModel.executionContext(executionContextId);
|
|
386
|
+
if (!executionContext) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const frameId = executionContext.frameId;
|
|
391
|
+
if (!frameId) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const frameManager = SDK.FrameManager.FrameManager.instance();
|
|
396
|
+
return await frameManager.getOrWaitForFrame(frameId);
|
|
397
|
+
}
|
|
398
|
+
|
|
396
399
|
async #onBindingCalled(event: {data: Protocol.Runtime.BindingCalledEvent}): Promise<void> {
|
|
397
400
|
const {data} = event;
|
|
398
401
|
if (data.name !== Spec.EVENT_BINDING_NAME) {
|
|
@@ -404,8 +407,22 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
404
407
|
await this.#mutex.run(async () => {
|
|
405
408
|
const webVitalsEvent = JSON.parse(data.payload) as Spec.WebVitalsEvent;
|
|
406
409
|
|
|
407
|
-
//
|
|
408
|
-
|
|
410
|
+
// This ensures that `#lastResetContextId` will always be an execution context on the
|
|
411
|
+
// primary frame. If we receive events from this execution context then we automatically
|
|
412
|
+
// know that they are for the primary frame.
|
|
413
|
+
if (this.#lastResetContextId !== data.executionContextId) {
|
|
414
|
+
if (webVitalsEvent.name !== 'reset') {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// We should avoid calling this function for every event.
|
|
419
|
+
// If an interaction triggers a pre-rendered navigation then the old primary frame could
|
|
420
|
+
// be removed before we reach this point, and then it will hang forever.
|
|
421
|
+
const frame = await this.#getFrameForExecutionContextId(data.executionContextId);
|
|
422
|
+
if (!frame?.isPrimaryFrame()) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
409
426
|
this.#lastResetContextId = data.executionContextId;
|
|
410
427
|
}
|
|
411
428
|
|
|
@@ -447,7 +464,6 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
447
464
|
}
|
|
448
465
|
|
|
449
466
|
async targetAdded(target: SDK.Target.Target): Promise<void> {
|
|
450
|
-
// Scoped observers can also receive events for OOPIFs and workers.
|
|
451
467
|
if (target !== SDK.TargetManager.TargetManager.instance().primaryPageTarget()) {
|
|
452
468
|
return;
|
|
453
469
|
}
|
|
@@ -456,12 +472,20 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
456
472
|
}
|
|
457
473
|
|
|
458
474
|
async targetRemoved(target: SDK.Target.Target): Promise<void> {
|
|
459
|
-
// Scoped observers can also receive events for OOPIFs and workers.
|
|
460
475
|
if (target !== this.#target) {
|
|
461
476
|
return;
|
|
462
477
|
}
|
|
463
478
|
await this.disable();
|
|
464
479
|
this.#target = undefined;
|
|
480
|
+
|
|
481
|
+
// If the user navigates to a page that was pre-rendered then the primary page target
|
|
482
|
+
// will be swapped and the old target will be removed. We should ensure live metrics
|
|
483
|
+
// remain enabled on the new primary page target.
|
|
484
|
+
const primaryPageTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
485
|
+
if (primaryPageTarget) {
|
|
486
|
+
this.#target = primaryPageTarget;
|
|
487
|
+
await this.enable();
|
|
488
|
+
}
|
|
465
489
|
}
|
|
466
490
|
|
|
467
491
|
async enable(): Promise<void> {
|
|
@@ -531,10 +555,6 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
531
555
|
return;
|
|
532
556
|
}
|
|
533
557
|
|
|
534
|
-
// Reset to ensure clean state when re-enabling on a new target.
|
|
535
|
-
// See crbug.com/478832430.
|
|
536
|
-
this.#lastResetContextId = undefined;
|
|
537
|
-
|
|
538
558
|
await this.#killAllLiveMetricContexts();
|
|
539
559
|
|
|
540
560
|
const runtimeModel = this.#target.model(SDK.RuntimeModel.RuntimeModel);
|
|
@@ -26,6 +26,7 @@ const UIStrings = {
|
|
|
26
26
|
const str_ = i18n.i18n.registerUIStrings('panels/accessibility/ARIAAttributesView.ts', UIStrings);
|
|
27
27
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
28
28
|
const {render, html} = Lit;
|
|
29
|
+
const {widget} = UI.Widget;
|
|
29
30
|
|
|
30
31
|
interface ViewInput {
|
|
31
32
|
propertyCompletions: Map<SDK.DOMModel.Attribute, string[]>;
|
|
@@ -62,8 +63,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
62
63
|
html`
|
|
63
64
|
<style>${accessibilityPropertiesStyles}</style>
|
|
64
65
|
<devtools-widget
|
|
65
|
-
|
|
66
|
-
{text: i18nString(UIStrings.noAriaAttributes)})}
|
|
66
|
+
${widget(UI.EmptyWidget.EmptyWidget, {text: i18nString(UIStrings.noAriaAttributes)})}
|
|
67
67
|
class="gray-info-message info-message-overflow"></devtools-widget>` :
|
|
68
68
|
html`<devtools-tree
|
|
69
69
|
hide-overflow
|