chrome-devtools-frontend 1.0.1592129 → 1.0.1593518

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 (125) hide show
  1. package/front_end/Images/src/tab-move.svg +1 -0
  2. package/front_end/application_tokens.css +4 -4
  3. package/front_end/core/host/UserMetrics.ts +2 -1
  4. package/front_end/core/root/ExperimentNames.ts +1 -0
  5. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  6. package/front_end/generated/InspectorBackendCommands.ts +3 -1
  7. package/front_end/generated/SupportedCSSProperties.js +2 -2
  8. package/front_end/generated/protocol-mapping.d.ts +13 -0
  9. package/front_end/generated/protocol-proxy-api.d.ts +12 -0
  10. package/front_end/generated/protocol.ts +75 -0
  11. package/front_end/models/ai_assistance/agents/AiAgent.ts +24 -1
  12. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +320 -26
  13. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgentOverlay.ts +87 -0
  14. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +8 -8
  15. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +79 -48
  16. package/front_end/models/ai_assistance/agents/StylingAgent.ts +16 -2
  17. package/front_end/models/computed_style/ComputedStyleModel.ts +40 -6
  18. package/front_end/models/javascript_metadata/NativeFunctions.js +6 -6
  19. package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +5 -3
  20. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +39 -7
  21. package/front_end/panels/ai_assistance/README.md +61 -0
  22. package/front_end/panels/ai_assistance/components/ChatMessage.ts +141 -27
  23. package/front_end/panels/ai_assistance/components/ChatView.ts +5 -1
  24. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +55 -1
  25. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +19 -7
  26. package/front_end/panels/ai_assistance/components/chatMessage.css +30 -0
  27. package/front_end/panels/ai_assistance/components/walkthroughView.css +12 -7
  28. package/front_end/panels/animation/AnimationGroupPreviewUI.ts +1 -1
  29. package/front_end/panels/application/CookieItemsView.ts +194 -134
  30. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +1 -1
  31. package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +1 -0
  32. package/front_end/panels/application/StorageItemsToolbar.ts +25 -2
  33. package/front_end/panels/application/components/BackForwardCacheView.ts +4 -2
  34. package/front_end/panels/application/cookieItemsView.css +28 -26
  35. package/front_end/panels/autofill/AutofillView.ts +1 -1
  36. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +161 -154
  37. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +1 -0
  38. package/front_end/panels/browser_debugger/domBreakpointsSidebarPane.css +58 -36
  39. package/front_end/panels/console/ConsoleViewMessage.ts +2 -1
  40. package/front_end/panels/console/consoleView.css +7 -2
  41. package/front_end/panels/elements/ComputedStyleWidget.ts +16 -26
  42. package/front_end/panels/elements/ElementsPanel.ts +26 -3
  43. package/front_end/panels/elements/ElementsTreeElement.ts +1 -0
  44. package/front_end/panels/elements/LayoutPane.ts +8 -7
  45. package/front_end/panels/elements/StandaloneStylesContainer.ts +254 -0
  46. package/front_end/panels/elements/StylePropertyTreeElement.ts +33 -0
  47. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +148 -1
  48. package/front_end/panels/elements/components/ComputedStyleTrace.ts +4 -0
  49. package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +1 -1
  50. package/front_end/panels/elements/components/StylePropertyEditor.ts +2 -1
  51. package/front_end/panels/elements/components/computedStyleProperty.css +1 -1
  52. package/front_end/panels/elements/elements-meta.ts +3 -5
  53. package/front_end/panels/elements/elements.ts +2 -0
  54. package/front_end/panels/elements/stylePropertiesTreeOutline.css +6 -0
  55. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +1 -1
  56. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +1 -1
  57. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +1 -1
  58. package/front_end/panels/media/PlayerListView.ts +1 -1
  59. package/front_end/panels/network/NetworkLogViewColumns.ts +2 -1
  60. package/front_end/panels/network/components/RequestHeaderSection.ts +1 -1
  61. package/front_end/panels/protocol_monitor/JSONEditor.ts +1 -1
  62. package/front_end/panels/recorder/components/RecordingListView.ts +1 -1
  63. package/front_end/panels/recorder/components/StepEditor.ts +3 -3
  64. package/front_end/panels/settings/KeybindsSettingsTab.ts +2 -1
  65. package/front_end/panels/sources/components/HeadersView.ts +2 -2
  66. package/front_end/panels/timeline/components/BreadcrumbsUI.ts +1 -1
  67. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +1 -1
  68. package/front_end/panels/timeline/components/metricCard.css +0 -4
  69. package/front_end/third_party/chromium/README.chromium +1 -1
  70. package/front_end/third_party/puppeteer/README.chromium +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Navigation.d.ts.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Navigation.js +1 -0
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Navigation.js.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.js +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +6 -6
  89. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Navigation.d.ts.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Navigation.js +1 -0
  91. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Navigation.js.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.js +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  104. package/front_end/third_party/puppeteer/package/package.json +4 -4
  105. package/front_end/third_party/puppeteer/package/src/bidi/core/Navigation.ts +1 -0
  106. package/front_end/third_party/puppeteer/package/src/cdp/BrowserContext.ts +1 -1
  107. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +1 -1
  108. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
  109. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  110. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  111. package/front_end/ui/components/suggestion_input/SuggestionInput.ts +7 -12
  112. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +13 -2
  113. package/front_end/ui/components/tree_outline/TreeOutline.ts +6 -2
  114. package/front_end/ui/components/tree_outline/treeOutline.css +5 -0
  115. package/front_end/ui/legacy/ListControl.ts +5 -3
  116. package/front_end/ui/legacy/ListWidget.ts +1 -1
  117. package/front_end/ui/legacy/SoftContextMenu.ts +2 -1
  118. package/front_end/ui/legacy/Treeoutline.ts +1 -0
  119. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +44 -24
  120. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +4 -2
  121. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +1 -1
  122. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +31 -0
  123. package/front_end/ui/visual_logging/Debugging.ts +4 -0
  124. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  125. package/package.json +1 -1
