chrome-devtools-frontend 1.0.1724140 → 1.0.1726686

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 (95) hide show
  1. package/.agents/skills/review-ui-widgets/SKILL.md +7 -7
  2. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +12 -0
  3. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +3 -3
  4. package/.agents/skills/ui-widgets/SKILL.md +2 -1
  5. package/docs/cookbook/dependencies.md +6 -4
  6. package/front_end/core/i18n/i18nImpl.ts +1 -1
  7. package/front_end/core/root/Runtime.ts +1 -1
  8. package/front_end/core/sdk/ResourceTreeModel.ts +16 -0
  9. package/front_end/entrypoints/js_app/js_app.ts +2 -5
  10. package/front_end/entrypoints/node_app/node_app.ts +2 -3
  11. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  12. package/front_end/generated/protocol.ts +1 -0
  13. package/front_end/models/ai_assistance/skills/lighthouse.md +2 -1
  14. package/front_end/models/ai_assistance/skills/network.md +1 -0
  15. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +26 -2
  16. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +1 -1
  17. package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -7
  18. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +2 -2
  19. package/front_end/models/comment_manager/CommentManager.ts +0 -6
  20. package/front_end/models/comment_manager/CommentThread.ts +23 -19
  21. package/front_end/models/elements/StyleChanges.ts +66 -0
  22. package/front_end/models/trace/EventsSerializer.ts +2 -7
  23. package/front_end/models/trace/ModelImpl.ts +0 -2
  24. package/front_end/models/trace/handlers/FlowsHandler.ts +37 -29
  25. package/front_end/models/trace/helpers/SyntheticEvents.ts +10 -37
  26. package/front_end/models/trace/insights/RenderBlocking.ts +3 -1
  27. package/front_end/models/trace/lantern/simulation/Simulator.ts +4 -0
  28. package/front_end/models/trace/lantern/types/Lantern.ts +1 -0
  29. package/front_end/panels/accessibility/SourceOrderView.ts +1 -1
  30. package/front_end/panels/application/ApplicationPanelSidebar.ts +90 -0
  31. package/front_end/panels/application/StorageView.ts +196 -174
  32. package/front_end/panels/application/SubAppsView.ts +2 -2
  33. package/front_end/panels/application/preloading/PreloadingView.ts +360 -336
  34. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -2
  35. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +6 -3
  36. package/front_end/panels/application/preloading/components/RuleSetGrid.ts +17 -17
  37. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -2
  38. package/front_end/panels/application/preloading/preloadingView.css +12 -4
  39. package/front_end/panels/application/preloading/preloadingViewDropDown.css +1 -1
  40. package/front_end/panels/application/storageView.css +16 -21
  41. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -2
  42. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +6 -8
  43. package/front_end/panels/comments/CommentsPane.ts +26 -9
  44. package/front_end/panels/comments/commentsPane.css +25 -6
  45. package/front_end/panels/common/CommentThreadWidget.ts +2 -2
  46. package/front_end/panels/common/CommentsOverlayWidget.ts +94 -35
  47. package/front_end/panels/common/commentThreadWidget.css +17 -0
  48. package/front_end/panels/common/commentsOverlay.css +5 -10
  49. package/front_end/panels/common/common-meta.ts +11 -1
  50. package/front_end/panels/elements/ClassesPaneWidget.ts +130 -88
  51. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +7 -33
  52. package/front_end/panels/elements/DOMTreeWidget.ts +7 -3
  53. package/front_end/panels/elements/ElementStatePaneWidget.ts +11 -1
  54. package/front_end/panels/elements/PropertyRenderer.ts +3 -2
  55. package/front_end/panels/elements/StyleEditorWidget.ts +60 -41
  56. package/front_end/panels/elements/StylePropertiesSection.ts +32 -5
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +227 -162
  58. package/front_end/panels/event_listeners/EventListenersView.ts +11 -8
  59. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  60. package/front_end/panels/network/NetworkDataGridNode.ts +3 -1
  61. package/front_end/panels/network/RequestConditionsDrawer.ts +1 -1
  62. package/front_end/panels/network/RequestPayloadView.ts +12 -5
  63. package/front_end/panels/profiler/HeapProfileView.ts +28 -6
  64. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +7 -3
  65. package/front_end/panels/protocol_monitor/JSONEditor.ts +0 -1
  66. package/front_end/panels/recorder/RecorderPanel.ts +2 -2
  67. package/front_end/panels/settings/SettingsScreen.ts +1 -1
  68. package/front_end/panels/sources/NavigatorView.ts +105 -45
  69. package/front_end/panels/sources/ScopeChainSidebarPane.ts +8 -5
  70. package/front_end/panels/sources/SourcesNavigator.ts +421 -213
  71. package/front_end/panels/sources/SourcesPanel.ts +6 -4
  72. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +10 -5
  73. package/front_end/panels/sources/sources-meta.ts +4 -15
  74. package/front_end/panels/sources/sourcesNavigator.css +5 -0
  75. package/front_end/panels/timeline/ModificationsManager.ts +0 -2
  76. package/front_end/panels/timeline/README.md +2 -0
  77. package/front_end/panels/timeline/TimelineCommentController.ts +132 -30
  78. package/front_end/panels/timeline/TimelinePanel.ts +4 -0
  79. package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
  80. package/front_end/panels/timeline/utils/Treemap.ts +8 -0
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/ui/comments/CommentAnchorResolver.ts +3 -0
  83. package/front_end/ui/comments/CommentOverlayManager.ts +2 -4
  84. package/front_end/ui/components/lists/List.ts +14 -1
  85. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  86. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +23 -14
  87. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +3 -0
  88. package/front_end/ui/legacy/Toolbar.ts +0 -1
  89. package/front_end/ui/legacy/Treeoutline.ts +7 -2
  90. package/front_end/ui/legacy/UIUtils.ts +4 -1
  91. package/front_end/ui/legacy/components/inline_editor/Swatches.ts +19 -6
  92. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +20 -11
  93. package/front_end/ui/legacy/components/source_frame/XMLView.ts +27 -7
  94. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  95. package/package.json +1 -1
