chrome-devtools-frontend 1.0.1646714 → 1.0.1649421

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.
Files changed (42) hide show
  1. package/extension-api/ExtensionAPI.d.ts +26 -0
  2. package/front_end/core/common/Debouncer.ts +9 -1
  3. package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
  4. package/front_end/core/sdk/CSSMetadata.ts +1 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
  6. package/front_end/generated/InspectorBackendCommands.ts +3 -2
  7. package/front_end/generated/SupportedCSSProperties.js +533 -19
  8. package/front_end/generated/protocol.ts +26 -4
  9. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  10. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
  11. package/front_end/models/extensions/ExtensionAPI.ts +47 -21
  12. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  13. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +39 -1
  14. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  15. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
  16. package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
  17. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
  18. package/front_end/panels/application/CookieItemsView.ts +55 -6
  19. package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
  20. package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
  21. package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
  22. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
  23. package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
  24. package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
  25. package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
  26. package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
  27. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
  28. package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
  29. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
  30. package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
  31. package/front_end/panels/timeline/components/components.ts +2 -0
  32. package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
  33. package/front_end/panels/timeline/timeline.ts +2 -0
  34. package/front_end/third_party/chromium/README.chromium +1 -1
  35. package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
  36. package/front_end/ui/components/buttons/floatingButton.css +2 -2
  37. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
  38. package/front_end/ui/legacy/TextPrompt.ts +24 -18
  39. package/front_end/ui/legacy/Treeoutline.ts +31 -4
  40. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  42. package/package.json +1 -1
@@ -50,6 +50,14 @@ const UIStrings = {
50
50
  * @description Label for checkbox to show URL-decoded cookie values
51
51
  */
52
52
  showUrlDecoded: 'Show URL-decoded',
53
+ /**
54
+ * @description Text of a context menu item to start a chat with AI
55
+ */
56
+ startAChat: 'Start a chat',
57
+ /**
58
+ * @description Text of a context menu item to explain a web cookie with AI
59
+ */
60
+ explainCookie: 'Explain this cookie',
53
61
  /**
54
62
  * @description Text in Cookie Items View of the Application panel to indicate that no cookie has been selected for preview
55
63
  */
@@ -174,6 +182,10 @@ interface CookieItemsViewInput {
174
182
  onDeleteSelectedItems: () => void;
175
183
  onDeleteAllItems: () => void;
176
184
  onRefreshItems: () => void;
185
+ aiButtonIsEnabled: boolean;
186
+ onPopulateAiContextMenu: (cookie: SDK.Cookie.Cookie, contextMenu: UI.ContextMenu.ContextMenu) => void;
187
+ onAiButtonClick: (cookie: SDK.Cookie.Cookie, event: Event) => void;
188
+ aiButtonTitle?: string;
177
189
  selectedCookie: SDK.Cookie.Cookie|null;
178
190
  }
179
191
 
@@ -203,6 +215,10 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
203
215
  refreshCallback: input.onRefresh,
204
216
  selectedCallback: input.onSelect,
205
217
  deleteCallback: input.onDelete,
218
+ aiButtonIsEnabled: input.aiButtonIsEnabled,
219
+ onAiButtonClick: input.onAiButtonClick,
220
+ onPopulateAiContextMenu: input.onPopulateAiContextMenu,
221
+ aiButtonTitle: input.aiButtonTitle,
206
222
  editable: true,
207
223
  })}
208
224
  ></devtools-widget>
@@ -220,8 +236,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
220
236
  </devtools-split-view>
221
237
  </devtools-widget>
