chrome-devtools-frontend 1.0.1596535 → 1.0.1597624

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 (101) hide show
  1. package/agents/prompts/ui-widgets.md +7 -8
  2. package/docs/ui_engineering.md +10 -11
  3. package/front_end/core/host/AidaClient.ts +4 -0
  4. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  5. package/front_end/core/host/UserMetrics.ts +12 -0
  6. package/front_end/core/root/Runtime.ts +5 -0
  7. package/front_end/core/sdk/CPUThrottlingManager.ts +14 -13
  8. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
  9. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +28 -0
  10. package/front_end/core/sdk/PageResourceLoader.ts +22 -1
  11. package/front_end/devtools_compatibility.js +2 -1
  12. package/front_end/models/ai_assistance/AiConversation.ts +29 -8
  13. package/front_end/models/ai_assistance/ChangeManager.ts +16 -0
  14. package/front_end/models/ai_assistance/ExtensionScope.ts +11 -3
  15. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +127 -0
  16. package/front_end/models/ai_assistance/agents/AiAgent.ts +26 -3
  17. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +11 -8
  19. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +24 -0
  20. package/front_end/models/ai_assistance/agents/StylingAgent.ts +323 -16
  21. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  22. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +27 -0
  23. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +21 -0
  24. package/front_end/models/greendev/Prototypes.ts +7 -1
  25. package/front_end/models/trace/Processor.ts +1 -0
  26. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -0
  27. package/front_end/models/trace/insights/CharacterSet.ts +172 -0
  28. package/front_end/models/trace/insights/Models.ts +1 -0
  29. package/front_end/models/trace/insights/types.ts +1 -0
  30. package/front_end/models/trace/types/TraceEvents.ts +17 -0
  31. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +51 -36
  32. package/front_end/panels/ai_assistance/PatchWidget.ts +6 -6
  33. package/front_end/panels/ai_assistance/components/ChatMessage.ts +93 -74
  34. package/front_end/panels/ai_assistance/components/ChatView.ts +6 -11
  35. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +18 -9
  36. package/front_end/panels/ai_assistance/components/StylingAgentMarkdownRenderer.ts +200 -0
  37. package/front_end/panels/ai_assistance/components/chatMessage.css +11 -8
  38. package/front_end/panels/application/AppManifestView.ts +3 -4
  39. package/front_end/panels/application/DeviceBoundSessionsView.ts +18 -22
  40. package/front_end/panels/application/FrameDetailsView.ts +9 -15
  41. package/front_end/panels/application/OriginTrialTreeView.ts +2 -3
  42. package/front_end/panels/application/ReportingApiView.ts +13 -17
  43. package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
  44. package/front_end/panels/application/components/BackForwardCacheView.ts +3 -3
  45. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -3
  46. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
  47. package/front_end/panels/changes/ChangesView.ts +6 -4
  48. package/front_end/panels/common/ExtensionServer.ts +15 -0
  49. package/front_end/panels/console/ConsolePinPane.ts +3 -3
  50. package/front_end/panels/coverage/CoverageListView.ts +1 -1
  51. package/front_end/panels/css_overview/CSSOverviewPanel.ts +11 -15
  52. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -5
  53. package/front_end/panels/elements/ElementsTreeElement.ts +55 -47
  54. package/front_end/panels/elements/ElementsTreeOutline.ts +149 -28
  55. package/front_end/panels/elements/EventListenersWidget.ts +3 -2
  56. package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -6
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +49 -4
  58. package/front_end/panels/layer_viewer/Layers3DView.ts +5 -4
  59. package/front_end/panels/lighthouse/LighthousePanel.ts +8 -0
  60. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +5 -6
  61. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +6 -11
  62. package/front_end/panels/network/RequestCookiesView.ts +3 -4
  63. package/front_end/panels/network/RequestInitiatorView.ts +7 -5
  64. package/front_end/panels/network/RequestResponseView.ts +10 -15
  65. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +3 -4
  66. package/front_end/panels/recorder/components/RecordingView.ts +31 -36
  67. package/front_end/panels/recorder/components/StepEditor.ts +6 -7
  68. package/front_end/panels/search/SearchView.ts +2 -3
  69. package/front_end/panels/settings/SettingsScreen.ts +3 -2
  70. package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -5
  71. package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -8
  72. package/front_end/panels/timeline/components/insights/Cache.ts +8 -10
  73. package/front_end/panels/timeline/components/insights/CharacterSet.ts +38 -0
  74. package/front_end/panels/timeline/components/insights/DOMSize.ts +16 -20
  75. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +2 -6
  76. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +3 -4
  77. package/front_end/panels/timeline/components/insights/FontDisplay.ts +3 -4
  78. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +5 -7
  79. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +3 -4
  80. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +3 -4
  81. package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -4
  82. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +2 -0
  83. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +5 -7
  84. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -4
  85. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +3 -4
  86. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +3 -4
  87. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +7 -11
  88. package/front_end/panels/timeline/components/insights/NodeLink.ts +2 -4
  89. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +3 -4
  90. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +7 -10
  91. package/front_end/panels/timeline/components/insights/ThirdParties.ts +5 -7
  92. package/front_end/panels/timeline/components/insights/insights.ts +2 -0
  93. package/front_end/panels/web_audio/WebAudioView.ts +3 -4
  94. package/front_end/ui/components/settings/SettingCheckbox.ts +2 -0
  95. package/front_end/ui/legacy/UIUtils.ts +5 -5
  96. package/front_end/ui/legacy/Widget.ts +33 -2
  97. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
  98. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -8
  99. package/front_end/ui/visual_logging/Debugging.ts +0 -32
  100. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  101. package/package.json +1 -1
