chrome-devtools-frontend 1.0.1611825 → 1.0.1613465
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/front_end/Images/src/flowsheet.svg +1 -0
- package/front_end/core/common/Color.ts +3 -2
- package/front_end/core/platform/StringUtilities.ts +5 -0
- package/front_end/core/sdk/NetworkManager.ts +7 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -2
- package/front_end/generated/protocol.ts +12 -2
- package/front_end/models/greendev/Prototypes.ts +7 -4
- package/front_end/models/har/HARFormat.ts +30 -0
- package/front_end/models/har/Importer.ts +12 -1
- package/front_end/models/har/Log.ts +28 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +62 -22
- package/front_end/panels/ai_assistance/PatchWidget.ts +7 -23
- package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +3 -3
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +7 -0
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/ChatView.ts +4 -0
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +12 -6
- package/front_end/panels/ai_assistance/components/OptInChangeDialog.ts +179 -0
- package/front_end/panels/ai_assistance/components/optInChangeDialog.css +95 -0
- package/front_end/panels/ai_assistance/components/walkthroughView.css +1 -1
- package/front_end/panels/application/CookieItemsView.ts +3 -2
- package/front_end/panels/changes/ChangesView.ts +0 -12
- package/front_end/panels/common/common.ts +0 -1
- package/front_end/panels/console/ConsolePrompt.ts +2 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +2 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +3 -1
- package/front_end/panels/elements/elements-meta.ts +14 -0
- package/front_end/panels/network/NetworkLogView.ts +58 -28
- package/front_end/panels/recorder/RecorderController.ts +4 -4
- package/front_end/panels/settings/AISettingsTab.ts +39 -3
- package/front_end/panels/settings/SettingsScreen.ts +2 -2
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/SplitWidget.ts +9 -6
- package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +3 -3
- package/front_end/ui/legacy/components/source_frame/FontView.ts +101 -88
- package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +4 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
- package/package.json +1 -1
- package/front_end/panels/common/CopyChangesToPrompt.ts +0 -233
|
@@ -422,9 +422,9 @@ export class SplitWidget extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
|
422
422
|
*/
|
|
423
423
|
#totalSizeDIP(): number {
|
|
424
424
|
if (!this.#totalSizeCSS) {
|
|
425
|
-
|
|
426
|
-
this.#
|
|
427
|
-
|
|
425
|
+
const {width, height} = this.contentElement.getBoundingClientRect();
|
|
426
|
+
this.#totalSizeCSS = this.#isVertical ? width : height;
|
|
427
|
+
this.#totalSizeOtherDimensionCSS = this.#isVertical ? height : width;
|
|
428
428
|
}
|
|
429
429
|
return ZoomManager.instance().cssToDIP(this.#totalSizeCSS);
|
|
430
430
|
}
|
|
@@ -457,9 +457,12 @@ export class SplitWidget extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
|
457
457
|
this.#removeAllLayoutProperties();
|
|
458
458
|
|
|
459
459
|
// this.#totalSizeDIP is available below since we successfully applied constraints.
|
|
460
|
-
const
|
|
461
|
-
const sidebarSizeValue =
|
|
462
|
-
const mainSizeValue = (this.#totalSizeCSS -
|
|
460
|
+
const sizeCSS = ZoomManager.instance().dipToCSS(sizeDIP);
|
|
461
|
+
const sidebarSizeValue = sizeCSS + 'px';
|
|
462
|
+
const mainSizeValue = (this.#totalSizeCSS - sizeCSS) + 'px';
|
|
463
|
+
// With `box-sizing: border-box` on the sidebar (set in splitWidget.css),
|
|
464
|
+
// flex-basis, width, and height all include the border, so the sidebar's
|
|
465
|
+
// border does not steal space from the main pane.
|
|
463
466
|
this.#sidebarElement.style.flexBasis = sidebarSizeValue;
|
|
464
467
|
|
|
465
468
|
// Make both sides relayout boundaries.
|
|
@@ -105,7 +105,7 @@ export function renderSettingSelect(setting: Common.Settings.Setting<unknown>, s
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export const renderControlForSetting = function(
|
|
108
|
-
setting: Common.Settings.Setting<unknown>, subtitle?: string): TemplateResult|
|
|
108
|
+
setting: Common.Settings.Setting<unknown>, subtitle?: string): TemplateResult|typeof nothing {
|
|
109
109
|
switch (setting.type()) {
|
|
110
110
|
case Common.Settings.SettingType.BOOLEAN: {
|
|
111
111
|
const onchange = (): void => {
|
|
@@ -123,14 +123,14 @@ export const renderControlForSetting = function(
|
|
|
123
123
|
}
|
|
124
124
|
default:
|
|
125
125
|
console.error('Invalid setting type: ' + setting.type());
|
|
126
|
-
return
|
|
126
|
+
return nothing;
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
|
|
130
130
|
export const createControlForSetting = function(
|
|
131
131
|
setting: Common.Settings.Setting<unknown>, subtitle?: string): HTMLElement|null {
|
|
132
132
|
const template = renderControlForSetting(setting, subtitle);
|
|
133
|
-
if (template ===
|
|
133
|
+
if (template === nothing) {
|
|
134
134
|
return null;
|
|
135
135
|
}
|
|
136
136
|
const fragment = document.createDocumentFragment();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright 2021 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
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
/*
|
|
7
6
|
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
|
|
@@ -34,6 +33,7 @@
|
|
|
34
33
|
import * as i18n from '../../../../core/i18n/i18n.js';
|
|
35
34
|
import * as Platform from '../../../../core/platform/platform.js';
|
|
36
35
|
import * as TextUtils from '../../../../models/text_utils/text_utils.js';
|
|
36
|
+
import {Directives, html, render} from '../../../lit/lit.js';
|
|
37
37
|
import * as VisualLogging from '../../../visual_logging/visual_logging.js';
|
|
38
38
|
import * as UI from '../../legacy.js';
|
|
39
39
|
|
|
@@ -52,24 +52,70 @@ const UIStrings = {
|
|
|
52
52
|
} as const;
|
|
53
53
|
const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/source_frame/FontView.ts', UIStrings);
|
|
54
54
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
55
|
+
|
|
56
|
+
const FONT_PREVIEW_LINES = ['ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ', 'abcdefghijklm', 'nopqrstuvwxyz', '1234567890'];
|
|
57
|
+
const MEASURE_FONT_SIZE = 50;
|
|
58
|
+
|
|
59
|
+
export interface ViewInput {
|
|
60
|
+
url: Platform.DevToolsPath.UrlString;
|
|
61
|
+
fontFaceRule: string;
|
|
62
|
+
fontFamily: string;
|
|
63
|
+
previewFontSize: string;
|
|
64
|
+
previewVisible: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ViewOutput {
|
|
68
|
+
measureDimensions?: () => {
|
|
69
|
+
width: number, height: number,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
|
|
74
|
+
|
|
75
|
+
// clang-format off
|
|
76
|
+
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
77
|
+
let dummyEl: HTMLElement|undefined;
|
|
78
|
+
render(html`
|
|
79
|
+
<style>${fontViewStyles}</style>
|
|
80
|
+
<style>${input.fontFaceRule}</style>
|
|
81
|
+
<div class="font-view"
|
|
82
|
+
aria-label=${i18nString(UIStrings.previewOfFontFromS, {PH1: input.url})}
|
|
83
|
+
style="font-family: ${input.fontFamily}; font-size: ${input.previewFontSize}"
|
|
84
|
+
aria-hidden="true"
|
|
85
|
+
?hidden=${!input.previewVisible}
|
|
86
|
+
>${FONT_PREVIEW_LINES.map((line, i) => html`${i > 0 ? html`<br>` : ''}${line}`)}</div>
|
|
87
|
+
<div ${Directives.ref(el => { dummyEl = el as HTMLElement; })}
|
|
88
|
+
style="visibility: hidden; z-index: -1; display: inline; position: absolute; font-family: ${input.fontFamily}; font-size: ${MEASURE_FONT_SIZE}px"
|
|
89
|
+
>${FONT_PREVIEW_LINES.map((line, i) => html`${i > 0 ? html`<br>` : ''}${line}`)}</div>
|
|
90
|
+
`, target);
|
|
91
|
+
output.measureDimensions = () => {
|
|
92
|
+
if (!dummyEl) {
|
|
93
|
+
return {width: 0, height: 0};
|
|
94
|
+
}
|
|
95
|
+
return {width: dummyEl.offsetWidth, height: dummyEl.offsetHeight};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
// clang-format on
|
|
99
|
+
|
|
55
100
|
export class FontView extends UI.View.SimpleView {
|
|
56
101
|
private readonly url: Platform.DevToolsPath.UrlString;
|
|
57
102
|
private readonly contentProvider: TextUtils.ContentProvider.ContentProvider;
|
|
58
103
|
private readonly mimeTypeLabel: UI.Toolbar.ToolbarText;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
104
|
+
readonly #view: View;
|
|
105
|
+
#fontFaceRule = '';
|
|
106
|
+
#fontFamily = '';
|
|
107
|
+
#previewFontSize = '';
|
|
108
|
+
#previewVisible = false;
|
|
109
|
+
#contentLoaded = false;
|
|
110
|
+
|
|
111
|
+
constructor(mimeType: string, contentProvider: TextUtils.ContentProvider.ContentProvider, view: View = DEFAULT_VIEW) {
|
|
64
112
|
super({
|
|
65
113
|
title: i18nString(UIStrings.font),
|
|
66
114
|
viewId: 'font',
|
|
67
115
|
jslog: `${VisualLogging.pane('font-view')}`,
|
|
68
116
|
});
|
|
69
|
-
this
|
|
70
|
-
this.element.classList.add('font-view');
|
|
117
|
+
this.#view = view;
|
|
71
118
|
this.url = contentProvider.contentURL();
|
|
72
|
-
UI.ARIAUtils.setLabel(this.element, i18nString(UIStrings.previewOfFontFromS, {PH1: this.url}));
|
|
73
119
|
this.contentProvider = contentProvider;
|
|
74
120
|
this.mimeTypeLabel = new UI.Toolbar.ToolbarText(mimeType);
|
|
75
121
|
}
|
|
@@ -78,96 +124,39 @@ export class FontView extends UI.View.SimpleView {
|
|
|
78
124
|
return [this.mimeTypeLabel];
|
|
79
125
|
}
|
|
80
126
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (!this.fontStyleElement) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this.fontStyleElement.textContent =
|
|
87
|
-
Platform.StringUtilities.sprintf('@font-face { font-family: "%s"; src: url(%s); }', uniqueFontName, url);
|
|
88
|
-
this.updateFontPreviewSize();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private createContentIfNeeded(): void {
|
|
92
|
-
if (this.fontPreviewElement) {
|
|
127
|
+
#loadContentIfNeeded(): void {
|
|
128
|
+
if (this.#contentLoaded) {
|
|
93
129
|
return;
|
|
94
130
|
}
|
|
131
|
+
this.#contentLoaded = true;
|
|
95
132
|
|
|
96
|
-
|
|
97
|
-
this.fontStyleElement = document.createElement('style');
|
|
133
|
+
this.#fontFamily = `WebInspectorFontPreview${++fontId}`;
|
|
98
134
|
void this.contentProvider.requestContentData().then(contentData => {
|
|
99
|
-
|
|
135
|
+
const url = TextUtils.ContentData.ContentData.isError(contentData) ? this.url : contentData.asDataUrl();
|
|
136
|
+
this.#fontFaceRule =
|
|
137
|
+
Platform.StringUtilities.sprintf('@font-face { font-family: "%s"; src: url(%s); }', this.#fontFamily, url);
|
|
138
|
+
this.#previewVisible = true;
|
|
139
|
+
this.requestUpdate();
|
|
100
140
|
});
|
|
101
|
-
this.element.appendChild(this.fontStyleElement);
|
|
102
|
-
|
|
103
|
-
const fontPreview = document.createElement('div');
|
|
104
|
-
for (let i = 0; i < FONT_PREVIEW_LINES.length; ++i) {
|
|
105
|
-
if (i > 0) {
|
|
106
|
-
fontPreview.createChild('br');
|
|
107
|
-
}
|
|
108
|
-
UI.UIUtils.createTextChild(fontPreview, FONT_PREVIEW_LINES[i]);
|
|
109
|
-
}
|
|
110
|
-
this.fontPreviewElement = (fontPreview.cloneNode(true) as HTMLDivElement);
|
|
111
|
-
if (!this.fontPreviewElement) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
UI.ARIAUtils.setHidden(this.fontPreviewElement, true);
|
|
115
|
-
this.fontPreviewElement.style.overflow = 'hidden';
|
|
116
|
-
this.fontPreviewElement.style.setProperty('font-family', uniqueFontName);
|
|
117
|
-
this.fontPreviewElement.style.setProperty('visibility', 'hidden');
|
|
118
|
-
|
|
119
|
-
this.dummyElement = fontPreview;
|
|
120
|
-
this.dummyElement.style.visibility = 'hidden';
|
|
121
|
-
this.dummyElement.style.zIndex = '-1';
|
|
122
|
-
this.dummyElement.style.display = 'inline';
|
|
123
|
-
this.dummyElement.style.position = 'absolute';
|
|
124
|
-
this.dummyElement.style.setProperty('font-family', uniqueFontName);
|
|
125
|
-
this.dummyElement.style.setProperty('font-size', MEASUURE_FONT_SIZE + 'px');
|
|
126
|
-
|
|
127
|
-
this.element.appendChild(this.fontPreviewElement);
|
|
128
141
|
}
|
|
129
142
|
|
|
130
143
|
override wasShown(): void {
|
|
131
144
|
super.wasShown();
|
|
132
|
-
this
|
|
133
|
-
|
|
134
|
-
this.updateFontPreviewSize();
|
|
145
|
+
this.#loadContentIfNeeded();
|
|
146
|
+
this.requestUpdate();
|
|
135
147
|
}
|
|
136
148
|
|
|
137
149
|
override onResize(): void {
|
|
138
|
-
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this.inResize = true;
|
|
143
|
-
try {
|
|
144
|
-
this.updateFontPreviewSize();
|
|
145
|
-
} finally {
|
|
146
|
-
this.inResize = null;
|
|
147
|
-
}
|
|
150
|
+
this.requestUpdate();
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
#calculateFontPreviewSize(dimension: {
|
|
151
154
|
width: number,
|
|
152
155
|
height: number,
|
|
153
|
-
} {
|
|
154
|
-
if (!this.
|
|
155
|
-
|
|
156
|
+
}): string {
|
|
157
|
+
if (!this.#previewVisible || !this.isShowing()) {
|
|
158
|
+
return '';
|
|
156
159
|
}
|
|
157
|
-
this.element.appendChild(this.dummyElement);
|
|
158
|
-
const result = {width: this.dummyElement.offsetWidth, height: this.dummyElement.offsetHeight};
|
|
159
|
-
this.element.removeChild(this.dummyElement);
|
|
160
|
-
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
updateFontPreviewSize(): void {
|
|
165
|
-
if (!this.fontPreviewElement || !this.isShowing()) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
this.fontPreviewElement.style.removeProperty('visibility');
|
|
170
|
-
const dimension = this.measureElement();
|
|
171
160
|
|
|
172
161
|
const height = dimension.height;
|
|
173
162
|
const width = dimension.width;
|
|
@@ -177,18 +166,42 @@ export class FontView extends UI.View.SimpleView {
|
|
|
177
166
|
const containerHeight = this.element.offsetHeight - 30;
|
|
178
167
|
|
|
179
168
|
if (!height || !width || !containerWidth || !containerHeight) {
|
|
180
|
-
|
|
181
|
-
return;
|
|
169
|
+
return '';
|
|
182
170
|
}
|
|
183
171
|
|
|
184
172
|
const widthRatio = containerWidth / width;
|
|
185
173
|
const heightRatio = containerHeight / height;
|
|
186
|
-
const finalFontSize = Math.floor(
|
|
174
|
+
const finalFontSize = Math.floor(MEASURE_FONT_SIZE * Math.min(widthRatio, heightRatio)) - 2;
|
|
187
175
|
|
|
188
|
-
|
|
176
|
+
return `${finalFontSize}px`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
override performUpdate(): void {
|
|
180
|
+
const output: ViewOutput = {};
|
|
181
|
+
this.#view(
|
|
182
|
+
{
|
|
183
|
+
url: this.url,
|
|
184
|
+
fontFaceRule: this.#fontFaceRule,
|
|
185
|
+
fontFamily: this.#fontFamily,
|
|
186
|
+
previewFontSize: this.#previewFontSize,
|
|
187
|
+
previewVisible: this.#previewVisible,
|
|
188
|
+
},
|
|
189
|
+
output,
|
|
190
|
+
this.contentElement,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (!output.measureDimensions) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const requestedFontSize = this.#calculateFontPreviewSize(output.measureDimensions());
|
|
198
|
+
if (requestedFontSize === this.#previewFontSize) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.#previewFontSize = requestedFontSize;
|
|
203
|
+
this.requestUpdate();
|
|
189
204
|
}
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
let fontId = 0;
|
|
193
|
-
const FONT_PREVIEW_LINES = ['ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ', 'abcdefghijklm', 'nopqrstuvwxyz', '1234567890'];
|
|
194
|
-
const MEASUURE_FONT_SIZE = 50;
|
|
@@ -65,20 +65,20 @@ const UIStrings = {
|
|
|
65
65
|
*/
|
|
66
66
|
doYouTrustThisCode: 'Do you trust this code?',
|
|
67
67
|
/**
|
|
68
|
-
* @description Warning shown to users when pasting text/code into DevTools.
|
|
68
|
+
* @description Warning shown to users when pasting text/code into DevTools. IMPORTANT: keep double quotes around PH1 and do not use single quotes.
|
|
69
69
|
* @example {allow pasting} PH1
|
|
70
70
|
*/
|
|
71
71
|
doNotPaste:
|
|
72
|
-
'Don\'t paste code you do not understand or have not reviewed yourself into DevTools. This could allow attackers to steal your identity or take control of your computer. Please type
|
|
72
|
+
'Don\'t paste code you do not understand or have not reviewed yourself into DevTools. This could allow attackers to steal your identity or take control of your computer. Please type “{PH1}” below to allow pasting.',
|
|
73
73
|
/**
|
|
74
74
|
* @description Text a user needs to type in order to confirm that they are aware of the danger of pasting code into the DevTools console.
|
|
75
75
|
*/
|
|
76
76
|
allowPasting: 'allow pasting',
|
|
77
77
|
/**
|
|
78
|
-
* @description Input box placeholder which instructs the user to type 'allow pasting' into the input box.
|
|
78
|
+
* @description Input box placeholder which instructs the user to type 'allow pasting' into the input box. IMPORTANT: keep double quotes around PH1 and do not use single quotes.
|
|
79
79
|
* @example {allow pasting} PH1
|
|
80
80
|
*/
|
|
81
|
-
typeAllowPasting: 'Type
|
|
81
|
+
typeAllowPasting: 'Type “{PH1}”',
|
|
82
82
|
/**
|
|
83
83
|
* @description Error message shown when the user tries to open a file that contains non-readable data. "Editor" refers to
|
|
84
84
|
* a text editor.
|
|
@@ -345,6 +345,9 @@ export const knownContextValues = new Set([
|
|
|
345
345
|
'ai-assistance-history-images',
|
|
346
346
|
'ai-assistance-patching-fre-completed',
|
|
347
347
|
'ai-assistance-patching-selected-project-id',
|
|
348
|
+
'ai-assistance-v2-opt-in-change-dialog-seen',
|
|
349
|
+
'ai-assistance-v2-opt-in.got-it',
|
|
350
|
+
'ai-assistance-v2-opt-in.manage-settings',
|
|
348
351
|
'ai-code-completion-citations',
|
|
349
352
|
'ai-code-completion-citations.citation-link',
|
|
350
353
|
'ai-code-completion-disclaimer',
|
|
@@ -366,6 +369,7 @@ export const knownContextValues = new Set([
|
|
|
366
369
|
'ai-export-for-agents.save-as-markdown',
|
|
367
370
|
'ai-hide-walkthrough-sidebar',
|
|
368
371
|
'ai-show-walkthrough-sidebar',
|
|
372
|
+
'ai-v2-opt-in-change-dialog',
|
|
369
373
|
'ai_assistance',
|
|
370
374
|
'align-content',
|
|
371
375
|
'align-content-center',
|
|
@@ -1104,6 +1108,7 @@ export const knownContextValues = new Set([
|
|
|
1104
1108
|
'css',
|
|
1105
1109
|
'css-angle',
|
|
1106
1110
|
'css-animation-name',
|
|
1111
|
+
'css-animations-only-when-animations-tab-open',
|
|
1107
1112
|
'css-font-palette',
|
|
1108
1113
|
'css-function',
|
|
1109
1114
|
'css-layers',
|
|
@@ -1809,6 +1814,7 @@ export const knownContextValues = new Set([
|
|
|
1809
1814
|
'greendev',
|
|
1810
1815
|
'greendev-ai-annotations-enabled',
|
|
1811
1816
|
'greendev-artifact-viewer-enabled',
|
|
1817
|
+
'greendev-beyond-styling-enabled',
|
|
1812
1818
|
'greendev-breakpoint-debugger-agent-enabled',
|
|
1813
1819
|
'greendev-copy-to-gemini-enabled',
|
|
1814
1820
|
'greendev-emulation-capabilities-enabled',
|
package/package.json
CHANGED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
// Copyright 2025 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 GreenDev from '../../models/greendev/greendev.js';
|
|
8
|
-
import type * as Workspace from '../../models/workspace/workspace.js';
|
|
9
|
-
import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
|
|
10
|
-
import * as Diff from '../../third_party/diff/diff.js';
|
|
11
|
-
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
12
|
-
import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
|
|
13
|
-
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
|
-
import * as Lit from '../../ui/lit/lit.js';
|
|
15
|
-
|
|
16
|
-
const {render, html} = Lit;
|
|
17
|
-
|
|
18
|
-
const UIStrings = {
|
|
19
|
-
/**
|
|
20
|
-
* @description The message shown in a toast when the response is copied to the clipboard.
|
|
21
|
-
*/
|
|
22
|
-
responseCopiedToClipboard: 'Response copied to clipboard',
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
const str_ = i18n.i18n.registerUIStrings('panels/common/CopyChangesToPrompt.ts', UIStrings);
|
|
26
|
-
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
27
|
-
|
|
28
|
-
interface CopyChangesDiff {
|
|
29
|
-
diff: Diff.Diff.DiffArray|undefined;
|
|
30
|
-
uiSourceCode: Workspace.UISourceCode.UISourceCode;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface GeminiChangesViewInput {
|
|
34
|
-
/**
|
|
35
|
-
* These are the set of diffs tracked by the Changes Panel.
|
|
36
|
-
*/
|
|
37
|
-
diffs: CopyChangesDiff[];
|
|
38
|
-
/**
|
|
39
|
-
* Supplied by the Patch Agent if it has made any changes on behalf of the
|
|
40
|
-
* user.
|
|
41
|
-
*/
|
|
42
|
-
patchAgentCSSChange: string|null;
|
|
43
|
-
onCopyToClipboard: (text: string) => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
type GeminiChangesView = (input: GeminiChangesViewInput, output: object, target: HTMLElement) => void;
|
|
47
|
-
|
|
48
|
-
export class CopyChangesToPrompt extends UI.Widget.Widget {
|
|
49
|
-
#workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
|
|
50
|
-
readonly #view: GeminiChangesView;
|
|
51
|
-
#patchAgentCSSChange: string|null = null;
|
|
52
|
-
|
|
53
|
-
constructor(target?: HTMLElement, view = GEMINI_CHANGES_VIEW) {
|
|
54
|
-
super(target);
|
|
55
|
-
this.#view = view;
|
|
56
|
-
this.#workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get patchAgentCSSChange(): string|null {
|
|
60
|
-
return this.#patchAgentCSSChange;
|
|
61
|
-
}
|
|
62
|
-
set patchAgentCSSChange(code: string) {
|
|
63
|
-
this.#patchAgentCSSChange = code;
|
|
64
|
-
this.requestUpdate();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
override wasShown(): void {
|
|
68
|
-
super.wasShown();
|
|
69
|
-
this.#workspaceDiff.addEventListener(
|
|
70
|
-
WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.#onDiffChange, this);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
override willHide(): void {
|
|
74
|
-
super.willHide();
|
|
75
|
-
this.#workspaceDiff.removeEventListener(
|
|
76
|
-
WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.#onDiffChange, this);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
#getModifiledFiles(): Workspace.UISourceCode.UISourceCode[] {
|
|
80
|
-
return this.#workspaceDiff.modifiedUISourceCodes().filter(modified => {
|
|
81
|
-
return !modified.url().startsWith('inspector://');
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
#onDiffChange(): void {
|
|
86
|
-
// TODO: track this and unsubscribe to files?
|
|
87
|
-
for (const file of this.#getModifiledFiles()) {
|
|
88
|
-
this.#workspaceDiff.subscribeToDiffChange(file, this.requestUpdate, this);
|
|
89
|
-
}
|
|
90
|
-
this.requestUpdate();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
set workspaceDiff(diff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl) {
|
|
94
|
-
this.#workspaceDiff = diff;
|
|
95
|
-
this.requestUpdate();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
override async performUpdate(): Promise<void> {
|
|
99
|
-
if (!GreenDev.Prototypes.instance().isEnabled('copyToGemini')) {
|
|
100
|
-
// We expect code that renders this component to only do so based on this flag, but this is a double-check.
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const diffs = await Promise.all(this.#getModifiledFiles().map(async modifiedUISourceCode => {
|
|
105
|
-
const diffResponse = await this.#workspaceDiff?.requestDiff(modifiedUISourceCode);
|
|
106
|
-
return {diff: diffResponse?.diff ?? [], uiSourceCode: modifiedUISourceCode};
|
|
107
|
-
}));
|
|
108
|
-
this.#view(
|
|
109
|
-
{
|
|
110
|
-
diffs,
|
|
111
|
-
patchAgentCSSChange: this.#patchAgentCSSChange,
|
|
112
|
-
onCopyToClipboard: (text: string) => {
|
|
113
|
-
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(text);
|
|
114
|
-
Snackbars.Snackbar.Snackbar.show({
|
|
115
|
-
message: i18nString(UIStrings.responseCopiedToClipboard),
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{}, this.contentElement);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const GEMINI_CHANGES_VIEW: GeminiChangesView = (input, _output, target) => {
|
|
123
|
-
const hasDiffs = input.diffs.some(d => {
|
|
124
|
-
return d.diff !== undefined && d.diff.length > 0;
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (!hasDiffs && !input.patchAgentCSSChange) {
|
|
128
|
-
render(Lit.nothing, target);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const promptForChangesPanel = hasDiffs ? buildGeminiCommand(input.diffs) : '';
|
|
133
|
-
const promptForPatchAgentCSS = input.patchAgentCSSChange ? buildPatchAgentCSSPrompt(input.patchAgentCSSChange) : '';
|
|
134
|
-
|
|
135
|
-
const finalPrompt = [PREAMBLE, promptForChangesPanel, promptForPatchAgentCSS].filter(x => x.length > 0).join(`\n`);
|
|
136
|
-
|
|
137
|
-
function copyClick(): void {
|
|
138
|
-
input.onCopyToClipboard(finalPrompt);
|
|
139
|
-
}
|
|
140
|
-
// clang-format off
|
|
141
|
-
render(html`<devtools-button
|
|
142
|
-
.iconName=${'copy'}
|
|
143
|
-
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
144
|
-
@click=${copyClick}>Copy prompt to clipboard</devtools-button>
|
|
145
|
-
`, target);
|
|
146
|
-
// clang-format on
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
function buildPatchAgentCSSPrompt(code: string): string {
|
|
150
|
-
// clang-format off
|
|
151
|
-
return `The DevTools CSS Patching Agent has also made some CSS changes on behalf of the user. These changes are listed below. Think carefully about how best to apply these changes.
|
|
152
|
-
|
|
153
|
-
**DevTools Patch Agent changes**
|
|
154
|
-
\`\`\`
|
|
155
|
-
${code}
|
|
156
|
-
\`\`\``;
|
|
157
|
-
// clang-format on
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function buildGeminiCommand(diffs: readonly CopyChangesDiff[]): string {
|
|
161
|
-
// clang-format off
|
|
162
|
-
const output = `Below this line are a list of files and the diff for each of them. Consider this diff and apply it to the codebase but remembering that the changes in DevTools may not be the most accurate fixes and you should not necessarily apply them directly as DevTools works with the deployed code and not the source code.
|
|
163
|
-
|
|
164
|
-
Within the diffs you will often see lines of code commented out. If the diff contains a new line that was some code being wrapped in comments, treat that as if the intent is to delete the code.
|
|
165
|
-
|
|
166
|
-
How to read a diff:
|
|
167
|
-
* If a line starts with \`-\` , DevTools removed it.
|
|
168
|
-
* If a line starts with \`+\` , DevTools added it.
|
|
169
|
-
* If a line starts with neither a \`+\` or \`-\` , DevTools did not change the line and you can safely ignore it.
|
|
170
|
-
|
|
171
|
-
${diffs.map(diff => {
|
|
172
|
-
if (!diff.diff || diff.diff.length === 0) {
|
|
173
|
-
return '';
|
|
174
|
-
}
|
|
175
|
-
return `Filename: ${diff.uiSourceCode.fullDisplayName()}
|
|
176
|
-
|
|
177
|
-
Diff:
|
|
178
|
-
${formatDiffForLLM(diff.diff)}`;
|
|
179
|
-
}).filter(x => x.length).join('\n')}`;
|
|
180
|
-
// clang-format on
|
|
181
|
-
return output;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function formatDiffForLLM(diffArray: Diff.Diff.DiffArray): string {
|
|
185
|
-
let formattedDiff = '';
|
|
186
|
-
for (const diffItem of diffArray) {
|
|
187
|
-
const operation = diffItem[0];
|
|
188
|
-
const lines = diffItem[1];
|
|
189
|
-
|
|
190
|
-
for (const line of lines) {
|
|
191
|
-
if (operation === Diff.Diff.Operation.Equal) {
|
|
192
|
-
formattedDiff += ' ' + line + '\n';
|
|
193
|
-
} else if (operation === Diff.Diff.Operation.Insert) {
|
|
194
|
-
formattedDiff += '+ ' + line + '\n';
|
|
195
|
-
} else if (operation === Diff.Diff.Operation.Delete) {
|
|
196
|
-
formattedDiff += '- ' + line + '\n';
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return formattedDiff;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const PREAMBLE =
|
|
204
|
-
`You are receiving a set of runtime changes (CSS, HTML, and JS) captured via Browser DevTools. Your goal is to persist these changes into the local source code by identifying the original source-of-truth.
|
|
205
|
-
|
|
206
|
-
Because DevTools reflects the "Flattened Result" of complex build logic, you must follow this "Source-Aware" strategy:
|
|
207
|
-
|
|
208
|
-
1. **Structural Mapping (HTML/DOM):**
|
|
209
|
-
- If a DOM element was added/removed, identify the source template (JSX, Vue, Svelte, HTML) responsible.
|
|
210
|
-
- **Logic Check:** Determine if the change should be a static element or if it requires a new conditional (\`if/else\`) or loop (\`map\`) based on existing component patterns.
|
|
211
|
-
|
|
212
|
-
2. **Style Mapping (CSS/Attributes):**
|
|
213
|
-
- Map raw styles to the project's specific styling architecture (Tailwind, SCSS, Styled-Components).
|
|
214
|
-
- Replace hard-coded values with existing Design Tokens or Theme Variables (\`var(--color-primary)\`, etc.) found in the codebase.
|
|
215
|
-
- CSS changes may not be applied to CSS files directly. Consider that CSS could be applied via JavaScript, especially if the codebase is using a component based frontend framework or web components.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
3. **Behavioral Mapping (JS/Event Listeners):**
|
|
219
|
-
- If logic or event handlers were modified, locate the corresponding functions or hooks in the source.
|
|
220
|
-
- Ensure new logic follows the project's state management patterns (e.g., React \`useState\`, Redux, or standard ES6+ modules).
|
|
221
|
-
|
|
222
|
-
4. **Safety & Ambiguity Protocol:**
|
|
223
|
-
- **Third-Party Code:** If a change targets a DOM element or style generated by an external library (e.g., a UI kit's internal wrapper), do not modify the library's source. Instead, find the appropriate override mechanism in the codebase.
|
|
224
|
-
- **Uncertainty:** If a DevTools change cannot be mapped to the source with 100% confidence (e.g., minified selectors or ambiguous origin), stop and report the conflict rather than guessing.
|
|
225
|
-
|
|
226
|
-
5. **Agentic Execution Workflow:**
|
|
227
|
-
- **Discovery:** Use your tools (\`grep\`, \`find\`, etc.) to locate unique strings or class names from the DevTools log.
|
|
228
|
-
- **Analysis:** Determine if the target is a reusable component or a specific page instance.
|
|
229
|
-
- **Implementation:** Execute file edits using the project's idiomatic syntax and formatting standards.
|
|
230
|
-
|
|
231
|
-
**INSTRUCTION:**
|
|
232
|
-
Begin by searching for the relevant source files. Explain your mapping logic before performing the edits.
|
|
233
|
-
`;
|