@@ -56,7 +56,7 @@ import {getCssDeclarationAsJavascriptProperty} from './StylePropertyUtils.js';
56
56
  import type {StylesContainer} from './StylesContainer.js';
57
57
  import {CSSPropertyPrompt, REGISTERED_PROPERTY_SECTION_NAME} from './StylesSidebarPane.js';
58
58
 
59
- const {html, nothing, render, Directives: {classMap, ref}} = Lit;
59
+ const {html, nothing, render, Directives: {classMap, ref, until}} = Lit;
60
60
  const ASTUtils = SDK.CSSPropertyParser.ASTUtils;
61
61
  const FlexboxEditor = ElementsComponents.StylePropertyEditor.FlexboxEditor;
62
62
  const GridEditor = ElementsComponents.StylePropertyEditor.GridEditor;
@@ -129,6 +129,11 @@ const UIStrings = {
129
129
  * @description Title of the button that opens the CSS position-area editor in the Styles tab of the Elements panel.
130
130
  */
131
131
  positionAreaEditorButton: 'Open `position-area` editor',
132
+ /**
133
+ * @description Tooltip text for an icon that opens the shadow editor. The shadow editor is a tool
134
+ * which allows the user to edit CSS shadow properties.
135
+ */
136
+ openShadowEditor: 'Open shadow editor',
132
137
  /**
133
138
  * @description A context menu item in the Styles tab of the Elements panel to copy CSS declaration as JavaScript property.
134
139
  */
@@ -205,33 +210,32 @@ export class EnvFunctionRenderer extends EnvFunctionRendererBase {
205
210
  readonly computedStyleExtraFields: Protocol.CSS.ComputedStyleExtraFields|null) {
206
211
  super();
207
212
  }
208
- override render(match: SDK.CSSPropertyParserMatchers.EnvFunctionMatch,
209
- context: RenderingContext): Node[]|Lit.LitTemplate {
213
+ override render(match: SDK.CSSPropertyParserMatchers.EnvFunctionMatch, context: RenderingContext): Lit.LitTemplate {
210
214
  const [, fallbackNodes] = ASTUtils.callArgs(match.node);
211
215
  if (match.value) {
212
216
  const substitution = context.tracing?.substitution();
213
217
  if (substitution) {
214
218
  if (match.varNameIsValid) {
215
- return [document.createTextNode(match.value)];
219
+ return html`${match.value}`;
216
220
  }
217
221
  return Renderer.render(fallbackNodes, substitution.renderingContext(context)).nodes;
218
222
  }
219
223
  }
220
224
 
221
- const span = document.createElement('span');
222
225
  const func =
223
226
  this.treeElement?.getTracingTooltip(
224
227
  'env', match.node, this.matchedStyles, this.computedStyles, this.computedStyleExtraFields, context) ??
225
228
  'env';
226
229
  const valueClass = classMap({'inactive-value': !match.varNameIsValid});
227
230
  const fallbackClass = classMap({'inactive-value': match.varNameIsValid});
228
- render(
229
- html`${func}(<span class=${valueClass}>${match.varName}</span>${
230
- fallbackNodes ?
231
- html`, <span class=${fallbackClass}>${Renderer.render(fallbackNodes, context).nodes}</span>` :
232
- nothing})`,
233
- span, {host: span});
234
- return [span];
231
+ // clang-format off
232
+ return html`<span>${
233
+ func}(<span class=${valueClass}>${match.varName}</span>${
234
+ fallbackNodes
235
+ ? html`, <span class=${fallbackClass}>${Renderer.render(fallbackNodes, context).nodes}</span>`
236
+ : nothing
237
+ })</span>`;
238
+ // clang-format on
235
239
  }
236
240
  }
237
241
  const FlexGridRendererBase: RendererBase<SDK.CSSPropertyParserMatchers.FlexGridGridLanesMatch> =
@@ -254,7 +258,7 @@ export class FlexGridRenderer extends FlexGridRendererBase {
254
258
  return children;
255
259
  }
256
260
  const key =
257
- `${this.#treeElement.section().getSectionIdx()}_${this.#treeElement.section().nextEditorTriggerButtonIdx}`;
261
+ `${this.#treeElement.section().getSectionIdx()}_${this.#treeElement.section().nextEditorTriggerButtonIdx++}`;
258
262
 
259
263
  function getEditorClass(layoutType: SDK.CSSPropertyParserMatchers.LayoutType): typeof FlexboxEditor|
260
264
  typeof GridEditor|typeof GridLanesEditor {
@@ -292,17 +296,9 @@ export class FlexGridRenderer extends FlexGridRendererBase {
292
296
 
293
297
  const button = StyleEditorWidget.createTriggerButton(
294
298
  this.#stylesContainer, this.#treeElement.section(), getEditorClass(match.layoutType),
295
- getButtonTitle(match.layoutType), key);
296
- button.tabIndex = -1;
297
- button.setAttribute('jslog', `${VisualLogging.showStyleEditor().track({click: true}).context(match.layoutType)}`);
298
- this.#treeElement.section().nextEditorTriggerButtonIdx++;
299
- button.addEventListener('click', () => {
300
- Host.userMetrics.swatchActivated(getSwatchType(match.layoutType));
301
- });
302
- const helper = this.#stylesContainer.swatchPopoverHelper();
303
- if (helper.isShowing(StyleEditorWidget.instance()) && StyleEditorWidget.instance().getTriggerKey() === key) {
304
- helper.setAnchorElement(button);
305
- }
299
+ getButtonTitle(match.layoutType), key, match.layoutType, () => {
300
+ Host.userMetrics.swatchActivated(getSwatchType(match.layoutType));
301
+ });
306
302
  return html`${children}${button}`;
307
303
  }
308
304
  }
