chrome-devtools-frontend 1.0.1543082 → 1.0.1544076

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 (149) hide show
  1. package/AUTHORS +2 -0
  2. package/front_end/core/common/Gzip.ts +4 -4
  3. package/front_end/core/common/common.ts +0 -2
  4. package/front_end/core/host/AidaClient.ts +10 -7
  5. package/front_end/core/host/DispatchHttpRequestClient.ts +18 -3
  6. package/front_end/core/root/DevToolsContext.ts +60 -0
  7. package/front_end/core/root/Runtime.ts +8 -7
  8. package/front_end/core/root/root.ts +6 -1
  9. package/front_end/core/sdk/CPUThrottlingManager.ts +0 -4
  10. package/front_end/core/sdk/CSSMatchedStyles.ts +7 -9
  11. package/front_end/core/sdk/CSSModel.ts +1 -1
  12. package/front_end/core/sdk/CSSRule.ts +18 -6
  13. package/front_end/core/sdk/ChildTargetManager.ts +2 -2
  14. package/front_end/core/sdk/TargetManager.ts +5 -6
  15. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +2 -0
  16. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -13
  17. package/front_end/entrypoints/main/MainImpl.ts +2 -20
  18. package/front_end/foundation/Universe.ts +24 -1
  19. package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -8
  20. package/front_end/models/ai_assistance/agents/PatchAgent.ts +7 -1
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +0 -5
  22. package/front_end/models/ai_assistance/agents/StylingAgent.ts +4 -8
  23. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
  24. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -3
  25. package/front_end/models/bindings/CSSWorkspaceBinding.ts +8 -7
  26. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +19 -15
  27. package/front_end/models/bindings/ResourceMapping.ts +57 -15
  28. package/front_end/models/live-metrics/LiveMetrics.ts +12 -20
  29. package/front_end/models/trace/handlers/SamplesHandler.ts +64 -6
  30. package/front_end/models/trace/types/TraceEvents.ts +16 -0
  31. package/front_end/models/workspace/IgnoreListManager.ts +10 -9
  32. package/front_end/models/workspace/WorkspaceImpl.ts +5 -10
  33. package/front_end/panels/accessibility/AccessibilityNodeView.ts +6 -2
  34. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -1
  35. package/front_end/panels/ai_assistance/components/ChatView.ts +2 -4
  36. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -1
  37. package/front_end/panels/animation/AnimationTimeline.ts +6 -6
  38. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -1
  39. package/front_end/panels/application/OpenedWindowDetailsView.ts +0 -2
  40. package/front_end/panels/application/ServiceWorkersView.ts +0 -2
  41. package/front_end/panels/application/StorageView.ts +0 -1
  42. package/front_end/panels/application/components/FrameDetailsView.ts +468 -447
  43. package/front_end/panels/application/components/ReportsGrid.ts +7 -2
  44. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +5 -3
  45. package/front_end/panels/application/components/TrustTokensView.ts +7 -1
  46. package/front_end/panels/application/preloading/PreloadingView.ts +10 -4
  47. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +7 -11
  48. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +15 -3
  49. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +12 -13
  50. package/front_end/panels/{elements → common}/DOMLinkifier.ts +6 -6
  51. package/front_end/panels/common/common.ts +1 -0
  52. package/front_end/panels/console/ConsoleView.ts +9 -7
  53. package/front_end/panels/console/ConsoleViewMessage.ts +23 -13
  54. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -1
  55. package/front_end/panels/elements/ElementsTreeElement.ts +3 -1
  56. package/front_end/panels/elements/StylePropertiesSection.ts +52 -15
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -3
  58. package/front_end/panels/elements/StylesSidebarPane.ts +24 -14
  59. package/front_end/panels/elements/elements-meta.ts +11 -2
  60. package/front_end/panels/elements/elements.ts +0 -3
  61. package/front_end/panels/explain/components/ConsoleInsight.ts +333 -318
  62. package/front_end/panels/issues/AffectedResourcesView.ts +2 -1
  63. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +2 -1
  64. package/front_end/panels/network/NetworkLogView.ts +1 -1
  65. package/front_end/panels/recorder/RecorderController.ts +7 -1
  66. package/front_end/panels/settings/SettingsScreen.ts +3 -6
  67. package/front_end/panels/settings/components/SyncSection.ts +218 -226
  68. package/front_end/panels/settings/components/syncSection.css +81 -80
  69. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +42 -294
  70. package/front_end/panels/sources/DebuggerPausedMessage.ts +3 -3
  71. package/front_end/panels/sources/DebuggerPlugin.ts +3 -1
  72. package/front_end/panels/sources/ResourceOriginPlugin.ts +7 -3
  73. package/front_end/panels/sources/SourcesPanel.ts +5 -1
  74. package/front_end/panels/timeline/TimelinePanel.ts +0 -21
  75. package/front_end/panels/timeline/TimelineUIUtils.ts +3 -2
  76. package/front_end/panels/timeline/components/LiveMetricsView.ts +7 -4
  77. package/front_end/panels/timeline/components/insights/NodeLink.ts +3 -2
  78. package/front_end/third_party/puppeteer/README.chromium +2 -2
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js +4 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js +8 -0
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +22 -0
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +34 -6
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js +4 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js +8 -0
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +22 -0
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  114. package/front_end/third_party/puppeteer/package/package.json +2 -2
  115. package/front_end/third_party/puppeteer/package/src/cdp/HTTPRequest.ts +5 -1
  116. package/front_end/third_party/puppeteer/package/src/cdp/NetworkEventManager.ts +16 -1
  117. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +28 -0
  118. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  119. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  120. package/front_end/ui/components/docs/component_docs.ts +0 -4
  121. package/front_end/ui/components/report_view/ReportView.ts +4 -1
  122. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +8 -5
  123. package/front_end/ui/i18n/i18n.ts +16 -0
  124. package/front_end/ui/legacy/ReportView.ts +0 -5
  125. package/front_end/ui/legacy/TextPrompt.ts +65 -19
  126. package/front_end/ui/legacy/UIUtils.ts +1 -1
  127. package/front_end/ui/legacy/Widget.ts +56 -25
  128. package/front_end/ui/legacy/XLink.ts +0 -2
  129. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +8 -4
  130. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +3 -1
  131. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +239 -284
  132. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +114 -184
  133. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  134. package/front_end/ui/legacy/inspectorCommon.css +0 -4
  135. package/front_end/ui/{components/docs/theme_colors/basic.ts → legacy/theme_support/ThemeColors.docs.ts} +33 -23
  136. package/mcp/mcp.ts +1 -0
  137. package/package.json +1 -1
  138. package/front_end/core/common/QueryParamHandler.ts +0 -7
  139. package/front_end/ui/components/docs/input/basic.html +0 -31
  140. package/front_end/ui/components/docs/input/basic.ts +0 -12
  141. package/front_end/ui/components/docs/report/basic.html +0 -27
  142. package/front_end/ui/components/docs/report/basic.ts +0 -48
  143. package/front_end/ui/components/docs/theme_colors/basic.html +0 -56
  144. package/front_end/ui/components/docs/toggle_dark_mode.ts +0 -36
  145. package/front_end/ui/components/docs/toggle_fonts.ts +0 -74
  146. package/front_end/ui/components/docs/user_agent_client_hints/basic.html +0 -25
  147. package/front_end/ui/components/docs/user_agent_client_hints/basic.ts +0 -26
  148. package/front_end/ui/components/expandable_list/ExpandableList.docs.ts +0 -30
  149. /package/front_end/panels/{elements → common}/domLinkifier.css +0 -0
