chrome-devtools-frontend 1.0.1595925 → 1.0.1596535

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 (129) hide show
  1. package/.stylelintrc.json +3 -1
  2. package/docs/ui_engineering.md +0 -36
  3. package/front_end/core/common/Console.ts +6 -6
  4. package/front_end/core/common/Settings.ts +12 -8
  5. package/front_end/core/host/UserMetrics.ts +0 -1
  6. package/front_end/core/root/DevToolsContext.ts +13 -7
  7. package/front_end/core/root/ExperimentNames.ts +0 -1
  8. package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
  9. package/front_end/core/sdk/DOMModel.ts +206 -0
  10. package/front_end/core/sdk/FrameManager.ts +7 -8
  11. package/front_end/core/sdk/SourceMapScopesInfo.ts +1 -1
  12. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
  13. package/front_end/entrypoints/greendev_floaty/floaty.css +2 -2
  14. package/front_end/entrypoints/main/MainImpl.ts +1 -7
  15. package/front_end/foundation/Universe.ts +22 -11
  16. package/front_end/generated/InspectorBackendCommands.ts +7 -0
  17. package/front_end/generated/protocol-mapping.d.ts +16 -0
  18. package/front_end/generated/protocol-proxy-api.d.ts +25 -0
  19. package/front_end/generated/protocol.ts +71 -0
  20. package/front_end/models/ai_assistance/AiConversation.ts +6 -95
  21. package/front_end/models/ai_assistance/ConversationHandler.ts +1 -1
  22. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
  23. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
  24. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -0
  26. package/front_end/panels/ai_assistance/components/ChatMessage.ts +73 -35
  27. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +30 -6
  28. package/front_end/panels/ai_assistance/components/chatMessage.css +4 -2
  29. package/front_end/panels/application/IndexedDBModel.ts +2 -4
  30. package/front_end/panels/application/ServiceWorkersView.ts +3 -11
  31. package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
  32. package/front_end/panels/elements/ComputedStyleWidget.ts +25 -16
  33. package/front_end/panels/elements/ElementsPanel.ts +0 -1
  34. package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
  35. package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
  36. package/front_end/panels/elements/StylePropertyTreeElement.ts +27 -5
  37. package/front_end/panels/elements/StylesContainer.ts +1 -0
  38. package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
  39. package/front_end/panels/elements/nodeStackTraceWidget.css +1 -1
  40. package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
  41. package/front_end/panels/emulation/DeviceModeToolbar.ts +171 -89
  42. package/front_end/panels/greendev/GreenDevPanel.css +2 -2
  43. package/front_end/panels/issues/AffectedPermissionElementsView.ts +9 -6
  44. package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
  45. package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
  46. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +11 -3
  47. package/front_end/panels/recorder/components/StepView.ts +0 -2
  48. package/front_end/panels/recorder/components/stepView.css +13 -13
  49. package/front_end/panels/recorder/components/timelineSection.css +6 -7
  50. package/front_end/panels/settings/components/SyncSection.ts +4 -13
  51. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +2 -15
  52. package/front_end/panels/timeline/RecordingMetadata.ts +1 -1
  53. package/front_end/panels/timeline/TimelineDetailsView.ts +2 -2
  54. package/front_end/panels/timeline/TimelinePanel.ts +18 -0
  55. package/front_end/panels/timeline/TimelineUIUtils.ts +7 -4
  56. package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
  57. package/front_end/panels/timeline/components/Sidebar.ts +17 -0
  58. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
  59. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
  60. package/front_end/panels/timeline/components/Utils.ts +2 -2
  61. package/front_end/panels/timeline/components/components.ts +2 -0
  62. package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
  63. package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
  64. package/front_end/panels/timeline/timeline-meta.ts +11 -0
  65. package/front_end/panels/timeline/utils/Helpers.ts +5 -1
  66. package/front_end/panels/webauthn/webauthnPane.css +1 -1
  67. package/front_end/third_party/chromium/README.chromium +1 -1
  68. package/front_end/third_party/puppeteer/README.chromium +2 -2
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +7 -0
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -0
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +7 -0
  91. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
  92. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
  93. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
  96. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -0
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/types.d.ts +7 -0
  112. package/front_end/third_party/puppeteer/package/package.json +1 -1
  113. package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
  114. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
  115. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
  116. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
  117. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  118. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  119. package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
  120. package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
  121. package/front_end/ui/legacy/ProgressIndicator.ts +1 -1
  122. package/front_end/ui/legacy/Toolbar.ts +1 -1
  123. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +14 -7
  124. package/front_end/ui/legacy/legacy.ts +0 -2
  125. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  126. package/inspector_overlay/loadCSS.rollup.js +2 -2
  127. package/inspector_overlay/tool_source_order.css +1 -0
  128. package/package.json +1 -1
  129. package/front_end/ui/legacy/Fragment.ts +0 -234