@@ -321,20 +317,20 @@ export class CSSWideKeywordRenderer extends CSSWideKeywordRendererBase {
321
317
  }
322
318
 
323
319
  override render(match: SDK.CSSPropertyParserMatchers.CSSWideKeywordMatch,
324
- context: RenderingContext): Node[]|Lit.LitTemplate {
320
+ context: RenderingContext): Lit.LitTemplate {
325
321
  const resolvedProperty = match.resolveProperty();
326
322
  if (!resolvedProperty) {
327
- return [document.createTextNode(match.text)];
323
+ return html`${match.text}`;
328
324
  }
329
325
 
330
- const swatch = new InlineEditor.LinkSwatch.LinkSwatch();
331
- swatch.data = {
326
+ const swatch = html`<devtools-link-swatch .data=${{
332
327
  text: match.text,
333
328
  tooltip: resolvedProperty ? undefined : {title: i18nString(UIStrings.sIsNotDefined, {PH1: match.text})},
334
329
  isDefined: Boolean(resolvedProperty),
335
330
  onLinkActivate: () => resolvedProperty && this.#stylesContainer.jumpToDeclaration(resolvedProperty),
336
331
  jslogContext: 'css-wide-keyword-link',
337
- };
332
+ }
333
+ }></devtools-link-swatch>`;
338
334
 
339
335
  if (SDK.CSSMetadata.cssMetadata().isColorAwareProperty(resolvedProperty.name) ||
340
336
  SDK.CSSMetadata.cssMetadata().isCustomProperty(resolvedProperty.name)) {
@@ -344,11 +340,11 @@ export class CSSWideKeywordRenderer extends CSSWideKeywordRendererBase {
344
340
  new ColorRenderer(this.#stylesContainer, this.#treeElement).renderColorSwatch(color, match, context);
345
341
  return this.#treeElement?.editable() ?
346
342
  html`<devtools-widget ${widget(EditableContent)}>${colorSwatch}${swatch}</devtools-widget>` :
347
- [colorSwatch, swatch];
343
+ html`${colorSwatch}${swatch}`;
348
344
  }
349
345
  }
350
346
 
351
- return [swatch];
347
+ return swatch;
352
348
  }
353
349
  }
354
350
 
@@ -389,8 +385,7 @@ export class VariableRenderer extends VariableRendererBase {
389
385
  this.#computedStyleExtraFields = computedStyleExtraFields;
390
386
  }
391
387
 
392
- override render(match: SDK.CSSPropertyParserMatchers.VariableMatch,
393
- context: RenderingContext): Node[]|Lit.LitTemplate {
388
+ override render(match: SDK.CSSPropertyParserMatchers.VariableMatch, context: RenderingContext): Lit.LitTemplate {
394
389
  if (this.#treeElement?.property.ownerStyle.parentRule instanceof SDK.CSSRule.CSSFunctionRule) {
395
390
  return Renderer.render(ASTUtils.children(match.node), context).nodes;
396
391
  }
@@ -400,7 +395,6 @@ export class VariableRenderer extends VariableRendererBase {
400
395
  const computedValue = variableValue ?? match.fallbackValue();
401
396
  const onLinkActivate = (name: string): void =>
402
397
  handleVarDefinitionActivate(declaration ?? name, this.#stylesContainer);
403
- const varSwatch = document.createElement('span');
404
398
 
405
399
  const substitution = context.tracing?.substitution({match, context});
406
400
  if (substitution) {
@@ -429,34 +423,25 @@ export class VariableRenderer extends VariableRendererBase {
429
423
  const tooltipId = this.#treeElement?.getTooltipId('custom-property-var');
430
424
  const tooltip = tooltipId ? {tooltipId} : undefined;
431
425
  // clang-format off
432
- render(html`
433
- <span data-title=${computedValue || ''}
434
- jslog=${VisualLogging.link('css-variable').track({click: true, hover: true})}>
435
- ${varCall ?? 'var'}(
436
- <devtools-link-swatch class=css-var-link .data=${{
437
- tooltip,
438
- text: match.name,
439
- isDefined: computedValue !== null && !fromFallback,
440
- onLinkActivate,
441
- }}>
442
- </devtools-link-swatch>
443
- ${renderedFallback ? html`, ${renderedFallback.nodes}` : nothing})
444
- </span>
445
- ${tooltipId ? html`
446
- <devtools-tooltip
426
+ const varSwatch = html`<span
427
+ data-title=${computedValue || ''}
428
+ jslog=${VisualLogging.link('css-variable').track({click: true, hover: true})}>${
429
+ varCall ?? 'var'}(<devtools-link-swatch class=css-var-link .data=${{
430
+ tooltip,
431
+ text: match.name,
432
+ isDefined: computedValue !== null && !fromFallback,
433
+ onLinkActivate,
434
+ }}></devtools-link-swatch>${
435
+ renderedFallback ? html`, ${renderedFallback.nodes}` : nothing})</span>${
436
+ tooltipId ? html`<devtools-tooltip
447
437
  id=${tooltipId}
448
438
  variant=rich
449
- jslogContext=elements.css-var
450
- >
451
- ${tooltipContents}
452
- </devtools-tooltip>
453
- ` : ''}
454
- `, varSwatch);
439
+ jslogContext=elements.css-var>${tooltipContents}</devtools-tooltip>` : nothing}`;
455
440
  // clang-format on
456
441
 
457
442
  const color = computedValue && Common.Color.parse(computedValue);
458
443
  if (!color) {
459
- return [varSwatch];
444
+ return varSwatch;
460
445
  }
461
446
 
462
447
  const colorSwatch =
@@ -472,7 +457,7 @@ export class VariableRenderer extends VariableRendererBase {
472
457
 
473
458
  return this.#treeElement?.editable() ?
474
459
  html`<devtools-widget ${widget(EditableContent)}>${colorSwatch}${varSwatch}</devtools-widget>` :
475
- [colorSwatch, varSwatch];
460
+ html`${colorSwatch}${varSwatch}`;
476
461
  }
477
462
  }
478
463
 
@@ -492,11 +477,9 @@ export class VariableNameRenderer extends VariableNameRendererBase {
492
477
  this.#matchedStyles = matchedStyles;
493
478
  }
494
479
 
495
- override render(match: SDK.CSSPropertyParserMatchers.VariableNameMatch, _context: RenderingContext): Node[] {
496
- const varSwatch = document.createElement('span');
480
+ override render(match: SDK.CSSPropertyParserMatchers.VariableNameMatch, _context: RenderingContext): Lit.LitTemplate {
497
481
  if (this.#treeElement?.property.ownerStyle.parentRule instanceof SDK.CSSRule.CSSFunctionRule) {
498
- render(html`${match.text}`, varSwatch);
499
- return [varSwatch];
482
+ return html`${match.text}`;
500
483
  }
501
484
 
502
485
  const {declaration, value: variableValue} = match.resolveVariable() ?? {};
@@ -509,27 +492,16 @@ export class VariableNameRenderer extends VariableNameRendererBase {
509
492
  const tooltipId = this.#treeElement?.getTooltipId('custom-property-var');
510
493
  const tooltip = tooltipId ? {tooltipId} : undefined;
511
494
  // clang-format off
512
- render(html`
513
- <devtools-link-swatch class=css-var-link .data=${{
514
- tooltip,
515
- text: match.text,
516
- isDefined,
517
- onLinkActivate,
518
- }}>
519
- </devtools-link-swatch>
520
- ${tooltipId ? html`
521
- <devtools-tooltip
522
- id=${tooltipId}
523
- variant=rich
524
- jslogContext=elements.css-var
525
- >
526
- ${tooltipContents}
527
- </devtools-tooltip>
528
- ` : ''}
529
- `, varSwatch);
495
+ return html`<devtools-link-swatch class=css-var-link .data=${{
496
+ tooltip,
497
+ text: match.text,
498
+ isDefined,
499
+ onLinkActivate,
500
+ }}></devtools-link-swatch>${tooltipId ? html`<devtools-tooltip
501
+ id=${tooltipId}
502
+ variant=rich
503
+ jslogContext=elements.css-var>${tooltipContents}</devtools-tooltip>` : nothing}`;
530
504
  // clang-format on
531
-
532
- return [varSwatch];
533
505
  }
534
506
  }
535
507
 
@@ -555,8 +527,7 @@ export class AttributeRenderer extends AttributeRendererBase {
555
527
  this.#computedStyleExtraFields = computedStyleExtraFields;
556
528
  }
557
529
 
558
- override render(match: SDK.CSSPropertyParserMatchers.AttributeMatch,
559
- context: RenderingContext): Node[]|Lit.LitTemplate {
530
+ override render(match: SDK.CSSPropertyParserMatchers.AttributeMatch, context: RenderingContext): Lit.LitTemplate {
560
531
  if (this.#treeElement?.property.ownerStyle.parentRule instanceof SDK.CSSRule.CSSFunctionRule) {
561
532
  return Renderer.render(ASTUtils.children(match.node), context).nodes;
562
533
  }
@@ -570,7 +541,6 @@ export class AttributeRenderer extends AttributeRendererBase {
570
541
  const typeClass = typeError ? 'inactive' : '';
571
542
  const fallbackClass = fromFallback ? '' : 'inactive';
572
543
  const computedValue = attributeValue ?? match.fallbackValue();
573
- const varSwatch = document.createElement('span');
574
544
 
575
545
  const substitution = context.tracing?.substitution({match, context});
576
546
  if (substitution) {
@@ -606,28 +576,27 @@ export class AttributeRenderer extends AttributeRendererBase {
606
576
  const tooltipId = attributeMissing ? undefined : this.#treeElement?.getTooltipId('custom-attribute');
607
577
  const tooltip = tooltipId ? {tooltipId} : undefined;
608
578
  // clang-format off
609
- render(html`
610
- <span data-title=${computedValue || ''}
611
- jslog=${VisualLogging.link('css-variable').track({click: true, hover: true})}
579
+ const varSwatch = html`<span
580
+ data-title=${computedValue || ''}
581
+ jslog=${VisualLogging.link('css-variable').track({click: true, hover: true})}
612
582
  >${attrCall ?? 'attr'}(<devtools-link-swatch class=${attributeClass} .data=${{
613
- tooltip,
614
- text: match.name,
615
- isDefined: true,
616
- onLinkActivate: () => this.#handleAttributeActivate(this.#matchedStyles.originatingNodeForStyle(match.style), match.name),
617
- }}></devtools-link-swatch>${tooltipId ? html`
618
- <devtools-tooltip
619
- id=${tooltipId}
620
- variant=rich
621
- jslogContext=elements.css-var
622
- >${JSON.stringify(rawValue)}</devtools-tooltip>` : nothing}${
623
- match.type ? html` <span class=${typeClass}>${match.type}</span>` : nothing
583
+ tooltip,
584
+ text: match.name,
585
+ isDefined: true,
586
+ onLinkActivate: () => this.#handleAttributeActivate(this.#matchedStyles.originatingNodeForStyle(match.style), match.name),
587
+ }}></devtools-link-swatch>${tooltipId ? html`<devtools-tooltip
588
+ id=${tooltipId}
589
+ variant=rich
590
+ jslogContext=elements.css-var
591
+ >${JSON.stringify(rawValue)}</devtools-tooltip>` : nothing}${
592
+ match.type ? html` <span class=${typeClass}>${match.type}</span>` : nothing
624
593
  }${renderedFallback ? html`, <span class=${fallbackClass}>${renderedFallback.nodes}</span>` : nothing
625
- })</span>`, varSwatch);
594
+ })</span>`;
626
595
  // clang-format on
627
596
 
628
597
  const color = computedValue && Common.Color.parse(computedValue);
629
598
  if (!color) {
630
- return [varSwatch];
599
+ return varSwatch;
631
600
  }
632
601
 
633
602
  const colorSwatch =
@@ -643,7 +612,7 @@ export class AttributeRenderer extends AttributeRendererBase {
643
612
 
644
613
  return this.#treeElement?.editable() ?
645
614
  html`<devtools-widget ${widget(EditableContent)}>${colorSwatch}${varSwatch}</devtools-widget>` :
646
- [colorSwatch, varSwatch];
615
+ html`${colorSwatch}${varSwatch}`;
647
616
  }
648
617
 
649
618
  #handleAttributeActivate(node: SDK.DOMModel.DOMNode|null, attribute: string): void {
@@ -695,40 +664,32 @@ export class RelativeColorChannelRenderer extends RelativeColorChannelRendererBa
695
664
  super();
696
665
  this.#treeElement = treeElement;
697
666
  }
698
- override render(match: SDK.CSSPropertyParserMatchers.RelativeColorChannelMatch, context: RenderingContext): Node[] {
667
+ override render(match: SDK.CSSPropertyParserMatchers.RelativeColorChannelMatch,
668
+ context: RenderingContext): Lit.LitTemplate {
699
669
  const color = context.findParent(match.node, SDK.CSSPropertyParserMatchers.ColorMatch);
700
670
  if (!color?.relativeColor) {
701
- return [document.createTextNode(match.text)];
671
+ return html`${match.text}`;
702
672
  }
703
673
 
704
674
  const value = match.getColorChannelValue(color.relativeColor);
705
675
 
706
676
  if (value === null) {
707
- return [document.createTextNode(match.text)];
677
+ return html`${match.text}`;
708
678
  }
709
679
 
710
- const evaluation =
711
- context.tracing?.applyEvaluation([], () => ({placeholder: [document.createTextNode(value.toFixed(3))]}));
680
+ const evaluation = context.tracing?.applyEvaluation([], () => ({placeholder: html`${value.toFixed(3)}`}));
712
681
  if (evaluation) {
713
682
  return evaluation;
714
683
  }
715
684
 
716
- const span = document.createElement('span');
717
- span.append(match.text);
718
685
  const tooltipId = this.#treeElement?.getTooltipId('relative-color-channel');
719
686
  if (!tooltipId) {
720
- return [span];
687
+ return html`<span>${match.text}</span>`;
721
688
  }
722
- span.setAttribute('aria-details', tooltipId);
723
- const tooltip = new Tooltips.Tooltip.Tooltip({
724
- id: tooltipId,
725
- variant: 'rich',
726
- anchor: span,
727
- jslogContext: 'elements.relative-color-channel',
728
- });
729
- tooltip.append(value.toFixed(3));
730
-
731
- return [span, tooltip];
689
+ return html`<span aria-details=${tooltipId}>${match.text}</span><devtools-tooltip
690
+ id=${tooltipId}
691
+ variant=rich
692
+ jslogContext=elements.relative-color-channel>${value.toFixed(3)}</devtools-tooltip>`;
732
693
  }
733
694
  }
734
695
 
@@ -916,25 +877,28 @@ export class LightDarkColorRenderer extends LightDarkColorRendererBase {
916
877
  this.#matchedStyles = matchedStyles;
917
878
  }
918
879
 
880
+ #renderContent(match: SDK.CSSPropertyParserMatchers.LightDarkColorMatch, light: Lit.LitTemplate,
881
+ dark: Lit.LitTemplate, activeColor?: CodeMirror.SyntaxNode[]): Lit.LitTemplate {
882
+ const lightClasses = classMap({'inactive-value': activeColor === match.dark});
883
+ const darkClasses = classMap({'inactive-value': activeColor === match.light});
884
+ return html`<span>light-dark(<span class=${lightClasses}>${light}</span>, <span class=${darkClasses}>${
885
+ dark}</span>)</span>`;
886
+ }
887
+
919
888
  override render(match: SDK.CSSPropertyParserMatchers.LightDarkColorMatch,
920
- context: RenderingContext): Node[]|Lit.LitTemplate {
921
- const content = document.createElement('span');
922
- content.appendChild(document.createTextNode('light-dark('));
923
- const light = content.appendChild(document.createElement('span'));
924
- content.appendChild(document.createTextNode(', '));
925
- const dark = content.appendChild(document.createElement('span'));
926
- content.appendChild(document.createTextNode(')'));
927
- const {cssControls: lightControls} = Renderer.renderInto(match.light, context, light);
928
- const {cssControls: darkControls} = Renderer.renderInto(match.dark, context, dark);
889
+ context: RenderingContext): Lit.LitTemplate {
890
+ const {nodes: light, cssControls: lightControls} = Renderer.render(match.light, context);
891
+ const {nodes: dark, cssControls: darkControls} = Renderer.render(match.dark, context);
892
+ const content = this.#renderContent(match, light, dark);
929
893
 
930
894
  if (context.matchedResult.hasUnresolvedSubstitutions(match.node)) {
931
- return [content];
895
+ return content;
932
896
  }
933
897
 
934
898
  const color = Common.Color.parse(
935
899
  context.matchedResult.getComputedTextRange(match.light[0], match.light[match.light.length - 1]));
936
900
  if (!color) {
937
- return [content];
901
+ return content;
938
902
  }
939
903
 
940
904
  // Pass an undefined color here to insert a placeholder swatch that will be filled in from the async
@@ -942,36 +906,33 @@ export class LightDarkColorRenderer extends LightDarkColorRendererBase {
942
906
  const colorSwatch =
943
907
  new ColorRenderer(this.#stylesContainer, this.#treeElement).renderColorSwatch(undefined, match, context);
944
908
  context.addControl('color', colorSwatch);
945
- void this.applyColorScheme(match, context, colorSwatch, light, dark, lightControls, darkControls);
909
+ const styledContent =
910
+ until(this.applyColorScheme(match, context, colorSwatch, light, dark, lightControls, darkControls), content);
946
911
 
947
912
  return this.#treeElement?.editable() ?
948
- html`<devtools-widget ${widget(EditableContent)}>${colorSwatch}${content}</devtools-widget>` :
949
- [colorSwatch, content];
913
+ html`<devtools-widget ${widget(EditableContent)}>${colorSwatch}${styledContent}</devtools-widget>` :
914
+ html`${colorSwatch}${styledContent}`;
950
915
  }
951
916
 
952
917
  async applyColorScheme(match: SDK.CSSPropertyParserMatchers.LightDarkColorMatch, context: RenderingContext,
953
- colorSwatch: InlineEditor.ColorSwatch.ColorSwatch, light: HTMLSpanElement,
954
- dark: HTMLSpanElement, lightControls: CSSControlMap,
955
- darkControls: CSSControlMap): Promise<void> {
918
+ colorSwatch: InlineEditor.ColorSwatch.ColorSwatch, light: Lit.LitTemplate,
919
+ dark: Lit.LitTemplate, lightControls: CSSControlMap,
920
+ darkControls: CSSControlMap): Promise<Lit.LitTemplate> {
956
921
  const activeColor = await this.#activeColor(match);
957
- if (context.signal?.aborted) {
958
- return;
959
- }
960
- if (!activeColor) {
961
- return;
922
+ if (context.signal?.aborted || !activeColor) {
923
+ return this.#renderContent(match, light, dark);
962
924
  }
963
925
 
964
926
  const activeColorSwatches = (activeColor === match.light ? lightControls : darkControls).get('color');
965
927
  activeColorSwatches?.forEach(
966
928
  swatch => swatch.addEventListener(
967
929
  InlineEditor.ColorSwatch.ColorChangedEvent.eventName, ev => colorSwatch.setColor(ev.data.color)));
968
- const inactiveColor = (activeColor === match.light) ? dark : light;
969
930
  const colorText = context.matchedResult.getComputedTextRange(activeColor[0], activeColor[activeColor.length - 1]);
970
931
  const color = colorText && Common.Color.parse(colorText);
971
- inactiveColor.classList.add('inactive-value');
972
932
  if (color) {
973
933
  colorSwatch.renderColor(color);
974
934
  }
935
+ return this.#renderContent(match, light, dark, activeColor);
975
936
  }
976
937
 
977
938
  // Returns the syntax node group corresponding the active color scheme:
@@ -1609,7 +1570,7 @@ export class ShadowModel implements InlineEditor.CSSShadowEditor.CSSShadowModel
1609
1570
  return parts;
1610
1571
  }
1611
1572
 
1612
- renderContents(span: HTMLSpanElement): void {
1573
+ renderContents(): Lit.LitTemplate {
1613
1574
  const parts: Array<string|Lit.LitTemplate> = [];
1614
1575
  for (const part of this.#parts()) {
1615
1576
  if (parts.length > 0) {
@@ -1617,7 +1578,7 @@ export class ShadowModel implements InlineEditor.CSSShadowEditor.CSSShadowModel
1617
1578
  }
1618
1579
  parts.push(typeof part === 'string' ? part : Renderer.render(part.node, part.context).nodes);
1619
1580
  }
1620
- render(html`${parts}`, span);
1581
+ return html`${parts}`;
1621
1582
  }
1622
1583
  // The text of the shadow as `renderContents` renders it, including the changes that renderers recorded for its parts.
1623
1584
  text(): string {
@@ -1632,6 +1593,85 @@ export class ShadowModel implements InlineEditor.CSSShadowEditor.CSSShadowModel
1632
1593
  }
1633
1594
  }
1634
1595
 
1596
+ interface ShadowWidgetViewInput {
1597
+ model: ShadowModel;
1598
+ showPopover: (swatch: InlineEditor.Swatches.CSSShadowSwatch) => void;
1599
+ }
1600
+
1601
+ type ShadowWidgetView = (input: ShadowWidgetViewInput, output: undefined, target: HTMLElement) => void;
1602
+
1603
+ const SHADOW_WIDGET_DEFAULT_VIEW: ShadowWidgetView = (input, _output, target) => {
1604
+ // clang-format off
1605
+ render(html`<css-shadow-swatch
1606
+ role="button"
1607
+ .cssShadowModel=${input.model}
1608
+ title=${i18nString(UIStrings.openShadowEditor)}
1609
+ jslog=${VisualLogging.showStyleEditor('css-shadow').track({click: true})}
1610
+ @click=${function(this: InlineEditor.Swatches.CSSShadowSwatch, event: Event) {
1611
+ event.consume(true);
1612
+ input.showPopover(this);
1613
+ }}
1614
+ @keydown=${function(this: InlineEditor.Swatches.CSSShadowSwatch, event: KeyboardEvent) {
1615
+ if (!event.isComposing && Platform.KeyboardUtilities.isEnterOrSpaceKey(event)) {
1616
+ event.consume(true);
1617
+ input.showPopover(this);
1618
+ }
1619
+ }}
1620
+ @mousedown=${(event: Event) => event.consume()}
1621
+ ></css-shadow-swatch>${input.model.renderContents()}`, target);
1622
+ // clang-format on
1623
+ };
1624
+
1625
+ class ShadowWidget extends UI.Widget.Widget {
1626
+ readonly #view: ShadowWidgetView;
1627
+ #model?: ShadowModel;
1628
+ #treeElement?: StylePropertyTreeElement;
1629
+ #popoverHelper?: ShadowSwatchPopoverHelper;
1630
+
1631
+ constructor(element?: HTMLElement, view: ShadowWidgetView = SHADOW_WIDGET_DEFAULT_VIEW) {
1632
+ super(element);
1633
+ this.element.classList.remove('vbox', 'flex-auto', 'widget');
1634
+ this.#view = view;
1635
+ }
1636
+
1637
+ set model(model: ShadowModel) {
1638
+ if (this.#model === model) {
1639
+ return;
1640
+ }
1641
+ this.#model = model;
1642
+ this.performUpdate();
1643
+ }
1644
+
1645
+ set treeElement(treeElement: StylePropertyTreeElement) {
1646
+ if (this.#treeElement === treeElement) {
1647
+ return;
1648
+ }
1649
+ this.#treeElement = treeElement;
1650
+ this.#popoverHelper =
1651
+ new ShadowSwatchPopoverHelper(treeElement, treeElement.stylesContainer().swatchPopoverHelper());
1652
+ this.#popoverHelper.addEventListener(ShadowEvents.SHADOW_CHANGED, () => {
1653
+ this.performUpdate();
1654
+ void treeElement.applyStyleText(treeElement.renderedPropertyText(), false);
1655
+ });
1656
+ this.performUpdate();
1657
+ }
1658
+
1659
+ override performUpdate(): void {
1660
+ if (!this.#model || !this.#popoverHelper) {
1661
+ return;
1662
+ }
1663
+ const popoverHelper = this.#popoverHelper;
1664
+ this.#view({
1665
+ model: this.#model,
1666
+ showPopover: swatch => {
1667
+ Host.userMetrics.swatchActivated(Host.UserMetrics.SwatchType.SHADOW);
1668
+ popoverHelper.showPopover(swatch);
1669
+ },
1670
+ },
1671
+ undefined, this.contentElement);
1672
+ }
1673
+ }
1674
+
1635
1675
  const ShadowRendererBase: RendererBase<SDK.CSSPropertyParserMatchers.ShadowMatch> =
1636
1676
  rendererBase(SDK.CSSPropertyParserMatchers.ShadowMatch);
1637
1677
  // clang-format off
@@ -1762,22 +1802,8 @@ export class ShadowRenderer extends ShadowRendererBase {
1762
1802
  continue;
1763
1803
  }
1764
1804
 
1765
- const swatch = new InlineEditor.Swatches.CSSShadowSwatch(model);
1766
- swatch.setAttribute('jslog', `${VisualLogging.showStyleEditor('css-shadow').track({click: true})}`);
1767
- swatch.iconElement().addEventListener('click', () => {
1768
- Host.userMetrics.swatchActivated(Host.UserMetrics.SwatchType.SHADOW);
1769
- });
1770
-
1771
- const contents = document.createElement('span');
1772
- model.renderContents(contents);
1773
- const popoverHelper = new ShadowSwatchPopoverHelper(
1774
- this.#treeElement, this.#treeElement.stylesContainer().swatchPopoverHelper(), swatch);
1775
- const treeElement = this.#treeElement;
1776
- popoverHelper.addEventListener(ShadowEvents.SHADOW_CHANGED, () => {
1777
- model.renderContents(contents);
1778
- void treeElement.applyStyleText(treeElement.renderedPropertyText(), false);
1779
- });
1780
- result.push(swatch, contents);
1805
+ result.push(
1806
+ html`<devtools-widget ${widget(ShadowWidget, {model, treeElement: this.#treeElement})}></devtools-widget>`);
1781
1807
  texts.push(() =>
1782
1808
  isImportant ? `${model.text()} ${nestedEdits.textRange(isImportant, isImportant)}` : model.text());
1783
1809
 
@@ -2178,6 +2204,7 @@ export class PositionAreaRenderer extends PositionAreaRendererBase {
2178
2204
  edit: (value: string) => void,
2179
2205
  key?: string,
2180
2206
  };
2207
+ static readonly #EDITABLE_PROPERTIES: readonly string[] = ['align-self', 'justify-self'];
2181
2208
  readonly #treeElement: StylePropertyTreeElement|null;
2182
2209
  readonly #stylesContainer: StylesContainer;
2183
2210
  constructor(stylesContainer: StylesContainer, treeElement: StylePropertyTreeElement|null) {
@@ -2207,10 +2234,16 @@ export class PositionAreaRenderer extends PositionAreaRendererBase {
2207
2234
  }
2208
2235
  }
2209
2236
 
2210
- return activeSection?.propertiesTreeOutline?.rootElement().children().find(
2237
+ return this.#findPropertyInSection(activeSection, propertyName);
2238
+ }
2239
+
2240
+ #findPropertyInSection(section: StylePropertiesSection|undefined, propertyName: string): StylePropertyTreeElement
2241
+ |undefined {
2242
+ return section?.propertiesTreeOutline?.rootElement().children().find(
2211
2243
  (child): child is StylePropertyTreeElement =>
2212
2244
  child instanceof StylePropertyTreeElement && child.property.name === propertyName);
2213
2245
  }
2246
+
2214
2247
  override render(match: SDK.CSSPropertyParserMatchers.PositionAreaMatch, context: RenderingContext): Lit.LitTemplate {
2215
2248
  const children = Renderer.render(ASTUtils.siblings(ASTUtils.declValue(match.node)), context).nodes;
2216
2249
  // The text of the match is the value as authored, which may contain comments or var()s that resolve to the area.
@@ -2234,6 +2267,7 @@ export class PositionAreaRenderer extends PositionAreaRendererBase {
2234
2267
  Boolean(PositionAreaRenderer.#active && popoverHelper.isShowing(PositionAreaRenderer.#active.editor) &&
2235
2268
  key !== undefined && PositionAreaRenderer.#active.key === key);
2236
2269
  if (isActive && PositionAreaRenderer.#active) {
2270
+ treeElement.carryOverCommittedProperty(PositionAreaRenderer.#active.treeElement);
2237
2271
  PositionAreaRenderer.#active.treeElement = treeElement;
2238
2272
  PositionAreaRenderer.#active.edit = edit;
2239
2273
  }
@@ -2249,6 +2283,17 @@ export class PositionAreaRenderer extends PositionAreaRendererBase {
2249
2283
  const valueText = context.edits?.getEdit(match) ?? context.matchedResult.getComputedPropertyValueText();
2250
2284
  editor.area = InlineEditor.PositionAreaEditor.parsePositionArea(valueText) ?? undefined;
2251
2285
 
2286
+ const initialProperties = new Map<string, {value: string, section: StylePropertiesSection}>();
2287
+ for (const propertyName of PositionAreaRenderer.#EDITABLE_PROPERTIES) {
2288
+ const target = this.#findTreeElementForProperty(propertyName);
2289
+ if (target) {
2290
+ initialProperties.set(propertyName, {
2291
+ value: target.property.value,
2292
+ section: target.section(),
2293
+ });
2294
+ }
2295
+ }
2296
+
2252
2297
  const updateProperty = (propertyName: string, computedStyle: Map<string, string>|null): void => {
2253
2298
  const matchedStyles = active.treeElement.matchedStyles();
2254
2299
  const resolved = matchedStyles.resolveProperty(propertyName, active.treeElement.property.ownerStyle);
@@ -2319,6 +2364,22 @@ export class PositionAreaRenderer extends PositionAreaRendererBase {
2319
2364
  editor.removeEventListener(InlineEditor.PositionAreaEditor.Events.PROPERTY_CHANGED, onPropertyChanged);
2320
2365
  const propertyText = commitEdit ? active.treeElement.renderedPropertyText() : originalPropertyText || '';
2321
2366
  void active.treeElement.applyStyleText(propertyText, true);
2367
+ const node = this.#stylesContainer.node() ?? active.treeElement.matchedStyles().node();
2368
+ for (const propertyName of PositionAreaRenderer.#EDITABLE_PROPERTIES) {
2369
+ const originalTarget = initialProperties.get(propertyName);
2370
+ const targetSection = originalTarget?.section ?? active.treeElement.section();
2371
+ const target = this.#findPropertyInSection(targetSection, propertyName);
2372
+ const oldValue = originalTarget?.value ?? '';
2373
+ const newValue = target?.property.value ?? '';
2374
+ const ruleHeader = targetSection.headerText();
2375
+ Elements.StyleChanges.trackStylePropertyEdit(this.#stylesContainer.changeTracker, node, {
2376
+ ruleHeader,
2377
+ oldName: oldValue ? propertyName : '',
2378
+ oldValue,
2379
+ newName: newValue ? propertyName : '',
2380
+ newValue,
2381
+ });
2382
+ }
2322
2383
  if (PositionAreaRenderer.#active === active) {
2323
2384
  PositionAreaRenderer.#active = undefined;
2324
2385
  }
@@ -2545,6 +2606,10 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2545
2606
  super.onunbind();
2546
2607
  }
2547
2608
 
2609
+ carryOverCommittedProperty(previous: StylePropertyTreeElement): void {
2610
+ this.#lastCommittedProperty = previous.#lastCommittedProperty;
2611
+ }
2612
+
2548
2613
  async gridNames(): Promise<Set<string>> {
2549
2614
  if (!SDK.CSSMetadata.cssMetadata().isGridNameAwareProperty(this.name)) {
2550
2615
  return new Set();