chrome-devtools-frontend 1.0.1596535 → 1.0.1597448
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/prompts/ui-widgets.md +7 -8
- package/docs/ui_engineering.md +10 -11
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/UserMetrics.ts +12 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CPUThrottlingManager.ts +9 -12
- package/front_end/core/sdk/PageResourceLoader.ts +22 -1
- package/front_end/devtools_compatibility.js +2 -1
- package/front_end/models/ai_assistance/AiConversation.ts +5 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +4 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +24 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +289 -12
- package/front_end/models/greendev/Prototypes.ts +7 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +35 -33
- package/front_end/panels/ai_assistance/PatchWidget.ts +6 -6
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +3 -28
- package/front_end/panels/ai_assistance/components/ChatView.ts +6 -11
- package/front_end/panels/ai_assistance/components/StylingAgentMarkdownRenderer.ts +200 -0
- package/front_end/panels/ai_assistance/components/chatMessage.css +0 -8
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
- package/front_end/panels/common/ExtensionServer.ts +15 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +55 -47
- package/front_end/panels/elements/ElementsTreeOutline.ts +149 -28
- package/front_end/panels/lighthouse/LighthousePanel.ts +8 -0
- package/front_end/panels/settings/SettingsScreen.ts +3 -2
- package/front_end/ui/legacy/UIUtils.ts +5 -5
- package/front_end/ui/legacy/Widget.ts +33 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ Adhere strictly to the Model-View-Presenter (MVP) architecture.
|
|
|
11
11
|
### Presenter (`Widget`) Rules
|
|
12
12
|
|
|
13
13
|
* Location: Co-located in the same file as its View.
|
|
14
|
-
* MUST extend a base `UI.Widget` class (e.g., `UI.Widget.Widget`). Note that `UI.Widget.Widget` is not an `HTMLElement` and must be appended via `.show()` or
|
|
14
|
+
* MUST extend a base `UI.Widget` class (e.g., `UI.Widget.Widget`). Note that `UI.Widget.Widget` is not an `HTMLElement` and must be appended via `.show()` or `UI.Widget.widget`
|
|
15
15
|
* Constructor MUST assign the injected view function to a private `#view` field.
|
|
16
16
|
* Constructor MUST call `super()`. If taking `element?: HTMLElement`, pass it to `super(element)`. `super(true)` is forbidden.
|
|
17
17
|
* Styling MUST be handled within the View. `this.registerCSSFiles()` is forbidden.
|
|
@@ -50,10 +50,10 @@ const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement)
|
|
|
50
50
|
// clang-format on
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
### Composition
|
|
53
|
+
### Composition
|
|
54
54
|
|
|
55
|
-
* To render a child widget, the parent's View MUST use
|
|
56
|
-
* Configuration is passed via the
|
|
55
|
+
* To render a child widget, the parent's View MUST use lit-html directive `UI.Widget.wiget`
|
|
56
|
+
* Configuration is passed via the parameters.
|
|
57
57
|
* Properties passed from a parent MUST be declared as public fields on the child presenter class.
|
|
58
58
|
* The framework automatically updates these properties and calls `requestUpdate()` on the child when the parent re-renders.
|
|
59
59
|
|
|
@@ -66,7 +66,7 @@ When migrating imperative components (extending `UI.VBox`, `UI.Panel`, or `HTMLE
|
|
|
66
66
|
* Prefer extending `UI.Widget.Widget`.
|
|
67
67
|
* **Special Case:** If the component *must* extend `UI.Panel.Panel` or `UI.Dialog.Dialog` (to retain specific functionality), you cannot use `requestUpdate()`. Instead, call `this.performUpdate()` directly on state changes.
|
|
68
68
|
3. **State Migration:** Move DOM-stored state to private class fields.
|
|
69
|
-
4. **Update Usage:** Replace `new MyComponent()` instantiations with declarative
|
|
69
|
+
4. **Update Usage:** Replace `new MyComponent()` instantiations with declarative `widget(MyComponent, {params})` in parent templates.
|
|
70
70
|
5. **Scoping Styles:** Ensure your CSS file uses the `@scope` block to prevent style leaks:
|
|
71
71
|
```css
|
|
72
72
|
@scope to (devtools-widget > *) {
|
|
@@ -101,7 +101,7 @@ render(html`
|
|
|
101
101
|
|
|
102
102
|
### Legacy Interop & Refs
|
|
103
103
|
* **Raw Elements:** Use `Lit.Directives.ref` to obtain a reference to a raw `HTMLElement` if needed for imperative APIs (e.g., `splitWidget.installResizer(element)`).
|
|
104
|
-
* **Child Widgets:** Use `UI.Widget.widgetRef` to obtain the class instance of a child
|
|
104
|
+
* **Child Widgets:** Use `UI.Widget.widgetRef` to obtain the class instance of a child widget if you need to call methods on it directly (though declarative data flow is preferred).
|
|
105
105
|
|
|
106
106
|
### Dependencies
|
|
107
107
|
The `DEFAULT_VIEW` is typically a module-level function. Ensure all dependencies (enums, constants, other components) are imported at the top of the file so they are available in the function scope.
|
|
@@ -236,8 +236,7 @@ const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement):
|
|
|
236
236
|
<button @click=${input.onTitleChange}>Change Child Title</button>
|
|
237
237
|
|
|
238
238
|
<!-- Pass properties to the child widget. -->
|
|
239
|
-
|
|
240
|
-
</devtools-widget>
|
|
239
|
+
${widget(MyExampleWidget, {title: input.title})}
|
|
241
240
|
</div>
|
|
242
241
|
`, target);
|
|
243
242
|
};
|
package/docs/ui_engineering.md
CHANGED
|
@@ -40,11 +40,11 @@ To test the `DEFAULT_VIEW` function itself, we should use screenshot and e2e tes
|
|
|
40
40
|
|
|
41
41
|
We should no longer use imperative API to update DOM. Instead we rely on orchestrated rendering of lit-html templates. The view function described above should be a call to lit-html `render`. The view function should be called from `UI.Widget`’s `performUpdate` method, which by default is scheduled using `requestAnimationFrame`.
|
|
42
42
|
|
|
43
|
-
To embed another presenter (`UI.Widget`) in the lit-html template, use
|
|
43
|
+
To embed another presenter (`UI.Widget`) in the lit-html template, use `widget(<class>, {foo: 1, bar: 2})`
|
|
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 `widgetConfig` can also be a factory function:
|
|
47
|
+
For backwards compatibility, the first argument to `widgetConfig` 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 `widgetConfig` 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.
|
|
@@ -83,12 +83,12 @@ render(html`
|
|
|
83
83
|
<div class="container">
|
|
84
84
|
<h3 class="title">My Widget</h3>
|
|
85
85
|
...
|
|
86
|
-
|
|
86
|
+
${widget(NestedWidget)}
|
|
87
87
|
</div>
|
|
88
88
|
`, this.element);
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
In this example, the `.title` style will apply within the parent widget but will not leak into the nested
|
|
91
|
+
In this example, the `.title` style will apply within the parent widget but will not leak into the nested widget. Because this convention relies on developer discipline, it is important to verify its correct application during code reviews.
|
|
92
92
|
|
|
93
93
|
## Examples
|
|
94
94
|
|
|
@@ -97,8 +97,8 @@ In this example, the `.title` style will apply within the parent widget but will
|
|
|
97
97
|
<devtools-split-view>
|
|
98
98
|
<devtools-widget slot="main" .widgetConfig=${widgetConfig(ElementsTree)}></devtools-widget>
|
|
99
99
|
<devtools-tab-pane slot="sidebar">
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
${widget(StylesPane, {element: input.element})}
|
|
101
|
+
${widget(ComputedPane, {element: input.element})}
|
|
102
102
|
...
|
|
103
103
|
</devtools-tab-pane>
|
|
104
104
|
</devtools-split-view>
|
|
@@ -109,8 +109,7 @@ In this example, the `.title` style will apply within the parent widget but will
|
|
|
109
109
|
type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
|
|
110
110
|
const DEFAULT_VIEW = (input, output, target) => {
|
|
111
111
|
render(html`
|
|
112
|
-
|
|
113
|
-
</devtools-widget>
|
|
112
|
+
${widget(MetricsPane, {element: input.element})}
|
|
114
113
|
<devtools-toolbar>
|
|
115
114
|
<devtools-filter-input @change=${input.onFilter}></devtools-filter-input>
|
|
116
115
|
<devtools-checkbox @change=${input.onShowAll}>Show All</devtools-checkbox>
|
|
@@ -1236,7 +1235,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1236
1235
|
|
|
1237
1236
|
## Migrating `EmptyWidget`
|
|
1238
1237
|
|
|
1239
|
-
Replace the imperative `EmptyWidget` with a declarative
|
|
1238
|
+
Replace the imperative `EmptyWidget` with a declarative `widget` and configure it to render an `EmptyWidget`.
|
|
1240
1239
|
|
|
1241
1240
|
**Before:**
|
|
1242
1241
|
```typescript
|
|
@@ -1258,9 +1257,9 @@ class SomeWidget extends UI.Widget.Widget {
|
|
|
1258
1257
|
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
1259
1258
|
render(html`
|
|
1260
1259
|
<div>
|
|
1261
|
-
|
|
1260
|
+
${widget(UI.EmptyWidget.EmptyWidget,{
|
|
1262
1261
|
header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
|
|
1263
|
-
link: 'http://www.google.com',})}
|
|
1262
|
+
link: 'http://www.google.com',})}
|
|
1264
1263
|
</div>`,
|
|
1265
1264
|
target, {host: input});
|
|
1266
1265
|
};
|
|
@@ -556,5 +556,6 @@ export const enum EnumeratedHistogram {
|
|
|
556
556
|
LighthouseCategoryUsed = 'DevTools.LighthouseCategoryUsed',
|
|
557
557
|
SwatchActivated = 'DevTools.SwatchActivated',
|
|
558
558
|
BuiltInAiAvailability = 'DevTools.BuiltInAiAvailability',
|
|
559
|
+
ExtensionEvalTarget = 'DevTools.ExtensionEvalTarget',
|
|
559
560
|
// LINT.ThenChange(/front_end/devtools_compatibility.js:EnumeratedHistogram)
|
|
560
561
|
}
|
|
@@ -322,6 +322,11 @@ export class UserMetrics {
|
|
|
322
322
|
InspectorFrontendHostInstance.recordPerformanceHistogram(
|
|
323
323
|
'DevTools.Insights.ShortTeaserGenerationTime', timeInMilliseconds);
|
|
324
324
|
}
|
|
325
|
+
|
|
326
|
+
extensionEvalTarget(target: ExtensionEvalTarget): void {
|
|
327
|
+
InspectorFrontendHostInstance.recordEnumeratedHistogram(
|
|
328
|
+
EnumeratedHistogram.ExtensionEvalTarget, target, ExtensionEvalTarget.MAX_VALUE);
|
|
329
|
+
}
|
|
325
330
|
}
|
|
326
331
|
|
|
327
332
|
/**
|
|
@@ -1264,3 +1269,10 @@ export const enum BuiltInAiAvailability {
|
|
|
1264
1269
|
DISABLED_NO_GPU = 9,
|
|
1265
1270
|
MAX_VALUE = 10,
|
|
1266
1271
|
}
|
|
1272
|
+
|
|
1273
|
+
export const enum ExtensionEvalTarget {
|
|
1274
|
+
WEB_PAGE = 0,
|
|
1275
|
+
SAME_EXTENSION = 1,
|
|
1276
|
+
OTHER_EXTENSION = 2,
|
|
1277
|
+
MAX_VALUE = 3,
|
|
1278
|
+
}
|
|
@@ -476,6 +476,10 @@ export interface HostConfigAiAssistanceFileAgent {
|
|
|
476
476
|
userTier: string;
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
+
export interface HostConfigAiAssistanceAccessibilityAgent {
|
|
480
|
+
enabled: boolean;
|
|
481
|
+
}
|
|
482
|
+
|
|
479
483
|
export interface HostConfigAiCodeCompletion {
|
|
480
484
|
modelId: string;
|
|
481
485
|
temperature: number;
|
|
@@ -632,6 +636,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
|
|
632
636
|
devToolsAiAssistanceNetworkAgent: HostConfigAiAssistanceNetworkAgent,
|
|
633
637
|
devToolsAiAssistanceFileAgent: HostConfigAiAssistanceFileAgent,
|
|
634
638
|
devToolsAiAssistancePerformanceAgent: HostConfigAiAssistancePerformanceAgent,
|
|
639
|
+
devToolsAiAssistanceAccessibilityAgent: HostConfigAiAssistanceAccessibilityAgent,
|
|
635
640
|
devToolsAiAssistanceV2: HostConfigAiAssistanceV2,
|
|
636
641
|
devToolsAiCodeCompletion: HostConfigAiCodeCompletion,
|
|
637
642
|
devToolsAiCodeGeneration: HostConfigAiCodeGeneration,
|
|
@@ -39,27 +39,24 @@ let throttlingManagerInstance: CPUThrottlingManager;
|
|
|
39
39
|
|
|
40
40
|
export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
|
|
41
41
|
SDKModelObserver<EmulationModel> {
|
|
42
|
-
readonly #targetManager: TargetManager;
|
|
43
42
|
#cpuThrottlingOption: CPUThrottlingOption;
|
|
44
43
|
#calibratedThrottlingSetting: Common.Settings.Setting<CalibratedCPUThrottling>;
|
|
45
44
|
#hardwareConcurrency?: number;
|
|
46
45
|
#pendingMainTargetPromise?: (r: number) => void;
|
|
47
46
|
|
|
48
|
-
private constructor(
|
|
47
|
+
private constructor() {
|
|
49
48
|
super();
|
|
50
|
-
this.#targetManager = targetManager;
|
|
51
49
|
this.#cpuThrottlingOption = NoThrottlingOption;
|
|
52
|
-
this.#calibratedThrottlingSetting =
|
|
50
|
+
this.#calibratedThrottlingSetting = Common.Settings.Settings.instance().createSetting<CalibratedCPUThrottling>(
|
|
53
51
|
'calibrated-cpu-throttling', {}, Common.Settings.SettingStorageType.GLOBAL);
|
|
54
52
|
this.#calibratedThrottlingSetting.addChangeListener(this.#onCalibratedSettingChanged, this);
|
|
55
|
-
|
|
53
|
+
TargetManager.instance().observeModels(EmulationModel, this);
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
static instance(opts: {forceNew: boolean|null} = {forceNew: null}): CPUThrottlingManager {
|
|
59
57
|
const {forceNew} = opts;
|
|
60
58
|
if (!throttlingManagerInstance || forceNew) {
|
|
61
|
-
throttlingManagerInstance =
|
|
62
|
-
new CPUThrottlingManager(Common.Settings.Settings.instance(), TargetManager.instance());
|
|
59
|
+
throttlingManagerInstance = new CPUThrottlingManager();
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
return throttlingManagerInstance;
|
|
@@ -87,7 +84,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
|
87
84
|
return;
|
|
88
85
|
}
|
|
89
86
|
|
|
90
|
-
for (const emulationModel of
|
|
87
|
+
for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
|
|
91
88
|
void emulationModel.setCPUThrottlingRate(rate);
|
|
92
89
|
}
|
|
93
90
|
this.dispatchEventToListeners(Events.RATE_CHANGED, rate);
|
|
@@ -99,7 +96,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
this.#cpuThrottlingOption = option;
|
|
102
|
-
for (const emulationModel of
|
|
99
|
+
for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
|
|
103
100
|
void emulationModel.setCPUThrottlingRate(this.#cpuThrottlingOption.rate());
|
|
104
101
|
}
|
|
105
102
|
this.dispatchEventToListeners(Events.RATE_CHANGED, this.#cpuThrottlingOption.rate());
|
|
@@ -107,7 +104,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
|
107
104
|
|
|
108
105
|
setHardwareConcurrency(concurrency: number): void {
|
|
109
106
|
this.#hardwareConcurrency = concurrency;
|
|
110
|
-
for (const emulationModel of
|
|
107
|
+
for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
|
|
111
108
|
void emulationModel.setHardwareConcurrency(concurrency);
|
|
112
109
|
}
|
|
113
110
|
this.dispatchEventToListeners(Events.HARDWARE_CONCURRENCY_CHANGED, this.#hardwareConcurrency);
|
|
@@ -118,14 +115,14 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
|
118
115
|
// target may error. So if we get any errors here at all, assume that we do
|
|
119
116
|
// not have a target.
|
|
120
117
|
try {
|
|
121
|
-
return
|
|
118
|
+
return TargetManager.instance().primaryPageTarget() !== null;
|
|
122
119
|
} catch {
|
|
123
120
|
return false;
|
|
124
121
|
}
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
async getHardwareConcurrency(): Promise<number> {
|
|
128
|
-
const target =
|
|
125
|
+
const target = TargetManager.instance().primaryPageTarget();
|
|
129
126
|
const existingCallback = this.#pendingMainTargetPromise;
|
|
130
127
|
|
|
131
128
|
// If the main target hasn't attached yet, block callers until it appears.
|
|
@@ -334,6 +334,27 @@ export class PageResourceLoader extends Common.ObjectWrapper.ObjectWrapper<Event
|
|
|
334
334
|
initiator.target;
|
|
335
335
|
Host.userMetrics.developerResourceScheme(this.getDeveloperResourceScheme(parsedURL));
|
|
336
336
|
if (eligibleForLoadFromTarget) {
|
|
337
|
+
let mustEnforceCSP = false;
|
|
338
|
+
const isHttp = parsedURL.scheme === 'http' || parsedURL.scheme === 'https';
|
|
339
|
+
if (isHttp && initiator.target) {
|
|
340
|
+
const networkManager = initiator.target.model(NetworkManager);
|
|
341
|
+
if (networkManager) {
|
|
342
|
+
let status = await networkManager.getSecurityIsolationStatus(initiator.frameId);
|
|
343
|
+
if (!status && initiator.frameId) {
|
|
344
|
+
status = await networkManager.getSecurityIsolationStatus(null);
|
|
345
|
+
}
|
|
346
|
+
if (status?.csp) {
|
|
347
|
+
for (const csp of status.csp) {
|
|
348
|
+
const directives = csp.effectiveDirectives;
|
|
349
|
+
if (directives.includes('connect-src') || directives.includes('default-src')) {
|
|
350
|
+
mustEnforceCSP = true;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
337
358
|
try {
|
|
338
359
|
Host.userMetrics.developerResourceLoaded(Host.UserMetrics.DeveloperResourceLoaded.LOAD_THROUGH_PAGE_VIA_TARGET);
|
|
339
360
|
const result = await this.loadFromTarget(initiator.target, initiator.frameId, url, isBinary);
|
|
@@ -341,7 +362,7 @@ export class PageResourceLoader extends Common.ObjectWrapper.ObjectWrapper<Event
|
|
|
341
362
|
} catch (e) {
|
|
342
363
|
if (e instanceof Error) {
|
|
343
364
|
Host.userMetrics.developerResourceLoaded(Host.UserMetrics.DeveloperResourceLoaded.LOAD_THROUGH_PAGE_FAILURE);
|
|
344
|
-
if (e.message.includes('CSP violation')) {
|
|
365
|
+
if (mustEnforceCSP || e.message.includes('CSP violation')) {
|
|
345
366
|
return {
|
|
346
367
|
success: false,
|
|
347
368
|
content: '',
|
|
@@ -444,7 +444,8 @@
|
|
|
444
444
|
TimelineNavigationSettingState: 'DevTools.TimelineNavigationSettingState',
|
|
445
445
|
SyncSetting: 'DevTools.SyncSetting',
|
|
446
446
|
SwatchActivated: 'DevTools.SwatchActivated',
|
|
447
|
-
BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability'
|
|
447
|
+
BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability',
|
|
448
|
+
ExtensionEvalTarget: 'DevTools.ExtensionEvalTarget'
|
|
448
449
|
// LINT.ThenChange(/front_end/core/host/InspectorFrontendHostAPI.ts:EnumeratedHistogram)
|
|
449
450
|
};
|
|
450
451
|
|
|
@@ -162,6 +162,11 @@ export class AiConversation {
|
|
|
162
162
|
return this.#contexts.at(0);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
getPendingMultimodalInput(): MultimodalInput|undefined {
|
|
166
|
+
const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
|
|
167
|
+
return greenDevEmulationEnabled ? this.#agent.popPendingMultimodalInput() : undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
165
170
|
#reconstructHistory(historyWithoutImages: ResponseData[]): ResponseData[] {
|
|
166
171
|
const imageHistory = AiHistoryStorage.instance().getImageHistory();
|
|
167
172
|
if (imageHistory && imageHistory.length > 0) {
|
|
@@ -121,6 +121,30 @@ Content:
|
|
|
121
121
|
"title"
|
|
122
122
|
]
|
|
123
123
|
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "activateDeviceEmulation",
|
|
127
|
+
"description": "Sets emulation viewing mode for a specific device and optionally enables vision deficiency emulation.",
|
|
128
|
+
"parameters": {
|
|
129
|
+
"type": 6,
|
|
130
|
+
"description": "",
|
|
131
|
+
"nullable": false,
|
|
132
|
+
"properties": {
|
|
133
|
+
"deviceName": {
|
|
134
|
+
"type": 1,
|
|
135
|
+
"description": "The name of the device to emulate. Allowed values: Pixel 3 XL, Pixel 7, Samsung Galaxy S8+, Samsung Galaxy S20 Ultra, Surface Pro 7, Surface Duo, Galaxy Z Fold 5, Asus Zenbook Fold, Samsung Galaxy A51/71, Nest Hub Max, Nest Hub, iPhone 4, iPhone 5/SE, iPhone 6/7/8, iPhone SE, iPhone XR, iPhone 12 Pro, iPhone 14 Pro Max, iPad Mini, iPad Air, iPad Pro.",
|
|
136
|
+
"nullable": false
|
|
137
|
+
},
|
|
138
|
+
"visionDeficiency": {
|
|
139
|
+
"type": 1,
|
|
140
|
+
"description": "Optional vision deficiency to emulate. Allowed values: blurredVision, reducedContrast, achromatopsia, deuteranopia, protanopia, tritanopia.",
|
|
141
|
+
"nullable": true
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"required": [
|
|
145
|
+
"deviceName"
|
|
146
|
+
]
|
|
147
|
+
}
|
|
124
148
|
}
|
|
125
149
|
],
|
|
126
150
|
"options": {
|