@@ -11,7 +11,9 @@ import * as i18n from '../../../core/i18n/i18n.js';
11
11
  import type * as Platform from '../../../core/platform/platform.js';
12
12
  import * as Root from '../../../core/root/root.js';
13
13
  import * as SDK from '../../../core/sdk/sdk.js';
14
- import type {AiWidget, ComputedStyleAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
14
+ import type * as Protocol from '../../../generated/protocol.js';
15
+ import type {
16
+ AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
15
17
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
16
18
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
17
19
  import * as Marked from '../../../third_party/marked/marked.js';
@@ -24,6 +26,8 @@ import * as UI from '../../../ui/legacy/legacy.js';
24
26
  import * as Lit from '../../../ui/lit/lit.js';
25
27
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
26
28
  import * as Elements from '../../elements/elements.js';
29
+ import * as TimelineComponents from '../../timeline/components/components.js';
30
+ import * as TimelineUtils from '../../timeline/utils/utils.js';
27
31
 
28
32
  import chatMessageStyles from './chatMessage.css.js';
29
33
  import {walkthroughTitle, WalkthroughView} from './WalkthroughView.js';
@@ -278,6 +282,7 @@ export interface MessageInput {
278
282
  isExpanded: boolean,
279
283
  onToggle: (isOpen: boolean) => void,
280
284
  isInlined: boolean,
285
+ activeMessage: ModelChatMessage|null,
281
286
  };
282
287
  }
283
288
 
@@ -483,14 +488,18 @@ function renderStepDetails({
483
488
 
484
489
  function renderWalkthroughSidebarButton(
485
490
  input: ChatMessageViewInput,
486
- lastStep: Step,
491
+ steps: Step[],
487
492
  ): Lit.LitTemplate {
488
493
  const {message, walkthrough} = input;
489
- if (walkthrough.isInlined) {
494
+ const lastStep = steps.at(-1);
495
+ if (walkthrough.isInlined || !lastStep) {
490
496
  return Lit.nothing;
491
497
  }
498
+
499
+ const hasOneStepWithWidget = steps.some(step => step.widgets?.length);
492
500
  const title = walkthroughTitle({
493
501
  isLoading: input.isLoading,
502
+ hasWidgets: hasOneStepWithWidget,
494
503
  lastStep,
495
504
  });
496
505
 
@@ -505,7 +514,7 @@ function renderWalkthroughSidebarButton(
505
514
  .jslogContext=${walkthrough.isExpanded ? 'ai-hide-walkthrough-sidebar' : 'ai-show-walkthrough-sidebar'}
506
515
  data-show-walkthrough
507
516
  @click=${() => {
508
- if(walkthrough.isExpanded) {
517
+ if(walkthrough.activeMessage === input.message && walkthrough.isExpanded) {
509
518
  walkthrough.onToggle(false);
510
519
  } else {
511
520
  // Can't just toggle the visibility here; we need to ensure we
@@ -535,7 +544,7 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
535
544
  // If the walkthrough is in the sidebar, we render a button into the
536
545
  // ChatView to open it.
537
546
  const openWalkThroughSidebarButton =
538
- !input.walkthrough.isInlined ? renderWalkthroughSidebarButton(input, lastStep) : Lit.nothing;
547
+ !input.walkthrough.isInlined ? renderWalkthroughSidebarButton(input, steps) : Lit.nothing;
539
548
 
540
549
  // If there are side-effect steps, and the walkthrough is not open, we render
541
550
  // those inline so that the user can see them and approve them.
@@ -554,6 +563,9 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
554
563
 
555
564
  // If the walkthrough is inlined (narrow width screens), render it here.
556
565
  // Note that we force it open if there is a side-effect.
566
+
567
+ const isExpanded = (input.walkthrough.isExpanded && input.walkthrough.activeMessage === input.message) ||
568
+ steps.some(s => s.requestApproval);
557
569
  // clang-format off
558
570
  const walkthroughInline = input.walkthrough.isInlined ? html`
559
571
  <div class="walkthrough-container">
@@ -562,9 +574,9 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
562
574
  isLoading: input.isLoading && input.isLastMessage,
563
575
  markdownRenderer: input.markdownRenderer,
564
576
  isInlined: true,
565
- isExpanded: input.isLastMessage &&
566
- (input.walkthrough.isExpanded || steps.some(step => Boolean(step.requestApproval))),
577
+ isExpanded,
567
578
  onToggle: input.walkthrough.onToggle,
579
+ onOpen: input.walkthrough.onOpen,
568
580
  })}></devtools-widget>
569
581
  </div>
570
582
  ` : Lit.nothing;
@@ -649,37 +661,55 @@ interface WidgetMakerResponse {
649
661
  revealable: unknown;
650
662
  }
651
663
 
664
+ const computedStyleNodeCache = new Map<Protocol.DOM.BackendNodeId, SDK.DOMModel.DOMNode>();
665
+
652
666
  async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promise<WidgetMakerResponse|null> {
653
- const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
654
- if (!target) {
655
- return null;
656
- }
657
- const node = new SDK.DOMModel.DeferredDOMNode(
658
- target,
659
- widgetData.data.backendNodeId,
660
- );
661
- const resolved = await node.resolvePromise();
662
- if (!resolved) {
663
- return null;
667
+ let domNodeForId = computedStyleNodeCache.get(widgetData.data.backendNodeId);
668
+ if (!domNodeForId) {
669
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
670
+ if (!target) {
671
+ return null;
672
+ }
673
+ const node = new SDK.DOMModel.DeferredDOMNode(
674
+ target,
675
+ widgetData.data.backendNodeId,
676
+ );
677
+ const resolved = await node.resolvePromise();
678
+ if (!resolved) {
679
+ return null;
680
+ }
681
+ domNodeForId = resolved;
682
+ computedStyleNodeCache.set(widgetData.data.backendNodeId, resolved);
664
683
  }
665
- const model = new ComputedStyle.ComputedStyleModel.ComputedStyleModel(resolved);
666
- const styles = new ComputedStyle.ComputedStyleModel.ComputedStyle(resolved, widgetData.data.computedStyles);
684
+ const styles = new ComputedStyle.ComputedStyleModel.ComputedStyle(domNodeForId, widgetData.data.computedStyles);
667
685
 
668
686
  const widgetConfig = UI.Widget.widgetConfig(Elements.ComputedStyleWidget.ComputedStyleWidget, {
669
687
  nodeStyle: styles,
670
688
  matchedStyles: widgetData.data.matchedCascade,
671
689
  // This disables showing the nested traces and detailed information in the widget.
672
690
  propertyTraces: null,
673
- computedStyleModel: model,
674
691
  allowUserControl: false,
675
692
  filterText: new RegExp(widgetData.data.properties.join('|'), 'i')
676
693
  });
677
694
 
678
695
  // clang-format off
679
- const widget = html`<devtools-widget class="computed-styles-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
696
+ const widget = html`<devtools-widget class="computed-styles-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
680
697
  // clang-format on
681
698
 
682
- return {renderedWidget: widget, revealable: new Elements.ElementsPanel.NodeComputedStyles(resolved)};
699
+ return {renderedWidget: widget, revealable: new Elements.ElementsPanel.NodeComputedStyles(domNodeForId)};
700
+ }
701
+
702
+ async function makeCoreVitalsWidget(widgetData: CoreVitalsAiWidget): Promise<WidgetMakerResponse|null> {
703
+ const widgetConfig = UI.Widget.widgetConfig(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data});
704
+
705
+ // clang-format off
706
+ const widget = html`<devtools-widget class="core-vitals-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
707
+ // clang-format on
708
+
709
+ return {
710
+ renderedWidget: widget,
711
+ revealable: new TimelineUtils.Helpers.RevealableCoreVitals(widgetData.data.insightSetKey)
712
+ };
683
713
  }
684
714
 
685
715
  function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
@@ -696,15 +726,17 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
696
726
 
697
727
  // clang-format off
698
728
  return html`
699
- <div class="widget-content-container">
700
- ${response.renderedWidget}
701
- </div>
702
- <div class="widget-reveal-container">
703
- <devtools-button class="widget-reveal"
704
- .iconName=${'tab-move'}
705
- .variant=${Buttons.Button.Variant.TEXT}
706
- @click=${onReveal}
707
- >${lockedString(UIStringsNotTranslate.reveal)}</devtools-button>
729
+ <div class="widget-and-revealer-container">
730
+ <div class="widget-content-container">
731
+ ${response.renderedWidget}
732
+ </div>
733
+ <div class="widget-reveal-container">
734
+ <devtools-button class="widget-reveal"
735
+ .iconName=${'tab-move'}
736
+ .variant=${Buttons.Button.Variant.TEXT}
737
+ @click=${onReveal}
738
+ >${lockedString(UIStringsNotTranslate.reveal)}</devtools-button>
739
+ </div>
708
740
  </div>
709
741
  `;
710
742
  // clang-format on
@@ -716,10 +748,11 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
716
748
  * iterates through the `step.widgets` array. For each widget, it determines
717
749
  * the appropriate rendering logic based on the `widgetData.name`.
718
750
  *
719
- * Currently, only 'COMPUTED_STYLES' widgets are supported. For these, the
720
- * `makeComputedStyleWidget` function is called to construct the necessary
751
+ * Currently, only 'COMPUTED_STYLES' and 'CORE_VITALS' widgets are supported. For these, the
752
+ * `makeComputedStyleWidget` and `makeCoreVitalsWidget` functions are called to construct the necessary
721
753
  * data and configuration for the `Elements.ComputedStyleWidget.ComputedStyleWidget`
722
- * component. The widget is then rendered using the `<devtools-widget>`
754
+ * and `TimelineComponents.CWVMetrics.CWVMetrics`
755
+ * components. The widget is then rendered using the `<devtools-widget>`
723
756
  * custom element, which dynamically instantiates and displays the specified
724
757
  * UI.Widget subclass with the provided configuration.
725
758
  *
@@ -736,6 +769,10 @@ async function renderStepWidgets(step: Step): Promise<Lit.LitTemplate> {
736
769
  const response = await makeComputedStyleWidget(widgetData);
737
770
  return renderWidgetResponse(response);
738
771
  }
772
+ if (widgetData.name === 'CORE_VITALS') {
773
+ const response = await makeCoreVitalsWidget(widgetData);
774
+ return renderWidgetResponse(response);
775
+ }
739
776
  return Lit.nothing;
740
777
  }));
741
778
  return html`${ui}`;
@@ -994,6 +1031,7 @@ export class ChatMessage extends UI.Widget.Widget {
994
1031
  onToggle: () => {},
995
1032
  isInlined: false,
996
1033
  isExpanded: false,
1034
+ activeMessage: null,
997
1035
  };
998
1036
 
999
1037
  #suggestionsResizeObserver = new ResizeObserver(() => this.#handleSuggestionsScrollOrResize());
@@ -25,11 +25,15 @@ const UIStrings = {
25
25
  /**
26
26
  * @description Title for the walkthrough view.
27
27
  */
28
- title: 'Investigation steps',
28
+ title: 'Agent walkthrough',
29
29
  /**
30
- * @description Title for the button that shows the thinking process (walkthrough).
30
+ * @description Title for the button that shows the walkthrough when there are no widgets in the walkthrough.
31
31
  */
32
32
  showThinking: 'Show thinking',
33
+ /**
34
+ * @description Title for the button that shows the walkthrough when there are widgets in the walkthrough.
35
+ */
36
+ showAgentWalkthrough: 'Show agent walkthrough',
33
37
  } as const;
34
38
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/WalkthroughView.ts', UIStrings);
35
39
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -41,14 +45,21 @@ export interface ViewInput {
41
45
  isInlined: boolean;
42
46
  isExpanded: boolean;
43
47
  onToggle: (isOpen: boolean) => void;
48
+ onOpen: (message: ModelChatMessage) => void;
44
49
  }
45
50
 
46
51
  export function walkthroughTitle(input: {
47
52
  isLoading: boolean,
53
+ hasWidgets: boolean,
48
54
  lastStep: Step,
49
55
  }): string {
50
- const title = input.isLoading ? titleForStep(input.lastStep) : lockedString(UIStrings.showThinking);
51
- return title;
56
+ if (input.isLoading) {
57
+ return titleForStep(input.lastStep);
58
+ }
59
+ if (input.hasWidgets) {
60
+ return lockedString(UIStrings.showAgentWalkthrough);
61
+ }
62
+ return lockedString(UIStrings.showThinking);
52
63
  }
53
64
 
54
65
  function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate, steps: Step[]): Lit.LitTemplate {
@@ -61,12 +72,14 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate,
61
72
  input.onToggle((event.target as HTMLDetailsElement).open);
62
73
  }
63
74
 
75
+ const hasWidgets = steps.some(s => s.widgets?.length);
76
+
64
77
  // clang-format off
65
78
  return html`
66
79
  <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle}>
67
80
  <summary>
68
81
  ${input.isLoading ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
69
- ${walkthroughTitle({isLoading: input.isLoading, lastStep,})}
82
+ ${walkthroughTitle({isLoading: input.isLoading, lastStep, hasWidgets})}
70
83
  <devtools-icon name="chevron-down"></devtools-icon>
71
84
  </summary>
72
85
  ${stepsOutput}
@@ -88,7 +101,7 @@ function renderSidebarWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate
88
101
  <devtools-button
89
102
  .data=${{
90
103
  variant: Buttons.Button.Variant.TOOLBAR,
91
- iconName: 'right-panel-open',
104
+ iconName: 'cross',
92
105
  title: i18nString(UIStrings.close),
93
106
  jslogContext: 'close-walkthrough',
94
107
  } as Buttons.Button.ButtonData}
@@ -155,6 +168,7 @@ export class WalkthroughView extends UI.Widget.Widget {
155
168
  #isLoading = false;
156
169
  #markdownRenderer: MarkdownLitRenderer|null = null;
157
170
  #onToggle: (isOpen: boolean) => void = () => {};
171
+ #onOpen: (message: ModelChatMessage) => void = () => {};
158
172
  #isInlined = false;
159
173
  #isExpanded = false;
160
174
 
@@ -185,6 +199,15 @@ export class WalkthroughView extends UI.Widget.Widget {
185
199
  return this.#message;
186
200
  }
187
201
 
202
+ get onOpen(): (message: ModelChatMessage) => void {
203
+ return this.#onOpen;
204
+ }
205
+
206
+ set onOpen(onOpen: (message: ModelChatMessage) => void) {
207
+ this.#onOpen = onOpen;
208
+ this.requestUpdate();
209
+ }
210
+
188
211
  set message(message: ModelChatMessage|null) {
189
212
  this.#message = message;
190
213
  this.requestUpdate();
@@ -214,6 +237,7 @@ export class WalkthroughView extends UI.Widget.Widget {
214
237
  isLoading: this.#isLoading,
215
238
  markdownRenderer: this.#markdownRenderer,
216
239
  onToggle: this.#onToggle,
240
+ onOpen: this.#onOpen,
217
241
  isInlined: this.#isInlined,
218
242
  isExpanded: this.#isExpanded,
219
243
  message: this.#message,
@@ -364,10 +364,13 @@
364
364
 
365
365
  .step-widgets-wrapper {
366
366
  width: fit-content;
367
+ display: flex;
368
+ flex-direction: column;
369
+ align-items: flex-start;
370
+ gap: var(--sys-size-5);
367
371
  }
368
372
 
369
373
  .widget-reveal-container {
370
- width: 100%;
371
374
  background: var(--sys-color-surface5);
372
375
  border-bottom-right-radius: var(--sys-shape-corner-medium);
373
376
  border-bottom-left-radius: var(--sys-shape-corner-medium);
@@ -378,7 +381,6 @@
378
381
  padding: var(--sys-size-4);
379
382
  border-top-left-radius: var(--sys-shape-corner-medium);
380
383
  border-top-right-radius: var(--sys-shape-corner-medium);
381
- width: 100%;
382
384
  overflow-x: auto;
383
385
  background-color: var(--sys-color-surface3);
384
386
 
@@ -143,8 +143,7 @@ export class IndexedDBModel extends SDK.SDKModel.SDKModel<EventTypes> implements
143
143
  }
144
144
 
145
145
  for (const [storageBucketName] of this.databaseNamesByStorageKeyAndBucket.get(storageKey) || []) {
146
- const storageBucket =
147
- this.storageBucketModel?.getBucketByName(storageKey, storageBucketName ?? undefined)?.bucket;
146
+ const storageBucket = this.storageBucketModel?.getBucketByName(storageKey, storageBucketName)?.bucket;
148
147
  if (storageBucket) {
149
148
  this.removeStorageBucket(storageBucket);
150
149
  }
@@ -169,8 +168,7 @@ export class IndexedDBModel extends SDK.SDKModel.SDKModel<EventTypes> implements
169
168
  for (const [storageKey] of this.databaseNamesByStorageKeyAndBucket) {
170
169
  const storageBucketNames = this.databaseNamesByStorageKeyAndBucket.get(storageKey)?.keys() || [];
171
170
  for (const storageBucketName of storageBucketNames) {
172
- const storageBucket =
173
- this.storageBucketModel?.getBucketByName(storageKey, storageBucketName ?? undefined)?.bucket;
171
+ const storageBucket = this.storageBucketModel?.getBucketByName(storageKey, storageBucketName)?.bucket;
174
172
  if (storageBucket) {
175
173
  await this.loadDatabaseNamesByStorageBucket(storageBucket);
176
174
  }
@@ -10,6 +10,7 @@ import * as SDK from '../../core/sdk/sdk.js';
10
10
  import type * as Protocol from '../../generated/protocol.js';
11
11
  import * as NetworkForward from '../../panels/network/forward/forward.js';
12
12
  import * as Buttons from '../../ui/components/buttons/buttons.js';
13
+ import {Link} from '../../ui/kit/kit.js';
13
14
  import * as Components from '../../ui/legacy/components/utils/utils.js';
14
15
  import * as UI from '../../ui/legacy/legacy.js';
15
16
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -227,17 +228,8 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
227
228
  othersView.show(othersDiv);
228
229
  const othersSection = othersView.appendSection(i18nString(UIStrings.serviceWorkersFromOtherOrigins));
229
230
  const othersSectionRow = othersSection.appendRow();
230
- const seeOthers =
231
- UI.Fragment
232
- .html`<a class="devtools-link" role="link" tabindex="0" href="chrome://serviceworker-internals" target="_blank" style="display: inline; cursor: pointer;">${
233
- i18nString(UIStrings.seeAllRegistrations)}</a>`;
234
- seeOthers.setAttribute('jslog', `${VisualLogging.link('view-all').track({click: true})}`);
235
- self.onInvokeElement(seeOthers, event => {
236
- const rootTarget = SDK.TargetManager.TargetManager.instance().rootTarget();
237
- rootTarget &&
238
- void rootTarget.targetAgent().invoke_createTarget({url: 'chrome://serviceworker-internals?devtools'});
239
- event.consume(true);
240
- });
231
+ const seeOthers = Link.create(
232
+ 'chrome://serviceworker-internals', i18nString(UIStrings.seeAllRegistrations), undefined, 'view-all');
241
233
  othersSectionRow.appendChild(seeOthers);
242
234
 
243
235
  this.toolbar.appendToolbarItem(
@@ -104,7 +104,7 @@ const DEFAULT_VIEW: View = (input, _output, target) => {
104
104
  if (!filter?.regex) {
105
105
  return '';
106
106
  }
107
- const matches = filter.regex.exec(textContent ?? '');
107
+ const matches = filter.regex.exec(textContent);
108
108
  if (!matches?.length) {
109
109
  return '';
110
110
  }
@@ -294,7 +294,14 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
294
294
  };
295
295
 
296
296
  export class ComputedStyleWidget extends UI.Widget.VBox {
297
- #computedStyleModel?: ComputedStyleModule.ComputedStyleModel.ComputedStyleModel;
297
+ /**
298
+ * We store these because they are used when calculating the dimensions for the image preview.
299
+ * When we need to get those dimensions, we try to resolve them against the
300
+ * node fresh (in case the dimensions have changed), but if that doesn't work,
301
+ * we fallback to the precomputed ones, which helps to deal with situations
302
+ * where the node might have been removed from the DOM.
303
+ */
304
+ #storedNodeFeatures: Components.ImagePreview.PrecomputedFeatures|null = null;
298
305
  #nodeStyle: ComputedStyleModule.ComputedStyleModel.ComputedStyle|null = null;
299
306
  #matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles|null = null;
300
307
  #propertyTraces: Map<string, SDK.CSSProperty.CSSProperty[]>|null = null;
@@ -363,7 +370,8 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
363
370
  return null;
364
371
  },
365
372
  async () => {
366
- return await Components.ImagePreview.loadPrecomputedFeatures(this.#computedStyleModel?.node);
373
+ const liveFeatures = await Components.ImagePreview.loadPrecomputedFeatures(this.#nodeStyle?.node);
374
+ return liveFeatures ?? this.#storedNodeFeatures ?? undefined;
367
375
  });
368
376
 
369
377
  this.#updateView({hasMatches: true});
@@ -430,7 +438,13 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
430
438
 
431
439
  set nodeStyle(nodeStyle: ComputedStyleModule.ComputedStyleModel.ComputedStyle|null) {
432
440
  this.#nodeStyle = nodeStyle;
433
- this.requestUpdate();
441
+ if (nodeStyle) {
442
+ // Make sure we get the node features before we request an update so we
443
+ // don't run the update before we have the features fetched.
444
+ void this.#storeNodeFeatures(nodeStyle.node).then(() => this.requestUpdate());
445
+ } else {
446
+ this.requestUpdate();
447
+ }
434
448
  }
435
449
 
436
450
  get matchedStyles(): SDK.CSSMatchedStyles.CSSMatchedStyles|null {
@@ -447,13 +461,13 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
447
461
  this.requestUpdate();
448
462
  }
449
463
 
450
- get computedStyleModel(): ComputedStyleModule.ComputedStyleModel.ComputedStyleModel|undefined {
451
- return this.#computedStyleModel;
452
- }
453
-
454
- set computedStyleModel(computedStyleModel: ComputedStyleModule.ComputedStyleModel.ComputedStyleModel) {
455
- this.#computedStyleModel = computedStyleModel;
456
- this.requestUpdate();
464
+ async #storeNodeFeatures(node: SDK.DOMModel.DOMNode|null): Promise<void> {
465
+ if (node) {
466
+ const features = await Components.ImagePreview.loadPrecomputedFeatures(node);
467
+ this.#storedNodeFeatures = features ?? null;
468
+ } else {
469
+ this.#storedNodeFeatures = null;
470
+ }
457
471
  }
458
472
 
459
473
  #shouldGroupStyles(): boolean {
@@ -483,10 +497,6 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
483
497
  matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles): Promise<void> {
484
498
  this.imagePreviewPopover.hide();
485
499
  this.linkifier.reset();
486
- const cssModel = this.#computedStyleModel?.cssModel();
487
- if (!cssModel) {
488
- return;
489
- }
490
500
 
491
501
  const uniqueProperties = [...nodeStyle.computedStyle.keys()];
492
502
  uniqueProperties.sort(propertySorter);
@@ -526,8 +536,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
526
536
  matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles|null): Promise<void> {
527
537
  this.imagePreviewPopover.hide();
528
538
  this.linkifier.reset();
529
- const cssModel = this.#computedStyleModel?.cssModel();
530
- if (!nodeStyle || !matchedStyles || !cssModel) {
539
+ if (!nodeStyle || !matchedStyles) {
531
540
  this.#updateView({hasMatches: false});
532
541
  return;
533
542
  }
@@ -316,7 +316,6 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
316
316
 
317
317
  this.stylesWidget = new StylesSidebarPane(this.#computedStyleModel);
318
318
  this.#computedStyleWidget = new ComputedStyleWidget();
319
- this.#computedStyleWidget.computedStyleModel = this.#computedStyleModel;
320
319
  this.#computedStyleModel.addEventListener(
321
320
  ComputedStyle.ComputedStyleModel.Events.COMPUTED_STYLE_CHANGED, this.#updateComputedStyles, this);
322
321
  this.#computedStyleModel.addEventListener(
@@ -40,7 +40,16 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
40
40
  target);
41
41
  };
42
42
 
43
- export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesContainer {
43
+ export const enum Events {
44
+ STYLES_UPDATE_COMPLETED = 'StylesUpdateCompleted',
45
+ }
46
+
47
+ export interface EventTypes {
48
+ [Events.STYLES_UPDATE_COMPLETED]: void;
49
+ }
50
+
51
+ export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.VBox>(
52
+ UI.Widget.VBox) implements StylesContainer {
44
53
  activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null = null;
45
54
  isEditingStyle = false;
46
55
  readonly sectionByElement = new WeakMap<Node, StylePropertiesSection>();
@@ -49,7 +58,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
49
58
  new Components.Linkifier.Linkifier(23, /* useLinkDecorator */ true);
50
59
 
51
60
  #webCustomData: WebCustomData|undefined;
52
- #userOperation = false;
61
+ userOperation = false;
53
62
  #sections: StylePropertiesSection[] = [];
54
63
  #swatchPopoverHelper = new InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper();
55
64
  #computedStyleModelInternal = new ComputedStyle.ComputedStyleModel.ComputedStyleModel();
@@ -58,10 +67,24 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
58
67
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
59
68
  super(element, {useShadowDom: true});
60
69
  this.#view = view;
70
+ this.#computedStyleModelInternal.addEventListener(
71
+ ComputedStyle.ComputedStyleModel.Events.CSS_MODEL_CHANGED, this.#onCSSModelChanged, this);
61
72
  }
62
73
 
63
- get userOperation(): boolean {
64
- return this.#userOperation;
74
+ async #onCSSModelChanged(
75
+ event: Common.EventTarget.EventTargetEvent<ComputedStyle.ComputedStyleModel.CSSModelChangedEvent>):
76
+ Promise<void> {
77
+ // We only recreate sections if this update is more than an "edit" operation.
78
+ // Sections will pull their own updates in the case of an "edit".
79
+ if (event?.data && 'edit' in event.data && event?.data.edit) {
80
+ return;
81
+ }
82
+
83
+ if (this.isEditingStyle || this.userOperation) {
84
+ return;
85
+ }
86
+
87
+ this.requestUpdate();
65
88
  }
66
89
 
67
90
  get webCustomData(): WebCustomData|undefined {
@@ -73,10 +96,12 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
73
96
  }
74
97
 
75
98
  async #updateSections(): Promise<void> {
99
+ for (const section of this.#sections) {
100
+ section.dispose();
101
+ }
76
102
  const node = this.node();
77
103
  if (!node) {
78
104
  this.#sections = [];
79
- this.requestUpdate();
80
105
  return;
81
106
  }
82
107
 
@@ -106,9 +131,8 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
106
131
  }
107
132
 
108
133
  override async performUpdate(): Promise<void> {
109
- if (this.isEditingStyle || this.#userOperation) {
110
- return;
111
- }
134
+ this.hideAllPopovers();
135
+ this.node()?.domModel().cssModel().discardCachedMatchedCascade();
112
136
 
113
137
  await this.#updateSections();
114
138
 
@@ -116,6 +140,11 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
116
140
  sections: this.#sections,
117
141
  };
118
142
  this.#view(viewInput, undefined, this.contentElement);
143
+ this.#onUpdateFinished();
144
+ }
145
+
146
+ #onUpdateFinished(): void {
147
+ this.dispatchEventToListeners(Events.STYLES_UPDATE_COMPLETED);
119
148
  }
120
149
 
121
150
  swatchPopoverHelper(): InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper {
@@ -154,6 +183,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
154
183
  }
155
184
 
156
185
  if (this.isEditingStyle) {
186
+ this.#onUpdateFinished();
157
187
  return;
158
188
  }
159
189
 
@@ -161,6 +191,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
161
191
  section.update(section === editedSection);
162
192
  }
163
193
  this.swatchPopoverHelper().reposition();
194
+ this.#onUpdateFinished();
164
195
  }
165
196
 
166
197
  filterRegex(): RegExp|null {
@@ -172,16 +203,19 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
172
203
  }
173
204
 
174
205
  setUserOperation(userOperation: boolean): void {
175
- this.#userOperation = userOperation;
206
+ this.userOperation = userOperation;
176
207
  }
177
208
 
178
209
  forceUpdate(): void {
179
- this.hideAllPopovers();
180
210
  this.requestUpdate();
181
211
  }
182
212
 
183
213
  hideAllPopovers(): void {
184
214
  this.#swatchPopoverHelper.hide();
215
+ if (this.activeCSSAngle) {
216
+ this.activeCSSAngle.minify();
217
+ this.activeCSSAngle = null;
218
+ }
185
219
  }
186
220
 
187
221
  allSections(): StylePropertiesSection[] {
@@ -246,9 +280,11 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
246
280
  jumpToDeclaration(_valueSource: SDK.CSSMatchedStyles.CSSValueSource): void {
247
281
  }
248
282
 
249
- addStyleUpdateListener(_listener: () => void): void {
283
+ addStyleUpdateListener(listener: () => void): void {
284
+ this.addEventListener(Events.STYLES_UPDATE_COMPLETED, listener);
250
285
  }
251
286
 
252
- removeStyleUpdateListener(_listener: () => void): void {
287
+ removeStyleUpdateListener(listener: () => void): void {
288
+ this.removeEventListener(Events.STYLES_UPDATE_COMPLETED, listener);
253
289
  }
254
290
  }