chrome-devtools-frontend 1.0.1541169 → 1.0.1542501
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/get_the_code.md +9 -0
- package/front_end/Tests.js +6 -1
- package/front_end/core/common/Settings.ts +140 -106
- package/front_end/core/host/UserMetrics.ts +5 -0
- package/front_end/core/sdk/IOModel.ts +1 -4
- package/front_end/core/sdk/ServerSentEventsProtocol.ts +4 -0
- package/front_end/entrypoints/main/MainImpl.ts +18 -7
- package/front_end/foundation/README.md +10 -0
- package/front_end/foundation/Universe.ts +21 -0
- package/front_end/foundation/foundation.ts +7 -0
- package/front_end/generated/SupportedCSSProperties.js +42 -42
- package/front_end/models/ai_assistance/BuiltInAi.ts +2 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +44 -34
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +72 -31
- package/front_end/models/har/Importer.ts +14 -0
- package/front_end/models/issues_manager/IssuesManager.ts +0 -5
- package/front_end/models/javascript_metadata/NativeFunctions.js +0 -4
- package/front_end/models/trace/handlers/ScriptsHandler.ts +26 -0
- package/front_end/models/trace/types/TraceEvents.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +117 -103
- package/front_end/panels/ai_assistance/components/ChatView.ts +7 -31
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +1 -1
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +1 -1
- package/front_end/panels/ai_assistance/components/chatView.css +1 -1
- package/front_end/panels/console/ConsoleInsightTeaser.ts +5 -0
- package/front_end/panels/console/ConsolePrompt.ts +8 -1
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +17 -1
- package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -1
- 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/cdp/NetworkManager.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +3 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.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/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 +4 -4
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +3 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.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/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/cdp/NetworkManager.ts +3 -1
- 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/components/markdown_view/MarkdownLinksMap.ts +0 -4
- package/front_end/ui/components/markdown_view/MarkdownView.docs.ts +95 -0
- package/front_end/ui/components/markdown_view/MarkdownView.ts +6 -7
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +246 -13
- package/front_end/ui/components/text_editor/config.ts +1 -1
- package/front_end/ui/legacy/Widget.ts +13 -4
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +43 -33
- package/front_end/ui/visual_logging/KnownContextValues.ts +12 -0
- package/mcp/mcp.ts +2 -0
- package/package.json +1 -1
- package/front_end/models/issues_manager/UserReidentificationIssue.ts +0 -72
- package/front_end/models/issues_manager/descriptions/userReidentificationBlocked.md +0 -5
- package/front_end/ui/components/docs/markdown_image/basic.html +0 -19
- package/front_end/ui/components/docs/markdown_image/basic.ts +0 -38
- package/front_end/ui/components/docs/markdown_link/basic.html +0 -17
- package/front_end/ui/components/docs/markdown_link/basic.ts +0 -19
- package/front_end/ui/components/docs/markdown_view/basic.html +0 -25
- package/front_end/ui/components/docs/markdown_view/basic.ts +0 -67
- package/front_end/ui/components/docs/markdown_view/code-block.html +0 -30
- package/front_end/ui/components/docs/markdown_view/code-block.ts +0 -71
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as Common from '../../../core/common/common.js';
|
|
6
6
|
import * as Host from '../../../core/host/host.js';
|
|
7
7
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
8
|
-
import * as
|
|
8
|
+
import * as AiCodeCompletion from '../../../models/ai_code_completion/ai_code_completion.js';
|
|
9
9
|
import * as PanelCommon from '../../../panels/common/common.js';
|
|
10
10
|
import * as CodeMirror from '../../../third_party/codemirror.next/codemirror.next.js';
|
|
11
11
|
import * as UI from '../../legacy/legacy.js';
|
|
@@ -13,9 +13,12 @@ import * as VisualLogging from '../../visual_logging/visual_logging.js';
|
|
|
13
13
|
|
|
14
14
|
import {AiCodeCompletionTeaserPlaceholder} from './AiCodeCompletionTeaserPlaceholder.js';
|
|
15
15
|
import {
|
|
16
|
+
acceptAiAutoCompleteSuggestion,
|
|
16
17
|
aiAutoCompleteSuggestion,
|
|
17
18
|
aiAutoCompleteSuggestionState,
|
|
19
|
+
hasActiveAiSuggestion,
|
|
18
20
|
setAiAutoCompleteSuggestion,
|
|
21
|
+
showCompletionHint,
|
|
19
22
|
} from './config.js';
|
|
20
23
|
import type {TextEditor} from './TextEditor.js';
|
|
21
24
|
|
|
@@ -45,15 +48,18 @@ export interface AiCodeCompletionConfig {
|
|
|
45
48
|
onFeatureDisabled: () => void;
|
|
46
49
|
onSuggestionAccepted: () => void;
|
|
47
50
|
onRequestTriggered: () => void;
|
|
51
|
+
// TODO(b/445394511): Move exposing citations to onSuggestionAccepted
|
|
48
52
|
onResponseReceived: (citations: Host.AidaClient.Citation[]) => void;
|
|
53
|
+
panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export const DELAY_BEFORE_SHOWING_RESPONSE_MS = 500;
|
|
52
57
|
export const AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS = 200;
|
|
58
|
+
const MAX_PREFIX_SUFFIX_LENGTH = 20_000;
|
|
53
59
|
|
|
54
|
-
// TODO(samiyac): Add code relevant to AiCodeCompletion and for triggering requests
|
|
55
60
|
export class AiCodeCompletionProvider {
|
|
56
61
|
#aidaClient?: Host.AidaClient.AidaClient;
|
|
62
|
+
#aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
|
|
57
63
|
#aiCodeCompletionSetting = Common.Settings.Settings.instance().createSetting('ai-code-completion-enabled', false);
|
|
58
64
|
#aiCodeCompletionTeaserDismissedSetting =
|
|
59
65
|
Common.Settings.Settings.instance().createSetting('ai-code-completion-teaser-dismissed', false);
|
|
@@ -66,7 +72,8 @@ export class AiCodeCompletionProvider {
|
|
|
66
72
|
#boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
|
|
67
73
|
|
|
68
74
|
constructor(aiCodeCompletionConfig: AiCodeCompletionConfig) {
|
|
69
|
-
|
|
75
|
+
const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
|
|
76
|
+
if (!AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionEnabled(devtoolsLocale.locale)) {
|
|
70
77
|
throw new Error('AI code completion feature is not enabled.');
|
|
71
78
|
}
|
|
72
79
|
this.#aiCodeCompletionConfig = aiCodeCompletionConfig;
|
|
@@ -74,10 +81,12 @@ export class AiCodeCompletionProvider {
|
|
|
74
81
|
|
|
75
82
|
extension(): CodeMirror.Extension[] {
|
|
76
83
|
return [
|
|
84
|
+
CodeMirror.EditorView.updateListener.of(update => this.#triggerAiCodeCompletion(update)),
|
|
77
85
|
this.#teaserCompartment.of([]),
|
|
78
86
|
aiAutoCompleteSuggestion,
|
|
79
87
|
aiCodeCompletionTeaserModeState,
|
|
80
88
|
aiAutoCompleteSuggestionState,
|
|
89
|
+
CodeMirror.Prec.highest(CodeMirror.keymap.of(this.#editorKeymap())),
|
|
81
90
|
];
|
|
82
91
|
}
|
|
83
92
|
|
|
@@ -105,6 +114,10 @@ export class AiCodeCompletionProvider {
|
|
|
105
114
|
void this.#updateAiCodeCompletionState();
|
|
106
115
|
}
|
|
107
116
|
|
|
117
|
+
clearCache(): void {
|
|
118
|
+
this.#aiCodeCompletion?.clearCachedRequest();
|
|
119
|
+
}
|
|
120
|
+
|
|
108
121
|
#setupAiCodeCompletion(): void {
|
|
109
122
|
if (!this.#editor || !this.#aiCodeCompletionConfig) {
|
|
110
123
|
return;
|
|
@@ -112,6 +125,11 @@ export class AiCodeCompletionProvider {
|
|
|
112
125
|
if (!this.#aidaClient) {
|
|
113
126
|
this.#aidaClient = new Host.AidaClient.AidaClient();
|
|
114
127
|
}
|
|
128
|
+
if (!this.#aiCodeCompletion) {
|
|
129
|
+
this.#aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
|
|
130
|
+
{aidaClient: this.#aidaClient}, this.#aiCodeCompletionConfig.panel, undefined,
|
|
131
|
+
this.#aiCodeCompletionConfig.completionContext.stopSequences);
|
|
132
|
+
}
|
|
115
133
|
this.#aiCodeCompletionConfig.onFeatureEnabled();
|
|
116
134
|
}
|
|
117
135
|
|
|
@@ -123,6 +141,7 @@ export class AiCodeCompletionProvider {
|
|
|
123
141
|
this.#editor?.dispatch({
|
|
124
142
|
effects: setAiAutoCompleteSuggestion.of(null),
|
|
125
143
|
});
|
|
144
|
+
this.#aiCodeCompletion = undefined;
|
|
126
145
|
this.#aiCodeCompletionConfig?.onFeatureDisabled();
|
|
127
146
|
}
|
|
128
147
|
|
|
@@ -145,6 +164,40 @@ export class AiCodeCompletionProvider {
|
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
|
|
167
|
+
#editorKeymap(): readonly CodeMirror.KeyBinding[] {
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
key: 'Escape',
|
|
171
|
+
run: (): boolean => {
|
|
172
|
+
if (!this.#aiCodeCompletion || !this.#editor || !hasActiveAiSuggestion(this.#editor.state)) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
this.#editor.dispatch({
|
|
176
|
+
effects: setAiAutoCompleteSuggestion.of(null),
|
|
177
|
+
});
|
|
178
|
+
return true;
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: 'Tab',
|
|
183
|
+
run: (): boolean => {
|
|
184
|
+
if (!this.#aiCodeCompletion || !this.#editor || !hasActiveAiSuggestion(this.#editor.state)) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const {accepted, suggestion} = acceptAiAutoCompleteSuggestion(this.#editor.editor);
|
|
188
|
+
if (!accepted) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
if (suggestion?.rpcGlobalId) {
|
|
192
|
+
this.#aiCodeCompletion?.registerUserAcceptance(suggestion.rpcGlobalId, suggestion.sampleId);
|
|
193
|
+
}
|
|
194
|
+
this.#aiCodeCompletionConfig?.onSuggestionAccepted();
|
|
195
|
+
return true;
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
148
201
|
#detachTeaser(): void {
|
|
149
202
|
if (!this.#teaser) {
|
|
150
203
|
return;
|
|
@@ -152,18 +205,198 @@ export class AiCodeCompletionProvider {
|
|
|
152
205
|
this.#editor?.editor.dispatch({effects: this.#teaserCompartment.reconfigure([])});
|
|
153
206
|
}
|
|
154
207
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
208
|
+
#triggerAiCodeCompletion(update: CodeMirror.ViewUpdate): void {
|
|
209
|
+
if (!update.docChanged || !this.#editor || !this.#aiCodeCompletion) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const {doc, selection} = update.state;
|
|
213
|
+
const query = doc.toString();
|
|
214
|
+
const cursor = selection.main.head;
|
|
215
|
+
|
|
216
|
+
let prefix = query.substring(0, cursor);
|
|
217
|
+
if (prefix.trim().length === 0) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const completionContextPrefix = this.#aiCodeCompletionConfig?.completionContext.getPrefix?.();
|
|
221
|
+
if (completionContextPrefix) {
|
|
222
|
+
prefix = completionContextPrefix + prefix;
|
|
161
223
|
}
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
224
|
+
if (prefix.length > MAX_PREFIX_SUFFIX_LENGTH) {
|
|
225
|
+
prefix = prefix.substring(prefix.length - MAX_PREFIX_SUFFIX_LENGTH);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const suffix = query.substring(cursor, cursor + MAX_PREFIX_SUFFIX_LENGTH);
|
|
229
|
+
|
|
230
|
+
this.#debouncedRequestAidaSuggestion(
|
|
231
|
+
prefix, suffix, cursor, this.#aiCodeCompletionConfig?.completionContext.inferenceLanguage,
|
|
232
|
+
this.#aiCodeCompletionConfig?.completionContext.additionalFiles);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#debouncedRequestAidaSuggestion = Common.Debouncer.debounce(
|
|
236
|
+
(prefix: string, suffix: string, cursorPositionAtRequest: number,
|
|
237
|
+
inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage,
|
|
238
|
+
additionalFiles?: Host.AidaClient.AdditionalFile[]) => {
|
|
239
|
+
void this.#requestAidaSuggestion(prefix, suffix, cursorPositionAtRequest, inferenceLanguage, additionalFiles);
|
|
240
|
+
},
|
|
241
|
+
AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS);
|
|
242
|
+
|
|
243
|
+
async #requestAidaSuggestion(
|
|
244
|
+
prefix: string, suffix: string, cursorPositionAtRequest: number,
|
|
245
|
+
inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage,
|
|
246
|
+
additionalFiles?: Host.AidaClient.AdditionalFile[]): Promise<void> {
|
|
247
|
+
if (!this.#aiCodeCompletion) {
|
|
248
|
+
AiCodeCompletion.debugLog('Ai Code Completion is not initialized');
|
|
249
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
250
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionError);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const startTime = performance.now();
|
|
255
|
+
this.#aiCodeCompletionConfig?.onRequestTriggered();
|
|
256
|
+
// Registering AiCodeCompletionRequestTriggered metric even if the request is served from cache
|
|
257
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionRequestTriggered);
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const completionResponse = await this.#aiCodeCompletion.completeCode(
|
|
261
|
+
prefix, suffix, cursorPositionAtRequest, inferenceLanguage, additionalFiles);
|
|
262
|
+
|
|
263
|
+
if (!completionResponse) {
|
|
264
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const {response, fromCache} = completionResponse;
|
|
269
|
+
|
|
270
|
+
if (!response) {
|
|
271
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const sampleResponse = await this.#generateSampleForRequest(response, prefix, suffix);
|
|
276
|
+
if (!sampleResponse) {
|
|
277
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const {
|
|
282
|
+
suggestionText,
|
|
283
|
+
sampleId,
|
|
284
|
+
citations,
|
|
285
|
+
rpcGlobalId,
|
|
286
|
+
} = sampleResponse;
|
|
287
|
+
const remainingDelay = Math.max(
|
|
288
|
+
AiCodeCompletion.AiCodeCompletion.DELAY_BEFORE_SHOWING_RESPONSE_MS - (performance.now() - startTime), 0);
|
|
289
|
+
this.#suggestionRenderingTimeout = window.setTimeout(() => {
|
|
290
|
+
const currentCursorPosition = this.#editor?.editor.state.selection.main.head;
|
|
291
|
+
if (currentCursorPosition !== cursorPositionAtRequest) {
|
|
292
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (this.#aiCodeCompletion) {
|
|
296
|
+
this.#editor?.dispatch({
|
|
297
|
+
effects: setAiAutoCompleteSuggestion.of({
|
|
298
|
+
text: suggestionText,
|
|
299
|
+
from: cursorPositionAtRequest,
|
|
300
|
+
rpcGlobalId,
|
|
301
|
+
sampleId,
|
|
302
|
+
startTime,
|
|
303
|
+
clearCachedRequest: this.clearCache.bind(this),
|
|
304
|
+
onImpression: this.#aiCodeCompletion?.registerUserImpression.bind(this.#aiCodeCompletion),
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (fromCache) {
|
|
309
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionResponseServedFromCache);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
AiCodeCompletion.debugLog(
|
|
313
|
+
'Suggestion dispatched to the editor', suggestionText, 'at cursor position', cursorPositionAtRequest);
|
|
314
|
+
this.#aiCodeCompletionConfig?.onResponseReceived(citations);
|
|
315
|
+
}, remainingDelay);
|
|
316
|
+
} catch (e) {
|
|
317
|
+
AiCodeCompletion.debugLog('Error while fetching code completion suggestions from AIDA', e);
|
|
318
|
+
this.#aiCodeCompletionConfig?.onResponseReceived([]);
|
|
319
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionError);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async #generateSampleForRequest(response: Host.AidaClient.CompletionResponse, prefix: string, suffix?: string):
|
|
324
|
+
Promise<{
|
|
325
|
+
suggestionText: string,
|
|
326
|
+
citations: Host.AidaClient.Citation[],
|
|
327
|
+
rpcGlobalId?: Host.AidaClient.RpcGlobalId,
|
|
328
|
+
sampleId?: number,
|
|
329
|
+
}|null> {
|
|
330
|
+
const suggestionSample = this.#pickSampleFromResponse(response);
|
|
331
|
+
if (!suggestionSample) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const shouldBlock =
|
|
336
|
+
suggestionSample.attributionMetadata?.attributionAction === Host.AidaClient.RecitationAction.BLOCK;
|
|
337
|
+
if (shouldBlock) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const isRepetitive = this.#checkIfSuggestionRepeatsExistingText(suggestionSample.generationString, prefix, suffix);
|
|
342
|
+
if (isRepetitive) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const suggestionText = this.#trimSuggestionOverlap(suggestionSample.generationString, suffix);
|
|
347
|
+
if (suggestionText.length === 0) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
suggestionText,
|
|
353
|
+
sampleId: suggestionSample.sampleId,
|
|
354
|
+
citations: suggestionSample.attributionMetadata?.citations ?? [],
|
|
355
|
+
rpcGlobalId: response.metadata.rpcGlobalId,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#pickSampleFromResponse(response: Host.AidaClient.CompletionResponse): Host.AidaClient.GenerationSample|null {
|
|
360
|
+
if (!response.generatedSamples.length) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// `currentHint` is the portion of a standard autocomplete suggestion that the user has not yet typed.
|
|
365
|
+
// For example, if the user types `document.queryS` and the autocomplete suggests `document.querySelector`,
|
|
366
|
+
// the `currentHint` is `elector`.
|
|
367
|
+
const currentHintInMenu = this.#editor?.editor.plugin(showCompletionHint)?.currentHint;
|
|
368
|
+
if (!currentHintInMenu) {
|
|
369
|
+
return response.generatedSamples[0];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// TODO(ergunsh): This does not handle looking for `selectedCompletion`. The `currentHint` is `null`
|
|
373
|
+
// for the Sources panel case.
|
|
374
|
+
// Even though there is no match, we still return the first suggestion which will be displayed
|
|
375
|
+
// when the traditional autocomplete menu is closed.
|
|
376
|
+
return response.generatedSamples.find(sample => sample.generationString.startsWith(currentHintInMenu)) ??
|
|
377
|
+
response.generatedSamples[0];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#checkIfSuggestionRepeatsExistingText(generationString: string, prefix: string, suffix?: string): boolean {
|
|
381
|
+
return Boolean(prefix.includes(generationString.trim()) || suffix?.includes(generationString.trim()));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Removes the end of a suggestion if it overlaps with the start of the suffix.
|
|
386
|
+
*/
|
|
387
|
+
#trimSuggestionOverlap(generationString: string, suffix?: string): string {
|
|
388
|
+
if (!suffix) {
|
|
389
|
+
return generationString;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Iterate from the longest possible overlap down to the shortest
|
|
393
|
+
for (let i = Math.min(generationString.length, suffix.length); i > 0; i--) {
|
|
394
|
+
const overlapCandidate = suffix.substring(0, i);
|
|
395
|
+
if (generationString.endsWith(overlapCandidate)) {
|
|
396
|
+
return generationString.slice(0, -i);
|
|
397
|
+
}
|
|
165
398
|
}
|
|
166
|
-
return
|
|
399
|
+
return generationString;
|
|
167
400
|
}
|
|
168
401
|
}
|
|
169
402
|
|
|
@@ -483,7 +483,7 @@ export function contentIncludingHint(view: CM.EditorView): string {
|
|
|
483
483
|
|
|
484
484
|
export const setAiAutoCompleteSuggestion = CM.StateEffect.define<ActiveSuggestion|null>();
|
|
485
485
|
|
|
486
|
-
interface ActiveSuggestion {
|
|
486
|
+
export interface ActiveSuggestion {
|
|
487
487
|
text: string;
|
|
488
488
|
from: number;
|
|
489
489
|
sampleId?: number;
|
|
@@ -113,8 +113,10 @@ function cancelUpdate(widget: Widget): void {
|
|
|
113
113
|
|
|
114
114
|
function runNextUpdate(): void {
|
|
115
115
|
pendingAnimationFrame = null;
|
|
116
|
-
currentUpdateQueue
|
|
117
|
-
|
|
116
|
+
if (!currentUpdateQueue) {
|
|
117
|
+
currentUpdateQueue = nextUpdateQueue;
|
|
118
|
+
nextUpdateQueue = new Map();
|
|
119
|
+
}
|
|
118
120
|
for (const [widget, {resolve}] of currentUpdateQueue) {
|
|
119
121
|
currentlyProcessed.add(widget);
|
|
120
122
|
void (async () => {
|
|
@@ -122,8 +124,15 @@ function runNextUpdate(): void {
|
|
|
122
124
|
resolve();
|
|
123
125
|
})();
|
|
124
126
|
}
|
|
125
|
-
currentUpdateQueue
|
|
126
|
-
|
|
127
|
+
currentUpdateQueue.clear();
|
|
128
|
+
queueMicrotask(() => {
|
|
129
|
+
if (currentUpdateQueue && currentUpdateQueue.size > 0) {
|
|
130
|
+
runNextUpdate();
|
|
131
|
+
} else {
|
|
132
|
+
currentUpdateQueue = null;
|
|
133
|
+
currentlyProcessed.clear();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
export class WidgetElement<WidgetT extends Widget> extends HTMLElement {
|
|
@@ -320,6 +320,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
export class ObjectTreeNode extends ObjectTreeNodeBase {
|
|
323
|
+
#path?: string;
|
|
323
324
|
constructor(
|
|
324
325
|
readonly property: SDK.RemoteObject.RemoteObjectProperty,
|
|
325
326
|
propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
@@ -336,6 +337,31 @@ export class ObjectTreeNode extends ObjectTreeNodeBase {
|
|
|
336
337
|
return this.property.name;
|
|
337
338
|
}
|
|
338
339
|
|
|
340
|
+
get path(): string {
|
|
341
|
+
if (!this.#path) {
|
|
342
|
+
if (this.property.synthetic) {
|
|
343
|
+
this.#path = this.name;
|
|
344
|
+
return this.name;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// https://tc39.es/ecma262/#prod-IdentifierName
|
|
348
|
+
const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
|
|
349
|
+
const isInteger = /^(?:0|[1-9]\d*)$/;
|
|
350
|
+
|
|
351
|
+
const parentPath =
|
|
352
|
+
(this.parent instanceof ObjectTreeNode && !this.parent.property.synthetic) ? this.parent.path : '';
|
|
353
|
+
|
|
354
|
+
if (this.property.private || useDotNotation.test(this.name)) {
|
|
355
|
+
this.#path = parentPath ? `${parentPath}.${this.name}` : this.name;
|
|
356
|
+
} else if (isInteger.test(this.name)) {
|
|
357
|
+
this.#path = `${parentPath}[${this.name}]`;
|
|
358
|
+
} else {
|
|
359
|
+
this.#path = `${parentPath}[${JSON.stringify(this.name)}]`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return this.#path;
|
|
363
|
+
}
|
|
364
|
+
|
|
339
365
|
override selfOrParentIfInternal(): ObjectTreeNodeBase {
|
|
340
366
|
return this.name === '[[Prototype]]' ? (this.parent ?? this) : this;
|
|
341
367
|
}
|
|
@@ -830,6 +856,11 @@ export class RootElement extends UI.TreeOutline.TreeElement {
|
|
|
830
856
|
this.listItemElement.addEventListener('contextmenu', this.onContextMenu.bind(this), false);
|
|
831
857
|
}
|
|
832
858
|
|
|
859
|
+
override invalidateChildren(): void {
|
|
860
|
+
super.invalidateChildren();
|
|
861
|
+
this.object.removeChildren();
|
|
862
|
+
}
|
|
863
|
+
|
|
833
864
|
override onexpand(): void {
|
|
834
865
|
if (this.treeOutline) {
|
|
835
866
|
this.treeOutline.element.classList.add('expanded');
|
|
@@ -1189,8 +1220,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1189
1220
|
if (this.property.property.synthetic) {
|
|
1190
1221
|
this.nameElement.classList.add('synthetic-property');
|
|
1191
1222
|
}
|
|
1192
|
-
|
|
1193
|
-
this.updatePropertyPath();
|
|
1223
|
+
this.nameElement.title = this.property.path;
|
|
1194
1224
|
|
|
1195
1225
|
const isInternalEntries = this.property.property.synthetic && this.property.name === '[[Entries]]';
|
|
1196
1226
|
if (isInternalEntries) {
|
|
@@ -1200,7 +1230,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1200
1230
|
const showPreview = this.property.name !== '[[Prototype]]';
|
|
1201
1231
|
this.propertyValue = ObjectPropertiesSection.createPropertyValueWithCustomSupport(
|
|
1202
1232
|
this.property.object, this.property.property.wasThrown, showPreview, this.linkifier,
|
|
1203
|
-
this.property.property.synthetic, this.path
|
|
1233
|
+
this.property.property.synthetic, this.property.path /* variableName */);
|
|
1204
1234
|
this.valueElement = this.propertyValue;
|
|
1205
1235
|
} else if (this.property.property.getter) {
|
|
1206
1236
|
this.valueElement = document.createElement('span');
|
|
@@ -1254,36 +1284,6 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1254
1284
|
this.listItemElement.appendChild(this.rowContainer);
|
|
1255
1285
|
}
|
|
1256
1286
|
|
|
1257
|
-
private updatePropertyPath(): void {
|
|
1258
|
-
if (this.nameElement.title) {
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
const name = this.property.name;
|
|
1263
|
-
|
|
1264
|
-
if (this.property.property.synthetic) {
|
|
1265
|
-
UI.Tooltip.Tooltip.install(this.nameElement, name);
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
// https://tc39.es/ecma262/#prod-IdentifierName
|
|
1270
|
-
const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
|
|
1271
|
-
const isInteger = /^(?:0|[1-9]\d*)$/;
|
|
1272
|
-
|
|
1273
|
-
const parentPath = (this.parent instanceof ObjectPropertyTreeElement && this.parent.nameElement &&
|
|
1274
|
-
!this.parent.property.property.synthetic) ?
|
|
1275
|
-
this.parent.nameElement.title :
|
|
1276
|
-
'';
|
|
1277
|
-
|
|
1278
|
-
if (this.property.property.private || useDotNotation.test(name)) {
|
|
1279
|
-
UI.Tooltip.Tooltip.install(this.nameElement, parentPath ? `${parentPath}.${name}` : name);
|
|
1280
|
-
} else if (isInteger.test(name)) {
|
|
1281
|
-
UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${name}]`);
|
|
1282
|
-
} else {
|
|
1283
|
-
UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${JSON.stringify(name)}]`);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
1287
|
getContextMenu(event: Event): UI.ContextMenu.ContextMenu {
|
|
1288
1288
|
const contextMenu = new UI.ContextMenu.ContextMenu(event);
|
|
1289
1289
|
contextMenu.appendApplicableItems(this);
|
|
@@ -1449,6 +1449,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1449
1449
|
}
|
|
1450
1450
|
}
|
|
1451
1451
|
|
|
1452
|
+
override invalidateChildren(): void {
|
|
1453
|
+
super.invalidateChildren();
|
|
1454
|
+
this.property.removeChildren();
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1452
1457
|
private onInvokeGetterClick(result: SDK.RemoteObject.CallFunctionResult): void {
|
|
1453
1458
|
if (!result.object) {
|
|
1454
1459
|
return;
|
|
@@ -1642,6 +1647,11 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1642
1647
|
ObjectPropertyTreeElement.populateWithProperties(treeNode, children, false, false, linkifier);
|
|
1643
1648
|
}
|
|
1644
1649
|
|
|
1650
|
+
override invalidateChildren(): void {
|
|
1651
|
+
super.invalidateChildren();
|
|
1652
|
+
this.#child.removeChildren();
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1645
1655
|
override async onpopulate(): Promise<void> {
|
|
1646
1656
|
this.removeChildren();
|
|
1647
1657
|
this.#child.removeChildren();
|
|
@@ -809,9 +809,14 @@ export const knownContextValues = new Set([
|
|
|
809
809
|
'column-rule',
|
|
810
810
|
'column-rule-break',
|
|
811
811
|
'column-rule-color',
|
|
812
|
+
'column-rule-edge-end-inset',
|
|
812
813
|
'column-rule-edge-end-outset',
|
|
814
|
+
'column-rule-edge-start-inset',
|
|
813
815
|
'column-rule-edge-start-outset',
|
|
816
|
+
'column-rule-inset',
|
|
817
|
+
'column-rule-interior-end-inset',
|
|
814
818
|
'column-rule-interior-end-outset',
|
|
819
|
+
'column-rule-interior-start-inset',
|
|
815
820
|
'column-rule-interior-start-outset',
|
|
816
821
|
'column-rule-outset',
|
|
817
822
|
'column-rule-style',
|
|
@@ -3167,9 +3172,14 @@ export const knownContextValues = new Set([
|
|
|
3167
3172
|
'row-rule',
|
|
3168
3173
|
'row-rule-break',
|
|
3169
3174
|
'row-rule-color',
|
|
3175
|
+
'row-rule-edge-end-inset',
|
|
3170
3176
|
'row-rule-edge-end-outset',
|
|
3177
|
+
'row-rule-edge-start-inset',
|
|
3171
3178
|
'row-rule-edge-start-outset',
|
|
3179
|
+
'row-rule-inset',
|
|
3180
|
+
'row-rule-interior-end-inset',
|
|
3172
3181
|
'row-rule-interior-end-outset',
|
|
3182
|
+
'row-rule-interior-start-inset',
|
|
3173
3183
|
'row-rule-interior-start-outset',
|
|
3174
3184
|
'row-rule-outset',
|
|
3175
3185
|
'row-rule-style',
|
|
@@ -3184,6 +3194,7 @@ export const knownContextValues = new Set([
|
|
|
3184
3194
|
'rule',
|
|
3185
3195
|
'rule-break',
|
|
3186
3196
|
'rule-color',
|
|
3197
|
+
'rule-inset',
|
|
3187
3198
|
'rule-outset',
|
|
3188
3199
|
'rule-set',
|
|
3189
3200
|
'rule-set-details',
|
|
@@ -3831,6 +3842,7 @@ export const knownContextValues = new Set([
|
|
|
3831
3842
|
'timeline.annotation-sidebar.delete',
|
|
3832
3843
|
'timeline.annotations-tab',
|
|
3833
3844
|
'timeline.annotations.',
|
|
3845
|
+
'timeline.annotations.ai-generate-label',
|
|
3834
3846
|
'timeline.annotations.create-entries-link',
|
|
3835
3847
|
'timeline.annotations.create-entry-label',
|
|
3836
3848
|
'timeline.annotations.create-entry-link',
|
package/mcp/mcp.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
export * as Common from '../front_end/core/common/common.js';
|
|
11
11
|
export * as I18n from '../front_end/core/i18n/i18n.js';
|
|
12
12
|
export {ConnectionTransport} from '../front_end/core/protocol_client/ConnectionTransport.js';
|
|
13
|
+
export * as Foundation from '../front_end/foundation/foundation.js';
|
|
13
14
|
export {
|
|
14
15
|
PerformanceInsightFormatter
|
|
15
16
|
} from '../front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js';
|
|
@@ -30,4 +31,5 @@ export {
|
|
|
30
31
|
IssuesManager
|
|
31
32
|
} from '../front_end/models/issues_manager/IssuesManager.js';
|
|
32
33
|
export {findTitleFromMarkdownAst} from '../front_end/models/issues_manager/MarkdownIssueDescription.js';
|
|
34
|
+
export * as TraceEngine from '../front_end/models/trace/trace.js';
|
|
33
35
|
export * as Marked from '../front_end/third_party/marked/marked.js';
|
package/package.json
CHANGED
|
@@ -1,72 +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 type * as SDK from '../../core/sdk/sdk.js';
|
|
6
|
-
import type * as Protocol from '../../generated/protocol.js';
|
|
7
|
-
|
|
8
|
-
import {Issue, IssueCategory, IssueKind} from './Issue.js';
|
|
9
|
-
import {
|
|
10
|
-
type LazyMarkdownIssueDescription,
|
|
11
|
-
type MarkdownIssueDescription,
|
|
12
|
-
resolveLazyDescription,
|
|
13
|
-
} from './MarkdownIssueDescription.js';
|
|
14
|
-
|
|
15
|
-
export class UserReidentificationIssue extends Issue {
|
|
16
|
-
#issueDetails: Protocol.Audits.UserReidentificationIssueDetails;
|
|
17
|
-
|
|
18
|
-
constructor(
|
|
19
|
-
issueDetails: Protocol.Audits.UserReidentificationIssueDetails, issuesModel: SDK.IssuesModel.IssuesModel|null) {
|
|
20
|
-
super('UserReidentificationIssue', issuesModel);
|
|
21
|
-
this.#issueDetails = issueDetails;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
primaryKey(): string {
|
|
25
|
-
const requestId = this.#issueDetails.request ? this.#issueDetails.request.requestId : 'no-request';
|
|
26
|
-
return `${this.code()}-(${requestId})`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
override requests(): Iterable<Protocol.Audits.AffectedRequest> {
|
|
30
|
-
return this.#issueDetails.request ? [this.#issueDetails.request] : [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
getCategory(): IssueCategory {
|
|
34
|
-
return IssueCategory.OTHER;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
getDescription(): MarkdownIssueDescription|null {
|
|
38
|
-
const description = issueDescriptions.get(this.code());
|
|
39
|
-
if (!description) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return resolveLazyDescription(description);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
getKind(): IssueKind {
|
|
46
|
-
return IssueKind.IMPROVEMENT;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static fromInspectorIssue(
|
|
50
|
-
issuesModel: SDK.IssuesModel.IssuesModel|null,
|
|
51
|
-
inspectorIssue: Protocol.Audits.InspectorIssue): UserReidentificationIssue[] {
|
|
52
|
-
const userReidentificationIssueDetails = inspectorIssue.details.userReidentificationIssueDetails;
|
|
53
|
-
if (!userReidentificationIssueDetails) {
|
|
54
|
-
console.warn('User Reidentification issue without details received.');
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
return [new UserReidentificationIssue(userReidentificationIssueDetails, issuesModel)];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Add new issue types to this map (with a unique code per type).
|
|
62
|
-
const issueDescriptions = new Map<string, LazyMarkdownIssueDescription>([
|
|
63
|
-
[
|
|
64
|
-
'UserReidentificationIssue',
|
|
65
|
-
{
|
|
66
|
-
file: 'userReidentificationBlocked.md',
|
|
67
|
-
// TODO(https://g-issues.chromium.org/issues/409596758): Add
|
|
68
|
-
// internationalized learn more link text.
|
|
69
|
-
links: [],
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
]);
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Resources suspected of tracking users are blocked
|
|
2
|
-
|
|
3
|
-
This is currently an experimental feature. In case of site breakage, you can disable it for this page via the eye icon in the URL bar or completely disable it via chrome://settings/incognito. You may also report bugs [here](userReidentificationBugReports).
|
|
4
|
-
|
|
5
|
-
Chrome identifies domains known to serve content which misuses web platform APIs to re-identify users across sites. Resources from these domains are blocked when users express a preference for additional privacy. Consider removing the following resources from your site:
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Copyright 2021 The Chromium Authors
|
|
3
|
-
Use of this source code is governed by a BSD-style license that can be
|
|
4
|
-
found in the LICENSE file.
|
|
5
|
-
-->
|
|
6
|
-
<!DOCTYPE html>
|
|
7
|
-
<html>
|
|
8
|
-
<head>
|
|
9
|
-
<meta charset="UTF-8" />
|
|
10
|
-
<meta name="viewport" content="width=device-width" />
|
|
11
|
-
<title>Basic Markdown Image example</title>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<div id="icon"></div>
|
|
15
|
-
<div id="image"></div>
|
|
16
|
-
|
|
17
|
-
<script type="module" src="./basic.js"></script>
|
|
18
|
-
</body>
|
|
19
|
-
</html>
|