chrome-devtools-frontend 1.0.1543082 → 1.0.1543472

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 (48) hide show
  1. package/AUTHORS +1 -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/root/DevToolsContext.ts +60 -0
  5. package/front_end/core/root/root.ts +6 -1
  6. package/front_end/core/sdk/TargetManager.ts +5 -6
  7. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -13
  8. package/front_end/entrypoints/main/MainImpl.ts +3 -5
  9. package/front_end/foundation/Universe.ts +13 -1
  10. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +11 -8
  11. package/front_end/models/trace/handlers/SamplesHandler.ts +64 -6
  12. package/front_end/models/trace/types/TraceEvents.ts +16 -0
  13. package/front_end/models/workspace/IgnoreListManager.ts +10 -9
  14. package/front_end/models/workspace/WorkspaceImpl.ts +5 -10
  15. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -1
  16. package/front_end/panels/application/OpenedWindowDetailsView.ts +0 -2
  17. package/front_end/panels/application/ServiceWorkersView.ts +0 -2
  18. package/front_end/panels/application/StorageView.ts +0 -1
  19. package/front_end/panels/application/components/FrameDetailsView.ts +468 -447
  20. package/front_end/panels/console/ConsoleView.ts +9 -7
  21. package/front_end/panels/console/ConsoleViewMessage.ts +19 -9
  22. package/front_end/panels/explain/components/ConsoleInsight.ts +314 -310
  23. package/front_end/panels/settings/SettingsScreen.ts +3 -6
  24. package/front_end/panels/settings/components/SyncSection.ts +218 -226
  25. package/front_end/panels/settings/components/syncSection.css +81 -80
  26. package/front_end/panels/sources/DebuggerPlugin.ts +3 -1
  27. package/front_end/panels/sources/ResourceOriginPlugin.ts +7 -3
  28. package/front_end/panels/timeline/TimelinePanel.ts +0 -21
  29. package/front_end/ui/components/docs/component_docs.ts +0 -4
  30. package/front_end/ui/components/report_view/ReportView.ts +4 -1
  31. package/front_end/ui/legacy/ReportView.ts +0 -5
  32. package/front_end/ui/legacy/TextPrompt.ts +65 -19
  33. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +8 -4
  34. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +90 -92
  35. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +114 -184
  36. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  37. package/front_end/ui/{components/docs/theme_colors/basic.ts → legacy/theme_support/ThemeColors.docs.ts} +33 -23
  38. package/package.json +1 -1
  39. package/front_end/core/common/QueryParamHandler.ts +0 -7
  40. package/front_end/ui/components/docs/input/basic.html +0 -31
  41. package/front_end/ui/components/docs/input/basic.ts +0 -12
  42. package/front_end/ui/components/docs/report/basic.html +0 -27
  43. package/front_end/ui/components/docs/report/basic.ts +0 -48
  44. package/front_end/ui/components/docs/theme_colors/basic.html +0 -56
  45. package/front_end/ui/components/docs/toggle_dark_mode.ts +0 -36
  46. package/front_end/ui/components/docs/toggle_fonts.ts +0 -74
  47. package/front_end/ui/components/docs/user_agent_client_hints/basic.html +0 -25
  48. package/front_end/ui/components/docs/user_agent_client_hints/basic.ts +0 -26
@@ -193,16 +193,18 @@ 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: (event: Event) => 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 {
@@ -274,10 +276,10 @@ function isSearchRagResponse(metadata: Host.AidaClient.ResponseMetadata): boolea
274
276
  return Boolean(metadata.factualityMetadata?.facts.length);
275
277
  }
276
278
 