222
238
  `,
223
- // clang-format on
224
- target, {container: {attributes: {jslog: `${VisualLogging.pane('cookies-data')}`}}});
239
+ // clang-format on
240
+ target, {container: {attributes: {jslog: `${VisualLogging.pane('cookies-data')}`}}});
225
241
  };
226
242
 
227
243
  export class CookieItemsView extends UI.Widget.VBox {
@@ -261,7 +277,6 @@ export class CookieItemsView extends UI.Widget.VBox {
261
277
  this.cookieDomain = domain;
262
278
  this.refreshItems();
263
279
  this.model.addEventListener(SDK.CookieModel.Events.COOKIE_LIST_UPDATED, this.onCookieListUpdate, this);
264
- this.updateAiAssistanceContext(null);
265
280
  }
266
281
 
267
282
  override performUpdate(): void {
@@ -296,6 +311,12 @@ export class CookieItemsView extends UI.Widget.VBox {
296
311
  onDeleteAllItems: this.deleteAllItems.bind(this),
297
312
  onRefreshItems: this.refreshItems.bind(this),
298
313
  selectedCookie: this.selectedCookie,
314
+ aiButtonIsEnabled: this.isAiButtonEnabled(),
315
+ onPopulateAiContextMenu: this.#onPopulateAiContextMenu.bind(this),
316
+ onAiButtonClick: this.#onAiButtonClick.bind(this),
317
+ aiButtonTitle: this.isAiButtonEnabled() ?
318
+ UI.ActionRegistry.ActionRegistry.instance().getAction('ai-assistance.storage-floating-button').title() :
319
+ undefined,
299
320
  };
300
321
 
301
322
  this.view(input, output, this.contentElement);
@@ -304,7 +325,6 @@ export class CookieItemsView extends UI.Widget.VBox {
304
325
  override wasShown(): void {
305
326
  super.wasShown();
306
327
  this.refreshItems();
307
- this.updateAiAssistanceContext(this.selectedCookie);
308
328
  }
309
329
 
310
330
  private showPreview(cookie: SDK.Cookie.Cookie|null): void {
@@ -313,10 +333,9 @@ export class CookieItemsView extends UI.Widget.VBox {
313
333
  }
314
334
  this.selectedCookie = cookie;
315
335
  this.requestUpdate();
316
- this.updateAiAssistanceContext(cookie);
317
336
  }
318
337
 
319
- private updateAiAssistanceContext(cookie: SDK.Cookie.Cookie|null): void {
338
+ #updateAiAssistanceContext(cookie: SDK.Cookie.Cookie|null): void {
320
339
  if (cookie && cookie.httpOnly()) {
321
340
  UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, null);
322
341
  return;
@@ -342,6 +361,7 @@ export class CookieItemsView extends UI.Widget.VBox {
342
361
  }
343
362
  this.#toolbar.setCanDeleteSelected(Boolean(selectedCookie));
344
363
  this.showPreview(selectedCookie);
364
+ this.#updateAiAssistanceContext(selectedCookie);
345
365
  }
346
366
 
347
367
  private async saveCookie(newCookie: SDK.Cookie.Cookie, oldCookie: SDK.Cookie.Cookie|null): Promise<boolean> {
@@ -393,6 +413,7 @@ export class CookieItemsView extends UI.Widget.VBox {
393
413
  * This will only delete the currently visible cookies.
394
414
  */
395
415
  deleteAllItems(): void {
416
+ UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, null);
396
417
  this.showPreview(null);
397
418
  void this.model.deleteCookies(this.shownCookies);
398
419
  }
@@ -412,4 +433,32 @@ export class CookieItemsView extends UI.Widget.VBox {
412
433
  refreshItems(): void {
413
434
  void this.model.getCookiesForDomain(this.cookieDomain, true).then(this.updateWithCookies.bind(this));
414
435
  }
436
+
437
+ private isAiButtonEnabled(): boolean {
438
+ return UI.ActionRegistry.ActionRegistry.instance().hasAction('ai-assistance.storage-floating-button');
439
+ }
440
+
441
+ #onPopulateAiContextMenu(cookie: SDK.Cookie.Cookie, contextMenu: UI.ContextMenu.ContextMenu): void {
442
+ const openAiAssistanceId = 'ai-assistance.application-panel-context';
443
+ if (this.isAiButtonEnabled() && UI.ActionRegistry.ActionRegistry.instance().hasAction(openAiAssistanceId)) {
444
+ this.#updateAiAssistanceContext(cookie);
445
+ if (UI.Context.Context.instance().flavor(AiAssistanceModel.StorageItem.StorageItem)) {
446
+ const action = UI.ActionRegistry.ActionRegistry.instance().getAction(openAiAssistanceId);
447
+ const submenu = contextMenu.footerSection().appendSubMenuItem(action.title(), false, openAiAssistanceId);
448
+ submenu.defaultSection().appendAction(openAiAssistanceId, i18nString(UIStrings.startAChat));
449
+ submenu.defaultSection().appendItem(
450
+ i18nString(UIStrings.explainCookie), () => action.execute({prompt: 'What is the purpose of this cookie?'}),
451
+ {disabled: !action.enabled(), jslogContext: openAiAssistanceId + '.cookies'});
452
+ }
453
+ }
454
+ }
455
+
456
+ #onAiButtonClick(cookie: SDK.Cookie.Cookie, _event: Event): void {
457
+ this.#updateAiAssistanceContext(cookie);
458
+ const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
459
+ const storageFloatingButtonId = 'ai-assistance.storage-floating-button';
460
+ if (actionRegistry.hasAction(storageFloatingButtonId)) {
461
+ void actionRegistry.getAction(storageFloatingButtonId).execute();
462
+ }
463
+ }
415
464
  }
@@ -55,6 +55,14 @@ const UIStrings = {
55
55
  * @description Text for announcing a DOM Storage key/value item has been deleted
56
56
  */
57
57
  domStorageItemDeleted: 'The storage item was deleted.',
58
+ /**
59
+ * @description Text of a context menu item to start a chat with AI
60
+ */
61
+ startAChat: 'Start a chat',
62
+ /**
63
+ * @description Text of a context menu item to explain a storage item of a storage bucket with AI
64
+ */
65
+ explainItem: 'Explain this item',
58
66
  } as const;
59
67
  const str_ = i18n.i18n.registerUIStrings('panels/application/DOMStorageItemsView.ts', UIStrings);
60
68
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -111,7 +119,6 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
111
119
  SDK.DOMStorageModel.DOMStorage.Events.DOM_STORAGE_ITEM_UPDATED, this.domStorageItemUpdated, this),
112
120
  ];
113
121
  this.refreshItems();
114
- this.selectedItemChanged(null);
115
122
  }
116
123
 
117
124
  private domStorageItemsCleared(): void {
@@ -174,13 +181,7 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
174
181
  this.showItems(filteredItems);
175
182
  }
176
183
 
177
- override deleteAllItems(): void {
178
- this.domStorage.clear();
179
- // explicitly clear the view because the event won't be fired when it has no items
180
- this.domStorageItemsCleared();
181
- }
182
-
183
- protected override selectedItemChanged(item: {key: string, value: string}|null): void {
184
+ #setAiStorageContext(item: {key: string, value: string}|null): void {
184
185
  const storageKey = this.domStorage.storageKey;
185
186
  if (!storageKey) {
186
187
  return;
@@ -205,10 +206,44 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
205
206
  UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, storageItem);
206
207
  }
207
208
 
209
+ override deleteAllItems(): void {
210
+ this.domStorage.clear();
211
+ // explicitly clear the view because the event won't be fired when it has no items
212
+ this.domStorageItemsCleared();
213
+ }
214
+
215
+ protected override selectedItemChanged(item: {key: string, value: string}|null): void {
216
+ this.#setAiStorageContext(item);
217
+ }
218
+
208
219
  protected override isAiButtonEnabled(): boolean {
209
220
  return UI.ActionRegistry.ActionRegistry.instance().hasAction('ai-assistance.storage-floating-button');
210
221
  }
211
222
 
223
+ protected override populateContextMenu(item: {key: string, value: string},
224
+ contextMenu: UI.ContextMenu.ContextMenu): void {
225
+ const openAiAssistanceId = 'ai-assistance.application-panel-context';
226
+ const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
227
+ if (actionRegistry.hasAction(openAiAssistanceId)) {
228
+ this.#setAiStorageContext(item);
229
+ const action = actionRegistry.getAction(openAiAssistanceId);
230
+ const submenu = contextMenu.footerSection().appendSubMenuItem(action.title(), false, openAiAssistanceId);
231
+ submenu.defaultSection().appendAction(openAiAssistanceId, i18nString(UIStrings.startAChat));
232
+ submenu.defaultSection().appendItem(i18nString(UIStrings.explainItem),
233
+ () => action.execute({prompt: 'Explain this storage item.'}),
234
+ {disabled: !action.enabled(), jslogContext: openAiAssistanceId + '.storage'});
235
+ }
236
+ }
237
+
238
+ protected override onAiButtonClick(item: {key: string, value: string}, _event: Event): void {
239
+ this.#setAiStorageContext(item);
240
+ const aiFloatingActionId = 'ai-assistance.storage-floating-button';
241
+ const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
242
+ if (actionRegistry.hasAction(aiFloatingActionId)) {
243
+ void actionRegistry.getAction(aiFloatingActionId).execute();
244
+ }
245
+ }
246
+
212
247
  protected removeItem(key: string): void {
213
248
  this.domStorage?.removeItem(key);
214
249
  }
@@ -92,6 +92,7 @@ export interface ViewInput {
92
92
  onDelete: (key: string) => void;
93
93
  onDeleteSelected: () => void;
94
94
  onDeleteAll: () => void;
95
+ onContextMenu?: (item: {key: string, value: string}, contextMenu: UI.ContextMenu.ContextMenu) => void;
95
96
  jslog?: string;
96
97
  classes?: string[];
97
98
  aiButtonTitle?: string;
@@ -175,6 +176,7 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
175
176
  @edit=${(e: CustomEvent<{columnId: string, valueBeforeEditing: string, newText: string}>) =>
176
177
  input.onEdit(item.key, item.value, e.detail.columnId, e.detail.valueBeforeEditing, e.detail.newText)}
177
178
  @delete=${() => input.onDelete(item.key)}
179
+ @contextmenu=${(e: CustomEvent<UI.ContextMenu.ContextMenu>) => input.onContextMenu?.(item, e.detail)}
178
180
  selected=${(input.selectedKey === item.key) || nothing}>
179
181
  <td>${input.showAiButton ? html`