@@ -0,0 +1,172 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as i18n from '../../../core/i18n/i18n.js';
6
+ import type * as Handlers from '../handlers/handlers.js';
7
+ import type * as Types from '../types/types.js';
8
+
9
+ import {
10
+ type Checklist,
11
+ InsightCategory,
12
+ InsightKeys,
13
+ type InsightModel,
14
+ type InsightSetContext,
15
+ InsightWarning,
16
+ type PartialInsightModel,
17
+ } from './types.js';
18
+
19
+ export const UIStrings = {
20
+ /**
21
+ * @description Title of an insight that checks whether the page declares a character encoding early enough.
22
+ */
23
+ title: 'Declare a character encoding',
24
+ /**
25
+ * @description Description of an insight that checks whether the page has a proper character encoding declaration via HTTP header or early meta tag.
26
+ */
27
+ description:
28
+ 'A character encoding declaration is required. It can be done with a meta charset tag in the first 1024 bytes of the HTML or in the Content-Type HTTP response header. [Learn more about declaring the character encoding](https://developer.chrome.com/docs/insights/charset/).',
29
+ /**
30
+ * @description Text to tell the user that the charset is declared in the Content-Type HTTP response header.
31
+ */
32
+ passingHttpHeader: 'Declares charset in HTTP header',
33
+ /**
34
+ * @description Text to tell the user that the charset is NOT declared in the Content-Type HTTP response header.
35
+ */
36
+ failedHttpHeader: 'Does not declare charset in HTTP header',
37
+ /**
38
+ * @description Text to tell the user that a meta charset tag was found in the first 1024 bytes of the HTML.
39
+ */
40
+ passingMetaCharsetEarly: 'Declares charset using a meta tag in the first 1024 bytes',
41
+ /**
42
+ * @description Text to tell the user that a meta charset tag was found, but too late in the HTML.
43
+ */
44
+ failedMetaCharsetLate: 'Declares charset using a meta tag after the first 1024 bytes',
45
+ /**
46
+ * @description Text to tell the user that no meta charset tag was found in the HTML.
47
+ */
48
+ failedMetaCharsetMissing: 'Does not declare charset using a meta tag',
49
+ /**
50
+ * @description Text to tell the user that trace data did not include the Blink signal for meta charset.
51
+ */
52
+ failedMetaCharsetUnknown: 'Could not determine meta charset declaration from trace',
53
+ } as const;
54
+
55
+ const str_ = i18n.i18n.registerUIStrings('models/trace/insights/CharacterSet.ts', UIStrings);
56
+ export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
57
+
58
+ const CHARSET_HTTP_REGEX = /charset\s*=\s*[a-zA-Z0-9\-_:.()]{2,}/i;
59
+
60
+ export type CharacterSetInsightModel = InsightModel<typeof UIStrings, {
61
+ data?: {
62
+ hasHttpCharset: boolean,
63
+ checklist: Checklist<'httpCharset'|'metaCharset'>,
64
+ metaCharsetDisposition?: Types.Events.MetaCharsetDisposition,
65
+ documentRequest?: Types.Events.SyntheticNetworkRequest,
66
+ },
67
+ }>;
68
+
69
+ export function isCharacterSetInsight(model: InsightModel): model is CharacterSetInsightModel {
70
+ return model.insightKey === InsightKeys.CHARACTER_SET;
71
+ }
72
+
73
+ function finalize(partialModel: PartialInsightModel<CharacterSetInsightModel>): CharacterSetInsightModel {
74
+ let hasFailure = false;
75
+ if (partialModel.data) {
76
+ hasFailure = !partialModel.data.checklist.httpCharset.value && !partialModel.data.checklist.metaCharset.value;
77
+ }
78
+
79
+ return {
80
+ insightKey: InsightKeys.CHARACTER_SET,
81
+ strings: UIStrings,
82
+ title: i18nString(UIStrings.title),
83
+ description: i18nString(UIStrings.description),
84
+ docs: 'https://developer.chrome.com/docs/insights/charset/',
85
+ category: InsightCategory.ALL,
86
+ state: hasFailure ? 'fail' : 'pass',
87
+ ...partialModel,
88
+ };
89
+ }
90
+
91
+ function hasCharsetInContentType(request: Types.Events.SyntheticNetworkRequest): boolean {
92
+ if (!request.args.data.responseHeaders) {
93
+ return false;
94
+ }
95
+ for (const header of request.args.data.responseHeaders) {
96
+ if (header.name.toLowerCase() === 'content-type') {
97
+ return CHARSET_HTTP_REGEX.test(header.value);
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+
103
+ function findMetaCharsetDisposition(
104
+ data: Handlers.Types.HandlerData,
105
+ context: InsightSetContext,
106
+ ): Types.Events.MetaCharsetDisposition|undefined {
107
+ if (!context.navigation) {
108
+ return undefined;
109
+ }
110
+ return data.PageLoadMetrics.metaCharsetCheckEventsByNavigation.get(context.navigation)
111
+ ?.at(-1)
112
+ ?.args.data?.disposition;
113
+ }
114
+
115
+ function metaCharsetLabel(disposition: Types.Events.MetaCharsetDisposition|undefined): ReturnType<typeof i18nString> {
116
+ switch (disposition) {
117
+ case 'found-in-first-1024-bytes':
118
+ return i18nString(UIStrings.passingMetaCharsetEarly);
119
+ case 'found-after-first-1024-bytes':
120
+ return i18nString(UIStrings.failedMetaCharsetLate);
121
+ case 'not-found':
122
+ return i18nString(UIStrings.failedMetaCharsetMissing);
123
+ default:
124
+ return i18nString(UIStrings.failedMetaCharsetUnknown);
125
+ }
126
+ }
127
+
128
+ export function generateInsight(
129
+ data: Handlers.Types.HandlerData, context: InsightSetContext): CharacterSetInsightModel {
130
+ if (!context.navigation) {
131
+ return finalize({});
132
+ }
133
+
134
+ const documentRequest = data.NetworkRequests.byId.get(context.navigationId);
135
+ if (!documentRequest) {
136
+ return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});
137
+ }
138
+
139
+ const hasHttpCharset = hasCharsetInContentType(documentRequest);
140
+ const metaCharsetDisposition = findMetaCharsetDisposition(data, context);
141
+ const hasMetaCharsetInFirst1024Bytes = metaCharsetDisposition === 'found-in-first-1024-bytes';
142
+
143
+ return finalize({
144
+ relatedEvents: [documentRequest],
145
+ data: {
146
+ hasHttpCharset,
147
+ metaCharsetDisposition,
148
+ documentRequest,
149
+ checklist: {
150
+ httpCharset: {
151
+ label: hasHttpCharset ? i18nString(UIStrings.passingHttpHeader) : i18nString(UIStrings.failedHttpHeader),
152
+ value: hasHttpCharset,
153
+ },
154
+ metaCharset: {
155
+ label: metaCharsetLabel(metaCharsetDisposition),
156
+ value: hasMetaCharsetInFirst1024Bytes,
157
+ },
158
+ },
159
+ },
160
+ });
161
+ }
162
+
163
+ export function createOverlays(model: CharacterSetInsightModel): Types.Overlays.Overlay[] {
164
+ if (!model.data?.documentRequest) {
165
+ return [];
166
+ }
167
+
168
+ return [{
169
+ type: 'ENTRY_SELECTED',
170
+ entry: model.data.documentRequest,
171
+ }];
172
+ }
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  export * as Cache from './Cache.js';
6
+ export * as CharacterSet from './CharacterSet.js';
6
7
  export * as CLSCulprits from './CLSCulprits.js';
7
8
  export * as DocumentLatency from './DocumentLatency.js';
8
9
  export * as DOMSize from './DOMSize.js';
@@ -164,4 +164,5 @@ export const enum InsightKeys {
164
164
  VIEWPORT = 'Viewport',
165
165
  MODERN_HTTP = 'ModernHTTP',
166
166
  CACHE = 'Cache',
167
+ CHARACTER_SET = 'CharacterSet',
167
168
  }
@@ -1360,6 +1360,22 @@ export function isParseMetaViewport(event: Event): event is ParseMetaViewport {
1360
1360
  return event.name === Name.PARSE_META_VIEWPORT;
1361
1361
  }
1362
1362
 
1363
+ export type MetaCharsetDisposition = 'found-in-first-1024-bytes'|'found-after-first-1024-bytes'|'not-found';
1364
+
1365
+ export interface MetaCharsetCheck extends Instant {
1366
+ name: Name.META_CHARSET_CHECK;
1367
+ args: Args&{
1368
+ data: {
1369
+ frame: string,
1370
+ disposition: MetaCharsetDisposition,
1371
+ },
1372
+ };
1373
+ }
1374
+
1375
+ export function isMetaCharsetCheck(event: Event): event is MetaCharsetCheck {
1376
+ return event.name === Name.META_CHARSET_CHECK;
1377
+ }
1378
+
1363
1379
  export interface LinkPreconnect extends Instant {
1364
1380
  name: Name.LINK_PRECONNECT;
1365
1381
  args: Args&{
@@ -3110,6 +3126,7 @@ export const enum Name {
3110
3126
  SELECTOR_STATS = 'SelectorStats',
3111
3127
  BEGIN_COMMIT_COMPOSITOR_FRAME = 'BeginCommitCompositorFrame',
3112
3128
  PARSE_META_VIEWPORT = 'ParseMetaViewport',
3129
+ META_CHARSET_CHECK = 'MetaCharsetCheck',
3113
3130
 
3114
3131
  /* Paint */
3115
3132
  SCROLL_LAYER = 'ScrollLayer',
@@ -43,6 +43,7 @@ import {DisabledWidget} from './components/DisabledWidget.js';
43
43
  import {ExploreWidget} from './components/ExploreWidget.js';
44
44
  import {MarkdownRendererWithCodeBlock} from './components/MarkdownRendererWithCodeBlock.js';
45
45
  import {PerformanceAgentMarkdownRenderer} from './components/PerformanceAgentMarkdownRenderer.js';
46
+ import {StylingAgentMarkdownRenderer} from './components/StylingAgentMarkdownRenderer.js';
46
47
  import {
47
48
  WalkthroughView,
48
49
  } from './components/WalkthroughView.js';
@@ -233,6 +234,10 @@ const lockedString = i18n.i18n.lockedString;
233
234
 
234
235
  function selectedElementFilter(maybeNode: SDK.DOMModel.DOMNode|null): SDK.DOMModel.DOMNode|null {
235
236
  if (maybeNode) {
237
+ if (Greendev.Prototypes.instance().isEnabled('emulationCapabilities')) {
238
+ return maybeNode;
239
+ }
240
+
236
241
  return maybeNode.nodeType() === Node.ELEMENT_NODE ? maybeNode : null;
237
242
  }
238
243
 
@@ -260,7 +265,12 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
260
265
  return [
261
266
  {title: 'What can you help me with?', jslogContext: 'styling-default'},
262
267
  {title: 'Why isn’t this element visible?', jslogContext: 'styling-default'},
263
- {title: 'How do I center this element?', jslogContext: 'styling-default'},
268
+ {
269
+ title: Greendev.Prototypes.instance().isEnabled('emulationCapabilities') ?
270
+ 'Are there display issues on this page for people using an Android phone?' :
271
+ 'How do I center this element?',
272
+ jslogContext: 'styling-default'
273
+ },
264
274
  ];
265
275
  case AiAssistanceModel.AiHistoryStorage.ConversationType.FILE:
266
276
  return [
@@ -313,6 +323,14 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
313
323
  } else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
314
324
  // Handle historical conversations (can't linkify anything).
315
325
  return new PerformanceAgentMarkdownRenderer();
326
+ } else if (
327
+ Greendev.Prototypes.instance().isEnabled('emulationCapabilities') &&
328
+ conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING &&
329
+ SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel)) {
330
+ const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
331
+ const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
332
+ const mainFrameId = resourceTreeModel?.mainFrame?.id;
333
+ return new StylingAgentMarkdownRenderer(mainFrameId);
316
334
  }
317
335
 
318
336
  return new MarkdownRendererWithCodeBlock();
@@ -602,12 +620,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
602
620
  #isLoading = false;
603
621
  // Stores the availability status of the `AidaClient` and the reason for unavailability, if any.
604
622
  #aidaAvailability: Host.AidaClient.AidaAccessPreconditions;
605
- // Info of the currently logged in user.
606
- #userInfo: {
607
- accountImage?: string,
608
- accountFullName?: string,
609
- accountGivenName?: string,
610
- };
611
623
  #timelinePanelInstance: TimelinePanel.TimelinePanel.TimelinePanel|null = null;
612
624
  #runAbortController = new AbortController();
613
625
  #walkthrough: WalkthroughState = {
@@ -616,10 +628,9 @@ export class AiAssistancePanel extends UI.Panel.Panel {
616
628
  activeMessage: null,
617
629
  };
618
630
 
619
- constructor(private view: View = defaultView, {aidaClient, aidaAvailability, syncInfo}: {
631
+ constructor(private view: View = defaultView, {aidaClient, aidaAvailability}: {
620
632
  aidaClient: Host.AidaClient.AidaClient,
621
633
  aidaAvailability: Host.AidaClient.AidaAccessPreconditions,
622
- syncInfo: Host.InspectorFrontendHostAPI.SyncInformation,
623
634
  }) {
624
635
  super(AiAssistancePanel.panelName);
625
636
  this.registerRequiredCSS(aiAssistancePanelStyles);
@@ -627,11 +638,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
627
638
 
628
639
  this.#aidaClient = aidaClient;
629
640
  this.#aidaAvailability = aidaAvailability;
630
- this.#userInfo = {
631
- accountImage: syncInfo.accountImage,
632
- accountFullName: syncInfo.accountFullName,
633
- accountGivenName: syncInfo.accountGivenName,
634
- };
635
641
 
636
642
  if (UI.ActionRegistry.ActionRegistry.instance().hasAction('elements.toggle-element-search')) {
637
643
  this.#toggleSearchElementAction =
@@ -706,7 +712,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
706
712
  isReadOnly: this.#conversation.isReadOnly ?? false,
707
713
  changeSummary: this.#getChangeSummary(),
708
714
  inspectElementToggled: this.#toggleSearchElementAction?.toggled() ?? false,
709
- userInfo: this.#userInfo,
710
715
  canShowFeedbackForm: this.#serverSideLoggingEnabled,
711
716
  multimodalInputEnabled: isAiAssistanceMultimodalInputEnabled() &&
712
717
  this.#conversation.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
@@ -792,11 +797,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
792
797
  const {forceNew} = opts;
793
798
  if (!panelInstance || forceNew) {
794
799
  const aidaClient = new Host.AidaClient.AidaClient();
795
- const syncInfoPromise = new Promise<Host.InspectorFrontendHostAPI.SyncInformation>(
796
- resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve));
797
- const [aidaAvailability, syncInfo] =
798
- await Promise.all([Host.AidaClient.AidaClient.checkAccessPreconditions(), syncInfoPromise]);
799
- panelInstance = new AiAssistancePanel(defaultView, {aidaClient, aidaAvailability, syncInfo});
800
+ const aidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
801
+ panelInstance = new AiAssistancePanel(defaultView, {aidaClient, aidaAvailability});
800
802
  }
801
803
 
802
804
  return panelInstance;
@@ -1059,13 +1061,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1059
1061
  const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
1060
1062
  if (currentAidaAvailability !== this.#aidaAvailability) {
1061
1063
  this.#aidaAvailability = currentAidaAvailability;
1062
- const syncInfo = await new Promise<Host.InspectorFrontendHostAPI.SyncInformation>(
1063
- resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve));
1064
- this.#userInfo = {
1065
- accountImage: syncInfo.accountImage,
1066
- accountFullName: syncInfo.accountFullName,
1067
- accountGivenName: syncInfo.accountGivenName,
1068
- };
1069
1064
  this.requestUpdate();
1070
1065
  }
1071
1066
  };
@@ -1635,12 +1630,19 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1635
1630
  if (this.#conversation.isEmpty) {
1636
1631
  Badges.UserBadges.instance().recordAction(Badges.BadgeAction.STARTED_AI_CONVERSATION);
1637
1632
  }
1638
- const multimodalInput = isAiAssistanceMultimodalInputEnabled() && imageInput && multimodalInputType ? {
1639
- input: imageInput,
1640
- id: crypto.randomUUID(),
1641
- type: multimodalInputType,
1642
- } :
1643
- undefined;
1633
+
1634
+ const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
1635
+ let multimodalInput: AiAssistanceModel.AiAgent.MultimodalInput|undefined;
1636
+ const pendingInput = this.#conversation.getPendingMultimodalInput();
1637
+ if (greenDevEmulationEnabled && pendingInput) {
1638
+ multimodalInput = pendingInput;
1639
+ } else if (isAiAssistanceMultimodalInputEnabled() && imageInput && multimodalInputType) {
1640
+ multimodalInput = {
1641
+ input: imageInput,
1642
+ id: crypto.randomUUID(),
1643
+ type: multimodalInputType,
1644
+ };
1645
+ }
1644
1646
 
1645
1647
  void VisualLogging.logFunctionCall(`start-conversation-${this.#conversation.type}`, 'ui');
1646
1648
 
@@ -1697,8 +1699,14 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1697
1699
  parts: [],
1698
1700
  };
