chrome-devtools-frontend 1.0.1539972 → 1.0.1541552

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 (106) hide show
  1. package/eslint.config.mjs +167 -151
  2. package/front_end/Tests.js +5 -1
  3. package/front_end/core/common/Revealer.ts +5 -0
  4. package/front_end/core/common/Settings.ts +106 -95
  5. package/front_end/core/host/InspectorFrontendHost.ts +10 -10
  6. package/front_end/core/sdk/NetworkManager.ts +16 -11
  7. package/front_end/core/sdk/sdk-meta.ts +0 -35
  8. package/front_end/entrypoints/main/MainImpl.ts +15 -7
  9. package/front_end/entrypoints/shell/shell.ts +1 -0
  10. package/front_end/entrypoints/trace_app/trace_app.ts +1 -0
  11. package/front_end/foundation/README.md +10 -0
  12. package/front_end/foundation/Universe.ts +29 -0
  13. package/front_end/foundation/foundation.ts +7 -0
  14. package/front_end/generated/InspectorBackendCommands.ts +6 -3
  15. package/front_end/generated/SupportedCSSProperties.js +13 -0
  16. package/front_end/generated/protocol.ts +58 -2
  17. package/front_end/models/ai_assistance/BuiltInAi.ts +2 -1
  18. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +44 -34
  19. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +121 -56
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +104 -62
  21. package/front_end/models/ai_assistance/performance/AIQueries.ts +56 -2
  22. package/front_end/{panels/issues → models/issues_manager}/IssueAggregator.ts +83 -65
  23. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  24. package/front_end/models/trace/Processor.ts +5 -4
  25. package/front_end/models/trace/insights/types.ts +1 -1
  26. package/front_end/models/trace/types/TraceEvents.ts +1 -1
  27. package/front_end/models/workspace/IgnoreListManager.ts +41 -47
  28. package/front_end/models/workspace/workspace-meta.ts +40 -0
  29. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +1 -1
  30. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +1 -1
  31. package/front_end/panels/animation/AnimationTimeline.ts +4 -4
  32. package/front_end/panels/animation/AnimationUI.ts +28 -34
  33. package/front_end/panels/elements/ElementsTreeElement.ts +37 -9
  34. package/front_end/panels/elements/LayoutPane.ts +2 -2
  35. package/front_end/panels/elements/components/AdornerManager.ts +9 -9
  36. package/front_end/panels/elements/layoutPane.css +5 -9
  37. package/front_end/panels/event_listeners/EventListenersView.ts +1 -1
  38. package/front_end/panels/explain/components/ConsoleInsight.ts +498 -449
  39. package/front_end/panels/issues/AffectedResourcesView.ts +3 -4
  40. package/front_end/panels/issues/CorsIssueDetailsView.ts +1 -2
  41. package/front_end/panels/issues/IssueView.ts +1 -1
  42. package/front_end/panels/issues/IssuesPane.ts +12 -15
  43. package/front_end/panels/issues/issues.ts +0 -2
  44. package/front_end/panels/network/NetworkDataGridNode.ts +2 -1
  45. package/front_end/panels/network/RequestConditionsDrawer.ts +149 -46
  46. package/front_end/panels/network/RequestTimingView.ts +13 -8
  47. package/front_end/panels/network/network-meta.ts +11 -0
  48. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +1 -1
  49. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  50. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +1 -1
  51. package/front_end/panels/sources/breakpointsView.css +1 -1
  52. package/front_end/panels/sources/sourcesPanel.css +2 -2
  53. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -3
  54. package/front_end/panels/timeline/TimelinePanel.ts +3 -3
  55. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +16 -10
  56. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -0
  57. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -1
  58. package/front_end/third_party/chromium/README.chromium +1 -1
  59. package/front_end/third_party/puppeteer/README.chromium +2 -2
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +3 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +3 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  75. package/front_end/third_party/puppeteer/package/package.json +1 -1
  76. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +3 -1
  77. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  78. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  79. package/front_end/ui/components/markdown_view/MarkdownView.ts +6 -6
  80. package/front_end/ui/components/snackbars/Snackbars.docs.ts +46 -0
  81. package/front_end/ui/{components/docs/context_menu/basic.ts → legacy/ContextMenu.docs.ts} +58 -25
  82. package/front_end/ui/legacy/UIUtils.ts +2 -1
  83. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -1
  84. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +148 -125
  85. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +3 -3
  86. package/front_end/ui/legacy/components/perf_ui/pieChart.css +1 -1
  87. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  88. package/front_end/ui/legacy/inspectorCommon.css +3 -2
  89. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  90. package/mcp/mcp.ts +16 -0
  91. package/package.json +2 -1
  92. package/front_end/ui/components/docs/context_menu/basic.html +0 -45
  93. package/front_end/ui/components/docs/linkifier/simple-url.html +0 -25
  94. package/front_end/ui/components/docs/linkifier/simple-url.ts +0 -25
  95. package/front_end/ui/components/docs/panel_feedback/basic.html +0 -25
  96. package/front_end/ui/components/docs/panel_feedback/basic.ts +0 -21
  97. package/front_end/ui/components/docs/panel_feedback/button.html +0 -25
  98. package/front_end/ui/components/docs/panel_feedback/button.ts +0 -19
  99. package/front_end/ui/components/docs/panel_introduction_steps/basic.html +0 -25
  100. package/front_end/ui/components/docs/panel_introduction_steps/basic.ts +0 -28
  101. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.html +0 -20
  102. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.ts +0 -20
  103. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.html +0 -20
  104. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.ts +0 -18
  105. package/front_end/ui/components/docs/snackbars/basic.html +0 -17
  106. package/front_end/ui/components/docs/snackbars/basic.ts +0 -50