180
182
  <span class="ai-button-container">
@@ -198,8 +200,8 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
198
200
  ${input.preview?.element}
199
201
  </devtools-widget>
200
202
  </devtools-split-view>`,
201
- // clang-format on
202
- target, {container: {attributes: {jslog: input.jslog}, classes: input.classes}});
203
+ // clang-format on
204
+ target, {container: {attributes: {jslog: input.jslog}, classes: input.classes}});
203
205
  };
204
206
  }
205
207
  super();
@@ -246,13 +248,13 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
246
248
  void this.#previewEntry(item);
247
249
  this.selectedItemChanged(item);
248
250
  },
249
- onAiButtonClick: (item: {key: string, value: string}, event: Event) => {
250
- event.stopPropagation();
251
- viewInput.onSelect(item);
252
- const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
253
- if (actionRegistry.hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID)) {
254
- void actionRegistry.getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).execute();
255
- }
251
+ onAiButtonClick: this.isAiButtonEnabled() ?
252
+ (item: {key: string, value: string}, event: Event) => {
253
+ this.onAiButtonClick(item, event);
254
+ } :
255
+ undefined,
256
+ onContextMenu: (item: {key: string, value: string}, contextMenu: UI.ContextMenu.ContextMenu) => {
257
+ this.populateContextMenu(item, contextMenu);
256
258
  },
257
259
 
258
260
  onSort: (ascending: boolean) => {
@@ -284,6 +286,12 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
284
286
  return false;
285
287
  }
286
288
 
289
+ protected populateContextMenu(_item: {key: string, value: string}, _contextMenu: UI.ContextMenu.ContextMenu): void {
290
+ }
291
+
292
+ protected onAiButtonClick(_item: {key: string, value: string}, _event: Event): void {
293
+ }
294
+
287
295
  protected get toolbar(): StorageItemsToolbar|undefined {
288
296
  return this.#toolbar;
289
297
  }
@@ -316,6 +316,21 @@ export class CSSWideKeywordRenderer extends rendererBase(SDK.CSSPropertyParserMa
316
316
  }
317
317
  }
318
318
 
319
+ function handleVarDefinitionActivate(variable: string|SDK.CSSMatchedStyles.CSSValueSource,
320
+ stylesContainer: StylesContainer): void {
321
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.CustomPropertyLinkClicked);
322
+ Host.userMetrics.swatchActivated(Host.UserMetrics.SwatchType.VAR_LINK);
323
+
324
+ if (typeof variable === 'string') {
325
+ stylesContainer.jumpToProperty(variable) ||
326
+ stylesContainer.jumpToProperty('initial-value', variable, REGISTERED_PROPERTY_SECTION_NAME);
327
+ } else if (variable.declaration instanceof SDK.CSSProperty.CSSProperty) {
328
+ stylesContainer.revealProperty(variable.declaration);
329
+ } else if (variable.declaration instanceof SDK.CSSMatchedStyles.CSSRegisteredProperty) {
330
+ stylesContainer.jumpToProperty('initial-value', variable.name, REGISTERED_PROPERTY_SECTION_NAME);
331
+ }
332
+ }
333
+
319
334
  // clang-format off
320
335
  export class VariableRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.VariableMatch) {
321
336
  // clang-format on
@@ -344,7 +359,8 @@ export class VariableRenderer extends rendererBase(SDK.CSSPropertyParserMatchers
344
359
  const {declaration, value: variableValue} = match.resolveVariable() ?? {};
345
360
  const fromFallback = variableValue === undefined;
346
361
  const computedValue = variableValue ?? match.fallbackValue();
347
- const onLinkActivate = (name: string): void => this.#handleVarDefinitionActivate(declaration ?? name);
362
+ const onLinkActivate = (name: string): void =>
363
+ handleVarDefinitionActivate(declaration ?? name, this.#stylesContainer);
348
364
  const varSwatch = document.createElement('span');
349
365
 
350
366
  const substitution = context.tracing?.substitution({match, context});
@@ -416,19 +432,59 @@ export class VariableRenderer extends rendererBase(SDK.CSSPropertyParserMatchers
416
432
 
417
433
  return [colorSwatch, varSwatch];
418
434
  }
435
+ }
419
436
 
420
- #handleVarDefinitionActivate(variable: string|SDK.CSSMatchedStyles.CSSValueSource): void {
421
- Host.userMetrics.actionTaken(Host.UserMetrics.Action.CustomPropertyLinkClicked);
422
- Host.userMetrics.swatchActivated(Host.UserMetrics.SwatchType.VAR_LINK);
437
+ // clang-format off
438
+ export class VariableNameRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.VariableNameMatch) {
439
+ // clang-format on
440
+ readonly #stylesContainer: StylesContainer;
441
+ readonly #treeElement: StylePropertyTreeElement|null;
442
+ readonly #matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles;
443
+ constructor(stylesContainer: StylesContainer, treeElement: StylePropertyTreeElement|null,
444
+ matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles) {
445
+ super();
446
+ this.#treeElement = treeElement;
447
+ this.#stylesContainer = stylesContainer;
448
+ this.#matchedStyles = matchedStyles;
449
+ }
423
450
 
424
- if (typeof variable === 'string') {
425
- this.#stylesContainer.jumpToProperty(variable) ||
426
- this.#stylesContainer.jumpToProperty('initial-value', variable, REGISTERED_PROPERTY_SECTION_NAME);
427
- } else if (variable.declaration instanceof SDK.CSSProperty.CSSProperty) {
428
- this.#stylesContainer.revealProperty(variable.declaration);
429
- } else if (variable.declaration instanceof SDK.CSSMatchedStyles.CSSRegisteredProperty) {
430
- this.#stylesContainer.jumpToProperty('initial-value', variable.name, REGISTERED_PROPERTY_SECTION_NAME);
451
+ override render(match: SDK.CSSPropertyParserMatchers.VariableNameMatch, context: RenderingContext): Node[] {
452
+ if (this.#treeElement?.property.ownerStyle.parentRule instanceof SDK.CSSRule.CSSFunctionRule) {
453
+ return Renderer.render(ASTUtils.children(match.node), context).nodes;
431
454
  }
455
+
456
+ const {declaration, value: variableValue} = match.resolveVariable() ?? {};
457
+ const isDefined = variableValue !== undefined;
458
+ const onLinkActivate = (name: string): void =>
459
+ handleVarDefinitionActivate(declaration ?? name, this.#stylesContainer);
460
+ const varSwatch = document.createElement('span');
461
+
462
+ const tooltipContents =
463
+ this.#stylesContainer.getVariablePopoverContents(this.#matchedStyles, match.text, variableValue ?? null);
464
+ const tooltipId = this.#treeElement?.getTooltipId('custom-property-var');
465
+ const tooltip = tooltipId ? {tooltipId} : undefined;
466
+ // clang-format off
467
+ render(html`
468
+ <devtools-link-swatch class=css-var-link .data=${{
469
+ tooltip,
470
+ text: match.text,
471
+ isDefined,
472
+ onLinkActivate,
473
+ }}>
474
+ </devtools-link-swatch>
475
+ ${tooltipId ? html`
476
+ <devtools-tooltip
477
+ id=${tooltipId}
478
+ variant=rich
479
+ jslogContext=elements.css-var
480
+ >
481
+ ${tooltipContents}
482
+ </devtools-tooltip>
483
+ ` : ''}
484
+ `, varSwatch);
485
+ // clang-format on
486
+
487
+ return [varSwatch];
432
488
  }
433
489
  }
434
490
 
@@ -2007,6 +2063,7 @@ export function getPropertyRenderers(
2007
2063
  Array<MatchRenderer<SDK.CSSPropertyParser.Match>> {
2008
2064
  return [
2009
2065
  new VariableRenderer(stylesContainer, treeElement, matchedStyles, computedStyles, computedStyleExtraFields),
2066
+ new VariableNameRenderer(stylesContainer, treeElement, matchedStyles),
2010
2067
  new ColorRenderer(stylesContainer, treeElement),
2011
2068
  new ColorMixRenderer(stylesContainer, matchedStyles, computedStyles, computedStyleExtraFields, treeElement),
2012
2069
  new ContrastColorRenderer(stylesContainer, treeElement),
@@ -2026,10 +2083,10 @@ export function getPropertyRenderers(
2026
2083
  new EnvFunctionRenderer(treeElement, matchedStyles, computedStyles, computedStyleExtraFields),
2027
2084
  new PositionTryRenderer(matchedStyles),
2028
2085
  new LengthRenderer(stylesContainer, propertyName, treeElement),
2029
- new MathFunctionRenderer(
2030
- stylesContainer, matchedStyles, computedStyles, computedStyleExtraFields, propertyName, treeElement),
2031
- new CustomFunctionRenderer(
2032
- stylesContainer, matchedStyles, computedStyles, computedStyleExtraFields, propertyName, treeElement),
2086
+ new MathFunctionRenderer(stylesContainer, matchedStyles, computedStyles, computedStyleExtraFields, propertyName,
2087
+ treeElement),
2088
+ new CustomFunctionRenderer(stylesContainer, matchedStyles, computedStyles, computedStyleExtraFields, propertyName,
2089
+ treeElement),
2033
2090
  new AutoBaseRenderer(computedStyles, computedStyleExtraFields),
2034
2091
  new BinOpRenderer(),
2035
2092
  new RelativeColorChannelRenderer(treeElement),
@@ -2082,7 +2139,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2082
2139
  this.#parentSection = section;
2083
2140
  this.isShorthand = isShorthand;
2084
2141
  this.newProperty = newProperty;
2085
- this.#lazyRender = stylesContainer.shouldRenderLazily();
2142
+ this.#lazyRender = !this.newProperty && stylesContainer.shouldRenderLazily();
2086
2143
  if (this.newProperty) {
2087
2144
  this.listItemElement.textContent = '';
2088
2145
  }
@@ -137,7 +137,15 @@ export class StylesAiCodeCompletionProvider {
137
137
  }
138
138
  currentPropertyString = currentPropertyString + text;
139
139
  prefix = prefix + text;
140
- const suffix = content.substring(propertyEndOffset);
140
+ let suffix = content.substring(propertyEndOffset);
141
+
142
+ const maxLength = TextEditor.AiCodeCompletionProvider.MAX_PREFIX_SUFFIX_LENGTH;
143
+ if (prefix.length > maxLength) {
144
+ prefix = prefix.substring(prefix.length - maxLength);
145
+ }
146
+ if (suffix.length > maxLength) {
147
+ suffix = suffix.substring(0, maxLength);
148
+ }
141
149
 
142
150
  const startTime = performance.now();
143
151
  // TODO(b/476098133): Consider adjusting cursor position
@@ -2038,11 +2038,23 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2038
2038
  override onInput(event: Event): void {
2039
2039
  super.onInput(event);
2040
2040
  if (this.aiCodeCompletionProvider) {
2041
- this.#updateAiCodeSuggestion();
2042
- this.#debouncedTriggerAiCodeCompletion();
2041
+ const inputEvent = event as InputEvent;
2042
+ const isDeletion = inputEvent.inputType?.startsWith('delete');
2043
+ if (isDeletion) {
2044
+ this.#debouncedTriggerAiCodeCompletion.cancel();
2045
+ this.setAiAutoCompletion(null);
2046
+ } else {
2047
+ this.#updateAiCodeSuggestion();
2048
+ this.#debouncedTriggerAiCodeCompletion();
2049
+ }
2043
2050
  }
2044
2051
  }
2045
2052
 
2053
+ override detach(): void {
2054
+ this.#debouncedTriggerAiCodeCompletion.cancel();
2055
+ super.detach();
2056
+ }
2057
+
2046
2058
  #handleEscape(keyboardEvent: KeyboardEvent): boolean {
2047
2059
  if (!this.aiCodeCompletionProvider || !this.treeElement.section().activeAiSuggestion) {
2048
2060
  return false;
@@ -2050,12 +2062,13 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2050
2062
  keyboardEvent.preventDefault();
2051
2063
  if (this.isSuggestBoxVisible()) {
2052
2064
  this.suggestBox?.hide();
2053
- // Required for ensuring the suggestion is not cleared.
2054
- keyboardEvent.consume(true);
2055
- return true;
2065
+ } else {
2066
+ this.setAiAutoCompletion(null);
2056
2067
  }
2057
- this.setAiAutoCompletion(null);
2058
- return false;
2068
+ // Consume the event to prevent it from bubbling up to cancel the editing session,
2069
+ // and return true to prevent TextPrompt from processing it.
2070
+ keyboardEvent.consume(true);
2071
+ return true;
2059
2072
  }
2060
2073
 
2061
2074
  private handleNameOrValueUpDown(event: Event): boolean {
@@ -2331,6 +2344,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2331
2344
  if (!args) {
2332
2345
  this.treeElement.section().activeAiSuggestion = undefined;
2333
2346
  this.activeAiSuggestionInfo = undefined;
2347
+ this.applySuggestion(null);
2334
2348
  return;
2335
2349
  }
2336
2350
 
@@ -2417,7 +2431,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2417
2431
  }
2418
2432
 
2419
2433
  private acceptCodeComplete(): boolean {
2420
- if (this.isSuggestBoxVisible()) {
2434
+ if (this.isSuggestBoxVisible() || this.currentSuggestion()) {
2421
2435
  // accept the suggestion from the traditional autocomplete menu
2422
2436
  this.acceptAutoComplete();
2423
2437