chrome-devtools-frontend 1.0.1606789 → 1.0.1608868

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 (75) hide show
  1. package/docs/ui_engineering.md +2 -0
  2. package/eslint.config.mjs +2 -0
  3. package/front_end/core/common/VersionController.ts +18 -1
  4. package/front_end/core/host/UserMetrics.ts +0 -2
  5. package/front_end/core/platform/node/HostRuntime.ts +1 -1
  6. package/front_end/core/root/ExperimentNames.ts +0 -2
  7. package/front_end/core/sdk/AccessibilityModel.ts +13 -1
  8. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -13
  9. package/front_end/core/sdk/StorageBucketsModel.ts +0 -12
  10. package/front_end/entrypoints/main/MainImpl.ts +2 -11
  11. package/front_end/generated/InspectorBackendCommands.ts +3 -33
  12. package/front_end/generated/SupportedCSSProperties.js +8 -4
  13. package/front_end/generated/protocol-mapping.d.ts +7 -26
  14. package/front_end/generated/protocol-proxy-api.d.ts +5 -24
  15. package/front_end/generated/protocol.ts +2 -296
  16. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +2 -2
  17. package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +1 -1
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -6
  19. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +2 -1
  20. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +46 -21
  21. package/front_end/{panels/profiler → models/heap_snapshot_model}/ChildrenProvider.ts +3 -4
  22. package/front_end/{panels/profiler → models/heap_snapshot_model}/HeapSnapshotProxy.ts +19 -36
  23. package/front_end/models/heap_snapshot_model/heap_snapshot_model.ts +4 -0
  24. package/front_end/models/javascript_metadata/NativeFunctions.js +0 -4
  25. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +53 -10
  26. package/front_end/panels/accessibility/accessibility.ts +0 -2
  27. package/front_end/panels/accessibility/accessibilitySidebarView.css +10 -0
  28. package/front_end/panels/ai_assistance/components/ChatMessage.ts +6 -5
  29. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +36 -17
  30. package/front_end/panels/ai_assistance/components/chatMessage.css +7 -0
  31. package/front_end/panels/ai_assistance/components/walkthroughView.css +39 -11
  32. package/front_end/panels/application/CrashReportContextView.ts +237 -0
  33. package/front_end/panels/application/IndexedDBModel.ts +0 -10
  34. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -11
  35. package/front_end/panels/application/ReportingApiTreeElement.ts +40 -1
  36. package/front_end/panels/application/SharedStorageModel.ts +0 -11
  37. package/front_end/panels/application/WebMCPView.ts +389 -52
  38. package/front_end/panels/application/application.ts +4 -0
  39. package/front_end/panels/application/components/CrashReportContextGrid.ts +154 -0
  40. package/front_end/panels/application/components/components.ts +2 -0
  41. package/front_end/panels/application/webMCPView.css +107 -10
  42. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +12 -2
  43. package/front_end/panels/elements/AccessibilityTreeView.ts +10 -6
  44. package/front_end/panels/elements/ElementsPanel.ts +9 -50
  45. package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
  46. package/front_end/panels/elements/elements-meta.ts +1 -0
  47. package/front_end/panels/elements/elementsPanel.css +4 -14
  48. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +23 -13
  49. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +30 -30
  50. package/front_end/panels/profiler/HeapSnapshotView.ts +16 -13
  51. package/front_end/panels/profiler/profiler.ts +0 -4
  52. package/front_end/panels/snippets/SnippetsQuickOpen.ts +5 -5
  53. package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +2 -2
  54. package/front_end/panels/sources/sources-meta.ts +7 -7
  55. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +1 -3
  56. package/front_end/panels/timeline/ThirdPartyTreeView.ts +6 -1
  57. package/front_end/panels/timeline/ThreadAppender.ts +1 -3
  58. package/front_end/panels/timeline/TimelineController.ts +2 -2
  59. package/front_end/panels/timeline/TimelinePanel.ts +2 -4
  60. package/front_end/panels/timeline/TimelineTreeView.ts +32 -2
  61. package/front_end/panels/timeline/components/LiveMetricsView.ts +75 -54
  62. package/front_end/panels/timeline/components/timelineRangeSummaryView.css +8 -0
  63. package/front_end/panels/timeline/thirdPartyTreeView.css +0 -1
  64. package/front_end/panels/timeline/timeline-meta.ts +13 -0
  65. package/front_end/third_party/chromium/README.chromium +1 -1
  66. package/front_end/ui/legacy/TabbedPane.ts +3 -1
  67. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +3 -3
  68. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +2 -5
  69. package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +4 -4
  70. package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +5 -1
  71. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -1
  72. package/front_end/ui/legacy/components/utils/imagePreview.css +1 -1
  73. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -3
  74. package/package.json +1 -1
  75. package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +0 -653