@@ -10,7 +10,10 @@ import * as Host from '../../../core/host/host.js';
10
10
  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
+ import * as SDK from '../../../core/sdk/sdk.js';
14
+ import type {AiWidget, ComputedStyleAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
13
15
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
16
+ import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
14
17
  import * as Marked from '../../../third_party/marked/marked.js';
15
18
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
16
19
  import * as Input from '../../../ui/components/input/input.js';
@@ -20,9 +23,10 @@ import * as UIHelpers from '../../../ui/helpers/helpers.js';
20
23
  import * as UI from '../../../ui/legacy/legacy.js';
21
24
  import * as Lit from '../../../ui/lit/lit.js';
22
25
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
26
+ import * as Elements from '../../elements/elements.js';
23
27
 
24
28
  import chatMessageStyles from './chatMessage.css.js';
25
- import {WalkthroughView} from './WalkthroughView.js';
29
+ import {walkthroughTitle, WalkthroughView} from './WalkthroughView.js';
26
30
 
27
31
  const {html, Directives: {ref, ifDefined}} = Lit;
28
32
  const lockedString = i18n.i18n.lockedString;
@@ -96,18 +100,14 @@ const UIStringsNotTranslate = {
96
100
  * @description Displayed when the user stop the response
97
101
  */
98
102
  stoppedResponse: 'You stopped this response',
99
- /**
100
- * @description Prompt for user to confirm code execution that may affect the page.
101
- */
102
- sideEffectConfirmationDescription: 'This code may modify page content. Continue?',
103
103
  /**
104
104
  * @description Button text that confirm code execution that may affect the page.
105
105
  */
106
- positiveSideEffectConfirmation: 'Continue',
106
+ confirmActionRequestApproval: 'Continue',
107
107
  /**
108
108
  * @description Button text that cancels code execution that may affect the page.
109
109
  */
110
- negativeSideEffectConfirmation: 'Cancel',
110
+ declineActionRequestApproval: 'Cancel',
111
111
  /**
112
112
  * @description The generic name of the AI agent (do not translate)
113
113
  */
@@ -168,6 +168,11 @@ const UIStringsNotTranslate = {
168
168
  * @description Title for the button that shows the thinking process (walkthrough).
169
169
  */
170
170
  showThinking: 'Show thinking',
171
+
172
+ /**
173
+ * @description Title for the button that takes the user into other DevTools panels to reveal items the AI references.
174
+ */
175
+ reveal: 'Reveal'
171
176
  } as const;
172
177
 
173
178
  export interface Step {
@@ -176,12 +181,14 @@ export interface Step {
176
181
  title?: string;
177
182
  code?: string;
178
183
  output?: string;
184
+ widgets?: AiWidget[];
179
185
  canceled?: boolean;
180
- sideEffect?: ConfirmSideEffectDialog;
186
+ requestApproval?: ConfirmSideEffectDialog;
181
187
  contextDetails?: [AiAssistanceModel.AiAgent.ContextDetail, ...AiAssistanceModel.AiAgent.ContextDetail[]];
182
188
  }
183
189
 
184
190
  export interface ConfirmSideEffectDialog {
191
+ description: string|null;
185
192
  onAnswer: (result: boolean) => void;
186
193
  }
187
194
 
@@ -386,13 +393,14 @@ function renderTextAsMarkdown(text: string, markdownRenderer: MarkdownLitRendere
386
393
  // clang-format on
387
394
  }
388
395
 
389
- function titleForStep(step: Step): string {
396
+ export function titleForStep(step: Step): string {
390
397
  return step.title ?? `${lockedString(UIStringsNotTranslate.investigating)}…`;
391
398
  }
392
399
 
393
400
  function renderTitle(step: Step): Lit.LitTemplate {
394
- const paused =
395
- step.sideEffect ? html`<span class="paused">${lockedString(UIStringsNotTranslate.paused)}: </span>` : Lit.nothing;
401
+ const paused = step.requestApproval ?
402
+ html`<span class="paused">${lockedString(UIStringsNotTranslate.paused)}: </span>` :
403
+ Lit.nothing;
396
404
 
397
405
  return html`<span class="title">${paused}${titleForStep(step)}</span>`;
398
406
  }
@@ -444,7 +452,7 @@ function renderStepDetails({
444
452
  markdownRenderer: MarkdownLitRenderer,
445
453
  isLast: boolean,
446
454
  }): Lit.LitTemplate {
447
- const sideEffects = isLast && step.sideEffect ? renderSideEffectConfirmationUi(step) : Lit.nothing;
455
+ const sideEffects = isLast && step.requestApproval ? renderSideEffectConfirmationUi(step) : Lit.nothing;
448
456
  const thought = step.thought ? html`<p>${renderTextAsMarkdown(step.thought, markdownRenderer)}</p>` : Lit.nothing;
449
457
 
450
458
  // clang-format off
@@ -481,7 +489,10 @@ function renderWalkthroughSidebarButton(
481
489
  if (walkthrough.isInlined) {
482
490
  return Lit.nothing;
483
491
  }
484
- const title = input.isLoading ? titleForStep(lastStep) : lockedString(UIStringsNotTranslate.showThinking);
492
+ const title = walkthroughTitle({
493
+ isLoading: input.isLoading,
494
+ lastStep,
495
+ });
485
496
 
486
497
  // clang-format off
487
498
  return html`
@@ -520,7 +531,7 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
520
531
  // No steps = no walkthrough UI in the chat view.
521
532
  return Lit.nothing;
522
533
  }
523
- const sideEffectSteps = steps.filter(s => s.sideEffect);
534
+ const sideEffectSteps = steps.filter(s => s.requestApproval);
524
535
  // If the walkthrough is in the sidebar, we render a button into the
525
536
  // ChatView to open it.
526
537
  const openWalkThroughSidebarButton =
@@ -552,7 +563,7 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
552
563
  markdownRenderer: input.markdownRenderer,
553
564
  isInlined: true,
554
565
  isExpanded: input.isLastMessage &&
555
- (input.walkthrough.isExpanded || steps.some(step => Boolean(step.sideEffect))),
566
+ (input.walkthrough.isExpanded || steps.some(step => Boolean(step.requestApproval))),
556
567
  onToggle: input.walkthrough.onToggle,
557
568
  })}></devtools-widget>
558
569
  </div>
@@ -571,14 +582,14 @@ function renderStepBadge({step, isLoading, isLast}: {
571
582
  isLoading: boolean,
572
583
  isLast: boolean,
573
584
  }): Lit.LitTemplate {
574
- if (isLoading && isLast && !step.sideEffect) {
585
+ if (isLoading && isLast && !step.requestApproval) {
575
586
  return html`<devtools-spinner></devtools-spinner>`;
576
587
  }
577
588
 
578
589
  let iconName = 'checkmark';
579
590
  let ariaLabel: string|undefined = lockedString(UIStringsNotTranslate.completed);
580
591
  let role: 'button'|undefined = 'button';
581
- if (isLast && step.sideEffect) {
592
+ if (isLast && step.requestApproval) {
582
593
  role = undefined;
583
594
  ariaLabel = undefined;
584
595
  iconName = 'pause-circle';
@@ -601,17 +612,18 @@ export function renderStep({step, isLoading, markdownRenderer, isLast}: {
601
612
  markdownRenderer: MarkdownLitRenderer,
602
613
  isLast: boolean,
603
614
  }): Lit.LitTemplate {
615
+ const shouldRenderWidgets = Boolean(step.widgets?.length && Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
604
616
  const stepClasses = Lit.Directives.classMap({
605
617
  step: true,
606
- empty: !step.thought && !step.code && !step.contextDetails && !step.sideEffect,
607
- paused: Boolean(step.sideEffect),
618
+ empty: !step.thought && !step.code && !step.contextDetails && !step.requestApproval,
619
+ paused: Boolean(step.requestApproval),
608
620
  canceled: Boolean(step.canceled),
609
621
  });
610
622
  // clang-format off
611
623
  return html`
612
624
  <details class=${stepClasses}
613
625
  jslog=${VisualLogging.section('step')}
614
- .open=${Boolean(step.sideEffect)}>
626
+ .open=${Boolean(step.requestApproval)}>
615
627
  <summary>
616
628
  <div class="summary">
617
629
  ${renderStepBadge({ step, isLoading, isLast })}
@@ -623,12 +635,114 @@ export function renderStep({step, isLoading, markdownRenderer, isLast}: {
623
635
  </div>
624
636
  </summary>
625
637
  ${renderStepDetails({step, markdownRenderer, isLast})}
626
- </details>`;
638
+ </details>
639
+ ${shouldRenderWidgets ? html`
640
+ <div class="step-widgets-wrapper">
641
+ ${Lit.Directives.until(renderStepWidgets(step))}
642
+ </div>` : Lit.nothing
643
+ }`;
644
+ // clang-format on
645
+ }
646
+
647
+ interface WidgetMakerResponse {
648
+ renderedWidget: Lit.LitTemplate;
649
+ revealable: unknown;
650
+ }
651
+
652
+ 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;
664
+ }
665
+ const model = new ComputedStyle.ComputedStyleModel.ComputedStyleModel(resolved);
666
+ const styles = new ComputedStyle.ComputedStyleModel.ComputedStyle(resolved, widgetData.data.computedStyles);
667
+
668
+ const widgetConfig = UI.Widget.widgetConfig(Elements.ComputedStyleWidget.ComputedStyleWidget, {
669
+ nodeStyle: styles,
670
+ matchedStyles: widgetData.data.matchedCascade,
671
+ // This disables showing the nested traces and detailed information in the widget.
672
+ propertyTraces: null,
673
+ computedStyleModel: model,
674
+ allowUserControl: false,
675
+ filterText: new RegExp(widgetData.data.properties.join('|'), 'i')
676
+ });
677
+
678
+ // clang-format off
679
+ const widget = html`<devtools-widget class="computed-styles-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
627
680
  // clang-format on
681
+
682
+ return {renderedWidget: widget, revealable: new Elements.ElementsPanel.NodeComputedStyles(resolved)};
683
+ }
684
+
685
+ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
686
+ if (response === null) {
687
+ return Lit.nothing;
688
+ }
689
+
690
+ function onReveal(): void {
691
+ if (response === null) {
692
+ return;
693
+ }
694
+ void Common.Revealer.reveal(response?.revealable);
695
+ }
696
+
697
+ // clang-format off
698
+ 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>
708
+ </div>
709
+ `;
710
+ // clang-format on
711
+ }
712
+
713
+ /**
714
+ * Renders AI-defined UI widgets within a step.
715
+ * When a ModelChatMessage contains a WidgetPart, the ChatMessage component
716
+ * iterates through the `step.widgets` array. For each widget, it determines
717
+ * the appropriate rendering logic based on the `widgetData.name`.
718
+ *
719
+ * Currently, only 'COMPUTED_STYLES' widgets are supported. For these, the
720
+ * `makeComputedStyleWidget` function is called to construct the necessary
721
+ * data and configuration for the `Elements.ComputedStyleWidget.ComputedStyleWidget`
722
+ * component. The widget is then rendered using the `<devtools-widget>`
723
+ * custom element, which dynamically instantiates and displays the specified
724
+ * UI.Widget subclass with the provided configuration.
725
+ *
726
+ * This allows for a flexible and extensible system where new widget types
727
+ * can be added to the AI responses and rendered in DevTools by adding
728
+ * corresponding `make...Widget` functions and handling them here.
729
+ */
730
+ async function renderStepWidgets(step: Step): Promise<Lit.LitTemplate> {
731
+ if (!step.widgets || step.widgets.length === 0) {
732
+ return Lit.nothing;
733
+ }
734
+ const ui = await Promise.all(step.widgets.map(async widgetData => {
735
+ if (widgetData.name === 'COMPUTED_STYLES') {
736
+ const response = await makeComputedStyleWidget(widgetData);
737
+ return renderWidgetResponse(response);
738
+ }
739
+ return Lit.nothing;
740
+ }));
741
+ return html`${ui}`;
628
742
  }
629
743
 
630
744
  function renderSideEffectConfirmationUi(step: Step): Lit.LitTemplate {
631
- if (!step.sideEffect) {
745
+ if (!step.requestApproval) {
632
746
  return Lit.nothing;
633
747
  }
634
748
 
@@ -637,7 +751,7 @@ function renderSideEffectConfirmationUi(step: Step): Lit.LitTemplate {
637
751
  class="side-effect-confirmation"
638
752
  jslog=${VisualLogging.section('side-effect-confirmation')}
639
753
  >
640
- <p>${lockedString(UIStringsNotTranslate.sideEffectConfirmationDescription)}</p>
754
+ ${step.requestApproval.description ? html`<p>${step.requestApproval.description}</p>` : Lit.nothing}
641
755
  <div class="side-effect-buttons-container">
642
756
  <devtools-button
643
757
  .data=${
@@ -646,9 +760,9 @@ function renderSideEffectConfirmationUi(step: Step): Lit.LitTemplate {
646
760
  jslogContext: 'decline-execute-code',
647
761
  } as Buttons.Button.ButtonData
648
762
  }
649
- @click=${() => step.sideEffect?.onAnswer(false)}
763
+ @click=${() => step.requestApproval?.onAnswer(false)}
650
764
  >${lockedString(
651
- UIStringsNotTranslate.negativeSideEffectConfirmation,
765
+ UIStringsNotTranslate.declineActionRequestApproval,
652
766
  )}</devtools-button>
653
767
  <devtools-button
654
768
  .data=${
@@ -658,9 +772,9 @@ function renderSideEffectConfirmationUi(step: Step): Lit.LitTemplate {
658
772
  iconName: 'play',
659
773
  } as Buttons.Button.ButtonData
660
774
  }
661
- @click=${() => step.sideEffect?.onAnswer(true)}
775
+ @click=${() => step.requestApproval?.onAnswer(true)}
662
776
  >${
663
- lockedString(UIStringsNotTranslate.positiveSideEffectConfirmation)
777
+ lockedString(UIStringsNotTranslate.confirmActionRequestApproval)
664
778
  }</devtools-button>
665
779
  </div>
666
780
  </div>`;
@@ -20,7 +20,11 @@ import chatViewStyles from './chatView.css.js';
20
20
 
21
21
  export {ChatInput, type ImageInputData} from './ChatInput.js';
22
22
 
23
- const {ref, repeat, classMap} = Directives;
23
+ const {
24
+ ref,
25
+ repeat,
26
+ classMap,
27
+ } = Directives;
24
28
 
25
29
  /*
26
30
  * Strings that don't need to be translated at this time.
@@ -2,9 +2,15 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as Common from '../../../core/common/common.js';
6
+ import * as Platform from '../../../core/platform/platform.js';
7
+ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
8
+ import * as Logs from '../../../models/logs/logs.js';
5
9
  import type * as Marked from '../../../third_party/marked/marked.js';
6
10
  import * as MarkdownView from '../../../ui/components/markdown_view/markdown_view.js';
7
- import type * as Lit from '../../../ui/lit/lit.js';
11
+ import * as Lit from '../../../ui/lit/lit.js';
12
+
13
+ const {html} = Lit;
8
14
 
9
15
  /**
10
16
  * The model returns multiline code blocks in an erroneous way with the language being in new line.
@@ -19,7 +25,43 @@ import type * as Lit from '../../../ui/lit/lit.js';
19
25
  * ```
20
26
  **/
21
27
  export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
28
+ #revealableLink(revealable: unknown, label: string): Lit.LitTemplate {
29
+ return html`<devtools-link @click=${(e: Event) => {
30
+ e.preventDefault();
31
+ e.stopPropagation();
32
+ void Common.Revealer.reveal(revealable);
33
+ }}>${Platform.StringUtilities.trimEndWithMaxLength(label, 100)}</devtools-link>`;
34
+ }
35
+
36
+ #renderLink(href: string): Lit.LitTemplate|null {
37
+ if (href.startsWith('#req-')) {
38
+ const request =
39
+ Logs.NetworkLog.NetworkLog.instance().requests().find(req => req.requestId() === href.substring(5));
40
+
41
+ if (request) {
42
+ return this.#revealableLink(request, request.url());
43
+ }
44
+
45
+ } else if (href.startsWith('#file-')) {
46
+ const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getUISourceCodes().find(
47
+ file => AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.uiSourceCodeId.get(file) ===
48
+ Number(href.substring(6)));
49
+
50
+ if (file) {
51
+ return this.#revealableLink(file, file.name());
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+
22
57
  override templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
58
+ if (token.type === 'link') {
59
+ const link = this.#renderLink(token.href);
60
+ if (link) {
61
+ return link;
62
+ }
63
+ }
64
+
23
65
  if (token.type === 'code') {
24
66
  const lines = (token.text).split('\n');
25
67
  if (lines[0]?.trim() === 'css') {
@@ -28,6 +70,18 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
28
70
  }
29
71
  }
30
72
 
73
+ if (token.type === 'codespan') {
74
+ // LLM likes outputting the link inside a codespan block.
75
+ // Remove the codespan and render the link directly
76
+ const matches = token.text.match(/^\[.*\]\((.+)\)$/);
77
+ if (matches?.[1]) {
78
+ const link = this.#renderLink(matches[1]);
79
+ if (link) {
80
+ return link;
81
+ }
82
+ }
83
+ }
84
+
31
85
  return super.templateForToken(token);
32
86
  }
33
87
  }
@@ -10,9 +10,11 @@ import * as UI from '../../../ui/legacy/legacy.js';
10
10
  import * as Lit from '../../../ui/lit/lit.js';
11
11
 
12
12
  import chatMessageStyles from './chatMessage.css.js';
13
- import {type ModelChatMessage, renderStep} from './ChatMessage.js';
13
+ import {type ModelChatMessage, renderStep, type Step, titleForStep} from './ChatMessage.js';
14
14
  import walkthroughViewStyles from './walkthroughView.css.js';
15
15
 
16
+ const lockedString = i18n.i18n.lockedString;
17
+
16
18
  const {html, render} = Lit;
17
19
 
18
20
  const UIStrings = {
@@ -25,9 +27,9 @@ const UIStrings = {
25
27
  */
26
28
  title: 'Investigation steps',
27
29
  /**
28
- * @description Title for the inline walkthrough view.
30
+ * @description Title for the button that shows the thinking process (walkthrough).
29
31
  */
30
- inlineTitle: 'Show thinking',
32
+ showThinking: 'Show thinking',
31
33
  } as const;
32
34
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/WalkthroughView.ts', UIStrings);
33
35
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -41,8 +43,17 @@ export interface ViewInput {
41
43
  onToggle: (isOpen: boolean) => void;
42
44
  }
43
45
 
44
- function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate): Lit.LitTemplate {
45
- if (!input.isInlined) {
46
+ export function walkthroughTitle(input: {
47
+ isLoading: boolean,
48
+ lastStep: Step,
49
+ }): string {
50
+ const title = input.isLoading ? titleForStep(input.lastStep) : lockedString(UIStrings.showThinking);
51
+ return title;
52
+ }
53
+
54
+ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate, steps: Step[]): Lit.LitTemplate {
55
+ const lastStep = steps.at(-1);
56
+ if (!input.isInlined || !lastStep) {
46
57
  return Lit.nothing;
47
58
  }
48
59
 
@@ -54,7 +65,8 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate)
54
65
  return html`
55
66
  <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle}>
56
67
  <summary>
57
- ${i18nString(UIStrings.inlineTitle)}
68
+ ${input.isLoading ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
69
+ ${walkthroughTitle({isLoading: input.isLoading, lastStep,})}
58
70
  <devtools-icon name="chevron-down"></devtools-icon>
59
71
  </summary>
60
72
  ${stepsOutput}
@@ -123,7 +135,7 @@ export const DEFAULT_VIEW = (
123
135
  ${chatMessageStyles}
124
136
  ${walkthroughViewStyles}
125
137
  </style>
126
- ${input.isInlined ? renderInlineWalkthrough(input, stepsOutput)
138
+ ${input.isInlined ? renderInlineWalkthrough(input, stepsOutput, steps)
127
139
  : renderSidebarWalkthrough(input, stepsOutput, steps.length)
128
140
  }`, target);
129
141
  // clang-format on
@@ -333,6 +333,7 @@
333
333
  }
334
334
  }
335
335
 
336
+
336
337
  .error-step {
337
338
  color: var(--sys-color-error);
338
339
  }
@@ -354,4 +355,33 @@
354
355
  gap: var(--sys-size-4);
355
356
  align-items: center;
356
357
  }
358
+
359
+
360
+ .computed-styles-widget {
361
+ display: block;
362
+ width: fit-content;
363
+ }
364
+
365
+ .step-widgets-wrapper {
366
+ width: fit-content;
367
+ }
368
+
369
+ .widget-reveal-container {
370
+ width: 100%;
371
+ background: var(--sys-color-surface5);
372
+ border-bottom-right-radius: var(--sys-shape-corner-medium);
373
+ border-bottom-left-radius: var(--sys-shape-corner-medium);
374
+ padding: 0 var(--sys-size-4) var(--sys-size-4) 0;
375
+ }
376
+
377
+ .widget-content-container {
378
+ padding: var(--sys-size-4);
379
+ border-top-left-radius: var(--sys-shape-corner-medium);
380
+ border-top-right-radius: var(--sys-shape-corner-medium);
381
+ width: 100%;
382
+ overflow-x: auto;
383
+ background-color: var(--sys-color-surface3);
384
+
385
+ --override-computed-style-property-white-space: normal;
386
+ }
357
387
  }
@@ -78,26 +78,31 @@
78
78
  border-radius: var(--sys-size-5);
79
79
  overflow: hidden;
80
80
  background-color: var(--sys-color-surface1);
81
+ width: fit-content;
82
+
83
+ &[open] {
84
+ width: auto;
85
+ }
81
86
  }
82
87
 
83
- .walkthrough-inline summary {
88
+ .walkthrough-inline > summary {
84
89
  display: flex;
85
90
  align-items: center;
86
- justify-content: space-between;
87
- padding: var(--sys-size-5) var(--sys-size-6);
91
+ padding: var(--sys-size-4) var(--sys-size-6);
88
92
  cursor: pointer;
89
93
  background-color: transparent;
90
- font-weight: 500;
91
- font-size: 11px;
94
+ font: var(--sys-typescale-body4-regular);
92
95
  user-select: none;
93
96
  list-style: none; /* Hide default triangle */
97
+ justify-content: flex-start;
98
+ gap: var(--sys-size-4);
94
99
  }
95
100
 
96
- .walkthrough-inline summary::-webkit-details-marker {
101
+ .walkthrough-inline > summary::-webkit-details-marker {
97
102
  display: none;
98
103
  }
99
104
 
100
- .walkthrough-inline summary:hover {
105
+ .walkthrough-inline > summary:hover {
101
106
  background-color: var(--sys-color-state-hover-on-subtle);
102
107
  }
103
108
 
@@ -98,7 +98,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
98
98
  render(html`
99
99
  <div class="animation-group-preview-ui">
100
100
  <button
101
- jslog=${VisualLogging.item(`animations.buffer-preview${input.isScrollDrivenAnimationGroup ? '-sda' : ''}`).track({click: true})}
101
+ jslog=${VisualLogging.item(`animations.buffer-preview${input.isScrollDrivenAnimationGroup ? '-sda' : ''}`).track({click: true, resize: true})}
102
102
  class=${classes}
103
103
  role="option"
104
104
  aria-label=${input.label}