@@ -187,6 +187,28 @@ const REPORT_URL = 'https://support.google.com/legal/troubleshooter/1114905?hl=e
187
187
  Platform.DevToolsPath.UrlString;
188
188
  const SIGN_IN_URL = 'https://accounts.google.com' as Platform.DevToolsPath.UrlString;
189
189
 
190
+ interface ViewInput {
191
+ state: StateData;
192
+ disableAnimations: boolean;
193
+ renderer: MarkdownView.MarkdownView.MarkdownInsightRenderer;
194
+ selectedRating?: boolean;
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;
206
+ }
207
+
208
+ interface ViewOutput {
209
+ referenceDetailsRef: Lit.Directives.Ref<HTMLDetailsElement>;
210
+ }
211
+
190
212
  const enum State {
191
213
  INSIGHT = 'insight',
192
214
  LOADING = 'loading',
@@ -209,6 +231,7 @@ type StateData = {
209
231
  isPageReloadRecommended: boolean,
210
232
  completed: boolean,
211
233
  directCitationUrls: string[],
234
+ relatedUrls: string[],
212
235
  timedOut?: boolean,
213
236
  }&Host.AidaClient.DoConversationResponse|{
214
237
  type: State.ERROR,
@@ -247,10 +270,420 @@ const markedExtension = {
247
270
  renderer: () => '',
248
271
  };
249
272
 
273
+ function isSearchRagResponse(metadata: Host.AidaClient.ResponseMetadata): boolean {
274
+ return Boolean(metadata.factualityMetadata?.facts.length);
275
+ }
276
+
277
+ function renderSearchButton(input: ViewInput): Lit.TemplateResult {
278
+ // clang-format off
279
+ return html`<devtools-button
280
+ @click=${input.onSearch}
281
+ class="search-button"
282
+ .variant=${Buttons.Button.Variant.OUTLINED}
283
+ .jslogContext=${'search'}
284
+ >
285
+ ${i18nString(UIStrings.search)}
286
+ </devtools-button>`;
287
+ // clang-format on
288
+ }
289
+
290
+ function renderLearnMoreAboutInsights(): Lit.TemplateResult {
291
+ // clang-format off
292
+ return html`<x-link href=${LEARN_MORE_URL} class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
293
+ ${i18nString(UIStrings.learnMore)}
294
+ </x-link>`;
295
+ // clang-format on
296
+ }
297
+
298
+ function maybeRenderSources(state: StateData): Lit.LitTemplate {
299
+ if (state.type !== State.INSIGHT || !state.directCitationUrls.length) {
300
+ return Lit.nothing;
301
+ }
302
+
303
+ // clang-format off
304
+ return html`
305
+ <ol class="sources-list">
306
+ ${state.directCitationUrls.map((url, index) => html`
307
+ <li>
308
+ <x-link
309
+ href=${url}
310
+ class="link"
311
+ data-index=${index + 1}
312
+ jslog=${VisualLogging.link('references.console-insights').track({click: true})}
313
+ >
314
+ ${url}
315
+ </x-link>
316
+ </li>
317
+ `)}
318
+ </ol>
319
+ `;
320
+ // clang-format on
321
+ }
322
+
323
+ function maybeRenderRelatedContent(state: StateData): Lit.LitTemplate {
324
+ if (state.type !== State.INSIGHT || state.relatedUrls.length === 0) {
325
+ return Lit.nothing;
326
+ }
327
+ // clang-format off
328
+ return html`
329
+ ${state.directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
330
+ <ul class="references-list">
331
+ ${state.relatedUrls.map(relatedUrl => html`
332
+ <li>
333
+ <x-link
334
+ href=${relatedUrl}
335
+ class="link"
336
+ jslog=${VisualLogging.link('references.console-insights').track({click: true})}
337
+ >
338
+ ${relatedUrl}
339
+ </x-link>
340
+ </li>
341
+ `)}
342
+ </ul>
343
+ `;
344
+ // clang-format on
345
+ }
346
+
347
+ function renderMain(input: ViewInput, output: ViewOutput): Lit.TemplateResult {
348
+ const jslog = `${VisualLogging.section(input.state.type).track({resize: true})}`;
349
+
350
+ // 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
+ return html`
366
+ <main jslog=${jslog}>
367
+ ${
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
371
+ }
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})}>
375
+ <summary>${i18nString(UIStrings.references)}</summary>
376
+ ${maybeRenderSources(input.state)}
377
+ ${maybeRenderRelatedContent(input.state)}
378
+ </details>
379
+ ` : Lit.nothing}
380
+ <details jslog=${VisualLogging.expand('sources').track({click: true})}>
381
+ <summary>${i18nString(UIStrings.inputData)}</summary>
382
+ <devtools-console-insight-sources-list .sources=${input.state.sources} .isPageReloadRecommended=${input.state.isPageReloadRecommended}>
383
+ </devtools-console-insight-sources-list>
384
+ </details>
385
+ <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
+ }
467
+ // clang-format on
468
+ }
469
+
470
+ function renderDisclaimer(input: ViewInput): Lit.LitTemplate {
471
+ // clang-format off
472
+ return html`<span>
473
+ AI tools may generate inaccurate info that doesn't represent Google's views. ${input.noLogging
474
+ ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
475
+ : 'Data sent to Google may be seen by human reviewers to improve this feature.'
476
+ } <button class="link" role="link" @click=${input.onDisclaimerSettingsLink}
477
+ jslog=${VisualLogging.action('open-ai-settings').track({click: true})}>
478
+ Open settings
479
+ </button> or <x-link href=${LEARN_MORE_URL}
480
+ class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
481
+ learn more
482
+ </x-link>
483
+ </span>`;
484
+ // clang-format on
485
+ }
486
+
487
+ function renderFooter(input: ViewInput): Lit.LitTemplate {
488
+ const disclaimer = renderDisclaimer(input);
489
+ // 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
+ }
612
+ // clang-format on
613
+ }
614
+
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
632
+ }
633
+ }
634
+
635
+ function renderSpinner(state: StateData): Lit.LitTemplate {
636
+ // clang-format off
637
+ if (state.type === State.INSIGHT && !state.completed) {
638
+ return html`<devtools-spinner></devtools-spinner>`;
639
+ }
640
+ return Lit.nothing;
641
+ // clang-format on
642
+ }
643
+
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;
649
+ // clang-format off
650
+ return html`
651
+ <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}
658
+ <div class="filler">
659
+ <h2 tabindex="-1">
660
+ ${getHeader(input.state)}
661
+ </h2>
662
+ ${renderSpinner(input.state)}
663
+ </div>
664
+ <div class="close-button">
665
+ <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
+ }
674
+ jslog=${VisualLogging.close().track({click: true})}
675
+ @click=${input.onClose}
676
+ ></devtools-button>
677
+ </div>
678
+ </header>
679
+ `;
680
+ // clang-format on
681
+ }
682
+
250
683
  export class ConsoleInsight extends HTMLElement {
251
684
  static async create(promptBuilder: PublicPromptBuilder, aidaClient: PublicAidaClient): Promise<ConsoleInsight> {
252
- const aidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
253
- return new ConsoleInsight(promptBuilder, aidaClient, aidaAvailability);
685
+ const aidaPreconditions = await Host.AidaClient.AidaClient.checkAccessPreconditions();
686
+ return new ConsoleInsight(promptBuilder, aidaClient, aidaPreconditions);
254
687
  }
255
688
 
256
689
  readonly #shadow = this.attachShadow({mode: 'open'});
@@ -270,17 +703,17 @@ export class ConsoleInsight extends HTMLElement {
270
703
  #selectedRating?: boolean;
271
704
 
272
705
  #consoleInsightsEnabledSetting: Common.Settings.Setting<boolean>|undefined;
273
- #aidaAvailability: Host.AidaClient.AidaAccessPreconditions;
706
+ #aidaPreconditions: Host.AidaClient.AidaAccessPreconditions;
274
707
  #boundOnAidaAvailabilityChange: () => Promise<void>;
275
708
  #marked: Marked.Marked.Marked;
276
709
 
277
710
  constructor(
278
711
  promptBuilder: PublicPromptBuilder, aidaClient: PublicAidaClient,
279
- aidaAvailability: Host.AidaClient.AidaAccessPreconditions) {
712
+ aidaPreconditions: Host.AidaClient.AidaAccessPreconditions) {
280
713
  super();
281
714
  this.#promptBuilder = promptBuilder;
282
715
  this.#aidaClient = aidaClient;
283
- this.#aidaAvailability = aidaAvailability;
716
+ this.#aidaPreconditions = aidaPreconditions;
284
717
  this.#consoleInsightsEnabledSetting = this.#getConsoleInsightsEnabledSetting();
285
718
  this.#renderer = new MarkdownView.MarkdownView.MarkdownInsightRenderer(this.#citationClickHandler.bind(this));
286
719
  this.#marked = new Marked.Marked.Marked({extensions: [markedExtension]});
@@ -330,7 +763,7 @@ export class ConsoleInsight extends HTMLElement {
330
763
  }
331
764
 
332
765
  #getStateFromAidaAvailability(): StateData {
333
- switch (this.#aidaAvailability) {
766
+ switch (this.#aidaPreconditions) {
334
767
  case Host.AidaClient.AidaAccessPreconditions.AVAILABLE: {
335
768
  // Allows skipping the consent reminder if the user enabled the feature via settings in the current session
336
769
  const skipReminder =
@@ -400,8 +833,8 @@ export class ConsoleInsight extends HTMLElement {
400
833
 
401
834
  async #onAidaAvailabilityChange(): Promise<void> {
402
835
  const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
403
- if (currentAidaAvailability !== this.#aidaAvailability) {
404
- this.#aidaAvailability = currentAidaAvailability;
836
+ if (currentAidaAvailability !== this.#aidaPreconditions) {
837
+ this.#aidaPreconditions = currentAidaAvailability;
405
838
  this.#state = this.#getStateFromAidaAvailability();
406
839
  void this.#generateInsightIfNeeded();
407
840
  }
@@ -528,7 +961,7 @@ export class ConsoleInsight extends HTMLElement {
528
961
  #insertCitations(explanation: string, metadata: Host.AidaClient.ResponseMetadata):
529
962
  {explanationWithCitations: string, directCitationUrls: string[]} {
530
963
  const directCitationUrls: string[] = [];
531
- if (!this.#isSearchRagResponse(metadata) || !metadata.attributionMetadata) {
964
+ if (!isSearchRagResponse(metadata) || !metadata.attributionMetadata) {
532
965
  return {explanationWithCitations: explanation, directCitationUrls};
533
966
  }
534
967
 
@@ -576,10 +1009,33 @@ export class ConsoleInsight extends HTMLElement {
576
1009
  }
577
1010
  }
578
1011
 
1012
+ #deriveRelatedUrls(directCitationUrls: string[], metadata: Host.AidaClient.ResponseMetadata): string[] {
1013
+ if (!metadata.factualityMetadata?.facts.length) {
1014
+ return [];
1015
+ }
1016
+
1017
+ const relatedUrls =
1018
+ metadata.factualityMetadata.facts.filter(fact => fact.sourceUri && !directCitationUrls.includes(fact.sourceUri))
1019
+ .map(fact => fact.sourceUri as string) ||
1020
+ [];
1021
+ const trainingDataUrls =
1022
+ metadata.attributionMetadata?.citations
1023
+ .filter(
1024
+ citation => citation.sourceType === Host.AidaClient.CitationSourceType.TRAINING_DATA &&
1025
+ (citation.uri || citation.repository))
1026
+ .map(citation => citation.uri || `https://www.github.com/${citation.repository}`) ||
1027
+ [];
1028
+ const dedupedTrainingDataUrls =
1029
+ [...new Set(trainingDataUrls.filter(url => !relatedUrls.includes(url) && !directCitationUrls.includes(url)))];
1030
+ relatedUrls.push(...dedupedTrainingDataUrls);
1031
+ return relatedUrls;
1032
+ }
1033
+
579
1034
  async #generateInsight(): Promise<void> {
580
1035
  try {
581
1036
  for await (const {sources, isPageReloadRecommended, explanation, metadata, completed} of this.#getInsight()) {
582
1037
  const {explanationWithCitations, directCitationUrls} = this.#insertCitations(explanation, metadata);
1038
+ const relatedUrls = this.#deriveRelatedUrls(directCitationUrls, metadata);
583
1039
  const tokens = this.#validateMarkdown(explanationWithCitations);
584
1040
  const valid = tokens !== false;
585
1041
  if (valid) {
@@ -595,6 +1051,7 @@ export class ConsoleInsight extends HTMLElement {
595
1051
  isPageReloadRecommended,
596
1052
  completed,
597
1053
  directCitationUrls,
1054
+ relatedUrls,
598
1055
  });
599
1056
  }
600
1057
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightGenerated);
@@ -669,465 +1126,57 @@ export class ConsoleInsight extends HTMLElement {
669
1126
  }, {once: true});
670
1127
  }
671
1128
 
672
- #renderSearchButton(): Lit.TemplateResult {
673
- // clang-format off
674
- return html`<devtools-button
675
- @click=${this.#onSearch}
676
- class="search-button"
677
- .data=${
678
- {
679
- variant: Buttons.Button.Variant.OUTLINED,
680
- jslogContext: 'search',
681
- } as Buttons.Button.ButtonData
682
- }
683
- >
684
- ${i18nString(UIStrings.search)}
685
- </devtools-button>`;
686
- // clang-format on
687
- }
688
-
689
- #renderLearnMoreAboutInsights(): Lit.TemplateResult {
690
- // clang-format off
691
- return html`<x-link href=${LEARN_MORE_URL} class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
692
- ${i18nString(UIStrings.learnMore)}
693
- </x-link>`;
694
- // clang-format on
695
- }
696
-
697
- #maybeRenderSources(): Lit.LitTemplate {
698
- if (this.#state.type !== State.INSIGHT || !this.#state.directCitationUrls.length) {
699
- return Lit.nothing;
700
- }
701
-
702
- // clang-format off
703
- return html`
704
- <ol class="sources-list">
705
- ${this.#state.directCitationUrls.map((url, index) => html`
706
- <li>
707
- <x-link
708
- href=${url}
709
- class="link"
710
- data-index=${index + 1}
711
- jslog=${VisualLogging.link('references.console-insights').track({click: true})}
712
- >
713
- ${url}
714
- </x-link>
715
- </li>
716
- `)}
717
- </ol>
718
- `;
719
- // clang-format on
720
- }
721
-
722
- #maybeRenderRelatedContent(): Lit.LitTemplate {
723
- if (this.#state.type !== State.INSIGHT || !this.#state.metadata.factualityMetadata?.facts.length) {
724
- return Lit.nothing;
725
- }
726
- const directCitationUrls = this.#state.directCitationUrls;
727
- const relatedUrls = this.#state.metadata.factualityMetadata.facts
728
- .filter(fact => fact.sourceUri && !directCitationUrls.includes(fact.sourceUri))
729
- .map(fact => fact.sourceUri as string);
730
- const trainingDataUrls =
731
- this.#state.metadata.attributionMetadata?.citations
732
- .filter(
733
- citation => citation.sourceType === Host.AidaClient.CitationSourceType.TRAINING_DATA &&
734
- (citation.uri || citation.repository))
735
- .map(citation => citation.uri || `https://www.github.com/${citation.repository}`) ||
736
- [];
737
- const dedupedTrainingDataUrls =
738
- [...new Set(trainingDataUrls.filter(url => !relatedUrls.includes(url) && !directCitationUrls.includes(url)))];
739
- relatedUrls.push(...dedupedTrainingDataUrls);
740
-
741
- if (relatedUrls.length === 0) {
742
- return Lit.nothing;
743
- }
744
- // clang-format off
745
- return html`
746
- ${this.#state.directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
747
- <ul class="references-list">
748
- ${relatedUrls.map(relatedUrl => html`
749
- <li>
750
- <x-link
751
- href=${relatedUrl}
752
- class="link"
753
- jslog=${VisualLogging.link('references.console-insights').track({click: true})}
754
- >
755
- ${relatedUrl}
756
- </x-link>
757
- </li>
758
- `)}
759
- </ul>
760
- `;
761
- // clang-format on
762
- }
763
-
764
- #isSearchRagResponse(metadata: Host.AidaClient.ResponseMetadata): boolean {
765
- return Boolean(metadata.factualityMetadata?.facts.length);
766
- }
767
-
768
1129
  #onToggleReferenceDetails(): void {
769
1130
  if (this.#referenceDetailsRef.value) {
770
1131
  this.#areReferenceDetailsOpen = this.#referenceDetailsRef.value.open;
771
1132
  }
772
1133
  }
773
1134
 
774
- #renderMain(): Lit.TemplateResult {
775
- const jslog = `${VisualLogging.section(this.#state.type).track({resize: true})}`;
776
- const noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
777
- Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
778
-
779
- // clang-format off
780
- switch (this.#state.type) {
781
- case State.LOADING:
782
- return html`<main jslog=${jslog}>
783
- <div role="presentation" aria-label="Loading" class="loader" style="clip-path: url('#clipPath');">
784
- <svg width="100%" height="64">
785
- <clipPath id="clipPath">
786
- <rect x="0" y="0" width="100%" height="16" rx="8"></rect>
787
- <rect x="0" y="24" width="100%" height="16" rx="8"></rect>
788
- <rect x="0" y="48" width="100%" height="16" rx="8"></rect>
789
- </clipPath>
790
- </svg>
791
- </div>
792
- </main>`;
793
- case State.INSIGHT:
794
- return html`
795
- <main jslog=${jslog}>
796
- ${
797
- this.#state.validMarkdown ? html`<devtools-markdown-view
798
- .data=${{tokens: this.#state.tokens, renderer: this.#renderer, animationEnabled: !this.disableAnimations} as MarkdownView.MarkdownView.MarkdownViewData}>
799
- </devtools-markdown-view>`: this.#state.explanation
800
- }
801
- ${this.#state.timedOut ? html`<p class="error-message">${i18nString(UIStrings.timedOut)}</p>` : Lit.nothing}
802
- ${this.#isSearchRagResponse(this.#state.metadata) ? html`
803
- <details class="references" ${Lit.Directives.ref(this.#referenceDetailsRef)} @toggle=${this.#onToggleReferenceDetails} jslog=${VisualLogging.expand('references').track({click: true})}>
804
- <summary>${i18nString(UIStrings.references)}</summary>
805
- ${this.#maybeRenderSources()}
806
- ${this.#maybeRenderRelatedContent()}
807
- </details>
808
- ` : Lit.nothing}
809
- <details jslog=${VisualLogging.expand('sources').track({click: true})}>
810
- <summary>${i18nString(UIStrings.inputData)}</summary>
811
- <devtools-console-insight-sources-list .sources=${this.#state.sources} .isPageReloadRecommended=${this.#state.isPageReloadRecommended}>
812
- </devtools-console-insight-sources-list>
813
- </details>
814
- <div class="buttons">
815
- ${this.#renderSearchButton()}
816
- </div>
817
- </main>`;
818
- case State.ERROR:
819
- return html`
820
- <main jslog=${jslog}>
821
- <div class="error">${i18nString(UIStrings.errorBody)}</div>
822
- </main>`;
823
- case State.CONSENT_REMINDER:
824
- return html`
825
- <main class="reminder-container" jslog=${jslog}>
826
- <h3>Things to consider</h3>
827
- <div class="reminder-items">
828
- <div>
829
- <devtools-icon name="google" class="medium">
830
- </devtools-icon>
831
- </div>
832
- <div>The console message, associated stack trace, related source code, and the associated network headers are sent to Google to generate explanations. ${noLogging
833
- ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
834
- : 'This data may be seen by human reviewers to improve this feature. Avoid sharing sensitive or personal information.'}
835
- </div>
836
- <div>
837
- <devtools-icon name="policy" class="medium">
838
- </devtools-icon>
839
- </div>
840
- <div>Use of this feature is subject to the <x-link
841
- href=${TERMS_OF_SERVICE_URL}
842
- class="link"
843
- jslog=${VisualLogging.link('terms-of-service.console-insights').track({click: true})}>
844
- Google Terms of Service
845
- </x-link> and <x-link
846
- href=${PRIVACY_POLICY_URL}
847
- class="link"
848
- jslog=${VisualLogging.link('privacy-policy.console-insights').track({click: true})}>
849
- Google Privacy Policy
850
- </x-link>
851
- </div>
852
- <div>
853
- <devtools-icon name="warning" class="medium">
854
- </devtools-icon>
855
- </div>
856
- <div>
857
- <x-link
858
- href=${CODE_SNIPPET_WARNING_URL}
859
- class="link"
860
- jslog=${VisualLogging.link('code-snippets-explainer.console-insights').track({click: true})}
861
- >Use generated code snippets with caution</x-link>
862
- </div>
863
- </div>
864
- </main>
865
- `;
866
- case State.SETTING_IS_NOT_TRUE: {
867
- const settingsLink = html`<button
868
- class="link" role="link"
869
- jslog=${VisualLogging.action('open-ai-settings').track({click: true})}
870
- @click=${() => {
871
- Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightsOptInTeaserSettingsLinkClicked);
872
- void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
873
- }}
874
- >${i18nString(UIStrings.settingsLink)}</button>`;
875
-
876
- return html`<main class="opt-in-teaser" jslog=${jslog}>
877
- <div class="badge">
878
- <devtools-icon name="lightbulb-spark" class="medium">
879
- </devtools-icon>
880
- </div>
881
- <div>
882
- ${i18nTemplate(UIStrings.turnOnInSettings, {PH1: settingsLink})} ${
883
- this.#renderLearnMoreAboutInsights()}
884
- </div>
885
- </main>`;
886
- }
887
- case State.NOT_LOGGED_IN:
888
- case State.SYNC_IS_PAUSED:
889
- return html`
890
- <main jslog=${jslog}>
891
- <div class="error">${Root.Runtime.hostConfig.isOffTheRecord ? i18nString(UIStrings.notAvailableInIncognitoMode) : i18nString(UIStrings.notLoggedIn)}</div>
892
- </main>`;
893
- case State.OFFLINE:
894
- return html`
895
- <main jslog=${jslog}>
896
- <div class="error">${i18nString(UIStrings.offline)}</div>
897
- </main>`;
898
- }
899
- // clang-format on
900
- }
901
-
902
- #renderDisclaimer(): Lit.LitTemplate {
903
- const noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
904
- Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
905
-
906
- // clang-format off
907
- return html`<span>
908
- AI tools may generate inaccurate info that doesn't represent Google's views. ${noLogging
909
- ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
910
- : 'Data sent to Google may be seen by human reviewers to improve this feature.'
911
- } <button class="link" role="link" @click=${() => UI.ViewManager.ViewManager.instance().showView('chrome-ai')}
912
- jslog=${VisualLogging.action('open-ai-settings').track({click: true})}>
913
- Open settings
914
- </button> or <x-link href=${LEARN_MORE_URL}
915
- class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
916
- learn more
917
- </x-link>
918
- </span>`;
919
- // clang-format on
920
- }
921
-
922
- #renderFooter(): Lit.LitTemplate {
923
- const disclaimer = this.#renderDisclaimer();
924
- // clang-format off
925
- switch (this.#state.type) {
926
- case State.LOADING:
927
- case State.SETTING_IS_NOT_TRUE:
928
- return Lit.nothing;
929
- case State.ERROR:
930
- case State.OFFLINE:
931
- return html`<footer jslog=${VisualLogging.section('footer')}>
932
- <div class="disclaimer">
933
- ${disclaimer}
934
- </div>
935
- </footer>`;
936
- case State.NOT_LOGGED_IN:
937
- case State.SYNC_IS_PAUSED:
938
- if (Root.Runtime.hostConfig.isOffTheRecord) {
939
- return Lit.nothing;
940
- }
941
- return html`<footer jslog=${VisualLogging.section('footer')}>
942
- <div class="filler"></div>
943
- <div>
944
- <devtools-button
945
- @click=${this.#onGoToSignIn}
946
- .data=${
947
- {
948
- variant: Buttons.Button.Variant.PRIMARY,
949
- jslogContext: 'update-settings',
950
- } as Buttons.Button.ButtonData
951
- }
952
- >
953
- ${UIStrings.signIn}
954
- </devtools-button>
955
- </div>
956
- </footer>`;
957
- case State.CONSENT_REMINDER:
958
- return html`<footer jslog=${VisualLogging.section('footer')}>
959
- <div class="filler"></div>
960
- <div class="buttons">
961
- <devtools-button
962
- @click=${() => {
963
- Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightsReminderTeaserSettingsLinkClicked);
964
- void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
965
- }}
966
- .data=${
967
- {
968
- variant: Buttons.Button.Variant.TONAL,
969
- jslogContext: 'settings',
970
- title: 'Settings',
971
- } as Buttons.Button.ButtonData
972
- }
973
- >
974
- Settings
975
- </devtools-button>
976
- <devtools-button
977
- class='continue-button'
978
- @click=${this.#onConsentReminderConfirmed}
979
- .data=${
980
- {
981
- variant: Buttons.Button.Variant.PRIMARY,
982
- jslogContext: 'continue',
983
- title: 'continue',
984
- } as Buttons.Button.ButtonData
985
- }
986
- >
987
- Continue
988
- </devtools-button>
989
- </div>
990
- </footer>`;
991
- case State.INSIGHT:
992
- return html`<footer jslog=${VisualLogging.section('footer')}>
993
- <div class="disclaimer">
994
- ${disclaimer}
995
- </div>
996
- <div class="filler"></div>
997
- <div class="rating">
998
- <devtools-button
999
- data-rating="true"
1000
- .data=${
1001
- {
1002
- variant: Buttons.Button.Variant.ICON_TOGGLE,
1003
- size: Buttons.Button.Size.SMALL,
1004
- iconName: 'thumb-up',
1005
- toggledIconName: 'thumb-up',
1006
- toggleOnClick: false,
1007
- toggleType: Buttons.Button.ToggleType.PRIMARY,
1008
- disabled: this.#selectedRating !== undefined,
1009
- toggled: this.#selectedRating === true,
1010
- title: i18nString(UIStrings.goodResponse),
1011
- jslogContext: 'thumbs-up',
1012
- } as Buttons.Button.ButtonData
1013
- }
1014
- @click=${this.#onRating}
1015
- ></devtools-button>
1016
- <devtools-button
1017
- data-rating="false"
1018
- .data=${
1019
- {
1020
- variant: Buttons.Button.Variant.ICON_TOGGLE,
1021
- size: Buttons.Button.Size.SMALL,
1022
- iconName: 'thumb-down',
1023
- toggledIconName: 'thumb-down',
1024
- toggleOnClick: false,
1025
- toggleType: Buttons.Button.ToggleType.PRIMARY,
1026
- disabled: this.#selectedRating !== undefined,
1027
- toggled: this.#selectedRating === false,
1028
- title: i18nString(UIStrings.badResponse),
1029
- jslogContext: 'thumbs-down',
1030
- } as Buttons.Button.ButtonData
1031
- }
1032
- @click=${this.#onRating}
1033
- ></devtools-button>
1034
- <devtools-button
1035
- .data=${
1036
- {
1037
- variant: Buttons.Button.Variant.ICON,
1038
- size: Buttons.Button.Size.SMALL,
1039
- iconName: 'report',
1040
- title: i18nString(UIStrings.report),
1041
- jslogContext: 'report',
1042
- } as Buttons.Button.ButtonData
1043
- }
1044
- @click=${this.#onReport}
1045
- ></devtools-button>
1046
- </div>
1047
-
1048
- </footer>`;
1049
- }
1050
- // clang-format on
1051
- }
1052
-
1053
- #getHeader(): string {
1054
- switch (this.#state.type) {
1055
- case State.NOT_LOGGED_IN:
1056
- case State.SYNC_IS_PAUSED:
1057
- return i18nString(UIStrings.signInToUse);
1058
- case State.OFFLINE:
1059
- return i18nString(UIStrings.offlineHeader);
1060
- case State.LOADING:
1061
- return i18nString(UIStrings.generating);
1062
- case State.INSIGHT:
1063
- return i18nString(UIStrings.insight);
1064
- case State.ERROR:
1065
- return i18nString(UIStrings.error);
1066
- case State.CONSENT_REMINDER:
1067
- return 'Understand console messages with AI';
1068
- case State.SETTING_IS_NOT_TRUE:
1069
- return ''; // not reached
1070
- }
1135
+ #onDisclaimerSettingsLink(): void {
1136
+ void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
1071
1137
  }
1072
1138
 
1073
- #renderSpinner(): Lit.LitTemplate {
1074
- // clang-format off
1075
- if (this.#state.type === State.INSIGHT && !this.#state.completed) {
1076
- return html`<devtools-spinner></devtools-spinner>`;
1077
- }
1078
- return Lit.nothing;
1079
- // clang-format on
1139
+ #onReminderSettingsLink(): void {
1140
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightsReminderTeaserSettingsLinkClicked);
1141
+ void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
1080
1142
  }
1081
1143
 
1082
- #renderHeader(): Lit.LitTemplate {
1083
- if (this.#state.type === State.SETTING_IS_NOT_TRUE) {
1084
- return Lit.nothing;
1085
- }
1086
- const hasIcon = this.#state.type === State.CONSENT_REMINDER;
1087
- // clang-format off
1088
- return html`
1089
- <header>
1090
- ${hasIcon ? html`
1091
- <div class="header-icon-container">
1092
- <devtools-icon name="lightbulb-spark" class="large">
1093
- </devtools-icon>
1094
- </div>`
1095
- : Lit.nothing}
1096
- <div class="filler">
1097
- <h2 tabindex="-1">
1098
- ${this.#getHeader()}
1099
- </h2>
1100
- ${this.#renderSpinner()}
1101
- </div>
1102
- <div class="close-button">
1103
- <devtools-button
1104
- .data=${
1105
- {
1106
- variant: Buttons.Button.Variant.ICON,
1107
- size: Buttons.Button.Size.SMALL,
1108
- iconName: 'cross',
1109
- title: i18nString(UIStrings.closeInsight),
1110
- } as Buttons.Button.ButtonData
1111
- }
1112
- jslog=${VisualLogging.close().track({click: true})}
1113
- @click=${this.#onClose}
1114
- ></devtools-button>
1115
- </div>
1116
- </header>
1117
- `;
1118
- // clang-format on
1144
+ #onEnableInsightsInSettingsLink(): void {
1145
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightsOptInTeaserSettingsLinkClicked);
1146
+ void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
1119
1147
  }
1120
1148
 
1121
1149
  #render(): void {
1150
+ const input: ViewInput = {
1151
+ state: this.#state,
1152
+ disableAnimations: this.disableAnimations,
1153
+ renderer: this.#renderer,
1154
+ selectedRating: this.#selectedRating,
1155
+ noLogging: Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
1156
+ 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),
1167
+ };
1168
+ const output: ViewOutput = {
1169
+ referenceDetailsRef: this.#referenceDetailsRef,
1170
+ };
1122
1171
  // clang-format off
1123
1172
  render(html`
1124
1173
  <style>${styles}</style>
1125
1174
  <style>${Input.checkboxStyles}</style>
1126
1175
  <div class="wrapper" jslog=${VisualLogging.pane('console-insights').track({resize: true})}>
1127
1176
  <div class="animation-wrapper">
1128
- ${this.#renderHeader()}
1129
- ${this.#renderMain()}
1130
- ${this.#renderFooter()}
1177
+ ${renderHeader(input)}
1178
+ ${renderMain(input, output)}
1179
+ ${renderFooter(input)}
1131
1180
  </div>
1132
1181
  </div>
1133
1182
  `, this.#shadow, {