@@ -193,20 +193,24 @@ interface ViewInput {
193
193
  renderer: MarkdownView.MarkdownView.MarkdownInsightRenderer;
194
194
  selectedRating?: boolean;
195
195
  noLogging: boolean;
196
- onClose: () => void;
197
- onSearch: () => void;
198
- onRating: (event: Event) => void;
199
- onReport: () => void;
200
- onGoToSignIn: () => void;
201
- onConsentReminderConfirmed: () => Promise<void>;
202
- onToggleReferenceDetails: () => void;
203
- onDisclaimerSettingsLink: () => void;
204
- onReminderSettingsLink: () => void;
205
- onEnableInsightsInSettingsLink: () => void;
196
+ callbacks: {
197
+ onClose: () => void,
198
+ onSearch: () => void,
199
+ onRating: (isPositive: boolean) => void,
200
+ onReport: () => void,
201
+ onGoToSignIn: () => void,
202
+ onConsentReminderConfirmed: () => Promise<void>,
203
+ onToggleReferenceDetails: () => void,
204
+ onDisclaimerSettingsLink: () => void,
205
+ onReminderSettingsLink: () => void,
206
+ onEnableInsightsInSettingsLink: () => void,
207
+ };
206
208
  }
207
209
 
208
210
  interface ViewOutput {
209
211
  referenceDetailsRef: Lit.Directives.Ref<HTMLDetailsElement>;
212
+ headerRef: Lit.Directives.Ref<HTMLHeadingElement>;
213
+ citationLinks: HTMLElement[];
210
214
  }
211
215
 
212
216
  const enum State {
@@ -274,10 +278,10 @@ function isSearchRagResponse(metadata: Host.AidaClient.ResponseMetadata): boolea
274
278
  return Boolean(metadata.factualityMetadata?.facts.length);
275
279
  }
276
280
 
