chrome-devtools-frontend 1.0.1562885 → 1.0.1563377
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/AUTHORS +1 -0
- package/front_end/core/sdk/CSSMatchedStyles.ts +46 -10
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +2 -190
- package/front_end/models/ai_code_generation/AiCodeGeneration.ts +13 -5
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -17
- package/front_end/panels/ai_assistance/components/ChatInput.ts +22 -0
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +39 -5
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -0
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +211 -15
- package/front_end/panels/common/aiCodeGenerationTeaser.css +64 -0
- package/front_end/panels/console/ConsoleView.ts +2 -1
- package/front_end/panels/event_listeners/EventListenersView.ts +2 -1
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +6 -2
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +26 -8
- package/front_end/ui/components/text_editor/AiCodeCompletionTeaserPlaceholder.ts +4 -0
- package/front_end/ui/components/text_editor/AiCodeGenerationParser.ts +77 -0
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +77 -40
- package/front_end/ui/components/text_editor/text_editor.ts +1 -0
- package/front_end/ui/legacy/components/utils/Linkifier.ts +3 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -5,6 +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 type * as AiCodeCompletion from '../../../models/ai_code_completion/ai_code_completion.js';
|
|
8
9
|
import * as AiCodeGeneration from '../../../models/ai_code_generation/ai_code_generation.js';
|
|
9
10
|
import * as PanelCommon from '../../../panels/common/common.js';
|
|
10
11
|
import * as CodeMirror from '../../../third_party/codemirror.next/codemirror.next.js';
|
|
@@ -12,6 +13,7 @@ import * as UI from '../../../ui/legacy/legacy.js';
|
|
|
12
13
|
import * as VisualLogging from '../../visual_logging/visual_logging.js';
|
|
13
14
|
|
|
14
15
|
import {AiCodeCompletionTeaserPlaceholder} from './AiCodeCompletionTeaserPlaceholder.js';
|
|
16
|
+
import {AiCodeGenerationParser} from './AiCodeGenerationParser.js';
|
|
15
17
|
import {
|
|
16
18
|
acceptAiAutoCompleteSuggestion,
|
|
17
19
|
aiAutoCompleteSuggestion,
|
|
@@ -43,6 +45,7 @@ export interface AiCodeGenerationConfig {
|
|
|
43
45
|
onRequestTriggered: () => void;
|
|
44
46
|
// TODO(b/445394511): Move exposing citations to onSuggestionAccepted
|
|
45
47
|
onResponseReceived: (citations: Host.AidaClient.Citation[]) => void;
|
|
48
|
+
panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export class AiCodeGenerationProvider {
|
|
@@ -64,6 +67,9 @@ export class AiCodeGenerationProvider {
|
|
|
64
67
|
throw new Error('AI code generation feature is not enabled.');
|
|
65
68
|
}
|
|
66
69
|
this.#generationTeaser = new PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaser();
|
|
70
|
+
this.#generationTeaser.disclaimerTooltipId =
|
|
71
|
+
aiCodeGenerationConfig.panel + '-ai-code-generation-disclaimer-tooltip';
|
|
72
|
+
this.#generationTeaser.panel = aiCodeGenerationConfig.panel;
|
|
67
73
|
this.#aiCodeGenerationConfig = aiCodeGenerationConfig;
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -74,11 +80,11 @@ export class AiCodeGenerationProvider {
|
|
|
74
80
|
extension(): CodeMirror.Extension[] {
|
|
75
81
|
return [
|
|
76
82
|
CodeMirror.EditorView.updateListener.of(update => this.#activateTeaser(update)),
|
|
77
|
-
CodeMirror.EditorView.updateListener.of(update => this.#
|
|
83
|
+
CodeMirror.EditorView.updateListener.of(update => this.#abortOrDismissGenerationDuringUpdate(update)),
|
|
78
84
|
aiAutoCompleteSuggestion,
|
|
79
85
|
aiAutoCompleteSuggestionState,
|
|
80
86
|
aiCodeGenerationTeaserModeState,
|
|
81
|
-
this.#generationTeaserCompartment.of([]),
|
|
87
|
+
CodeMirror.Prec.highest(this.#generationTeaserCompartment.of([])),
|
|
82
88
|
CodeMirror.Prec.highest(CodeMirror.keymap.of(this.#editorKeymap())),
|
|
83
89
|
];
|
|
84
90
|
}
|
|
@@ -137,9 +143,7 @@ export class AiCodeGenerationProvider {
|
|
|
137
143
|
return false;
|
|
138
144
|
}
|
|
139
145
|
if (hasActiveAiSuggestion(this.#editor.state)) {
|
|
140
|
-
this.#
|
|
141
|
-
effects: setAiAutoCompleteSuggestion.of(null),
|
|
142
|
-
});
|
|
146
|
+
this.#dismissTeaserAndSuggestion();
|
|
143
147
|
return true;
|
|
144
148
|
}
|
|
145
149
|
const generationTeaserIsLoading = this.#generationTeaser.displayState ===
|
|
@@ -147,7 +151,7 @@ export class AiCodeGenerationProvider {
|
|
|
147
151
|
if (this.#generationTeaser.isShowing() && generationTeaserIsLoading) {
|
|
148
152
|
this.#controller.abort();
|
|
149
153
|
this.#controller = new AbortController();
|
|
150
|
-
this.#
|
|
154
|
+
this.#dismissTeaserAndSuggestion();
|
|
151
155
|
return true;
|
|
152
156
|
}
|
|
153
157
|
return false;
|
|
@@ -189,9 +193,14 @@ export class AiCodeGenerationProvider {
|
|
|
189
193
|
];
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
#
|
|
196
|
+
#dismissTeaserAndSuggestion(): void {
|
|
193
197
|
this.#generationTeaser.displayState = PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.TRIGGER;
|
|
194
|
-
this.#editor?.dispatch({
|
|
198
|
+
this.#editor?.dispatch({
|
|
199
|
+
effects: [
|
|
200
|
+
setAiCodeGenerationTeaserMode.of(AiCodeGenerationTeaserMode.DISMISSED),
|
|
201
|
+
setAiAutoCompleteSuggestion.of(null),
|
|
202
|
+
]
|
|
203
|
+
});
|
|
195
204
|
}
|
|
196
205
|
|
|
197
206
|
#activateTeaser(update: CodeMirror.ViewUpdate): void {
|
|
@@ -206,26 +215,35 @@ export class AiCodeGenerationProvider {
|
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
/**
|
|
209
|
-
* Monitors editor changes to cancel an ongoing AI generation
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
218
|
+
* Monitors editor changes to cancel an ongoing AI generation or dismiss one
|
|
219
|
+
* if it already exists.
|
|
220
|
+
* We abort the request (or dismiss suggestion) and dismiss the teaser if the
|
|
221
|
+
* user modifies the document or moves their cursor/selection. These actions
|
|
222
|
+
* indicate the user is no longer focused on the current generation point or
|
|
223
|
+
* has manually resumed editing, making the suggestion irrelevant.
|
|
214
224
|
*/
|
|
215
|
-
#
|
|
225
|
+
#abortOrDismissGenerationDuringUpdate(update: CodeMirror.ViewUpdate): void {
|
|
216
226
|
if (!update.docChanged && update.state.selection.main.head === update.startState.selection.main.head) {
|
|
217
227
|
return;
|
|
218
228
|
}
|
|
219
229
|
const currentTeaserMode = update.state.field(aiCodeGenerationTeaserModeState);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (
|
|
230
|
+
if (currentTeaserMode === AiCodeGenerationTeaserMode.DISMISSED) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (this.#generationTeaser.displayState ===
|
|
234
|
+
PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.LOADING) {
|
|
235
|
+
this.#controller.abort();
|
|
236
|
+
this.#controller = new AbortController();
|
|
237
|
+
this.#dismissTeaserAndSuggestion();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (this.#generationTeaser.displayState ===
|
|
241
|
+
PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.GENERATED) {
|
|
242
|
+
update.view.dispatch({effects: setAiAutoCompleteSuggestion.of(null)});
|
|
243
|
+
this.#generationTeaser.displayState =
|
|
244
|
+
PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.TRIGGER;
|
|
224
245
|
return;
|
|
225
246
|
}
|
|
226
|
-
this.#controller.abort();
|
|
227
|
-
this.#controller = new AbortController();
|
|
228
|
-
this.#dismissTeaser();
|
|
229
247
|
}
|
|
230
248
|
|
|
231
249
|
async #triggerAiCodeGeneration(options?: {signal?: AbortSignal}): Promise<void> {
|
|
@@ -235,9 +253,8 @@ export class AiCodeGenerationProvider {
|
|
|
235
253
|
|
|
236
254
|
this.#generationTeaser.displayState = PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.LOADING;
|
|
237
255
|
const cursor = this.#editor.state.selection.main.head;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (query.trim().length === 0) {
|
|
256
|
+
const query = AiCodeGenerationParser.extractCommentText(this.#editor.state, cursor);
|
|
257
|
+
if (!query || query.trim().length === 0) {
|
|
241
258
|
return;
|
|
242
259
|
}
|
|
243
260
|
|
|
@@ -251,7 +268,7 @@ export class AiCodeGenerationProvider {
|
|
|
251
268
|
this.#aiCodeGenerationConfig?.generationContext.inferenceLanguage, options);
|
|
252
269
|
|
|
253
270
|
if (this.#generationTeaser) {
|
|
254
|
-
this.#
|
|
271
|
+
this.#dismissTeaserAndSuggestion();
|
|
255
272
|
}
|
|
256
273
|
|
|
257
274
|
if (!generationResponse || generationResponse.samples.length === 0) {
|
|
@@ -265,20 +282,30 @@ export class AiCodeGenerationProvider {
|
|
|
265
282
|
return;
|
|
266
283
|
}
|
|
267
284
|
|
|
285
|
+
const backtickRegex = /^```(?:\w+)?\n([\s\S]*?)\n```$/;
|
|
286
|
+
const matchArray = topSample.generationString.match(backtickRegex);
|
|
287
|
+
const suggestionText = matchArray ? matchArray[1].trim() : topSample.generationString;
|
|
288
|
+
|
|
268
289
|
this.#editor.dispatch({
|
|
269
|
-
effects:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
290
|
+
effects: [
|
|
291
|
+
setAiAutoCompleteSuggestion.of({
|
|
292
|
+
text: '\n' + suggestionText,
|
|
293
|
+
from: cursor,
|
|
294
|
+
rpcGlobalId: generationResponse.metadata.rpcGlobalId,
|
|
295
|
+
sampleId: topSample.sampleId,
|
|
296
|
+
startTime,
|
|
297
|
+
onImpression: this.#aiCodeGeneration?.registerUserImpression.bind(this.#aiCodeGeneration),
|
|
298
|
+
}),
|
|
299
|
+
setAiCodeGenerationTeaserMode.of(AiCodeGenerationTeaserMode.ACTIVE)
|
|
300
|
+
]
|
|
277
301
|
});
|
|
302
|
+
this.#generationTeaser.displayState =
|
|
303
|
+
PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.GENERATED;
|
|
278
304
|
|
|
279
|
-
AiCodeGeneration.debugLog('Suggestion dispatched to the editor',
|
|
305
|
+
AiCodeGeneration.debugLog('Suggestion dispatched to the editor', suggestionText);
|
|
280
306
|
const citations = topSample.attributionMetadata?.citations ?? [];
|
|
281
307
|
this.#aiCodeGenerationConfig?.onResponseReceived(citations);
|
|
308
|
+
return;
|
|
282
309
|
} catch (e) {
|
|
283
310
|
AiCodeGeneration.debugLog('Error while fetching code generation suggestions from AIDA', e);
|
|
284
311
|
this.#aiCodeGenerationConfig?.onResponseReceived([]);
|
|
@@ -286,7 +313,7 @@ export class AiCodeGenerationProvider {
|
|
|
286
313
|
}
|
|
287
314
|
|
|
288
315
|
if (this.#generationTeaser) {
|
|
289
|
-
this.#
|
|
316
|
+
this.#dismissTeaserAndSuggestion();
|
|
290
317
|
}
|
|
291
318
|
}
|
|
292
319
|
}
|
|
@@ -318,8 +345,7 @@ function aiCodeGenerationTeaserExtension(teaser: PanelCommon.AiCodeGenerationTea
|
|
|
318
345
|
const line = this.#view.state.doc.lineAt(cursorPosition);
|
|
319
346
|
|
|
320
347
|
const isEmptyLine = line.length === 0;
|
|
321
|
-
|
|
322
|
-
const isComment = line.text.startsWith('//');
|
|
348
|
+
const isComment = Boolean(AiCodeGenerationParser.extractCommentText(this.#view.state, cursorPosition));
|
|
323
349
|
const isCursorAtEndOfLine = cursorPosition >= line.to;
|
|
324
350
|
|
|
325
351
|
if ((isEmptyLine) || (isComment && isCursorAtEndOfLine)) {
|
|
@@ -332,9 +358,10 @@ function aiCodeGenerationTeaserExtension(teaser: PanelCommon.AiCodeGenerationTea
|
|
|
332
358
|
}
|
|
333
359
|
|
|
334
360
|
#updateTeaserState(state: CodeMirror.EditorState): void {
|
|
335
|
-
// Only handle non loading states, as updates during generation are handled by
|
|
336
|
-
// #
|
|
337
|
-
if (teaser.displayState === PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.LOADING
|
|
361
|
+
// Only handle non loading and non generated states, as updates during and after generation are handled by
|
|
362
|
+
// #abortOrDismissGenerationDuringUpdate in AiCodeGenerationProvider
|
|
363
|
+
if (teaser.displayState === PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.LOADING ||
|
|
364
|
+
teaser.displayState === PanelCommon.AiCodeGenerationTeaser.AiCodeGenerationTeaserDisplayState.GENERATED) {
|
|
338
365
|
return;
|
|
339
366
|
}
|
|
340
367
|
const cursorPosition = state.selection.main.head;
|
|
@@ -348,5 +375,15 @@ function aiCodeGenerationTeaserExtension(teaser: PanelCommon.AiCodeGenerationTea
|
|
|
348
375
|
}
|
|
349
376
|
}, {
|
|
350
377
|
decorations: v => v.decorations,
|
|
378
|
+
eventHandlers: {
|
|
379
|
+
mousemove(event: MouseEvent): boolean {
|
|
380
|
+
// Required for mouse hover to propagate to the info button in teaser.
|
|
381
|
+
return (event.target instanceof Node && teaser.contentElement.contains(event.target));
|
|
382
|
+
},
|
|
383
|
+
mousedown(event: MouseEvent): boolean {
|
|
384
|
+
// Required for mouse click to propagate to the info tooltip in teaser.
|
|
385
|
+
return (event.target instanceof Node && teaser.contentElement.contains(event.target));
|
|
386
|
+
},
|
|
387
|
+
},
|
|
351
388
|
});
|
|
352
389
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export * as AiCodeCompletionProvider from './AiCodeCompletionProvider.js';
|
|
6
6
|
export * as AiCodeCompletionTeaserPlaceholder from './AiCodeCompletionTeaserPlaceholder.js';
|
|
7
|
+
export * as AiCodeGenerationParser from './AiCodeGenerationParser.js';
|
|
7
8
|
export * as AiCodeGenerationProvider from './AiCodeGenerationProvider.js';
|
|
8
9
|
export * as AutocompleteHistory from './AutocompleteHistory.js';
|
|
9
10
|
export * as Config from './config.js';
|
|
@@ -320,13 +320,14 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
linkifyRawLocation(
|
|
323
|
-
rawLocation: SDK.DebuggerModel.Location, fallbackUrl: Platform.DevToolsPath.UrlString,
|
|
324
|
-
|
|
323
|
+
rawLocation: SDK.DebuggerModel.Location, fallbackUrl: Platform.DevToolsPath.UrlString, className?: string,
|
|
324
|
+
options?: LinkifyOptions): HTMLElement {
|
|
325
325
|
return this.linkifyScriptLocation(
|
|
326
326
|
rawLocation.debuggerModel.target(), rawLocation.scriptId, fallbackUrl, rawLocation.lineNumber, {
|
|
327
327
|
columnNumber: rawLocation.columnNumber,
|
|
328
328
|
className,
|
|
329
329
|
inlineFrameIndex: rawLocation.inlineFrameIndex,
|
|
330
|
+
tabStop: options?.tabStop,
|
|
330
331
|
});
|
|
331
332
|
}
|
|
332
333
|
|
|
@@ -352,6 +352,7 @@ export const knownContextValues = new Set([
|
|
|
352
352
|
'ai-code-completion-teaser-dismissed',
|
|
353
353
|
'ai-code-completion-teaser.dismiss',
|
|
354
354
|
'ai-code-completion-teaser.fre',
|
|
355
|
+
'ai-code-generation-teaser.info-button',
|
|
355
356
|
'ai-explorer',
|
|
356
357
|
'ai_assistance',
|
|
357
358
|
'align-content',
|
package/package.json
CHANGED