@@ -3,22 +3,12 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
- import * as i18n from '../../core/i18n/i18n.js';
7
6
  import type * as PlatformApi from '../../core/platform/api/api.js';
8
7
  import * as Platform from '../../core/platform/platform.js';
9
- import type * as HeapSnapshotModel from '../../models/heap_snapshot_model/heap_snapshot_model.js';
10
8
 
11
9
  import type {ChildrenProvider} from './ChildrenProvider.js';
10
+ import type * as HeapSnapshotModel from './HeapSnapshotModel.js';
12
11
 
13
- const UIStrings = {
14
- /**
15
- * @description Text in Heap Snapshot Proxy of a profiler tool
16
- * @example {functionName} PH1
17
- */
18
- anErrorOccurredWhenACallToMethod: 'An error occurred when a call to method \'\'{PH1}\'\' was requested',
19
- } as const;
20
- const str_ = i18n.i18n.registerUIStrings('panels/profiler/HeapSnapshotProxy.ts', UIStrings);
21
- const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
22
12
  export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<HeapSnapshotWorkerProxy.EventTypes> {
23
13
  readonly eventHandler: (arg0: string, arg1: string) => void;
24
14
  nextObjectId = 1;
@@ -167,10 +157,8 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
167
157
  return;
168
158
  }
169
159
  if (data.error) {
170
- if (data.errorMethodName) {
171
- Common.Console.Console.instance().error(
172
- i18nString(UIStrings.anErrorOccurredWhenACallToMethod, {PH1: data.errorMethodName}));
173
- }
160
+ Common.Console.Console.instance().error(
161
+ `An error occurred when a call to method '${data.errorMethodName}' was requested`);
174
162
  Common.Console.Console.instance().error(data['errorCallStack']);
175
163
  this.callbacks.delete(data.callId);
176
164
  return;
@@ -263,7 +251,7 @@ export class HeapSnapshotLoaderProxy extends HeapSnapshotProxyObject implements
263
251
  }
264
252
 
265
253
  export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
266
- staticData: HeapSnapshotModel.HeapSnapshotModel.StaticData|null;
254
+ staticData: HeapSnapshotModel.StaticData|null;
267
255
  profileUid?: string;
268
256
 
269
257
  constructor(worker: HeapSnapshotWorkerProxy, objectId: number) {
@@ -271,9 +259,7 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
271
259
  this.staticData = null;
272
260
  }
273
261
 
274
- search(
275
- searchConfig: HeapSnapshotModel.HeapSnapshotModel.SearchConfig,
276
- filter: HeapSnapshotModel.HeapSnapshotModel.NodeFilter): Promise<number[]> {
262
+ search(searchConfig: HeapSnapshotModel.SearchConfig, filter: HeapSnapshotModel.NodeFilter): Promise<number[]> {
277
263
  return this.callMethodPromise('search', searchConfig, filter);
278
264
  }
279
265
 
@@ -281,20 +267,19 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
281
267
  return this.callMethodPromise('interfaceDefinitions');
282
268
  }
283
269
 
