chrome-devtools-frontend 1.0.1597448 → 1.0.1598808
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/docs/ui_engineering.md +6 -6
- package/front_end/core/host/AidaClient.ts +6 -0
- package/front_end/core/sdk/CPUThrottlingManager.ts +5 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +28 -0
- package/front_end/models/ai_assistance/AiConversation.ts +40 -22
- package/front_end/models/ai_assistance/ChangeManager.ts +16 -0
- package/front_end/models/ai_assistance/ExtensionScope.ts +11 -3
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +127 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +35 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +11 -8
- package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +109 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +30 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +34 -4
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +27 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +21 -0
- package/front_end/models/trace/Processor.ts +1 -0
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -0
- package/front_end/models/trace/insights/CharacterSet.ts +172 -0
- package/front_end/models/trace/insights/Models.ts +1 -0
- package/front_end/models/trace/insights/types.ts +1 -0
- package/front_end/models/trace/types/TraceEvents.ts +17 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +22 -12
- package/front_end/panels/ai_assistance/PatchWidget.ts +1 -1
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +131 -63
- package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +18 -9
- package/front_end/panels/ai_assistance/components/chatMessage.css +11 -0
- package/front_end/panels/application/AppManifestView.ts +4 -5
- package/front_end/panels/application/CookieItemsView.ts +9 -15
- package/front_end/panels/application/DeviceBoundSessionsView.ts +20 -26
- package/front_end/panels/application/FrameDetailsView.ts +10 -16
- package/front_end/panels/application/OriginTrialTreeView.ts +2 -3
- package/front_end/panels/application/ReportingApiView.ts +14 -18
- package/front_end/panels/application/components/BackForwardCacheView.ts +3 -3
- package/front_end/panels/application/preloading/PreloadingView.ts +18 -3
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -3
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
- package/front_end/panels/changes/ChangesView.ts +13 -15
- package/front_end/panels/console/ConsolePinPane.ts +3 -3
- package/front_end/panels/coverage/CoverageListView.ts +1 -1
- package/front_end/panels/coverage/CoverageView.ts +4 -4
- package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +3 -3
- package/front_end/panels/css_overview/CSSOverviewPanel.ts +11 -15
- package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -5
- package/front_end/panels/elements/ElementsTreeOutline.ts +34 -8
- package/front_end/panels/elements/EventListenersWidget.ts +3 -2
- package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -6
- package/front_end/panels/elements/StylePropertiesSection.ts +129 -1
- package/front_end/panels/elements/StylePropertyHighlighter.ts +3 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +49 -4
- package/front_end/panels/elements/stylesSidebarPane.css +34 -0
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +3 -5
- package/front_end/panels/layer_viewer/LayerDetailsView.ts +2 -1
- package/front_end/panels/layer_viewer/Layers3DView.ts +5 -4
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +5 -6
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +6 -11
- package/front_end/panels/network/RequestConditionsDrawer.ts +4 -4
- package/front_end/panels/network/RequestCookiesView.ts +5 -6
- package/front_end/panels/network/RequestHeadersView.ts +4 -4
- package/front_end/panels/network/RequestInitiatorView.ts +7 -5
- package/front_end/panels/network/RequestResponseView.ts +10 -15
- package/front_end/panels/performance_monitor/PerformanceMonitor.ts +2 -2
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +5 -6
- package/front_end/panels/recorder/components/RecordingView.ts +31 -36
- package/front_end/panels/recorder/components/StepEditor.ts +6 -7
- package/front_end/panels/recorder/components/StepView.ts +2 -1
- package/front_end/panels/search/SearchView.ts +3 -4
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -5
- package/front_end/panels/timeline/TimelineDetailsView.ts +8 -6
- package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -8
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -7
- package/front_end/panels/timeline/components/insights/Cache.ts +8 -10
- package/front_end/panels/timeline/components/insights/CharacterSet.ts +34 -0
- package/front_end/panels/timeline/components/insights/DOMSize.ts +16 -20
- package/front_end/panels/timeline/components/insights/DocumentLatency.ts +2 -6
- package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +3 -4
- package/front_end/panels/timeline/components/insights/EventRef.ts +2 -2
- package/front_end/panels/timeline/components/insights/FontDisplay.ts +3 -4
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +5 -7
- package/front_end/panels/timeline/components/insights/INPBreakdown.ts +3 -4
- package/front_end/panels/timeline/components/insights/ImageDelivery.ts +3 -4
- package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -4
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +2 -0
- package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +5 -7
- package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -4
- package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +3 -4
- package/front_end/panels/timeline/components/insights/ModernHTTP.ts +3 -4
- package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +7 -11
- package/front_end/panels/timeline/components/insights/NodeLink.ts +2 -4
- package/front_end/panels/timeline/components/insights/RenderBlocking.ts +3 -4
- package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +7 -10
- package/front_end/panels/timeline/components/insights/ThirdParties.ts +5 -7
- package/front_end/panels/timeline/components/insights/insights.ts +2 -0
- package/front_end/panels/web_audio/WebAudioView.ts +3 -4
- package/front_end/panels/webauthn/WebauthnPane.ts +2 -2
- 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/bidi/Realm.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts +4 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js +13 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/util.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
- 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/Mutex.d.ts +2 -2
- 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/cjs/third_party/mitt/mitt.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/third_party/parsel-js/parsel-js.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/third_party/rxjs/rxjs.js +446 -446
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +166 -167
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts +4 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js +12 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/util.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
- 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/lib/esm/third_party/mitt/mitt.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/third_party/parsel-js/parsel-js.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/third_party/rxjs/rxjs.js +97 -97
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/bidi/Realm.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/bidi/util.ts +17 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/settings/SettingCheckbox.ts +2 -0
- package/front_end/ui/legacy/Widget.ts +42 -6
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -8
- package/front_end/ui/visual_logging/Debugging.ts +0 -32
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/package.json +1 -1
package/docs/ui_engineering.md
CHANGED
|
@@ -44,7 +44,7 @@ To embed another presenter (`UI.Widget`) in the lit-html template, use `widget(<
|
|
|
44
44
|
|
|
45
45
|
This will instantiate a `Widget` class with the web component as its `element` and, optionally, will set the properties provided in the second parameter. The widget won’t be re-instantiated on the subsequent template renders, but the properties would be updated. For this to work, the widget needs to accept `HTMLElement` as a sole constructor parameter and properties need to be public members or setters.
|
|
46
46
|
|
|
47
|
-
For backwards compatibility, the first argument to `
|
|
47
|
+
For backwards compatibility, the first argument to `widget` can also be a factory function: `widget(element => new MyWidget(foo, bar, element))`. Similar to the class constructor version, `element` is the actual `<devtools-widget>` so the following two invocations of `widget` are equivalent: `widget(MyWidget)` and `widget(element => new MyWidget(element))`.
|
|
48
48
|
|
|
49
49
|
## Styling
|
|
50
50
|
To prevent style conflicts in widgets without relying on shadow DOM, we use the CSS [`@scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope) at-rule for style encapsulation. This ensures that styles defined for a widget do not leak out and affect other components.
|
|
@@ -93,9 +93,9 @@ In this example, the `.title` style will apply within the parent widget but will
|
|
|
93
93
|
## Examples
|
|
94
94
|
|
|
95
95
|
```html
|
|
96
|
-
<devtools-widget
|
|
96
|
+
<devtools-widget ${widget(ElementsPanel)}>
|
|
97
97
|
<devtools-split-view>
|
|
98
|
-
<devtools-widget slot="main"
|
|
98
|
+
<devtools-widget slot="main" ${widget(ElementsTree)}></devtools-widget>
|
|
99
99
|
<devtools-tab-pane slot="sidebar">
|
|
100
100
|
${widget(StylesPane, {element: input.element})}
|
|
101
101
|
${widget(ComputedPane, {element: input.element})}
|
|
@@ -1169,12 +1169,12 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1169
1169
|
<div>
|
|
1170
1170
|
<devtools-split-view direction=${this.vertical ? 'column' : 'row'} sidebar-position="first"
|
|
1171
1171
|
sidebar-initial-size="200">
|
|
1172
|
-
<devtools-widget slot="sidebar"
|
|
1172
|
+
<devtools-widget slot="sidebar" ${widget(SidebarPanel,
|
|
1173
1173
|
{minimumSize: {width: 100, height: 25}})}></devtools-widget>
|
|
1174
1174
|
<devtools-split-view direction="column" sidebar-position="second" slot="main"
|
|
1175
1175
|
direction="row" sidebar-position="$this.dockedLeft ? 'second' : 'first'}">
|
|
1176
|
-
<devtools-widget slot="main"
|
|
1177
|
-
<devtools-widget slot="sidebar"
|
|
1176
|
+
<devtools-widget slot="main" ${widget(UI.Widget.EmptyWidget)}></devtools-widget>
|
|
1177
|
+
<devtools-widget slot="sidebar" ${widget(DetailsView)}></devtools-widget>
|
|
1178
1178
|
</devtools-split-view>
|
|
1179
1179
|
</devtools-split-view>
|
|
1180
1180
|
</div>`,
|
|
@@ -133,6 +133,12 @@ export enum ClientFeature {
|
|
|
133
133
|
CHROME_PATCH_AGENT = 12,
|
|
134
134
|
// Chrome AI Assistance Performance Agent.
|
|
135
135
|
CHROME_PERFORMANCE_FULL_AGENT = 24,
|
|
136
|
+
// Chrome Context Selection Agent.
|
|
137
|
+
CHROME_CONTEXT_SELECTION_AGENT = 25,
|
|
138
|
+
// Chrome Accessibility Agent
|
|
139
|
+
CHROME_ACCESSIBILITY_AGENT = 26,
|
|
140
|
+
// Chrome AI Assistance Conversation Summary Agent.
|
|
141
|
+
CHROME_CONVERSATION_SUMMARY_AGENT = 27,
|
|
136
142
|
|
|
137
143
|
// Removed features (for reference).
|
|
138
144
|
// Chrome AI Assistance Performance Insights Agent.
|
|
@@ -35,7 +35,7 @@ const str_ = i18n.i18n.registerUIStrings('core/sdk/CPUThrottlingManager.ts', UIS
|
|
|
35
35
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
36
36
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
37
37
|
|
|
38
|
-
let throttlingManagerInstance: CPUThrottlingManager;
|
|
38
|
+
let throttlingManagerInstance: CPUThrottlingManager|undefined;
|
|
39
39
|
|
|
40
40
|
export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
|
|
41
41
|
SDKModelObserver<EmulationModel> {
|
|
@@ -62,6 +62,10 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
|
62
62
|
return throttlingManagerInstance;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
static removeInstance(): void {
|
|
66
|
+
throttlingManagerInstance = undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
cpuThrottlingRate(): number {
|
|
66
70
|
return this.#cpuThrottlingOption.rate();
|
|
67
71
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
BinOpMatcher,
|
|
22
22
|
ColorMatcher,
|
|
23
23
|
ColorMixMatcher,
|
|
24
|
+
ContrastColorMatcher,
|
|
24
25
|
CustomFunctionMatcher,
|
|
25
26
|
defaultValueForCSSType,
|
|
26
27
|
EnvFunctionMatcher,
|
|
@@ -932,6 +933,7 @@ export class CSSMatchedStyles {
|
|
|
932
933
|
new VariableMatcher(this, style),
|
|
933
934
|
new ColorMatcher(() => computedStyles?.get('color') ?? null),
|
|
934
935
|
new ColorMixMatcher(),
|
|
936
|
+
new ContrastColorMatcher(),
|
|
935
937
|
new URLMatcher(),
|
|
936
938
|
new AngleMatcher(),
|
|
937
939
|
new LinkableNameMatcher(),
|
|
@@ -433,6 +433,34 @@ export class ColorMixMatcher extends matcherBase(ColorMixMatch) {
|
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
export class ContrastColorMatch implements Match {
|
|
437
|
+
constructor(readonly text: string, readonly node: CodeMirror.SyntaxNode, readonly color: CodeMirror.SyntaxNode[]) {
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// clang-format off
|
|
442
|
+
export class ContrastColorMatcher extends matcherBase(ContrastColorMatch) {
|
|
443
|
+
// clang-format on
|
|
444
|
+
override accepts(propertyName: string): boolean {
|
|
445
|
+
return cssMetadata().isColorAwareProperty(propertyName);
|
|
446
|
+
}
|
|
447
|
+
override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): ContrastColorMatch|null {
|
|
448
|
+
if (node.name !== 'CallExpression' || matching.ast.text(node.getChild('Callee')) !== 'contrast-color') {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (matching.getComputedText(node) === '') {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const args = ASTUtils.callArgs(node);
|
|
457
|
+
if (args.length !== 1) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
return new ContrastColorMatch(matching.ast.text(node), node, args[0]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
436
464
|
// clang-format off
|
|
437
465
|
export class URLMatch implements Match {
|
|
438
466
|
constructor(
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
|
|
4
|
+
import * as Common from '../../core/common/common.js';
|
|
5
5
|
import * as Host from '../../core/host/host.js';
|
|
6
6
|
import * as Root from '../../core/root/root.js';
|
|
7
|
-
import
|
|
7
|
+
import * as SDK from '../../core/sdk/sdk.js';
|
|
8
8
|
import type * as Trace from '../../models/trace/trace.js';
|
|
9
9
|
import * as Greendev from '../greendev/greendev.js';
|
|
10
10
|
import type * as NetworkTimeCalculator from '../network_time_calculator/network_time_calculator.js';
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type AiAgent,
|
|
14
14
|
type ContextDetail,
|
|
15
15
|
type ConversationContext,
|
|
16
|
+
ErrorType,
|
|
16
17
|
type MultimodalInput,
|
|
17
18
|
type ResponseData,
|
|
18
19
|
ResponseType,
|
|
@@ -264,21 +265,23 @@ export class AiConversation {
|
|
|
264
265
|
id: this.id,
|
|
265
266
|
history: this.history
|
|
266
267
|
.map(item => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
268
|
+
switch (item.type) {
|
|
269
|
+
case ResponseType.CONTEXT_CHANGE: {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
case ResponseType.USER_QUERY: {
|
|
273
|
+
return {...item, imageInput: undefined};
|
|
274
|
+
}
|
|
275
|
+
case ResponseType.SIDE_EFFECT: {
|
|
276
|
+
return {...item, confirm: undefined};
|
|
277
|
+
}
|
|
278
|
+
case ResponseType.CONTEXT:
|
|
279
|
+
case ResponseType.ACTION: {
|
|
280
|
+
return {...item, widgets: undefined};
|
|
281
|
+
}
|
|
282
|
+
default:
|
|
283
|
+
return item;
|
|
280
284
|
}
|
|
281
|
-
return item;
|
|
282
285
|
})
|
|
283
286
|
.filter(history => !!history),
|
|
284
287
|
type: this.#type,
|
|
@@ -313,6 +316,7 @@ export class AiConversation {
|
|
|
313
316
|
performanceRecordAndReload: this.#performanceRecordAndReload,
|
|
314
317
|
onInspectElement: this.#onInspectElement,
|
|
315
318
|
networkTimeCalculator: this.#networkTimeCalculator,
|
|
319
|
+
allowedOrigin: this.allowedOrigin,
|
|
316
320
|
history,
|
|
317
321
|
};
|
|
318
322
|
switch (type) {
|
|
@@ -369,12 +373,6 @@ export class AiConversation {
|
|
|
369
373
|
void this.addHistoryItem(userQuery);
|
|
370
374
|
yield userQuery;
|
|
371
375
|
|
|
372
|
-
this.#setOriginIfEmpty(this.selectedContext?.getOrigin());
|
|
373
|
-
|
|
374
|
-
if (this.isBlockedByOrigin) {
|
|
375
|
-
throw new Error('Cross-origin context data should not be included');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
376
|
yield* this.#runAgent(initialQuery, options);
|
|
379
377
|
}
|
|
380
378
|
|
|
@@ -390,6 +388,15 @@ export class AiConversation {
|
|
|
390
388
|
multimodalInput?: MultimodalInput,
|
|
391
389
|
} = {},
|
|
392
390
|
): AsyncGenerator<ResponseData, void, void> {
|
|
391
|
+
this.#setOriginIfEmpty(this.selectedContext?.getOrigin());
|
|
392
|
+
if (this.isBlockedByOrigin) {
|
|
393
|
+
yield {
|
|
394
|
+
type: ResponseType.ERROR,
|
|
395
|
+
error: ErrorType.CROSS_ORIGIN,
|
|
396
|
+
};
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
393
400
|
function shouldAddToHistory(data: ResponseData): boolean {
|
|
394
401
|
if (data.type === ResponseType.CONTEXT_CHANGE) {
|
|
395
402
|
return false;
|
|
@@ -445,6 +452,17 @@ export class AiConversation {
|
|
|
445
452
|
get type(): ConversationType {
|
|
446
453
|
return this.#type;
|
|
447
454
|
}
|
|
455
|
+
|
|
456
|
+
allowedOrigin = (): string|undefined => {
|
|
457
|
+
if (this.#origin) {
|
|
458
|
+
return this.#origin;
|
|
459
|
+
}
|
|
460
|
+
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
461
|
+
const inspectedURL = target?.inspectedURL();
|
|
462
|
+
this.#origin = inspectedURL ? new Common.ParsedURL.ParsedURL(inspectedURL).securityOrigin() : undefined;
|
|
463
|
+
|
|
464
|
+
return this.#origin;
|
|
465
|
+
};
|
|
448
466
|
}
|
|
449
467
|
|
|
450
468
|
function isAiAssistanceServerSideLoggingEnabled(): boolean {
|
|
@@ -9,6 +9,8 @@ import type * as Protocol from '../../generated/protocol.js';
|
|
|
9
9
|
|
|
10
10
|
export interface Change {
|
|
11
11
|
groupId: string;
|
|
12
|
+
// Optional turn ID to group changes from the same turn.
|
|
13
|
+
turnId?: number;
|
|
12
14
|
// Optional about where in the source the selector was defined.
|
|
13
15
|
sourceLocation?: string;
|
|
14
16
|
// Selector used by the page or a simple selector as the fallback.
|
|
@@ -17,6 +19,7 @@ export interface Change {
|
|
|
17
19
|
simpleSelector?: string;
|
|
18
20
|
className: string;
|
|
19
21
|
styles: Record<string, string>;
|
|
22
|
+
backendNodeId?: Protocol.DOM.BackendNodeId;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
function formatStyles(styles: Record<string, string>, indent = 2): string {
|
|
@@ -101,6 +104,7 @@ export class ChangeManager {
|
|
|
101
104
|
// it currently causes crashes in the Styles tab when duplicate selectors exist (crbug.com/393515428).
|
|
102
105
|
// This workaround avoids that crash.
|
|
103
106
|
existingChange.groupId = change.groupId;
|
|
107
|
+
existingChange.turnId = change.turnId;
|
|
104
108
|
} else {
|
|
105
109
|
changes.push({
|
|
106
110
|
...change,
|
|
@@ -122,6 +126,18 @@ export class ChangeManager {
|
|
|
122
126
|
.join('\n\n');
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
getChangedNodesForGroupId(groupId: string, turnId?: number): Protocol.DOM.BackendNodeId[] {
|
|
130
|
+
const nodes = new Set<Protocol.DOM.BackendNodeId>();
|
|
131
|
+
for (const changes of this.#stylesheetChanges.values()) {
|
|
132
|
+
for (const change of changes) {
|
|
133
|
+
if (change.groupId === groupId && change.backendNodeId && (turnId === undefined || change.turnId === turnId)) {
|
|
134
|
+
nodes.add(change.backendNodeId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return Array.from(nodes);
|
|
139
|
+
}
|
|
140
|
+
|
|
125
141
|
#formatChangesForInspectorStylesheet(changes: Change[]): string {
|
|
126
142
|
return changes
|
|
127
143
|
.map(change => {
|
|
@@ -22,6 +22,7 @@ interface ElementContext {
|
|
|
22
22
|
selector: string;
|
|
23
23
|
simpleSelector?: string;
|
|
24
24
|
sourceLocation?: string;
|
|
25
|
+
backendNodeId?: Protocol.DOM.BackendNodeId;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -33,6 +34,7 @@ export class ExtensionScope {
|
|
|
33
34
|
}) => Promise<void>> = [];
|
|
34
35
|
#changeManager: ChangeManager;
|
|
35
36
|
#agentId: string;
|
|
37
|
+
#turnId?: number;
|
|
36
38
|
/** Don't use directly use the getter */
|
|
37
39
|
#frameId?: Protocol.Page.FrameId|null;
|
|
38
40
|
/** Don't use directly use the getter */
|
|
@@ -40,15 +42,15 @@ export class ExtensionScope {
|
|
|
40
42
|
|
|
41
43
|
readonly #bindingMutex = new Common.Mutex.Mutex();
|
|
42
44
|
|
|
43
|
-
constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null) {
|
|
45
|
+
constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null, turnId?: number) {
|
|
44
46
|
this.#changeManager = changes;
|
|
45
47
|
const frameId = selectedNode?.frameId();
|
|
46
48
|
const target = selectedNode?.domModel().target();
|
|
47
49
|
this.#agentId = agentId;
|
|
50
|
+
this.#turnId = turnId;
|
|
48
51
|
this.#target = target;
|
|
49
52
|
this.#frameId = frameId;
|
|
50
53
|
}
|
|
51
|
-
|
|
52
54
|
get target(): SDK.Target.Target {
|
|
53
55
|
if (!this.#target) {
|
|
54
56
|
throw new Error('Target is not found for executing code');
|
|
@@ -274,6 +276,7 @@ export class ExtensionScope {
|
|
|
274
276
|
throw new Error('Node is not found');
|
|
275
277
|
}
|
|
276
278
|
|
|
279
|
+
const backendNodeId = node.backendNodeId();
|
|
277
280
|
try {
|
|
278
281
|
const matchedStyles = await cssModel.getMatchedStyles(node.id);
|
|
279
282
|
|
|
@@ -297,6 +300,7 @@ export class ExtensionScope {
|
|
|
297
300
|
selector,
|
|
298
301
|
simpleSelector: ExtensionScope.getSelectorForNode(node),
|
|
299
302
|
sourceLocation: ExtensionScope.getSourceLocation(styleRule),
|
|
303
|
+
backendNodeId,
|
|
300
304
|
};
|
|
301
305
|
} catch {
|
|
302
306
|
// no-op to allow the fallback below to run.
|
|
@@ -305,6 +309,7 @@ export class ExtensionScope {
|
|
|
305
309
|
// Fallback
|
|
306
310
|
return {
|
|
307
311
|
selector: ExtensionScope.getSelectorForNode(node),
|
|
312
|
+
backendNodeId,
|
|
308
313
|
};
|
|
309
314
|
}
|
|
310
315
|
|
|
@@ -337,7 +342,8 @@ export class ExtensionScope {
|
|
|
337
342
|
|
|
338
343
|
let context: ElementContext = {
|
|
339
344
|
// TODO: Should this a be a *?
|
|
340
|
-
selector: ''
|
|
345
|
+
selector: '',
|
|
346
|
+
backendNodeId: undefined,
|
|
341
347
|
};
|
|
342
348
|
try {
|
|
343
349
|
context = await this.#computeContextFromElement(element.object);
|
|
@@ -351,11 +357,13 @@ export class ExtensionScope {
|
|
|
351
357
|
const sanitizedStyles = await this.sanitizedStyleChanges(context.selector, arg.styles);
|
|
352
358
|
const styleChanges = await this.#changeManager.addChange(cssModel, this.frameId, {
|
|
353
359
|
groupId: this.#agentId,
|
|
360
|
+
turnId: this.#turnId,
|
|
354
361
|
sourceLocation: context.sourceLocation,
|
|
355
362
|
selector: context.selector,
|
|
356
363
|
simpleSelector: context.simpleSelector,
|
|
357
364
|
className: arg.className,
|
|
358
365
|
styles: sanitizedStyles,
|
|
366
|
+
backendNodeId: context.backendNodeId,
|
|
359
367
|
});
|
|
360
368
|
await this.#simpleEval(executionContext, `freestyler.respond(${id}, ${JSON.stringify(styleChanges)})`);
|
|
361
369
|
} catch (error) {
|
|
@@ -0,0 +1,127 @@
|
|
|
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 i18n from '../../../core/i18n/i18n.js';
|
|
7
|
+
import * as Root from '../../../core/root/root.js';
|
|
8
|
+
import type * as LHModel from '../../lighthouse/lighthouse.js';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
AiAgent,
|
|
12
|
+
type ContextDetail,
|
|
13
|
+
type ContextResponse,
|
|
14
|
+
ConversationContext,
|
|
15
|
+
type RequestOptions,
|
|
16
|
+
ResponseType,
|
|
17
|
+
} from './AiAgent.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* WARNING: preamble defined in code is only used when userTier is
|
|
21
|
+
* TESTERS. Otherwise, a server-side preamble is used (see
|
|
22
|
+
* chrome_preambles.gcl). Sync local changes with the server-side.
|
|
23
|
+
*/
|
|
24
|
+
const preamble = `You are an accessibility agent.
|
|
25
|
+
|
|
26
|
+
# Considerations
|
|
27
|
+
* Keep your analysis concise and focused, highlighting only the most critical aspects for a software engineer.
|
|
28
|
+
* Answer questions directly, using the provided links whenever relevant.
|
|
29
|
+
* Always double-check links to make sure they are complete and correct.
|
|
30
|
+
* **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.
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Strings that don't need to be translated at this time.
|
|
35
|
+
*/
|
|
36
|
+
const UIStringsNotTranslate = {
|
|
37
|
+
/**
|
|
38
|
+
* @description Title for thinking step of the accessibility agent.
|
|
39
|
+
*/
|
|
40
|
+
inspectingAudits: 'Inspecting audits',
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
const lockedString = i18n.i18n.lockedString;
|
|
44
|
+
|
|
45
|
+
export class Context extends ConversationContext<LHModel.ReporterTypes.ReportJSON> {
|
|
46
|
+
#lh: LHModel.ReporterTypes.ReportJSON;
|
|
47
|
+
|
|
48
|
+
constructor(report: LHModel.ReporterTypes.ReportJSON) {
|
|
49
|
+
super();
|
|
50
|
+
this.#lh = report;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#url(): string {
|
|
54
|
+
return this.#lh.finalUrl ?? this.#lh.finalDisplayedUrl;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override getOrigin(): string {
|
|
58
|
+
return new URL(this.#url()).origin;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override getItem(): LHModel.ReporterTypes.ReportJSON {
|
|
62
|
+
return this.#lh;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override getTitle(): string {
|
|
66
|
+
return `Lighthouse report: ${this.#url()}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* One agent instance handles one conversation. Create a new agent
|
|
72
|
+
* instance for a new conversation.
|
|
73
|
+
*/
|
|
74
|
+
export class AccessibilityAgent extends AiAgent<LHModel.ReporterTypes.ReportJSON> {
|
|
75
|
+
readonly preamble = preamble;
|
|
76
|
+
readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_ACCESSIBILITY_AGENT;
|
|
77
|
+
|
|
78
|
+
get userTier(): string|undefined {
|
|
79
|
+
// TODO(b/491772868): tidy up userTier & feature flags in the backend.
|
|
80
|
+
return Root.Runtime.hostConfig.devToolsFreestyler?.userTier;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get options(): RequestOptions {
|
|
84
|
+
// TODO(b/491772868): tidy up userTier & feature flags in the backend.
|
|
85
|
+
const temperature = Root.Runtime.hostConfig.devToolsAiAssistanceFileAgent?.temperature;
|
|
86
|
+
const modelId = Root.Runtime.hostConfig.devToolsAiAssistanceFileAgent?.modelId;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
temperature,
|
|
90
|
+
modelId,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async *
|
|
95
|
+
handleContextDetails(selectedFile: ConversationContext<LHModel.ReporterTypes.ReportJSON>|null):
|
|
96
|
+
AsyncGenerator<ContextResponse, void, void> {
|
|
97
|
+
if (!selectedFile) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
yield {
|
|
102
|
+
type: ResponseType.CONTEXT,
|
|
103
|
+
title: lockedString(UIStringsNotTranslate.inspectingAudits),
|
|
104
|
+
details: createContextDetails(selectedFile),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override async enhanceQuery(query: string, lhr: ConversationContext<LHModel.ReporterTypes.ReportJSON>|null):
|
|
109
|
+
Promise<string> {
|
|
110
|
+
const enhancedQuery = lhr ?
|
|
111
|
+
// TODO: formatter for LH report.
|
|
112
|
+
`# Lighthouse Report\n${JSON.stringify(lhr.getItem(), null, 2)}\n\n# User request\n\n` :
|
|
113
|
+
'';
|
|
114
|
+
return `${enhancedQuery}${query}`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createContextDetails(_lhr: ConversationContext<LHModel.ReporterTypes.ReportJSON>):
|
|
119
|
+
[ContextDetail, ...ContextDetail[]] {
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
title: 'Lighthouse report',
|
|
123
|
+
// TODO(b/491772868);
|
|
124
|
+
text: ''
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
@@ -29,6 +29,7 @@ export const enum ErrorType {
|
|
|
29
29
|
ABORT = 'abort',
|
|
30
30
|
MAX_STEPS = 'max-steps',
|
|
31
31
|
BLOCK = 'block',
|
|
32
|
+
CROSS_ORIGIN = 'cross-origin'
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export const enum MultimodalInputType {
|
|
@@ -49,6 +50,7 @@ export interface AnswerResponse {
|
|
|
49
50
|
complete: boolean;
|
|
50
51
|
rpcId?: Host.AidaClient.RpcGlobalId;
|
|
51
52
|
suggestions?: [string, ...string[]];
|
|
53
|
+
widgets?: AiWidget[];
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export interface SuggestionsResponse {
|
|
@@ -148,6 +150,7 @@ export interface AgentOptions {
|
|
|
148
150
|
confirmSideEffectForTest?: typeof Promise.withResolvers;
|
|
149
151
|
onInspectElement?: () => Promise<SDK.DOMModel.DOMNode|null>;
|
|
150
152
|
history?: Host.AidaClient.Content[];
|
|
153
|
+
allowedOrigin?: () => string | undefined;
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
export interface ParsedAnswer {
|
|
@@ -238,8 +241,23 @@ export interface CoreVitalsAiWidget {
|
|
|
238
241
|
};
|
|
239
242
|
}
|
|
240
243
|
|
|
244
|
+
export interface StylePropertiesAiWidget {
|
|
245
|
+
name: 'STYLE_PROPERTIES';
|
|
246
|
+
data: {
|
|
247
|
+
backendNodeId: Protocol.DOM.BackendNodeId,
|
|
248
|
+
selector?: string,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface DomTreeAiWidget {
|
|
253
|
+
name: 'DOM_TREE';
|
|
254
|
+
data: {
|
|
255
|
+
root: SDK.DOMModel.DOMNodeSnapshot,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
241
259
|
// This type will grow as we add more widgets.
|
|
242
|
-
export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget;
|
|
260
|
+
export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget|StylePropertiesAiWidget|DomTreeAiWidget;
|
|
243
261
|
|
|
244
262
|
export type FunctionCallHandlerResult<Result> = {
|
|
245
263
|
requiresApproval: true,
|
|
@@ -521,6 +539,10 @@ export abstract class AiAgent<T> {
|
|
|
521
539
|
return this.parseTextResponseForSuggestions(response.trim());
|
|
522
540
|
}
|
|
523
541
|
|
|
542
|
+
protected async finalizeAnswer(answer: AnswerResponse): Promise<AnswerResponse> {
|
|
543
|
+
return answer;
|
|
544
|
+
}
|
|
545
|
+
|
|
524
546
|
/**
|
|
525
547
|
* Declare a function that the AI model can call.
|
|
526
548
|
* @param name The name of the function
|
|
@@ -546,6 +568,9 @@ export abstract class AiAgent<T> {
|
|
|
546
568
|
this.#functionDeclarations.clear();
|
|
547
569
|
}
|
|
548
570
|
|
|
571
|
+
protected async preRun(): Promise<void> {
|
|
572
|
+
}
|
|
573
|
+
|
|
549
574
|
async *
|
|
550
575
|
run(
|
|
551
576
|
initialQuery: string,
|
|
@@ -555,6 +580,7 @@ export abstract class AiAgent<T> {
|
|
|
555
580
|
},
|
|
556
581
|
multimodalInput?: MultimodalInput,
|
|
557
582
|
): AsyncGenerator<ResponseData, void, void> {
|
|
583
|
+
await this.preRun();
|
|
558
584
|
await options.selected?.refresh();
|
|
559
585
|
if (options.selected) {
|
|
560
586
|
this.context = options.selected;
|
|
@@ -632,13 +658,13 @@ export abstract class AiAgent<T> {
|
|
|
632
658
|
});
|
|
633
659
|
}
|
|
634
660
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceAnswerReceived);
|
|
635
|
-
yield {
|
|
661
|
+
yield await this.finalizeAnswer({
|
|
636
662
|
type: ResponseType.ANSWER,
|
|
637
663
|
text: parsedResponse.answer,
|
|
638
664
|
suggestions: parsedResponse.suggestions,
|
|
639
665
|
complete: true,
|
|
640
666
|
rpcId,
|
|
641
|
-
};
|
|
667
|
+
});
|
|
642
668
|
if (!functionCall) {
|
|
643
669
|
break;
|
|
644
670
|
}
|
|
@@ -670,10 +696,12 @@ export abstract class AiAgent<T> {
|
|
|
670
696
|
|
|
671
697
|
return;
|
|
672
698
|
}
|
|
699
|
+
|
|
673
700
|
query = {
|
|
674
701
|
functionResponse: {
|
|
675
702
|
name: functionCall.name,
|
|
676
|
-
|
|
703
|
+
// Widgets are not sent back to the LLM
|
|
704
|
+
response: {...result, widgets: undefined},
|
|
677
705
|
},
|
|
678
706
|
};
|
|
679
707
|
request = this.buildRequest(query, Host.AidaClient.Role.ROLE_UNSPECIFIED);
|
|
@@ -701,7 +729,8 @@ export abstract class AiAgent<T> {
|
|
|
701
729
|
options?: FunctionHandlerOptions&{explanation?: string},
|
|
702
730
|
): AsyncGenerator<FunctionCallResponseData, {
|
|
703
731
|
result: unknown,
|
|
704
|
-
|
|
732
|
+
widgets?: AiWidget[],
|
|
733
|
+
}|{context: ConversationContext<unknown>, description: string, widgets?: AiWidget[]}> {
|
|
705
734
|
const call = this.#functionDeclarations.get(name);
|
|
706
735
|
if (!call) {
|
|
707
736
|
throw new Error(`Function ${name} is not found.`);
|
|
@@ -818,7 +847,7 @@ export abstract class AiAgent<T> {
|
|
|
818
847
|
return result;
|
|
819
848
|
}
|
|
820
849
|
|
|
821
|
-
return result as {result: unknown};
|
|
850
|
+
return result as {result: unknown, widgets?: AiWidget[]};
|
|
822
851
|
}
|
|
823
852
|
|
|
824
853
|
async *
|