1699
1701
  this.#messages.push(systemMessage);
1700
- if (Greendev.Prototypes.instance().isEnabled('breakpointDebuggerAgent') &&
1701
- this.#conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT) {
1702
+ // If the walkthrough is currently expanded in the sidebar, we want to
1703
+ // automatically swap it to the newly created message's walkthrough.
1704
+ // This ensures that when a user asks a new question, the sidebar updates
1705
+ // immediately to show the "loading" state of the new walkthrough.
1706
+ const isSidebarWalkthroughOpen = this.#walkthrough.isExpanded && !this.#walkthrough.isInlined;
1707
+ if (isSidebarWalkthroughOpen ||
1708
+ (Greendev.Prototypes.instance().isEnabled('breakpointDebuggerAgent') &&
1709
+ this.#conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT)) {
1702
1710
  this.#openWalkthrough(systemMessage);
1703
1711
  }
1704
1712
  break;
@@ -1786,6 +1794,13 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1786
1794
  systemMessage.parts.push(newPart);
1787
1795
  }
1788
1796
 
1797
+ if (data.widgets && Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled) {
1798
+ systemMessage.parts.push({
1799
+ type: 'widget',
1800
+ widgets: data.widgets,
1801
+ });
1802
+ }
1803
+
1789
1804
  // When there is an answer without any thinking steps, we don't want to show the thinking step.
1790
1805
  // TODO(crbug.com/463323934): Remove specially handling this case.
1791
1806
  if (systemMessage.parts.length > 1) {
@@ -1879,7 +1894,7 @@ export function getResponseMarkdown(message: ModelChatMessage): string {
1879
1894
  for (const part of message.parts) {
1880
1895
  if (part.type === 'answer') {
1881
1896
  contentParts.push(`### Answer\n\n${part.text}`);
1882
- } else {
1897
+ } else if (part.type === 'step') {
1883
1898
  const step = part.step;
1884
1899
  if (step.title) {
1885
1900
  contentParts.push(`### ${step.title}`);
@@ -125,6 +125,7 @@ const UIStringsNotTranslate = {
125
125
  const lockedString = i18n.i18n.lockedString;
126
126
 
127
127
  const CODE_SNIPPET_WARNING_URL = 'https://support.google.com/legal/answer/13505487';
128
+ const {widget} = UI.Widget;
128
129
 
129
130
  export enum PatchSuggestionState {
130
131
  /**
@@ -251,12 +252,11 @@ const DEFAULT_VIEW: View = (input, output, target) => {
251
252
  }
252
253
 
253
254
  if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
254
- return html`<devtools-widget .widgetConfig=${
255
- UI.Widget.widgetConfig(ChangesPanel.CombinedDiffView.CombinedDiffView, {
256
- workspaceDiff: input.workspaceDiff,
257
- // Ignore user creates inspector-stylesheets
258
- ignoredUrls: ['inspector://']
259
- })}></devtools-widget>`;
255
+ return html`${widget(ChangesPanel.CombinedDiffView.CombinedDiffView, {
256
+ workspaceDiff: input.workspaceDiff,
257
+ // Ignore user creates inspector-stylesheets
258
+ ignoredUrls: ['inspector://']
259
+ })}`;
260
260
  }
261
261
 
262
262
  return html`<devtools-code-block