284
- aggregatesWithFilter(filter: HeapSnapshotModel.HeapSnapshotModel.NodeFilter):
285
- Promise<Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo>> {
270
+ aggregatesWithFilter(filter: HeapSnapshotModel.NodeFilter):
271
+ Promise<Record<string, HeapSnapshotModel.AggregatedInfo>> {
286
272
  return this.callMethodPromise('aggregatesWithFilter', filter);
287
273
  }
288
274
 
289
- aggregatesForDiff(interfaceDefinitions: string):
290
- Promise<Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregateForDiff>> {
275
+ aggregatesForDiff(interfaceDefinitions: string): Promise<Record<string, HeapSnapshotModel.AggregateForDiff>> {
291
276
  return this.callMethodPromise('aggregatesForDiff', interfaceDefinitions);
292
277
  }
293
278
 
294
279
  calculateSnapshotDiff(
295
280
  baseSnapshotId: string,
296
- baseSnapshotAggregates: Record<string, HeapSnapshotModel.HeapSnapshotModel.AggregateForDiff>,
297
- ): Promise<Record<string, HeapSnapshotModel.HeapSnapshotModel.Diff>> {
281
+ baseSnapshotAggregates: Record<string, HeapSnapshotModel.AggregateForDiff>,
282
+ ): Promise<Record<string, HeapSnapshotModel.Diff>> {
298
283
  return this.callMethodPromise('calculateSnapshotDiff', baseSnapshotId, baseSnapshotAggregates);
299
284
  }
300
285
 
@@ -322,20 +307,19 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
322
307
  return this.callFactoryMethod('createNodesProvider', HeapSnapshotProviderProxy, filter);
323
308
  }
324
309
 
325
- createNodesProviderForClass(classKey: string, nodeFilter: HeapSnapshotModel.HeapSnapshotModel.NodeFilter):
326
- HeapSnapshotProviderProxy {
310
+ createNodesProviderForClass(classKey: string, nodeFilter: HeapSnapshotModel.NodeFilter): HeapSnapshotProviderProxy {
327
311
  return this.callFactoryMethod('createNodesProviderForClass', HeapSnapshotProviderProxy, classKey, nodeFilter);
328
312
  }
329
313
 
330
- allocationTracesTops(): Promise<HeapSnapshotModel.HeapSnapshotModel.SerializedAllocationNode[]> {
314
+ allocationTracesTops(): Promise<HeapSnapshotModel.SerializedAllocationNode[]> {
331
315
  return this.callMethodPromise('allocationTracesTops');
332
316
  }
333
317
 
334
- allocationNodeCallers(nodeId: number): Promise<HeapSnapshotModel.HeapSnapshotModel.AllocationNodeCallers> {
318
+ allocationNodeCallers(nodeId: number): Promise<HeapSnapshotModel.AllocationNodeCallers> {
335
319
  return this.callMethodPromise('allocationNodeCallers', nodeId);
336
320
  }
337
321
 
338
- allocationStack(nodeIndex: number): Promise<HeapSnapshotModel.HeapSnapshotModel.AllocationStackFrame[]|null> {
322
+ allocationStack(nodeIndex: number): Promise<HeapSnapshotModel.AllocationStackFrame[]|null> {
339
323
  return this.callMethodPromise('allocationStack', nodeIndex);
340
324
  }
341
325
 
@@ -361,15 +345,15 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
361
345
  this.staticData = await this.callMethodPromise('updateStaticData');
362
346
  }
363
347
 
364
- getStatistics(): Promise<HeapSnapshotModel.HeapSnapshotModel.Statistics> {
348
+ getStatistics(): Promise<HeapSnapshotModel.Statistics> {
365
349
  return this.callMethodPromise('getStatistics');
366
350
  }
367
351
 
368
- getLocation(nodeIndex: number): Promise<HeapSnapshotModel.HeapSnapshotModel.Location|null> {
352
+ getLocation(nodeIndex: number): Promise<HeapSnapshotModel.Location|null> {
369
353
  return this.callMethodPromise('getLocation', nodeIndex);
370
354
  }
371
355
 
372
- getSamples(): Promise<HeapSnapshotModel.HeapSnapshotModel.Samples|null> {
356
+ getSamples(): Promise<HeapSnapshotModel.Samples|null> {
373
357
  return this.callMethodPromise('getSamples');
374
358
  }
375
359
 
@@ -421,12 +405,11 @@ export class HeapSnapshotProviderProxy extends HeapSnapshotProxyObject implement
421
405
  return this.callMethodPromise('isEmpty');
422
406
  }
423
407
 
424
- serializeItemsRange(startPosition: number, endPosition: number):
425
- Promise<HeapSnapshotModel.HeapSnapshotModel.ItemsRange> {
408
+ serializeItemsRange(startPosition: number, endPosition: number): Promise<HeapSnapshotModel.ItemsRange> {
426
409
  return this.callMethodPromise('serializeItemsRange', startPosition, endPosition);
427
410
  }
428
411
 
429
- async sortAndRewind(comparator: HeapSnapshotModel.HeapSnapshotModel.ComparatorConfig): Promise<void> {
412
+ async sortAndRewind(comparator: HeapSnapshotModel.ComparatorConfig): Promise<void> {
430
413
  await this.callMethodPromise('sortAndRewind', comparator);
431
414
  }
432
415
  }
@@ -2,8 +2,12 @@
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 ChildrenProvider from './ChildrenProvider.js';
5
6
  import * as HeapSnapshotModel from './HeapSnapshotModel.js';
7
+ import * as HeapSnapshotProxy from './HeapSnapshotProxy.js';
6
8
 
7
9
  export {
10
+ ChildrenProvider,
8
11
  HeapSnapshotModel,
12
+ HeapSnapshotProxy,
9
13
  };
@@ -6908,10 +6908,6 @@ export const NativeFunctions = [
6908
6908
  name: "MouseEvent",
6909
6909
  signatures: [["type","?eventInitDict"]]
6910
6910
  },
6911
- {
6912
- name: "PageHideEvent",
6913
- signatures: [["type","?eventInitDict"]]
6914
- },
6915
6911
  {
6916
6912
  name: "PageTransitionEvent",
6917
6913
  signatures: [["type","?eventInitDict"]]
@@ -3,16 +3,30 @@
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable @devtools/no-imperative-dom-api */
5
5
 
6
+ import '../../ui/components/switch/switch.js';
7
+
6
8
  import type * as Common from '../../core/common/common.js';
7
- import * as Root from '../../core/root/root.js';
9
+ import * as i18n from '../../core/i18n/i18n.js';
8
10
  import * as SDK from '../../core/sdk/sdk.js';
9
11
  import * as UI from '../../ui/legacy/legacy.js';
12
+ import * as Lit from '../../ui/lit/lit.js';
10
13
 
11
14
  import {AXNodeSubPane} from './AccessibilityNodeView.js';
15
+ import accessibilitySidebarViewStyles from './accessibilitySidebarView.css.js';
12
16
  import {ARIAAttributesPane} from './ARIAAttributesView.js';
13
- import {AXBreadcrumbsPane} from './AXBreadcrumbsPane.js';
14
17
  import {SourceOrderPane} from './SourceOrderView.js';
15
18
 
19
+ const {html, render} = Lit;
20
+
21
+ const UIStrings = {
22
+ /**
23
+ * @description Text for a toggle to turn on the accessibility tree view.
24
+ */
25
+ showAccessibilityTree: 'Show accessibility tree',
26
+ } as const;
27
+ const str_ = i18n.i18n.registerUIStrings('panels/accessibility/AccessibilitySidebarView.ts', UIStrings);
28
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
29
+
16
30
  let accessibilitySidebarViewInstance: AccessibilitySidebarView;
17
31
 
18
32
  export class AccessibilitySidebarView extends UI.Widget.VBox {
@@ -20,19 +34,29 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
20
34
  #axNode: SDK.AccessibilityModel.AccessibilityNode|null;
21
35
  private skipNextPullNode: boolean;
22
36
  private readonly sidebarPaneStack: UI.View.ViewLocation;
23
- private readonly breadcrumbsSubPane: AXBreadcrumbsPane;
24
37
  private readonly ariaSubPane: ARIAAttributesPane;
25
38
  private readonly axNodeSubPane: AXNodeSubPane;
26
39
  private readonly sourceOrderSubPane: SourceOrderPane;
40
+ private readonly toggleContainer: HTMLElement;
41
+ private readonly toggleAction: UI.ActionRegistration.Action;
42
+
27
43
  private constructor() {
28
44
  super();
45
+ this.registerRequiredCSS(accessibilitySidebarViewStyles);
29
46
  this.element.classList.add('accessibility-sidebar-view');
30
47
  this.#node = null;
31
48
  this.#axNode = null;
32
49
  this.skipNextPullNode = false;
33
50
  this.sidebarPaneStack = UI.ViewManager.ViewManager.instance().createStackLocation();
34
- this.breadcrumbsSubPane = new AXBreadcrumbsPane(this);
35
- void this.sidebarPaneStack.showView(this.breadcrumbsSubPane);
51
+
52
+ this.toggleContainer = document.createElement('div');
53
+ this.toggleContainer.classList.add('accessibility-toggle-container');
54
+ this.element.appendChild(this.toggleContainer);
55
+
56
+ this.toggleAction = UI.ActionRegistry.ActionRegistry.instance().getAction('elements.toggle-a11y-tree');
57
+ this.toggleAction.addEventListener(UI.ActionRegistration.Events.TOGGLED, this.updateToggle, this);
58
+ this.updateToggle();
59
+
36
60
  this.ariaSubPane = new ARIAAttributesPane();
37
61
  void this.sidebarPaneStack.showView(this.ariaSubPane);
38
62
  this.axNodeSubPane = new AXNodeSubPane();
@@ -79,14 +103,12 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
79
103
  }
80
104
 
81
105
  this.axNodeSubPane.setAXNode(axNode);
82
- this.breadcrumbsSubPane.setAXNode(axNode);
83
106
  }
84
107
 
85
108
  override async performUpdate(): Promise<void> {
86
109
  const node = this.node();
87
110
  this.axNodeSubPane.setNode(node);
88
111
  this.ariaSubPane.setNode(node);
89
- this.breadcrumbsSubPane.setNode(node);
90
112
  void this.sourceOrderSubPane.setNodeAsync(node);
91
113
  if (!node) {
92
114
  return;
@@ -95,9 +117,6 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
95
117
  if (!accessibilityModel) {
96
118
  return;
97
119
  }
98
- if (!Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.FULL_ACCESSIBILITY_TREE)) {
99
- accessibilityModel.clear();
100
- }
101
120
  await accessibilityModel.requestPartialAXTree(node);
102
121
  this.accessibilityNodeCallback(accessibilityModel.axNodeForDOMNode(node));
103
122
  }
@@ -138,6 +157,30 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
138
157
  this.setNode(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode));
139
158
  }
140
159
 
160
+ private updateToggle(): void {
161
+ const isToggled = this.toggleAction.toggled();
162
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
163
+ render(
164
+ html`
165
+ <div style="display: flex; align-items: center; gap: 8px;">
166
+ <devtools-switch
167
+ role="switch"
168
+ aria-label=${i18nString(UIStrings.showAccessibilityTree)}
169
+ .checked=${isToggled}
170
+ .label=${i18nString(UIStrings.showAccessibilityTree)}
171
+ .jslogContext=${'elements.toggle-a11y-tree'}
172
+ @switchchange=${this.onToggleChange}
173
+ ></devtools-switch>
174
+ <span style="color: var(--sys-color-on-surface);">${i18nString(UIStrings.showAccessibilityTree)}</span>
175
+ </div>
176
+ `,
177
+ this.toggleContainer, {host: this});
178
+ }
179
+
180
+ private onToggleChange(_event: Event): void {
181
+ void this.toggleAction.execute();
182
+ }
183
+
141
184
  private onNodeChange(
142
185
  event: Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode, name: string}|SDK.DOMModel.DOMNode>):
143
186
  void {
@@ -8,7 +8,6 @@ import * as AccessibilityStrings from './AccessibilityStrings.js';
8
8
  import * as AccessibilitySubPane from './AccessibilitySubPane.js';
9
9
  import * as ARIAAttributesView from './ARIAAttributesView.js';
10
10
  import * as ARIAMetadata from './ARIAMetadata.js';
11
- import * as AXBreadcrumbsPane from './AXBreadcrumbsPane.js';
12
11
 
13
12
  export {
14
13
  AccessibilityNodeView,
@@ -17,5 +16,4 @@ export {
17
16
  AccessibilitySubPane,
18
17
  ARIAAttributesView,
19
18
  ARIAMetadata,
20
- AXBreadcrumbsPane,
21
19
  };
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ .accessibility-toggle-container {
8
+ padding: 12px 18px;
9
+ border-bottom: 1px solid var(--sys-color-divider);
10
+ }
@@ -622,12 +622,12 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
622
622
  input.walkthrough.inlineExpandedMessages.includes(input.message as ModelChatMessage) :
623
623
  (input.walkthrough.isExpanded && input.walkthrough.activeSidebarMessage === input.message);
624
624
 
625
- // When a side-effect step is present, it's shown in the main chat UI if the
626
- // walkthrough is closed, allowing the user to approve it without opening
627
- // the walkthrough. If the walkthrough is already open, the side-effect
628
- // step is displayed within the walkthrough instead.
625
+ // When a side-effect step is present and needs user approval, it's
626
+ // shown in the main chat UI, regardless of if the walkthrough is
627
+ // open or closed.
628
+ // Once the user has approved/denied it, it goes back into the sidebar.
629
629
  // clang-format off
630
- const sideEffectStepsUI = !isExpanded && sideEffectSteps.length > 0 ? sideEffectSteps.map(step => html`
630
+ const sideEffectStepsUI = sideEffectSteps.length > 0 ? sideEffectSteps.map(step => html`
631
631
  <div class="side-effect-container">
632
632
  ${renderStep({
633
633
  step,
@@ -866,6 +866,7 @@ async function makeBottomUpTimelineTreeWidget(widgetData: BottomUpTreeAiWidget):
866
866
  endTime,
867
867
  compactMode: true,
868
868
  maxLinkLength: 15,
869
+ maxRows: 10,
869
870
  })}></devtools-widget>`;
870
871
 
871
872
  return {
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as i18n from '../../../core/i18n/i18n.js';
6
+ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
6
7
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
7
8
  import * as Input from '../../../ui/components/input/input.js';
8
9
  import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
@@ -91,8 +92,8 @@ export function walkthroughCloseTitle(input: {
91
92
  return lockedString(UIStrings.hideThinking);
92
93
  }
93
94
 
94
- function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate, steps: Step[]): Lit.LitTemplate {
95
- const lastStep = steps.at(-1);
95
+ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate, allSteps: Step[]): Lit.LitTemplate {
96
+ const lastStep = allSteps.at(-1);
96
97
  if (!input.isInlined || !lastStep) {
97
98
  return Lit.nothing;
98
99
  }
@@ -109,18 +110,32 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate,
109
110
  }
110
111
  }
111
112
 
112
- const hasWidgets = steps.some(s => s.widgets?.length);
113
+ const hasWidgets = allSteps.some(s => s.widgets?.length);
114
+ const icon = AiAssistanceModel.AiUtils.getIconName();
113
115
 
114
116
  // clang-format off
115
117
  return html`
116
- <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle}>
117
- <summary ?data-has-widgets=${!input.isLoading && hasWidgets}>
118
- ${input.isLoading ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
119
- ${input.isExpanded ? walkthroughCloseTitle({hasWidgets, isInlined: true}) : walkthroughTitle({isLoading: input.isLoading, lastStep, hasWidgets})}
120
- <devtools-icon name="chevron-right"></devtools-icon>
121
- </summary>
122
- ${stepsOutput}
123
- </details>
118
+ <div class="inline-wrapper" ?data-open=${input.isExpanded}>
119
+ <span class="inline-icon">
120
+ ${input.isLoading ?
121
+ html`<devtools-spinner></devtools-spinner>` :
122
+ html`<devtools-icon name=${icon}></devtools-icon>`
123
+ }
124
+ </span>
125
+ <details class="walkthrough-inline" ?open=${input.isExpanded} @toggle=${onToggle}>
126
+ <summary ?data-has-widgets=${!input.isLoading && hasWidgets}>
127
+ <span class="walkthrough-inline-title">
128
+ ${input.isExpanded ?
129
+ walkthroughCloseTitle({hasWidgets, isInlined: true}) :
130
+ walkthroughTitle({isLoading: input.isLoading, lastStep, hasWidgets})
131
+ }
132
+ </span>
133
+ <devtools-icon name="chevron-right"></devtools-icon>
134
+ </summary>
135
+
136
+ ${stepsOutput}
137
+ </details>
138
+ </div>
124
139
  `;
125
140
  // clang-format on
126
141
  }
@@ -165,17 +180,21 @@ export const DEFAULT_VIEW = (
165
180
  output: ViewOutput,
166
181
  target: HTMLElement|DocumentFragment,
167
182
  ): void => {
168
- const steps = input.message?.parts.filter(t => t.type === 'step')?.map(p => p.step) ?? [];
183
+ const allSteps = input.message?.parts.filter(t => t.type === 'step')?.map(p => p.step) ?? [];
184
+ // Ensure that we render steps but not ones that need approval; a
185
+ // step that needs approval is always rendered into the main chat
186
+ // view regardless of if the walkthrough is open or not.
187
+ const renderableSteps = allSteps.filter(s => !s.requestApproval);
169
188
 
170
189
  // clang-format off
171
- const stepsOutput = steps.length > 0 ? html`
190
+ const stepsOutput = renderableSteps.length > 0 ? html`
172
191
  <div class="steps-container" @scroll=${input.handleScroll} ${ref(el => {
173
192
  output.scrollContainer = el as HTMLElement;
174
193
  })}>
175
194
  <div class="steps-scroll-content" ${ref(el => {
176
195
  output.stepsContainer = el as HTMLElement;
177
196
  })}>
178
- ${steps.map((step, index) => html`
197
+ ${renderableSteps.map((step, index) => html`
179
198
  <div class="walkthrough-step">
180
199
  <span class="step-number">${index + 1}</span>
181
200
  <div class="step-wrapper">
@@ -183,7 +202,7 @@ export const DEFAULT_VIEW = (
183
202
  step,
184
203
  isLoading: input.isLoading,
185
204
  markdownRenderer: input.markdownRenderer,
186
- isLast: index === steps.length - 1
205
+ isLast: index === renderableSteps.length - 1
187
206
  })}
188
207
  </div>
189
208
  </div>
@@ -198,8 +217,8 @@ export const DEFAULT_VIEW = (
198
217
  ${chatMessageStyles}
199
218
  ${walkthroughViewStyles}
200
219
  </style>
201
- ${input.isInlined ? renderInlineWalkthrough(input, stepsOutput, steps)
202
- : renderSidebarWalkthrough(input, stepsOutput, steps.length)
220
+ ${input.isInlined ? renderInlineWalkthrough(input, stepsOutput, allSteps)
221
+ : renderSidebarWalkthrough(input, stepsOutput, renderableSteps.length)
203
222
  }`, target);
204
223
  // clang-format on
205
224
  };
@@ -171,6 +171,13 @@
171
171
  margin: var(--sys-size-6) 0;
172
172
  }
173
173
 
174
+ &:not(.ai-v2) .answer-body-wrapper {
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: var(--sys-size-5);
178
+ width: 100%;
179
+ }
180
+
174
181
  &.ai-v2 .answer-body-wrapper {
175
182
  @container(min-width: 700px) {
176
183
  /* Purposefully not using design system variables, this is a
@@ -97,22 +97,47 @@
97
97
  font-size: 11px;
98
98
  }
99
99
 
100
+ .inline-wrapper {
101
+ display: flex;
102
+ /* Note: no gap here; the gap is dealt with in padding on the text */
103
+ align-items: flex-start;
104
+ justify-content: flex-start;
105
+
106
+ .inline-icon {
107
+ display: block;
108
+ margin-top: var(--sys-size-2);
109
+ }
110
+ }
111
+
100
112
  .walkthrough-inline {
101
113
  border-radius: var(--sys-size-5);
102
114
  overflow: hidden;
103
- background-color: var(--sys-color-surface2);
104
115
  width: fit-content;
116
+ max-width: 100%;
105
117
 
106
118
  &[open] {
107
119
  width: auto;
108
120
  background-color: var(--sys-color-surface1);
121
+ margin-left: calc(var(--sys-size-6) / 2);
122
+ flex-grow: 1;
123
+
124
+ > summary {
125
+ border-radius: var(--sys-shape-corner-medium-small);
126
+ border-bottom-right-radius: 0;
127
+ border-bottom-left-radius: 0;
128
+ background: var(--sys-color-surface5);
129
+ color: var(--sys-color-on-surface);
130
+
131
+ &[data-has-widgets] {
132
+ margin-left: 0;
133
+ }
134
+ }
109
135
  }
110
136
  }
111
137
 
112
138
  .walkthrough-inline > summary {
113
139
  display: flex;
114
140
  align-items: center;
115
- padding: 0 var(--sys-size-6);
116
141
  cursor: pointer;
117
142
  background-color: transparent;
118
143
  /* The same height as a DevTools Button */
@@ -124,6 +149,8 @@
124
149
  justify-content: flex-start;
125
150
  gap: var(--sys-size-4);
126
151
  color: var(--sys-color-primary);
152
+ padding: 0 var(--sys-size-6);
153
+ overflow: hidden;
127
154
 
128
155
  devtools-icon {
129
156
  color: var(--sys-color-primary);
@@ -134,11 +161,19 @@
134
161
  background: var(--sys-color-tonal-container);
135
162
  color: var(--sys-color-on-tonal-container);
136
163
  border-radius: var(--sys-shape-corner-full);
164
+ margin-left: var(--sys-size-6);
137
165
 
138
166
  devtools-icon {
139
167
  color: var(--sys-color-on-tonal-container);
140
168
  }
141
169
  }
170
+
171
+ > .walkthrough-inline-title {
172
+ overflow: hidden;
173
+ text-overflow: ellipsis;
174
+ white-space: nowrap;
175
+ min-width: 0;
176
+ }
142
177
  }
143
178
 
144
179
  .walkthrough-inline > summary::-webkit-details-marker {
@@ -155,25 +190,18 @@
155
190
  background-color: transparent;
156
191
  }
157
192
 
158
- .walkthrough-inline > summary > devtools-icon {
193
+ .walkthrough-inline > summary > devtools-icon[name='chevron-right'] {
159
194
  width: var(--sys-size-8);
160
195
  height: var(--sys-size-8);
161
196
  transition: transform 0.2s;
162
197
  margin-left: auto;
163
198
  }
164
199
 
165
- .walkthrough-inline[open] > summary {
166
- border-radius: var(--sys-shape-corner-medium-small);
167
- border-bottom-right-radius: 0;
168
- border-bottom-left-radius: 0;
169
- background: var(--sys-color-surface5);
170
- }
171
-
172
200
  .walkthrough-inline .step {
173
201
  background-color: var(--sys-color-surface5);
174
202
  }
175
203
 
176
- .walkthrough-inline[open] > summary > devtools-icon {
204
+ .walkthrough-inline[open] > summary > devtools-icon[name='chevron-right'] {
177
205
  transform: rotate(270deg);
178
206
  }
179
207
  }