277
- function renderSearchButton(input: ViewInput): Lit.TemplateResult {
279
+ function renderSearchButton(onSearch: ViewInput['callbacks']['onSearch']): Lit.TemplateResult {
278
280
  // clang-format off
279
281
  return html`<devtools-button
280
- @click=${input.onSearch}
282
+ @click=${onSearch}
281
283
  class="search-button"
282
284
  .variant=${Buttons.Button.Variant.OUTLINED}
283
285
  .jslogContext=${'search'}
@@ -295,15 +297,15 @@ function renderLearnMoreAboutInsights(): Lit.TemplateResult {
295
297
  // clang-format on
296
298
  }
297
299
 
298
- function maybeRenderSources(state: StateData): Lit.LitTemplate {
299
- if (state.type !== State.INSIGHT || !state.directCitationUrls.length) {
300
+ function maybeRenderSources(directCitationUrls: string[]): Lit.LitTemplate {
301
+ if (!directCitationUrls.length) {
300
302
  return Lit.nothing;
301
303
  }
302
304
 
303
305
  // clang-format off
304
306
  return html`
305
307
  <ol class="sources-list">
306
- ${state.directCitationUrls.map((url, index) => html`
308
+ ${directCitationUrls.map((url, index) => html`
307
309
  <li>
308
310
  <x-link
309
311
  href=${url}
@@ -320,15 +322,15 @@ function maybeRenderSources(state: StateData): Lit.LitTemplate {
320
322
  // clang-format on
321
323
  }
322
324
 
323
- function maybeRenderRelatedContent(state: StateData): Lit.LitTemplate {
324
- if (state.type !== State.INSIGHT || state.relatedUrls.length === 0) {
325
+ function maybeRenderRelatedContent(relatedUrls: string[], directCitationUrls: string[]): Lit.LitTemplate {
326
+ if (relatedUrls.length === 0) {
325
327
  return Lit.nothing;
326
328
  }
327
329
  // clang-format off
328
330
  return html`
329
- ${state.directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
331
+ ${directCitationUrls.length ? html`<h3>${i18nString(UIStrings.relatedContent)}</h3>` : Lit.nothing}
330
332
  <ul class="references-list">
331
- ${state.relatedUrls.map(relatedUrl => html`
333
+ ${relatedUrls.map(relatedUrl => html`
332
334
  <li>
333
335
  <x-link
334
336
  href=${relatedUrl}
@@ -344,136 +346,135 @@ function maybeRenderRelatedContent(state: StateData): Lit.LitTemplate {
344
346
  // clang-format on
345
347
  }
346
348
 
347
- function renderMain(input: ViewInput, output: ViewOutput): Lit.TemplateResult {
348
- const jslog = `${VisualLogging.section(input.state.type).track({resize: true})}`;
349
+ function renderLoading(): Lit.TemplateResult {
350
+ // clang-format off
351
+ return html`
352
+ <div role="presentation" aria-label="Loading" class="loader" style="clip-path: url('#clipPath');">
353
+ <svg width="100%" height="64">
354
+ <clipPath id="clipPath">
355
+ <rect x="0" y="0" width="100%" height="16" rx="8"></rect>
356
+ <rect x="0" y="24" width="100%" height="16" rx="8"></rect>
357
+ <rect x="0" y="48" width="100%" height="16" rx="8"></rect>
358
+ </clipPath>
359
+ </svg>
360
+ </div>`;
361
+ // clang-format on
362
+ }
349
363
 
364
+ function renderInsight(
365
+ insight: Extract<StateData, {type: State.INSIGHT}>, renderer: ViewInput['renderer'],
366
+ disableAnimations: ViewInput['disableAnimations'], callbacks: ViewInput['callbacks'],
367
+ output: ViewOutput): Lit.TemplateResult {
350
368
  // 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
369
  return html`
366
- <main jslog=${jslog}>
367
370
  ${
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
+ insight.validMarkdown ? html`<devtools-markdown-view
372
+ .data=${{tokens: insight.tokens, renderer, animationEnabled: !disableAnimations} as MarkdownView.MarkdownView.MarkdownViewData}>
373
+ </devtools-markdown-view>`: insight.explanation
371
374
  }
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
+ ${insight.timedOut ? html`<p class="error-message">${i18nString(UIStrings.timedOut)}</p>` : Lit.nothing}
376
+ ${isSearchRagResponse(insight.metadata) ? html`
377
+ <details class="references" ${Directives.ref(output.referenceDetailsRef)} @toggle=${callbacks.onToggleReferenceDetails} jslog=${VisualLogging.expand('references').track({click: true})}>
375
378
  <summary>${i18nString(UIStrings.references)}</summary>
376
- ${maybeRenderSources(input.state)}
377
- ${maybeRenderRelatedContent(input.state)}
379
+ ${maybeRenderSources(insight.directCitationUrls)}
380
+ ${maybeRenderRelatedContent(insight.relatedUrls, insight.directCitationUrls)}
378
381
  </details>
379
382
  ` : Lit.nothing}
380
383
  <details jslog=${VisualLogging.expand('sources').track({click: true})}>
381
384
  <summary>${i18nString(UIStrings.inputData)}</summary>
382
- <devtools-console-insight-sources-list .sources=${input.state.sources} .isPageReloadRecommended=${input.state.isPageReloadRecommended}>
385
+ <devtools-console-insight-sources-list .sources=${insight.sources} .isPageReloadRecommended=${insight.isPageReloadRecommended}>
383
386
  </devtools-console-insight-sources-list>
384
387
  </details>
385
388
  <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
- }
389
+ ${renderSearchButton(callbacks.onSearch)}
390
+ </div>`;
391
+ // clang-format on
392
+ }
393
+
394
+ function renderError(message: string): Lit.TemplateResult {
395
+ // clang-format off
396
+ return html`<div class="error">${message}</div>`;
467
397
  // clang-format on
468
398
  }
469
399
 
470
- function renderDisclaimer(input: ViewInput): Lit.LitTemplate {
400
+ function renderConsentReminder(noLogging: boolean): Lit.TemplateResult {
401
+ // clang-format off
402
+ return html`
403
+ <h3>Things to consider</h3>
404
+ <div class="reminder-items">
405
+ <div>
406
+ <devtools-icon name="google" class="medium">
407
+ </devtools-icon>
408
+ </div>
409
+ <div>The console message, associated stack trace, related source code, and the associated network headers are sent to Google to generate explanations. ${noLogging
410
+ ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
411
+ : 'This data may be seen by human reviewers to improve this feature. Avoid sharing sensitive or personal information.'}
412
+ </div>
413
+ <div>
414
+ <devtools-icon name="policy" class="medium">
415
+ </devtools-icon>
416
+ </div>
417
+ <div>Use of this feature is subject to the <x-link
418
+ href=${TERMS_OF_SERVICE_URL}
419
+ class="link"
420
+ jslog=${VisualLogging.link('terms-of-service.console-insights').track({click: true})}>
421
+ Google Terms of Service
422
+ </x-link> and <x-link
423
+ href=${PRIVACY_POLICY_URL}
424
+ class="link"
425
+ jslog=${VisualLogging.link('privacy-policy.console-insights').track({click: true})}>
426
+ Google Privacy Policy
427
+ </x-link>
428
+ </div>
429
+ <div>
430
+ <devtools-icon name="warning" class="medium">
431
+ </devtools-icon>
432
+ </div>
433
+ <div>
434
+ <x-link
435
+ href=${CODE_SNIPPET_WARNING_URL}
436
+ class="link"
437
+ jslog=${VisualLogging.link('code-snippets-explainer.console-insights').track({click: true})}
438
+ >Use generated code snippets with caution</x-link>
439
+ </div>
440
+ </div>`;
441
+ // clang-format on
442
+ }
443
+
444
+ function renderSettingIsNotTrue(onEnableInsightsInSettingsLink: () => void): Lit.TemplateResult {
445
+ // clang-format off
446
+ const settingsLink = html`
447
+ <button
448
+ class="link" role="link"
449
+ jslog=${VisualLogging.action('open-ai-settings').track({click: true})}
450
+ @click=${onEnableInsightsInSettingsLink}
451
+ >${i18nString(UIStrings.settingsLink)}</button>`;
452
+
453
+ return html`
454
+ <div class="badge">
455
+ <devtools-icon name="lightbulb-spark" class="medium">
456
+ </devtools-icon>
457
+ </div>
458
+ <div>
459
+ ${i18nTemplate(UIStrings.turnOnInSettings, {PH1: settingsLink})} ${
460
+ renderLearnMoreAboutInsights()}
461
+ </div>`;
462
+ // clang-format on
463
+ }
464
+
465
+ function renderNotLoggedIn(): Lit.TemplateResult {
466
+ return renderError(
467
+ Root.Runtime.hostConfig.isOffTheRecord ? i18nString(UIStrings.notAvailableInIncognitoMode) :
468
+ i18nString(UIStrings.notLoggedIn));
469
+ }
470
+
471
+ function renderDisclaimer(noLogging: boolean, onDisclaimerSettingsLink: () => void): Lit.LitTemplate {
471
472
  // clang-format off
472
473
  return html`<span>
473
- AI tools may generate inaccurate info that doesn't represent Google's views. ${input.noLogging
474
+ AI tools may generate inaccurate info that doesn't represent Google's views. ${noLogging
474
475
  ? 'The content you submit and that is generated by this feature will not be used to improve Google’s AI models.'
475
476
  : 'Data sent to Google may be seen by human reviewers to improve this feature.'
476
- } <button class="link" role="link" @click=${input.onDisclaimerSettingsLink}
477
+ } <button class="link" role="link" @click=${onDisclaimerSettingsLink}
477
478
  jslog=${VisualLogging.action('open-ai-settings').track({click: true})}>
478
479
  Open settings
479
480
  </button> or <x-link href=${LEARN_MORE_URL}
@@ -484,195 +485,142 @@ function renderDisclaimer(input: ViewInput): Lit.LitTemplate {
484
485
  // clang-format on
485
486
  }
486
487
 
487
- function renderFooter(input: ViewInput): Lit.LitTemplate {
488
- const disclaimer = renderDisclaimer(input);
488
+ function renderDisclaimerFooter(noLogging: boolean, onDisclaimerSettingsLink: () => void): Lit.LitTemplate {
489
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
- }
490
+ return html`
491
+ <div class="disclaimer">
492
+ ${renderDisclaimer(noLogging, onDisclaimerSettingsLink)}
493
+ </div>`;
612
494
  // clang-format on
613
495
  }
614
496
 
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
497
+ function renderSignInFooter(onGoToSignIn: () => void): Lit.LitTemplate {
498
+ if (Root.Runtime.hostConfig.isOffTheRecord) {
499
+ return Lit.nothing;
632
500
  }
501
+ // clang-format off
502
+ return html`
503
+ <div class="filler"></div>
504
+ <div>
505
+ <devtools-button
506
+ @click=${onGoToSignIn}
507
+ .variant=${Buttons.Button.Variant.PRIMARY}
508
+ .jslogContext=${'update-settings'}
509
+ >
510
+ ${UIStrings.signIn}
511
+ </devtools-button>
512
+ </div>`;
633
513
  }
634
514
 
635
- function renderSpinner(state: StateData): Lit.LitTemplate {
515
+ function renderConsentReminderFooter(onReminderSettingsLink: () => void, onConsentReminderConfirmed: () => void): Lit.LitTemplate {
636
516
  // clang-format off
637
- if (state.type === State.INSIGHT && !state.completed) {
638
- return html`<devtools-spinner></devtools-spinner>`;
639
- }
640
- return Lit.nothing;
517
+ return html`
518
+ <div class="filler"></div>
519
+ <div class="buttons">
520
+ <devtools-button
521
+ @click=${onReminderSettingsLink}
522
+ .variant=${Buttons.Button.Variant.TONAL}
523
+ jslogContext=${'settings'}
524
+ title=${'Settings'}
525
+ >
526
+ Settings
527
+ </devtools-button>
528
+ <devtools-button
529
+ class='continue-button'
530
+ @click=${onConsentReminderConfirmed}
531
+ .variant=${Buttons.Button.Variant.PRIMARY}
532
+ .jslogContext=${'continue'}
533
+ .title=${'continue'}
534
+ >
535
+ Continue
536
+ </devtools-button>
537
+ </div>`;
538
+ }
539
+
540
+ function renderInsightFooter(noLogging: ViewInput['noLogging'], selectedRating: ViewInput['selectedRating'], callbacks: ViewInput['callbacks']): Lit.LitTemplate {
541
+ // clang-format off
542
+ return html`
543
+ <div class="disclaimer">
544
+ ${renderDisclaimer(noLogging, callbacks.onDisclaimerSettingsLink)}
545
+ </div>
546
+ <div class="filler"></div>
547
+ <div class="rating">
548
+ <devtools-button
549
+ data-rating="true"
550
+ .iconName=${'thumb-up'}
551
+ .toggledIconName=${'thumb-up'}
552
+ .variant=${Buttons.Button.Variant.ICON_TOGGLE}
553
+ .size=${Buttons.Button.Size.SMALL}
554
+ .toggleOnClick=${false}
555
+ .toggleType=${Buttons.Button.ToggleType.PRIMARY}
556
+ .disabled=${selectedRating !== undefined}
557
+ .toggled=${selectedRating === true}
558
+ .title=${i18nString(UIStrings.goodResponse)}
559
+ .jslogContext=${'thumbs-up'}
560
+ @click=${callbacks.onRating}
561
+ ></devtools-button>
562
+ <devtools-button
563
+ data-rating="false"
564
+ .iconName=${'thumb-down'}
565
+ .toggledIconName=${'thumb-down'}
566
+ .variant=${Buttons.Button.Variant.ICON_TOGGLE}
567
+ .size=${Buttons.Button.Size.SMALL}
568
+ .toggleOnClick=${false}
569
+ .toggleType=${Buttons.Button.ToggleType.PRIMARY}
570
+ .disabled=${selectedRating !== undefined}
571
+ .toggled=${selectedRating === false}
572
+ .title=${i18nString(UIStrings.badResponse)}
573
+ .jslogContext=${'thumbs-down'}
574
+ @click=${callbacks.onRating}
575
+ ></devtools-button>
576
+ <devtools-button
577
+ .iconName=${'report'}
578
+ .variant=${Buttons.Button.Variant.ICON}
579
+ .size=${Buttons.Button.Size.SMALL}
580
+ .title=${i18nString(UIStrings.report)}
581
+ .jslogContext=${'report'}
582
+ @click=${callbacks.onReport}
583
+ ></devtools-button>
584
+ </div>`;
641
585
  // clang-format on
642
586
  }
643
587
 
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;
588
+ function renderHeaderIcon(): Lit.LitTemplate {
589
+ // clang-format off
590
+ return html`
591
+ <div class="header-icon-container">
592
+ <devtools-icon name="lightbulb-spark" class="large">
593
+ </devtools-icon>
594
+ </div>`;
595
+ // clang-format on
596
+ }
597
+
598
+ interface HeaderInput {
599
+ headerText: string;
600
+ showIcon?: boolean;
601
+ showSpinner?: boolean;
602
+ onClose: ViewInput['callbacks']['onClose'];
603
+ }
604
+
605
+ function renderHeader({headerText, showIcon = false, showSpinner = false, onClose}: HeaderInput): Lit.LitTemplate {
649
606
  // clang-format off
650
607
  return html`
651
608
  <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}
609
+ ${showIcon ? renderHeaderIcon() : Lit.nothing}
658
610
  <div class="filler">
659
611
  <h2 tabindex="-1">
660
- ${getHeader(input.state)}
612
+ ${headerText}
661
613
  </h2>
662
- ${renderSpinner(input.state)}
614
+ ${showSpinner ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
663
615
  </div>
664
616
  <div class="close-button">
665
617
  <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
- }
618
+ .iconName=${'cross'}
619
+ .variant=${Buttons.Button.Variant.ICON}
620
+ .size=${Buttons.Button.Size.SMALL}
621
+ .title=${i18nString(UIStrings.closeInsight)}
674
622
  jslog=${VisualLogging.close().track({click: true})}
675
- @click=${input.onClose}
623
+ @click=${onClose}
676
624
  ></devtools-button>
677
625
  </div>
678
626
  </header>
@@ -1056,6 +1004,7 @@ export class ConsoleInsight extends HTMLElement {
1056
1004
  }
1057
1005
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightGenerated);
1058
1006
  } catch (err) {
1007
+ console.error('[ConsoleInsight] Error in #generateInsight:', err);
1059
1008
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.InsightErrored);
1060
1009
  if (err.message === 'doAidaConversation timed out' && this.#state.type === State.INSIGHT) {
1061
1010
  this.#state.timedOut = true;
@@ -1154,29 +1103,84 @@ export class ConsoleInsight extends HTMLElement {
1154
1103
  selectedRating: this.#selectedRating,
1155
1104
  noLogging: Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
1156
1105
  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),
1106
+ callbacks: {
1107
+ onClose: this.#onClose.bind(this),
1108
+ onSearch: this.#onSearch.bind(this),
1109
+ onRating: this.#onRating.bind(this),
1110
+ onReport: this.#onReport.bind(this),
1111
+ onGoToSignIn: this.#onGoToSignIn.bind(this),
1112
+ onConsentReminderConfirmed: this.#onConsentReminderConfirmed.bind(this),
1113
+ onToggleReferenceDetails: this.#onToggleReferenceDetails.bind(this),
1114
+ onDisclaimerSettingsLink: this.#onDisclaimerSettingsLink.bind(this),
1115
+ onReminderSettingsLink: this.#onReminderSettingsLink.bind(this),
1116
+ onEnableInsightsInSettingsLink: this.#onEnableInsightsInSettingsLink.bind(this),
1117
+ },
1167
1118
  };
1168
1119
  const output: ViewOutput = {
1169
1120
  referenceDetailsRef: this.#referenceDetailsRef,
1170
1121
  };
1122
+
1123
+ // Future Widget view function starts here.
1124
+ const {state, noLogging, callbacks} = input;
1125
+ const {onClose, onDisclaimerSettingsLink} = callbacks;
1126
+
1127
+ const jslog = `${VisualLogging.section(state.type).track({resize: true})}`;
1128
+ let header: Lit.LitTemplate = Lit.nothing;
1129
+ let main: Lit.LitTemplate = Lit.nothing;
1130
+ const mainClasses: Record<string, true> = {};
1131
+ let footer: Lit.LitTemplate|undefined;
1132
+
1133
+ switch (state.type) {
1134
+ case State.LOADING:
1135
+ header = renderHeader({headerText: i18nString(UIStrings.generating), onClose});
1136
+ main = renderLoading();
1137
+ break;
1138
+ case State.INSIGHT:
1139
+ header = renderHeader({headerText: i18nString(UIStrings.insight), onClose, showSpinner: !state.completed});
1140
+ main = renderInsight(state, input.renderer, input.disableAnimations, callbacks, output);
1141
+ footer = renderInsightFooter(noLogging, input.selectedRating, callbacks);
1142
+ break;
1143
+ case State.ERROR:
1144
+ header = renderHeader({headerText: i18nString(UIStrings.error), onClose});
1145
+ main = renderError(i18nString(UIStrings.errorBody));
1146
+ footer = renderDisclaimerFooter(noLogging, onDisclaimerSettingsLink);
1147
+ break;
1148
+ case State.CONSENT_REMINDER:
1149
+ header = renderHeader({headerText: 'Understand console messages with AI', onClose, showIcon: true});
1150
+ mainClasses['reminder-container'] = true;
1151
+ main = renderConsentReminder(noLogging);
1152
+ footer = renderConsentReminderFooter(callbacks.onReminderSettingsLink, callbacks.onConsentReminderConfirmed);
1153
+ break;
1154
+ case State.SETTING_IS_NOT_TRUE:
1155
+ mainClasses['opt-in-teaser'] = true;
1156
+ main = renderSettingIsNotTrue(callbacks.onEnableInsightsInSettingsLink);
1157
+ break;
1158
+ case State.NOT_LOGGED_IN:
1159
+ case State.SYNC_IS_PAUSED:
1160
+ header = renderHeader({headerText: i18nString(UIStrings.signInToUse), onClose});
1161
+ main = renderNotLoggedIn();
1162
+ footer = renderSignInFooter(callbacks.onGoToSignIn);
1163
+ break;
1164
+ case State.OFFLINE:
1165
+ header = renderHeader({headerText: i18nString(UIStrings.offlineHeader), onClose});
1166
+ main = renderError(i18nString(UIStrings.offline));
1167
+ footer = renderDisclaimerFooter(noLogging, onDisclaimerSettingsLink);
1168
+ break;
1169
+ }
1170
+
1171
1171
  // clang-format off
1172
1172
  render(html`
1173
1173
  <style>${styles}</style>
1174
1174
  <style>${Input.checkboxStyles}</style>
1175
1175
  <div class="wrapper" jslog=${VisualLogging.pane('console-insights').track({resize: true})}>
1176
1176
  <div class="animation-wrapper">
1177
- ${renderHeader(input)}
1178
- ${renderMain(input, output)}
1179
- ${renderFooter(input)}
1177
+ ${header}
1178
+ <main jslog=${jslog} class=${Directives.classMap(mainClasses)}>
1179
+ ${main}
1180
+ </main>
1181
+ ${footer?html`<footer jslog=${VisualLogging.section('footer')}>
1182
+ ${footer}
1183
+ </footer>`:Lit.nothing}
1180
1184
  </div>
1181
1185
  </div>
1182
1186
  `, this.#shadow, {