277
- function renderSearchButton(input: ViewInput): Lit.TemplateResult {
281
+ function renderSearchButton(onSearch: ViewInput['callbacks']['onSearch']): Lit.TemplateResult {
278
282
  // clang-format off
279
283
  return html`<devtools-button
280
- @click=${input.onSearch}
284
+ @click=${onSearch}
281
285
  class="search-button"
282
286
  .variant=${Buttons.Button.Variant.OUTLINED}
283
287
  .jslogContext=${'search'}
@@ -295,21 +299,21 @@ function renderLearnMoreAboutInsights(): Lit.TemplateResult {
295
299
  // clang-format on
296
300
  }
297
301
 
298
- function maybeRenderSources(state: StateData): Lit.LitTemplate {
299
- if (state.type !== State.INSIGHT || !state.directCitationUrls.length) {
302
+ function maybeRenderSources(directCitationUrls: string[], output: ViewOutput): Lit.LitTemplate {
303
+ if (!directCitationUrls.length) {
300
304
  return Lit.nothing;
301
305
  }
302
306
 
303
307
  // clang-format off
304
308
  return html`
305
309
  <ol class="sources-list">
306
- ${state.directCitationUrls.map((url, index) => html`
310
+ ${directCitationUrls.map((url, index) => html`
307
311
  <li>
308
312
  <x-link
309
313
  href=${url}
310
314
  class="link"
311
- data-index=${index + 1}
312
315
  jslog=${VisualLogging.link('references.console-insights').track({click: true})}
316
+ ${Directives.ref(e => { output.citationLinks[index] = e as HTMLElement; })}
313
317
  >
314
318
  ${url}
315
319
  </x-link>
@@ -320,15 +324,15 @@ function maybeRenderSources(state: StateData): Lit.LitTemplate {
320
324
  // clang-format on
321
325
  }
322
326
 
323
- function maybeRenderRelatedContent(state: StateData): Lit.LitTemplate {
324
- if (state.type !== State.INSIGHT || state.relatedUrls.length === 0) {
327
+ function maybeRenderRelatedContent(relatedUrls: string[], directCitationUrls: string[]): Lit.LitTemplate {
328
+ if (relatedUrls.length === 0) {
325
329
  return Lit.nothing;
326
330
  }
327
331
  // clang-format off
328
332
  return html`
329
- ${state.directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
333
+ ${directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
330
334
  <ul class="references-list">
331
- ${state.relatedUrls.map(relatedUrl => html`
335
+ ${relatedUrls.map(relatedUrl => html`
332
336
  <li>
333
337
  <x-link
334
338
  href=${relatedUrl}
@@ -344,136 +348,135 @@ function maybeRenderRelatedContent(state: StateData): Lit.LitTemplate {
344
348
  // clang-format on
345
349
  }
346
350
 
347
- function renderMain(input: ViewInput, output: ViewOutput): Lit.TemplateResult {
348
- const jslog = `${VisualLogging.section(input.state.type).track({resize: true})}`;
351
+ function renderLoading(): Lit.TemplateResult {
352
+ // clang-format off
353
+ return html`
354
+ <div role="presentation" aria-label="Loading" class="loader" style="clip-path: url('#clipPath');">
355
+ <svg width="100%" height="64">
356
+ <clipPath id="clipPath">
357
+ <rect x="0" y="0" width="100%" height="16" rx="8"></rect>
358
+ <rect x="0" y="24" width="100%" height="16" rx="8"></rect>
359
+ <rect x="0" y="48" width="100%" height="16" rx="8"></rect>
360
+ </clipPath>
361
+ </svg>
362
+ </div>`;
363
+ // clang-format on
364
+ }
349
365
 
366
+ function renderInsight(
367
+ insight: Extract<StateData, {type: State.INSIGHT}>, renderer: ViewInput['renderer'],
368
+ disableAnimations: ViewInput['disableAnimations'], callbacks: ViewInput['callbacks'],
369
+ output: ViewOutput): Lit.TemplateResult {
350
370
  // clang-format off
351
- switch (input.state.type) {
352
- case State.LOADING:
353
- return html`<main jslog=${jslog}>
354
- <div role="presentation" aria-label="Loading" class="loader" style="clip-path: url('#clipPath');">
355
- <svg width="100%" height="64">
356
- <clipPath id="clipPath">
357
- <rect x="0" y="0" width="100%" height="16" rx="8"></rect>
358
- <rect x="0" y="24" width="100%" height="16" rx="8"></rect>
359
- <rect x="0" y="48" width="100%" height="16" rx="8"></rect>
360
- </clipPath>
361
- </svg>
362
- </div>
363
- </main>`;
364
- case State.INSIGHT:
365
371
  return html`
366
- <main jslog=${jslog}>
367
372
  ${
368
- input.state.validMarkdown ? html`<devtools-markdown-view
369
- .data=${{tokens: input.state.tokens, renderer: input.renderer, animationEnabled: !input.disableAnimations} as MarkdownView.MarkdownView.MarkdownViewData}>
370
- </devtools-markdown-view>`: input.state.explanation
373
+ insight.validMarkdown ? html`<devtools-markdown-view
374
+ .data=${{tokens: insight.tokens, renderer, animationEnabled: !disableAnimations} as MarkdownView.MarkdownView.MarkdownViewData}>
375
+ </devtools-markdown-view>`: insight.explanation
371
376
  }
372
- ${input.state.timedOut ? html`<p class="error-message">${i18nString(UIStrings.timedOut)}</p>` : Lit.nothing}
373
- ${isSearchRagResponse(input.state.metadata) ? html`
374
- <details class="references" ${Lit.Directives.ref(output.referenceDetailsRef)} @toggle=${input.onToggleReferenceDetails} jslog=${VisualLogging.expand('references').track({click: true})}>
377
+ ${insight.timedOut ? html`<p class="error-message">${i18nString(UIStrings.timedOut)}</p>` : Lit.nothing}
378
+ ${isSearchRagResponse(insight.metadata) ? html`
379
+ <details class="references" ${Directives.ref(output.referenceDetailsRef)} @toggle=${callbacks.onToggleReferenceDetails} jslog=${VisualLogging.expand('references').track({click: true})}>
375
380
  <summary>${i18nString(UIStrings.references)}</summary>
376
- ${maybeRenderSources(input.state)}
377
- ${maybeRenderRelatedContent(input.state)}
381
+ ${maybeRenderSources(insight.directCitationUrls, output)}
382
+ ${maybeRenderRelatedContent(insight.relatedUrls, insight.directCitationUrls)}
378
383
  </details>
379
384
  ` : Lit.nothing}
380
385
  <details jslog=${VisualLogging.expand('sources').track({click: true})}>
381
386
  <summary>${i18nString(UIStrings.inputData)}</summary>
382
- <devtools-console-insight-sources-list .sources=${input.state.sources} .isPageReloadRecommended=${input.state.isPageReloadRecommended}>
387
+ <devtools-console-insight-sources-list .sources=${insight.sources} .isPageReloadRecommended=${insight.isPageReloadRecommended}>
383
388
  </devtools-console-insight-sources-list>
384
389
  </details>
385
390
  <div class="buttons">
386
- ${renderSearchButton(input)}
387
- </div>
388
- </main>`;
389
- case State.ERROR:
390
- return html`
391
- <main jslog=${jslog}>
392
- <div class="error">${i18nString(UIStrings.errorBody)}</div>
393
- </main>`;
394
- case State.CONSENT_REMINDER:
395
- return html`
396
- <main class="reminder-container" jslog=${jslog}>
397
- <h3>Things to consider</h3>
398
- <div class="reminder-items">
399
- <div>
400
- <devtools-icon name="google" class="medium">
401
- </devtools-icon>
402
- </div>
403
- <div>The console message, associated stack trace, related source code, and the associated network headers are sent to Google to generate explanations. ${input.noLogging
404
- ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
405
- : 'This data may be seen by human reviewers to improve this feature. Avoid sharing sensitive or personal information.'}
406
- </div>
407
- <div>
408
- <devtools-icon name="policy" class="medium">
409
- </devtools-icon>
410
- </div>
411
- <div>Use of this feature is subject to the <x-link
412
- href=${TERMS_OF_SERVICE_URL}
413
- class="link"
414
- jslog=${VisualLogging.link('terms-of-service.console-insights').track({click: true})}>
415
- Google Terms of Service
416
- </x-link> and <x-link
417
- href=${PRIVACY_POLICY_URL}
418
- class="link"
419
- jslog=${VisualLogging.link('privacy-policy.console-insights').track({click: true})}>
420
- Google Privacy Policy
421
- </x-link>
422
- </div>
423
- <div>
424
- <devtools-icon name="warning" class="medium">
425
- </devtools-icon>
426
- </div>
427
- <div>
428
- <x-link
429
- href=${CODE_SNIPPET_WARNING_URL}
430
- class="link"
431
- jslog=${VisualLogging.link('code-snippets-explainer.console-insights').track({click: true})}
432
- >Use generated code snippets with caution</x-link>
433
- </div>
434
- </div>
435
- </main>
436
- `;
437
- case State.SETTING_IS_NOT_TRUE: {
438
- const settingsLink = html`<button
439
- class="link" role="link"
440
- jslog=${VisualLogging.action('open-ai-settings').track({click: true})}
441
- @click=${input.onEnableInsightsInSettingsLink}
442
- >${i18nString(UIStrings.settingsLink)}</button>`;
443
-
444
- return html`<main class="opt-in-teaser" jslog=${jslog}>
445
- <div class="badge">
446
- <devtools-icon name="lightbulb-spark" class="medium">
447
- </devtools-icon>
448
- </div>
449
- <div>
450
- ${i18nTemplate(UIStrings.turnOnInSettings, {PH1: settingsLink})} ${
451
- renderLearnMoreAboutInsights()}
452
- </div>
453
- </main>`;
454
- }
455
- case State.NOT_LOGGED_IN:
456
- case State.SYNC_IS_PAUSED:
457
- return html`
458
- <main jslog=${jslog}>
459
- <div class="error">${Root.Runtime.hostConfig.isOffTheRecord ? i18nString(UIStrings.notAvailableInIncognitoMode) : i18nString(UIStrings.notLoggedIn)}</div>
460
- </main>`;
461
- case State.OFFLINE:
462
- return html`
463
- <main jslog=${jslog}>
464
- <div class="error">${i18nString(UIStrings.offline)}</div>
465
- </main>`;
466
- }
391
+ ${renderSearchButton(callbacks.onSearch)}
392
+ </div>`;
393
+ // clang-format on
394
+ }
395
+
396
+ function renderError(message: string): Lit.TemplateResult {
397
+ // clang-format off
398
+ return html`<div class="error">${message}</div>`;
467
399
  // clang-format on
468
400
  }
469
401
 
470
- function renderDisclaimer(input: ViewInput): Lit.LitTemplate {
402
+ function renderConsentReminder(noLogging: boolean): Lit.TemplateResult {
403
+ // clang-format off
404
+ return html`
405
+ <h3>Things to consider</h3>
406
+ <div class="reminder-items">
407
+ <div>
408
+ <devtools-icon name="google" class="medium">
409
+ </devtools-icon>
410
+ </div>
411
+ <div>The console message, associated stack trace, related source code, and the associated network headers are sent to Google to generate explanations. ${noLogging
412
+ ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
413
+ : 'This data may be seen by human reviewers to improve this feature. Avoid sharing sensitive or personal information.'}
414
+ </div>
415
+ <div>
416
+ <devtools-icon name="policy" class="medium">
417
+ </devtools-icon>
418
+ </div>
419
+ <div>Use of this feature is subject to the <x-link
420
+ href=${TERMS_OF_SERVICE_URL}
421
+ class="link"
422
+ jslog=${VisualLogging.link('terms-of-service.console-insights').track({click: true})}>
423
+ Google Terms of Service
424
+ </x-link> and <x-link
425
+ href=${PRIVACY_POLICY_URL}
426
+ class="link"
427
+ jslog=${VisualLogging.link('privacy-policy.console-insights').track({click: true})}>
428
+ Google Privacy Policy
429
+ </x-link>
430
+ </div>
431
+ <div>
432
+ <devtools-icon name="warning" class="medium">
433
+ </devtools-icon>
434
+ </div>
435
+ <div>
436
+ <x-link
437
+ href=${CODE_SNIPPET_WARNING_URL}
438
+ class="link"
439
+ jslog=${VisualLogging.link('code-snippets-explainer.console-insights').track({click: true})}
440
+ >Use generated code snippets with caution</x-link>
441
+ </div>
442
+ </div>`;
443
+ // clang-format on
444
+ }
445
+
446
+ function renderSettingIsNotTrue(onEnableInsightsInSettingsLink: () => void): Lit.TemplateResult {
447
+ // clang-format off
448
+ const settingsLink = html`
449
+ <button
450
+ class="link" role="link"
451
+ jslog=${VisualLogging.action('open-ai-settings').track({click: true})}
452
+ @click=${onEnableInsightsInSettingsLink}
453
+ >${i18nString(UIStrings.settingsLink)}</button>`;
454
+
455
+ return html`
456
+ <div class="badge">
457
+ <devtools-icon name="lightbulb-spark" class="medium">
458
+ </devtools-icon>
459
+ </div>
460
+ <div>
461
+ ${i18nTemplate(UIStrings.turnOnInSettings, {PH1: settingsLink})} ${
462
+ renderLearnMoreAboutInsights()}
463
+ </div>`;
464
+ // clang-format on
465
+ }
466
+
467
+ function renderNotLoggedIn(): Lit.TemplateResult {
468
+ return renderError(
469
+ Root.Runtime.hostConfig.isOffTheRecord ? i18nString(UIStrings.notAvailableInIncognitoMode) :
470
+ i18nString(UIStrings.notLoggedIn));
471
+ }
472
+
473
+ function renderDisclaimer(noLogging: boolean, onDisclaimerSettingsLink: () => void): Lit.LitTemplate {
471
474
  // clang-format off
472
475
  return html`<span>
473
- AI tools may generate inaccurate info that doesn't represent Google's views. ${input.noLogging
476
+ AI tools may generate inaccurate info that doesn't represent Google's views. ${noLogging
474
477
  ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
475
478
  : 'Data sent to Google may be seen by human reviewers to improve this feature.'
476
- } <button class="link" role="link" @click=${input.onDisclaimerSettingsLink}
479
+ } <button class="link" role="link" @click=${onDisclaimerSettingsLink}
477
480
  jslog=${VisualLogging.action('open-ai-settings').track({click: true})}>
478
481
  Open settings
479
482
  </button> or <x-link href=${LEARN_MORE_URL}
@@ -484,195 +487,144 @@ function renderDisclaimer(input: ViewInput): Lit.LitTemplate {
484
487
  // clang-format on
485
488
  }
486
489
 
487
- function renderFooter(input: ViewInput): Lit.LitTemplate {
488
- const disclaimer = renderDisclaimer(input);
490
+ function renderDisclaimerFooter(noLogging: boolean, onDisclaimerSettingsLink: () => void): Lit.LitTemplate {
489
491
  // clang-format off
490
- switch (input.state.type) {
491
- case State.LOADING:
492
- case State.SETTING_IS_NOT_TRUE:
493
- return Lit.nothing;
494
- case State.ERROR:
495
- case State.OFFLINE:
496
- return html`<footer jslog=${VisualLogging.section('footer')}>
497
- <div class="disclaimer">
498
- ${disclaimer}
499
- </div>
500
- </footer>`;
501
- case State.NOT_LOGGED_IN:
502
- case State.SYNC_IS_PAUSED:
503
- if (Root.Runtime.hostConfig.isOffTheRecord) {
504
- return Lit.nothing;
505
- }
506
- return html`<footer jslog=${VisualLogging.section('footer')}>
507
- <div class="filler"></div>
508
- <div>
509
- <devtools-button
510
- @click=${input.onGoToSignIn}
511
- .data=${
512
- {
513
- variant: Buttons.Button.Variant.PRIMARY,
514
- jslogContext: 'update-settings',
515
- } as Buttons.Button.ButtonData
516
- }
517
- >
518
- ${UIStrings.signIn}
519
- </devtools-button>
520
- </div>
521
- </footer>`;
522
- case State.CONSENT_REMINDER:
523
- return html`<footer jslog=${VisualLogging.section('footer')}>
524
- <div class="filler"></div>
525
- <div class="buttons">
526
- <devtools-button
527
- @click=${input.onReminderSettingsLink}
528
- .data=${
529
- {
530
- variant: Buttons.Button.Variant.TONAL,
531
- jslogContext: 'settings',
532
- title: 'Settings',
533
- } as Buttons.Button.ButtonData
534
- }
535
- >
536
- Settings
537
- </devtools-button>
538
- <devtools-button
539
- class='continue-button'
540
- @click=${input.onConsentReminderConfirmed}
541
- .data=${
542
- {
543
- variant: Buttons.Button.Variant.PRIMARY,
544
- jslogContext: 'continue',
545
- title: 'continue',
546
- } as Buttons.Button.ButtonData
547
- }
548
- >
549
- Continue
550
- </devtools-button>
551
- </div>
552
- </footer>`;
553
- case State.INSIGHT:
554
- return html`<footer jslog=${VisualLogging.section('footer')}>
555
- <div class="disclaimer">
556
- ${disclaimer}
557
- </div>
558
- <div class="filler"></div>
559
- <div class="rating">
560
- <devtools-button
561
- data-rating="true"
562
- .data=${
563
- {
564
- variant: Buttons.Button.Variant.ICON_TOGGLE,
565
- size: Buttons.Button.Size.SMALL,
566
- iconName: 'thumb-up',
567
- toggledIconName: 'thumb-up',
568
- toggleOnClick: false,
569
- toggleType: Buttons.Button.ToggleType.PRIMARY,
570
- disabled: input.selectedRating !== undefined,
571
- toggled: input.selectedRating === true,
572
- title: i18nString(UIStrings.goodResponse),
573
- jslogContext: 'thumbs-up',
574
- } as Buttons.Button.ButtonData
575
- }
576
- @click=${input.onRating}
577
- ></devtools-button>
578
- <devtools-button
579
- data-rating="false"
580
- .data=${
581
- {
582
- variant: Buttons.Button.Variant.ICON_TOGGLE,
583
- size: Buttons.Button.Size.SMALL,
584
- iconName: 'thumb-down',
585
- toggledIconName: 'thumb-down',
586
- toggleOnClick: false,
587
- toggleType: Buttons.Button.ToggleType.PRIMARY,
588
- disabled: input.selectedRating !== undefined,
589
- toggled: input.selectedRating === false,
590
- title: i18nString(UIStrings.badResponse),
591
- jslogContext: 'thumbs-down',
592
- } as Buttons.Button.ButtonData
593
- }
594
- @click=${input.onRating}
595
- ></devtools-button>
596
- <devtools-button
597
- .data=${
598
- {
599
- variant: Buttons.Button.Variant.ICON,
600
- size: Buttons.Button.Size.SMALL,
601
- iconName: 'report',
602
- title: i18nString(UIStrings.report),
603
- jslogContext: 'report',
604
- } as Buttons.Button.ButtonData
605
- }
606
- @click=${input.onReport}
607
- ></devtools-button>
608
- </div>
609
-
610
- </footer>`;
611
- }
492
+ return html`
493
+ <div class="disclaimer">
494
+ ${renderDisclaimer(noLogging, onDisclaimerSettingsLink)}
495
+ </div>`;
612
496
  // clang-format on
613
497
  }
614
498
 
615
- function getHeader(state: StateData): string {
616
- switch (state.type) {
617
- case State.NOT_LOGGED_IN:
618
- case State.SYNC_IS_PAUSED:
619
- return i18nString(UIStrings.signInToUse);
620
- case State.OFFLINE:
621
- return i18nString(UIStrings.offlineHeader);
622
- case State.LOADING:
623
- return i18nString(UIStrings.generating);
624
- case State.INSIGHT:
625
- return i18nString(UIStrings.insight);
626
- case State.ERROR:
627
- return i18nString(UIStrings.error);
628
- case State.CONSENT_REMINDER:
629
- return 'Understand console messages with AI';
630
- case State.SETTING_IS_NOT_TRUE:
631
- return ''; // not reached
499
+ function renderSignInFooter(onGoToSignIn: () => void): Lit.LitTemplate {
500
+ if (Root.Runtime.hostConfig.isOffTheRecord) {
501
+ return Lit.nothing;
632
502
  }
503
+ // clang-format off
504
+ return html`
505
+ <div class="filler"></div>
506
+ <div>
507
+ <devtools-button
508
+ @click=${onGoToSignIn}
509
+ .variant=${Buttons.Button.Variant.PRIMARY}
510
+ .jslogContext=${'update-settings'}
511
+ >
512
+ ${UIStrings.signIn}
513
+ </devtools-button>
514
+ </div>`;
633
515
  }
634
516
 
635
- function renderSpinner(state: StateData): Lit.LitTemplate {
517
+ function renderConsentReminderFooter(onReminderSettingsLink: () => void, onConsentReminderConfirmed: () => void): Lit.LitTemplate {
636
518
  // clang-format off
637
- if (state.type === State.INSIGHT && !state.completed) {
638
- return html`<devtools-spinner></devtools-spinner>`;
639
- }
640
- return Lit.nothing;
519
+ return html`
520
+ <div class="filler"></div>
521
+ <div class="buttons">
522
+ <devtools-button
523
+ @click=${onReminderSettingsLink}
524
+ .variant=${Buttons.Button.Variant.TONAL}
525
+ jslogContext=${'settings'}
526
+ title=${'Settings'}
527
+ >
528
+ Settings
529
+ </devtools-button>
530
+ <devtools-button
531
+ class='continue-button'
532
+ @click=${onConsentReminderConfirmed}
533
+ .variant=${Buttons.Button.Variant.PRIMARY}
534
+ .jslogContext=${'continue'}
535
+ .title=${'continue'}
536
+ >
537
+ Continue
538
+ </devtools-button>
539
+ </div>`;
540
+ }
541
+
542
+ function renderInsightFooter(noLogging: ViewInput['noLogging'], selectedRating: ViewInput['selectedRating'], callbacks: ViewInput['callbacks']): Lit.LitTemplate {
543
+ // clang-format off
544
+ return html`
545
+ <div class="disclaimer">
546
+ ${renderDisclaimer(noLogging, callbacks.onDisclaimerSettingsLink)}
547
+ </div>
548
+ <div class="filler"></div>
549
+ <div class="rating">
550
+ <devtools-button
551
+ data-rating="true"
552
+ .iconName=${'thumb-up'}
553
+ .toggledIconName=${'thumb-up'}
554
+ .variant=${Buttons.Button.Variant.ICON_TOGGLE}
555
+ .size=${Buttons.Button.Size.SMALL}
556
+ .toggleOnClick=${false}
557
+ .toggleType=${Buttons.Button.ToggleType.PRIMARY}
558
+ .disabled=${selectedRating !== undefined}
559
+ .toggled=${selectedRating === true}
560
+ .title=${i18nString(UIStrings.goodResponse)}
561
+ .jslogContext=${'thumbs-up'}
562
+ @click=${() => callbacks.onRating(true)}
563
+ ></devtools-button>
564
+ <devtools-button
565
+ data-rating="false"
566
+ .iconName=${'thumb-down'}
567
+ .toggledIconName=${'thumb-down'}
568
+ .variant=${Buttons.Button.Variant.ICON_TOGGLE}
569
+ .size=${Buttons.Button.Size.SMALL}
570
+ .toggleOnClick=${false}
571
+ .toggleType=${Buttons.Button.ToggleType.PRIMARY}
572
+ .disabled=${selectedRating !== undefined}
573
+ .toggled=${selectedRating === false}
574
+ .title=${i18nString(UIStrings.badResponse)}
575
+ .jslogContext=${'thumbs-down'}
576
+ @click=${() => callbacks.onRating(false)}
577
+ ></devtools-button>
578
+ <devtools-button
579
+ .iconName=${'report'}
580
+ .variant=${Buttons.Button.Variant.ICON}
581
+ .size=${Buttons.Button.Size.SMALL}
582
+ .title=${i18nString(UIStrings.report)}
583
+ .jslogContext=${'report'}
584
+ @click=${callbacks.onReport}
585
+ ></devtools-button>
586
+ </div>`;
641
587
  // clang-format on
642
588
  }
643
589
 
644
- function renderHeader(input: ViewInput): Lit.LitTemplate {
645
- if (input.state.type === State.SETTING_IS_NOT_TRUE) {
646
- return Lit.nothing;
647
- }
648
- const hasIcon = input.state.type === State.CONSENT_REMINDER;
590
+ function renderHeaderIcon(): Lit.LitTemplate {
591
+ // clang-format off
592
+ return html`
593
+ <div class="header-icon-container">
594
+ <devtools-icon name="lightbulb-spark" class="large">
595
+ </devtools-icon>
596
+ </div>`;
597
+ // clang-format on
598
+ }
599
+
600
+ interface HeaderInput {
601
+ headerText: string;
602
+ showIcon?: boolean;
603
+ showSpinner?: boolean;
604
+ onClose: ViewInput['callbacks']['onClose'];
605
+ }
606
+
607
+ function renderHeader(
608
+ {headerText, showIcon = false, showSpinner = false, onClose}: HeaderInput,
609
+ headerRef: Lit.Directives.Ref<HTMLHeadingElement>): Lit.LitTemplate {
649
610
  // clang-format off
650
611
  return html`
651
612
  <header>
652
- ${hasIcon ? html`
653
- <div class="header-icon-container">
654
- <devtools-icon name="lightbulb-spark" class="large">
655
- </devtools-icon>
656
- </div>`
657
- : Lit.nothing}
613
+ ${showIcon ? renderHeaderIcon() : Lit.nothing}
658
614
  <div class="filler">
659
- <h2 tabindex="-1">
660
- ${getHeader(input.state)}
615
+ <h2 tabindex="-1" ${Directives.ref(headerRef)}>
616
+ ${headerText}
661
617
  </h2>
662
- ${renderSpinner(input.state)}
618
+ ${showSpinner ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
663
619
  </div>
664
620
  <div class="close-button">
665
621
  <devtools-button
666
- .data=${
667
- {
668
- variant: Buttons.Button.Variant.ICON,
669
- size: Buttons.Button.Size.SMALL,
670
- iconName: 'cross',
671
- title: i18nString(UIStrings.closeInsight),
672
- } as Buttons.Button.ButtonData
673
- }
622
+ .iconName=${'cross'}
623
+ .variant=${Buttons.Button.Variant.ICON}
624
+ .size=${Buttons.Button.Size.SMALL}
625
+ .title=${i18nString(UIStrings.closeInsight)}
674
626
  jslog=${VisualLogging.close().track({click: true})}
675
- @click=${input.onClose}
627
+ @click=${onClose}
676
628
  ></devtools-button>
677
629
  </div>
678
630
  </header>
@@ -696,7 +648,9 @@ export class ConsoleInsight extends HTMLElement {
696
648
 
697
649
  // Main state.
698
650
  #state: StateData;
699
- #referenceDetailsRef = Lit.Directives.createRef<HTMLDetailsElement>();
651
+ #referenceDetailsRef = Directives.createRef<HTMLDetailsElement>();
652
+ #headerRef = Directives.createRef<HTMLHeadingElement>();
653
+ #citationLinks: HTMLElement[] = [];
700
654
  #areReferenceDetailsOpen = false;
701
655
 
702
656
  // Rating sub-form state.
@@ -746,8 +700,7 @@ export class ConsoleInsight extends HTMLElement {
746
700
  this.#areReferenceDetailsOpen = true;
747
701
  this.#render();
748
702
 
749
- const highlightedElement =
750
- this.#shadow.querySelector(`.sources-list x-link[data-index="${index}"]`) as HTMLElement | null;
703
+ const highlightedElement = this.#citationLinks[index - 1];
751
704
  if (highlightedElement) {
752
705
  UI.UIUtils.runCSSAnimationOnce(highlightedElement, 'highlighted');
753
706
  if (areDetailsAlreadyExpanded) {
@@ -908,7 +861,7 @@ export class ConsoleInsight extends HTMLElement {
908
861
  this.classList.add('closing');
909
862
  }
910
863
 
911
- #onRating(event: Event): void {
864
+ #onRating(isPositive: boolean): void {
912
865
  if (this.#state.type !== State.INSIGHT) {
913
866
  throw new Error('Unexpected state');
914
867
  }
@@ -920,7 +873,7 @@ export class ConsoleInsight extends HTMLElement {
920
873
  return;
921
874
  }
922
875
 
923
- this.#selectedRating = (event.target as HTMLElement).dataset.rating === 'true';
876
+ this.#selectedRating = isPositive;
924
877
  this.#render();
925
878
  if (this.#selectedRating) {
926
879
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightRatedPositive);
@@ -1056,6 +1009,7 @@ export class ConsoleInsight extends HTMLElement {
1056
1009
  }
1057
1010
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightGenerated);
1058
1011
  } catch (err) {
1012
+ console.error('[ConsoleInsight] Error in #generateInsight:', err);
1059
1013
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightErrored);
1060
1014
  if (err.message === 'doAidaConversation timed out' && this.#state.type === State.INSIGHT) {
1061
1015
  this.#state.timedOut = true;
@@ -1122,7 +1076,7 @@ export class ConsoleInsight extends HTMLElement {
1122
1076
 
1123
1077
  #focusHeader(): void {
1124
1078
  this.addEventListener('animationend', () => {
1125
- (this.#shadow.querySelector('header h2') as HTMLElement | undefined)?.focus();
1079
+ this.#headerRef.value?.focus();
1126
1080
  }, {once: true});
1127
1081
  }
1128
1082
 
@@ -1154,29 +1108,88 @@ export class ConsoleInsight extends HTMLElement {
1154
1108
  selectedRating: this.#selectedRating,
1155
1109
  noLogging: Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
1156
1110
  Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING,
1157
- onClose: this.#onClose.bind(this),
1158
- onSearch: this.#onSearch.bind(this),
1159
- onRating: this.#onRating.bind(this),
1160
- onReport: this.#onReport.bind(this),
1161
- onGoToSignIn: this.#onGoToSignIn.bind(this),
1162
- onConsentReminderConfirmed: this.#onConsentReminderConfirmed.bind(this),
1163
- onToggleReferenceDetails: this.#onToggleReferenceDetails.bind(this),
1164
- onDisclaimerSettingsLink: this.#onDisclaimerSettingsLink.bind(this),
1165
- onReminderSettingsLink: this.#onReminderSettingsLink.bind(this),
1166
- onEnableInsightsInSettingsLink: this.#onEnableInsightsInSettingsLink.bind(this),
1111
+ callbacks: {
1112
+ onClose: this.#onClose.bind(this),
1113
+ onSearch: this.#onSearch.bind(this),
1114
+ onRating: this.#onRating.bind(this),
1115
+ onReport: this.#onReport.bind(this),
1116
+ onGoToSignIn: this.#onGoToSignIn.bind(this),
1117
+ onConsentReminderConfirmed: this.#onConsentReminderConfirmed.bind(this),
1118
+ onToggleReferenceDetails: this.#onToggleReferenceDetails.bind(this),
1119
+ onDisclaimerSettingsLink: this.#onDisclaimerSettingsLink.bind(this),
1120
+ onReminderSettingsLink: this.#onReminderSettingsLink.bind(this),
1121
+ onEnableInsightsInSettingsLink: this.#onEnableInsightsInSettingsLink.bind(this),
1122
+ },
1167
1123
  };
1168
1124
  const output: ViewOutput = {
1169
1125
  referenceDetailsRef: this.#referenceDetailsRef,
1126
+ headerRef: this.#headerRef,
1127
+ citationLinks: [],
1170
1128
  };
1129
+
1130
+ // Future Widget view function starts here.
1131
+ const {state, noLogging, callbacks} = input;
1132
+ const {onClose, onDisclaimerSettingsLink} = callbacks;
1133
+
1134
+ const jslog = `${VisualLogging.section(state.type).track({resize: true})}`;
1135
+ let header: Lit.LitTemplate = Lit.nothing;
1136
+ let main: Lit.LitTemplate = Lit.nothing;
1137
+ const mainClasses: Record<string, true> = {};
1138
+ let footer: Lit.LitTemplate|undefined;
1139
+
1140
+ switch (state.type) {
1141
+ case State.LOADING:
1142
+ header = renderHeader({headerText: i18nString(UIStrings.generating), onClose}, output.headerRef);
1143
+ main = renderLoading();
1144
+ break;
1145
+ case State.INSIGHT:
1146
+ header = renderHeader(
1147
+ {headerText: i18nString(UIStrings.insight), onClose, showSpinner: !state.completed}, output.headerRef);
1148
+ main = renderInsight(state, input.renderer, input.disableAnimations, callbacks, output);
1149
+ footer = renderInsightFooter(noLogging, input.selectedRating, callbacks);
1150
+ break;
1151
+ case State.ERROR:
1152
+ header = renderHeader({headerText: i18nString(UIStrings.error), onClose}, output.headerRef);
1153
+ main = renderError(i18nString(UIStrings.errorBody));
1154
+ footer = renderDisclaimerFooter(noLogging, onDisclaimerSettingsLink);
1155
+ break;
1156
+ case State.CONSENT_REMINDER:
1157
+ header = renderHeader(
1158
+ {headerText: 'Understand console messages with AI', onClose, showIcon: true}, output.headerRef);
1159
+ mainClasses['reminder-container'] = true;
1160
+ main = renderConsentReminder(noLogging);
1161
+ footer = renderConsentReminderFooter(callbacks.onReminderSettingsLink, callbacks.onConsentReminderConfirmed);
1162
+ break;
1163
+ case State.SETTING_IS_NOT_TRUE:
1164
+ mainClasses['opt-in-teaser'] = true;
1165
+ main = renderSettingIsNotTrue(callbacks.onEnableInsightsInSettingsLink);
1166
+ break;
1167
+ case State.NOT_LOGGED_IN:
1168
+ case State.SYNC_IS_PAUSED:
1169
+ header = renderHeader({headerText: i18nString(UIStrings.signInToUse), onClose}, output.headerRef);
1170
+ main = renderNotLoggedIn();
1171
+ footer = renderSignInFooter(callbacks.onGoToSignIn);
1172
+ break;
1173
+ case State.OFFLINE:
1174
+ header = renderHeader({headerText: i18nString(UIStrings.offlineHeader), onClose}, output.headerRef);
1175
+ main = renderError(i18nString(UIStrings.offline));
1176
+ footer = renderDisclaimerFooter(noLogging, onDisclaimerSettingsLink);
1177
+ break;
1178
+ }
1179
+
1171
1180
  // clang-format off
1172
1181
  render(html`
1173
1182
  <style>${styles}</style>
1174
1183
  <style>${Input.checkboxStyles}</style>
1175
1184
  <div class="wrapper" jslog=${VisualLogging.pane('console-insights').track({resize: true})}>
1176
1185
  <div class="animation-wrapper">
1177
- ${renderHeader(input)}
1178
- ${renderMain(input, output)}
1179
- ${renderFooter(input)}
1186
+ ${header}
1187
+ <main jslog=${jslog} class=${Directives.classMap(mainClasses)}>
1188
+ ${main}
1189
+ </main>
1190
+ ${footer?html`<footer jslog=${VisualLogging.section('footer')}>
1191
+ ${footer}
1192
+ </footer>`:Lit.nothing}
1180
1193
  </div>
1181
1194
  </div>
1182
1195
  `, this.#shadow, {
@@ -1184,6 +1197,8 @@ export class ConsoleInsight extends HTMLElement {
1184
1197
  });
1185
1198
  // clang-format on
1186
1199
 
1200
+ this.#citationLinks = output.citationLinks;
1201
+
1187
1202
  if (this.#referenceDetailsRef.value) {
1188
1203
  this.#referenceDetailsRef.value.open = this.#areReferenceDetailsOpen;
1189
1204
  }