chrome-devtools-frontend 1.0.1537268 → 1.0.1538310

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 (98) hide show
  1. package/.env.template +10 -0
  2. package/docs/get_the_code.md +27 -0
  3. package/eslint.config.mjs +151 -149
  4. package/front_end/core/common/SettingRegistration.ts +10 -7
  5. package/front_end/core/common/Settings.ts +3 -0
  6. package/front_end/core/host/AidaClient.ts +1 -0
  7. package/front_end/core/host/UserMetrics.ts +3 -1
  8. package/front_end/core/root/Runtime.ts +8 -0
  9. package/front_end/core/sdk/sdk-meta.ts +8 -2
  10. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +4 -3
  11. package/front_end/generated/SupportedCSSProperties.js +1 -0
  12. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +23 -7
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +110 -5
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +50 -45
  15. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +151 -0
  16. package/front_end/models/ai_code_generation/ai_code_generation.ts +6 -0
  17. package/front_end/models/ai_code_generation/debug.ts +30 -0
  18. package/front_end/models/cpu_profile/ProfileTreeModel.ts +7 -7
  19. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +1 -1
  20. package/front_end/panels/application/PreloadingTreeElement.ts +10 -2
  21. package/front_end/panels/application/StorageView.ts +3 -2
  22. package/front_end/panels/application/components/BackForwardCacheView.ts +34 -51
  23. package/front_end/panels/application/components/OriginTrialTreeView.ts +141 -170
  24. package/front_end/panels/application/components/backForwardCacheView.css +4 -0
  25. package/front_end/panels/application/components/originTrialTreeView.css +37 -7
  26. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +2 -2
  27. package/front_end/panels/application/preloading/components/PreloadingString.ts +30 -1
  28. package/front_end/panels/autofill/AutofillView.ts +1 -1
  29. package/front_end/panels/console/ConsoleView.ts +11 -9
  30. package/front_end/panels/coverage/CoverageView.ts +1 -2
  31. package/front_end/panels/css_overview/CSSOverviewSidebarPanel.ts +1 -1
  32. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +1 -1
  33. package/front_end/panels/elements/ElementStatePaneWidget.ts +1 -1
  34. package/front_end/panels/elements/EventListenersWidget.ts +1 -2
  35. package/front_end/panels/elements/PropertiesWidget.ts +1 -1
  36. package/front_end/panels/emulation/components/DeviceSizeInputElement.ts +1 -0
  37. package/front_end/panels/network/NetworkConfigView.ts +2 -1
  38. package/front_end/panels/network/NetworkItemView.ts +1 -1
  39. package/front_end/panels/network/NetworkPanel.ts +5 -4
  40. package/front_end/panels/network/NetworkWaterfallColumn.ts +5 -6
  41. package/front_end/panels/network/RequestCookiesView.ts +2 -1
  42. package/front_end/panels/network/RequestTimingView.ts +404 -348
  43. package/front_end/panels/network/networkTimingTable.css +22 -2
  44. package/front_end/panels/profiler/HeapSnapshotView.ts +3 -2
  45. package/front_end/panels/sensors/SensorsView.ts +4 -3
  46. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +8 -6
  47. package/front_end/panels/settings/KeybindsSettingsTab.ts +3 -2
  48. package/front_end/panels/settings/SettingsScreen.ts +2 -1
  49. package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
  50. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +2 -1
  51. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  52. package/front_end/panels/sources/sources-meta.ts +8 -1
  53. package/front_end/panels/timeline/TimelinePanel.ts +4 -3
  54. package/front_end/panels/timeline/TimelineUIUtils.ts +4 -20
  55. package/front_end/panels/timeline/components/LiveMetricsView.ts +1 -0
  56. package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +42 -3
  57. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +2 -0
  58. package/front_end/panels/timeline/components/networkRequestTooltip.css +19 -0
  59. package/front_end/third_party/chromium/README.chromium +1 -1
  60. package/front_end/ui/components/adorners/Adorner.ts +3 -1
  61. package/front_end/ui/components/buttons/Button.docs.ts +195 -0
  62. package/front_end/ui/components/icon_button/IconButton.ts +1 -0
  63. package/front_end/ui/components/settings/SettingCheckbox.ts +50 -14
  64. package/front_end/ui/components/settings/settingCheckbox.css +6 -1
  65. package/front_end/ui/components/spinners/Spinners.docs.ts +13 -0
  66. package/front_end/ui/components/tooltips/Tooltip.docs.ts +76 -0
  67. package/front_end/ui/legacy/FilterBar.ts +1 -2
  68. package/front_end/ui/legacy/RadioButton.docs.ts +41 -0
  69. package/front_end/ui/legacy/SelectMenu.docs.ts +98 -0
  70. package/front_end/ui/legacy/Toolbar.ts +4 -6
  71. package/front_end/ui/legacy/Treeoutline.ts +15 -0
  72. package/front_end/ui/legacy/UIUtils.ts +117 -1
  73. package/front_end/ui/legacy/Widget.ts +68 -38
  74. package/front_end/ui/legacy/XLink.ts +1 -0
  75. package/front_end/ui/legacy/components/inline_editor/Swatches.ts +1 -0
  76. package/front_end/ui/legacy/components/perf_ui/BrickBreaker.ts +1 -0
  77. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +125 -0
  78. package/front_end/ui/legacy/components/settings_ui/settings_ui.ts +8 -0
  79. package/front_end/ui/legacy/legacy.ts +0 -2
  80. package/front_end/ui/legacy/popover.css +12 -11
  81. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  82. package/package.json +1 -1
  83. package/front_end/models/trace/lantern/testing/MetricTestUtils.ts +0 -62
  84. package/front_end/models/trace/lantern/testing/testing.ts +0 -5
  85. package/front_end/panels/application/components/badge.css +0 -25
  86. package/front_end/ui/components/docs/button/basic.html +0 -44
  87. package/front_end/ui/components/docs/button/basic.ts +0 -175
  88. package/front_end/ui/components/docs/radio_button/basic.html +0 -23
  89. package/front_end/ui/components/docs/radio_button/basic.ts +0 -50
  90. package/front_end/ui/components/docs/select_menu/basic.html +0 -19
  91. package/front_end/ui/components/docs/select_menu/basic.ts +0 -95
  92. package/front_end/ui/components/docs/select_menu/wide-option.html +0 -38
  93. package/front_end/ui/components/docs/select_menu/wide-option.ts +0 -43
  94. package/front_end/ui/components/docs/spinners/basic.html +0 -17
  95. package/front_end/ui/components/docs/spinners/basic.ts +0 -22
  96. package/front_end/ui/components/docs/tooltip/basic.html +0 -20
  97. package/front_end/ui/components/docs/tooltip/basic.ts +0 -82
  98. package/front_end/ui/legacy/SettingsUI.ts +0 -240
@@ -299,21 +299,24 @@ export interface SettingRegistration {
299
299
  * jump to the experiment. If a setting is not disabled, the experiment entry will be ignored.
300
300
  */
301
301
  deprecationNotice?: {disabled: boolean, warning: () => Platform.UIString.LocalizedString, experiment?: string};
302
-
303
302
  /**
304
- * Optional information to learn more about the setting. If provided, a `(?)` icon will show next to the setting
305
- * in the Settings panel with a link to learn more, and the `tooltip` will be presented to the user when hovering
306
- * the `(?)` icon.
303
+ * See {@link LearnMore} for more info.
307
304
  */
308
305
  learnMore?: LearnMore;
309
306
  }
310
307
 
311
308
  /**
312
- * Metadata to learn more about a setting. The `url` will be used to construct
313
- * a `(?)` icon link and the `tooltip` will be shown when hovering the icon.
309
+ * Optional information to learn more about the setting.
310
+ *
311
+ * If tooltip is provided creates a (i) icon with rich tooltip with said tooltip
312
+ *
313
+ * If url is provided creates a (?) icon with a link to said url
314
+ *
315
+ * If both tooltip is provided creates a (i) icon with rich tooltip
316
+ * and a link inside the rich tool tip with text `Learn more`
314
317
  */
315
318
  export interface LearnMore {
316
- tooltip: () => Platform.UIString.LocalizedString;
319
+ tooltip?: () => Platform.UIString.LocalizedString;
317
320
  url?: Platform.DevToolsPath.UrlString;
318
321
  }
319
322
 
@@ -565,6 +565,9 @@ export class Setting<V> {
565
565
  return null;
566
566
  }
567
567
 
568
+ /**
569
+ * See {@link LearnMore} for more info
570
+ */
568
571
  learnMore(): LearnMore|null {
569
572
  return this.#registration?.learnMore ?? null;
570
573
  }
@@ -342,6 +342,7 @@ export interface AidaRegisterClientEvent {
342
342
  disable_user_content_logging: boolean;
343
343
  do_conversation_client_event?: DoConversationClientEvent;
344
344
  complete_code_client_event?: {user_acceptance: UserAcceptance}|{user_impression: UserImpression};
345
+ generate_code_client_event?: {user_acceptance: UserAcceptance}|{user_impression: UserImpression};
345
346
  }
346
347
  /* eslint-enable @typescript-eslint/naming-convention */
347
348
 
@@ -527,7 +527,9 @@ export enum Action {
527
527
  InsightTeaserGenerationCompleted = 192,
528
528
  InsightTeaserGenerationAborted = 193,
529
529
  InsightTeaserGenerationErrored = 194,
530
- MAX_VALUE = 195,
530
+ AiCodeGenerationSuggestionDisplayed = 195,
531
+ AiCodeGenerationSuggestionAccepted = 196,
532
+ MAX_VALUE = 197,
531
533
  /* eslint-enable @typescript-eslint/naming-convention */
532
534
  }
533
535
 
@@ -422,6 +422,13 @@ export interface HostConfigAiCodeCompletion {
422
422
  userTier: string;
423
423
  }
424
424
 
425
+ export interface HostConfigAiCodeGeneration {
426
+ modelId: string;
427
+ temperature: number;
428
+ enabled: boolean;
429
+ userTier: string;
430
+ }
431
+
425
432
  export interface HostConfigDeepLinksViaExtensibilityApi {
426
433
  enabled: boolean;
427
434
  }
@@ -556,6 +563,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
556
563
  devToolsAiAssistanceFileAgent: HostConfigAiAssistanceFileAgent,
557
564
  devToolsAiAssistancePerformanceAgent: HostConfigAiAssistancePerformanceAgent,
558
565
  devToolsAiCodeCompletion: HostConfigAiCodeCompletion,
566
+ devToolsAiCodeGeneration: HostConfigAiCodeGeneration,
559
567
  devToolsVeLogging: HostConfigVeLogging,
560
568
  devToolsWellKnown: HostConfigWellKnown,
561
569
  devToolsPrivacyUI: HostConfigPrivacyUI,
@@ -353,8 +353,11 @@ const UIStrings = {
353
353
  /**
354
354
  * @description Label of a checkbox in the DevTools settings UI.
355
355
  */
356
- enableRemoteFileLoading:
357
- 'Allow `DevTools` to load resources, such as source maps, from remote file paths. Disabled by default for security reasons.',
356
+ enableRemoteFileLoading: 'Allow loading remote file path resources in DevTools',
357
+ /**
358
+ * @description Tooltip text for a setting that controls whether external resource can be loaded in DevTools.
359
+ */
360
+ remoteFileLoadingInfo: 'Example resource are source maps. Disabled by default for security reasons.',
358
361
  /**
359
362
  * @description Tooltip text for a setting that controls the network cache. Disabling the network cache can simulate the network connections of users that are visiting a page for the first time.
360
363
  */
@@ -1160,4 +1163,7 @@ Common.Settings.registerSettingExtension({
1160
1163
  settingName: 'network.enable-remote-file-loading',
1161
1164
  settingType: Common.Settings.SettingType.BOOLEAN,
1162
1165
  defaultValue: false,
1166
+ learnMore: {
1167
+ tooltip: i18nLazyString(UIStrings.remoteFileLoadingInfo),
1168
+ }
1163
1169
  });
@@ -7,6 +7,7 @@
7
7
  import * as Common from '../../core/common/common.js';
8
8
  import * as Host from '../../core/host/host.js';
9
9
  import * as i18n from '../../core/i18n/i18n.js';
10
+ import * as SettingsUI from '../../ui/legacy/components/settings_ui/settings_ui.js';
10
11
  import * as UI from '../../ui/legacy/legacy.js';
11
12
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
12
13
 
@@ -278,15 +279,15 @@ export class RenderingOptionsView extends UI.Widget.VBox {
278
279
 
279
280
  #appendCheckbox(
280
281
  label: Common.UIString.LocalizedString, subtitle: Common.UIString.LocalizedString,
281
- setting: Common.Settings.Setting<boolean>, metric?: UI.SettingsUI.UserMetricOptions): UI.UIUtils.CheckboxLabel {
282
+ setting: Common.Settings.Setting<boolean>, metric?: UI.UIUtils.UserMetricOptions): UI.UIUtils.CheckboxLabel {
282
283
  const checkbox = UI.UIUtils.CheckboxLabel.create(label, false, subtitle, setting.name);
283
- UI.SettingsUI.bindCheckbox(checkbox, setting, metric);
284
+ UI.UIUtils.bindCheckbox(checkbox, setting, metric);
284
285
  this.contentElement.appendChild(checkbox);
285
286
  return checkbox;
286
287
  }
287
288
 
288
289
  #appendSelect(label: string, setting: Common.Settings.Setting<unknown>): void {
289
- const control = UI.SettingsUI.createControlForSetting(setting, label);
290
+ const control = SettingsUI.SettingsUI.createControlForSetting(setting, label);
290
291
  if (control) {
291
292
  this.contentElement.appendChild(control);
292
293
  }
@@ -519,6 +519,7 @@ export const generatedProperties = [
519
519
  "inset-block-start",
520
520
  "inset-inline-end",
521
521
  "inset-inline-start",
522
+ "interactivity",
522
523
  "interest-delay-end",
523
524
  "interest-delay-start",
524
525
  "interpolate-size",
@@ -627,19 +627,24 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
627
627
  #declareFunctions(context: PerformanceTraceContext): void {
628
628
  const focus = context.getItem();
629
629
  const {parsedTrace} = focus;
630
- const insightSet = focus.primaryInsightSet;
631
630
 
632
- this.declareFunction<{insightName: string}, {details: string}>('getInsightDetails', {
631
+ this.declareFunction<{insightSetId: string, insightName: string}, {details: string}>('getInsightDetails', {
633
632
  description:
634
- 'Returns detailed information about a specific insight. Use this before commenting on any specific issue to get more information.',
633
+ 'Returns detailed information about a specific insight of an insight set. Use this before commenting on any specific issue to get more information.',
635
634
  parameters: {
636
635
  type: Host.AidaClient.ParametersTypes.OBJECT,
637
636
  description: '',
638
637
  nullable: false,
639
638
  properties: {
639
+ insightSetId: {
640
+ type: Host.AidaClient.ParametersTypes.STRING,
641
+ description:
642
+ 'The id for the specific insight set. Only use the ids given in the "Available insight sets" list.',
643
+ nullable: false,
644
+ },
640
645
  insightName: {
641
646
  type: Host.AidaClient.ParametersTypes.STRING,
642
- description: 'The name of the insight. Only use the insight names given in the Available Insights list.',
647
+ description: 'The name of the insight. Only use the insight names given in the "Available insights" list.',
643
648
  nullable: false,
644
649
  }
645
650
  },
@@ -647,19 +652,30 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
647
652
  displayInfoFromArgs: params => {
648
653
  return {
649
654
  title: lockedString(`Investigating insight ${params.insightName}…`),
650
- action: `getInsightDetails('${params.insightName}')`
655
+ action: `getInsightDetails('${params.insightSetId}', '${params.insightName}')`
651
656
  };
652
657
  },
653
658
  handler: async params => {
654
659
  debugLog('Function call: getInsightDetails', params);
660
+ const insightSet = parsedTrace.insights?.get(params.insightSetId);
661
+ if (!insightSet) {
662
+ const valid = ([...parsedTrace.insights?.values() ?? []])
663
+ .map(
664
+ insightSet => `id: ${insightSet.id}, url: ${insightSet.url}, bounds: ${
665
+ this.#formatter?.serializeBounds(insightSet.bounds)}`)
666
+ .join('; ');
667
+ return {error: `Invalid insight set id. Valid insight set ids are: ${valid}`};
668
+ }
669
+
655
670
  const insight = insightSet?.model[params.insightName as keyof Trace.Insights.Types.InsightModels];
656
671
  if (!insight) {
657
- return {error: 'No insight available'};
672
+ const valid = Object.keys(insightSet?.model).join(', ');
673
+ return {error: `No insight available. Valid insight names are: ${valid}`};
658
674
  }
659
675
 
660
676
  const details = new PerformanceInsightFormatter(focus, insight).formatInsight();
661
677
 
662
- const key = `getInsightDetails('${params.insightName}')`;
678
+ const key = `getInsightDetails('${params.insightSetId}', '${params.insightName}')`;
663
679
  this.#cacheFunctionResult(focus, key, details);
664
680
  return {result: {details}};
665
681
  },
@@ -504,9 +504,18 @@ IMPORTANT: Never show eventKey to the user.
504
504
  Title: PerformanceTraceFormatter formatTraceSummary web-dev.json.gz
505
505
  Content:
506
506
  URL: https://web.dev/cls/
507
- Bounds: {min: 1020034823047, max: 1020036087961}
507
+ Trace bounds: {min: 1020034823047, max: 1020036087961}
508
508
  CPU throttling: none
509
509
  Network throttling: none
510
+
511
+ # Available insight sets
512
+
513
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
514
+
515
+ ## insight set id: 86EB5E5C401E3E17ECE461B3FC627867
516
+
517
+ URL: https://web.dev/cls/
518
+ Bounds: {min: 1020034834921, max: 1020036087961}
510
519
  Metrics (lab / observed):
511
520
  - LCP: 118 ms, event: (eventKey: r-1802, ts: 1020034953358), nodeId: 209
512
521
  - LCP breakdown:
@@ -537,9 +546,18 @@ Available insights:
537
546
  Title: PerformanceTraceFormatter formatTraceSummary yahoo-news.json.gz
538
547
  Content:
539
548
  URL: https://news.yahoo.com/
540
- Bounds: {min: 157423484442, max: 157427277166}
549
+ Trace bounds: {min: 157423484442, max: 157427277166}
541
550
  CPU throttling: none
542
551
  Network throttling: none
552
+
553
+ # Available insight sets
554
+
555
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
556
+
557
+ ## insight set id: 513BDA1E9EC088611B53BFF7A859B5DD
558
+
559
+ URL: https://news.yahoo.com/
560
+ Bounds: {min: 157423488682, max: 157427277166}
543
561
  Metrics (lab / observed):
544
562
  - LCP: 464 ms, event: (eventKey: r-33210, ts: 157423953162), nodeId: 8
545
563
  - LCP breakdown:
@@ -596,12 +614,81 @@ Available insights:
596
614
  example question: How can I reduce the amount of legacy JavaScript on my page?
597
615
  === end content
598
616
 
617
+ Title: PerformanceTraceFormatter formatTraceSummary multiple-navigations-render-blocking.json.gz
618
+ Content:
619
+ URL: http://localhost:8080/render-blocking
620
+ Trace bounds: {min: 171605647473, max: 171616667355}
621
+ CPU throttling: 1x
622
+ Network throttling: Fast 3G
623
+
624
+ # Available insight sets
625
+
626
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
627
+
628
+ ## insight set id: 8671F33ECE0C8DBAEFBC2F9A2D1D6107
629
+
630
+ URL: http://localhost:8080/render-blocking
631
+ Bounds: {min: 171607579779, max: 171613750571}
632
+ Metrics (lab / observed):
633
+ - LCP: 1317 ms, event: (eventKey: r-6686, ts: 171608897210), nodeId: 10
634
+ - LCP breakdown:
635
+ - TTFB: 10 ms, bounds: {min: 171607579779, max: 171607589981.99997}
636
+ - Render delay: 1,307 ms, bounds: {min: 171607589981.99997, max: 171608897210}
637
+ - CLS: 0.00
638
+ Metrics (field / real users): n/a – no data for this page in CrUX
639
+ Available insights:
640
+ - insight name: LCPBreakdown
641
+ description: Each [subpart has specific improvement strategies](https://developer.chrome.com/docs/performance/insights/lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.
642
+ relevant trace bounds: {min: 171607579779, max: 171608897210}
643
+ example question: Help me optimize my LCP score
644
+ example question: Which LCP phase was most problematic?
645
+ example question: What can I do to reduce the LCP time for this page load?
646
+ - insight name: RenderBlocking
647
+ description: Requests are blocking the page's initial render, which may delay LCP. [Deferring or inlining](https://developer.chrome.com/docs/performance/insights/render-blocking) can move these network requests out of the critical path.
648
+ relevant trace bounds: {min: 171608170438, max: 171608877165}
649
+ example question: Show me the most impactful render blocking requests that I should focus on
650
+ example question: How can I reduce the number of render blocking requests?
651
+
652
+ ## insight set id: 1AE2016BBCC48AA090FDAE2CBBA01900
653
+
654
+ URL: http://localhost:8080/render-blocking
655
+ Bounds: {min: 171613750571, max: 171616667355}
656
+ Metrics (lab / observed):
657
+ - LCP: 1310 ms, event: (eventKey: r-15639, ts: 171615060776), nodeId: 18
658
+ - LCP breakdown:
659
+ - TTFB: 3 ms, bounds: {min: 171613750571, max: 171613753188}
660
+ - Render delay: 1,308 ms, bounds: {min: 171613753188, max: 171615060776}
661
+ - CLS: 0.00
662
+ Metrics (field / real users): n/a – no data for this page in CrUX
663
+ Available insights:
664
+ - insight name: LCPBreakdown
665
+ description: Each [subpart has specific improvement strategies](https://developer.chrome.com/docs/performance/insights/lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.
666
+ relevant trace bounds: {min: 171613750571, max: 171615060776}
667
+ example question: Help me optimize my LCP score
668
+ example question: Which LCP phase was most problematic?
669
+ example question: What can I do to reduce the LCP time for this page load?
670
+ - insight name: RenderBlocking
671
+ description: Requests are blocking the page's initial render, which may delay LCP. [Deferring or inlining](https://developer.chrome.com/docs/performance/insights/render-blocking) can move these network requests out of the critical path.
672
+ relevant trace bounds: {min: 171614330544, max: 171615043224}
673
+ example question: Show me the most impactful render blocking requests that I should focus on
674
+ example question: How can I reduce the number of render blocking requests?
675
+ === end content
676
+
599
677
  Title: PerformanceTraceFormatter formatTraceSummary deals with CrUX manager errors
600
678
  Content:
601
679
  URL: http://localhost/image-delivery-cases.html
602
- Bounds: {min: 59728641874, max: 59734400108}
680
+ Trace bounds: {min: 59728641874, max: 59734400108}
603
681
  CPU throttling: 1x
604
682
  Network throttling: No throttling
683
+
684
+ # Available insight sets
685
+
686
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
687
+
688
+ ## insight set id: 9094E106056B51D078CE44F3356FE194
689
+
690
+ URL: http://localhost/image-delivery-cases.html
691
+ Bounds: {min: 59728649746, max: 59734400108}
605
692
  Metrics (lab / observed):
606
693
  - LCP: 663 ms, event: (eventKey: r-14753, ts: 59729312744), nodeId: 12
607
694
  - LCP breakdown:
@@ -663,9 +750,18 @@ Available insights:
663
750
  Title: PerformanceTraceFormatter formatTraceSummary image-delivery.json.gz
664
751
  Content:
665
752
  URL: http://localhost/image-delivery-cases.html
666
- Bounds: {min: 59728641874, max: 59734400108}
753
+ Trace bounds: {min: 59728641874, max: 59734400108}
667
754
  CPU throttling: 1x
668
755
  Network throttling: No throttling
756
+
757
+ # Available insight sets
758
+
759
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
760
+
761
+ ## insight set id: 9094E106056B51D078CE44F3356FE194
762
+
763
+ URL: http://localhost/image-delivery-cases.html
764
+ Bounds: {min: 59728649746, max: 59734400108}
669
765
  Metrics (lab / observed):
670
766
  - LCP: 663 ms, event: (eventKey: r-14753, ts: 59729312744), nodeId: 12
671
767
  - LCP breakdown:
@@ -734,9 +830,18 @@ Available insights:
734
830
  Title: PerformanceTraceFormatter formatTraceSummary includes INP insight when there is no navigation
735
831
  Content:
736
832
  URL: https://b2607f8b04800100000289cb1c0a82ba72253000000000000000001.proxy.googlers.com/long-interaction/index.html?x=35
737
- Bounds: {min: 337943614456, max: 337947260898}
833
+ Trace bounds: {min: 337943614456, max: 337947260898}
738
834
  CPU throttling: none
739
835
  Network throttling: none
836
+
837
+ # Available insight sets
838
+
839
+ The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.
840
+
841
+ ## insight set id: NO_NAVIGATION
842
+
843
+ URL: https://b2607f8b04800100000289cb1c0a82ba72253000000000000000001.proxy.googlers.com/long-interaction/index.html?x=35
844
+ Bounds: {min: 337943614456, max: 337947260898}
740
845
  Metrics (lab / observed):
741
846
  - INP: 139 ms, event: (eventKey: s-3347, ts: 337944870984)
742
847
  - CLS: 0.00
@@ -116,62 +116,69 @@ export class PerformanceTraceFormatter {
116
116
 
117
117
  formatTraceSummary(): string {
118
118
  const parsedTrace = this.#parsedTrace;
119
- const insightSet = this.#insightSet;
120
119
  const traceMetadata = this.#parsedTrace.metadata;
121
120
  const data = parsedTrace.data;
122
121
 
123
122
  const parts = [];
124
123
 
125
- const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
126
- const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
127
- const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
128
-
129
124
  parts.push(`URL: ${data.Meta.mainFrameURL}`);
130
- parts.push(`Bounds: ${this.serializeBounds(data.Meta.traceBounds)}`);
125
+ parts.push(`Trace bounds: ${this.serializeBounds(data.Meta.traceBounds)}`);
131
126
  parts.push('CPU throttling: ' + (traceMetadata.cpuThrottling ? `${traceMetadata.cpuThrottling}x` : 'none'));
132
127
  parts.push(`Network throttling: ${traceMetadata.networkThrottling ?? 'none'}`);
133
- if (lcp || cls || inp) {
134
- parts.push('Metrics (lab / observed):');
135
- if (lcp) {
136
- const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
137
- const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
138
- parts.push(
139
- ` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
140
- const subparts = insightSet?.model.LCPBreakdown.subparts;
141
- if (subparts) {
142
- const serializeSubpart = (subpart: Trace.Insights.Models.LCPBreakdown.Subpart): string => {
143
- return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
144
- };
145
- parts.push(' - LCP breakdown:');
146
- parts.push(` - TTFB: ${serializeSubpart(subparts.ttfb)}`);
147
- if (subparts.loadDelay !== undefined) {
148
- parts.push(` - Load delay: ${serializeSubpart(subparts.loadDelay)}`);
149
- }
150
- if (subparts.loadDuration !== undefined) {
151
- parts.push(` - Load duration: ${serializeSubpart(subparts.loadDuration)}`);
128
+
129
+ parts.push('\n# Available insight sets\n');
130
+ parts.push(
131
+ 'The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.');
132
+
133
+ for (const insightSet of parsedTrace.insights?.values() ?? []) {
134
+ const lcp = insightSet ? Trace.Insights.Common.getLCP(insightSet) : null;
135
+ const cls = insightSet ? Trace.Insights.Common.getCLS(insightSet) : null;
136
+ const inp = insightSet ? Trace.Insights.Common.getINP(insightSet) : null;
137
+
138
+ parts.push(`\n## insight set id: ${insightSet.id}\n`);
139
+ parts.push(`URL: ${insightSet.url}`);
140
+ parts.push(`Bounds: ${this.serializeBounds(insightSet.bounds)}`);
141
+ if (lcp || cls || inp) {
142
+ parts.push('Metrics (lab / observed):');
143
+ if (lcp) {
144
+ const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
145
+ const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
146
+ parts.push(
147
+ ` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
148
+ const subparts = insightSet?.model.LCPBreakdown.subparts;
149
+ if (subparts) {
150
+ const serializeSubpart = (subpart: Trace.Insights.Models.LCPBreakdown.Subpart): string => {
151
+ return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
152
+ };
153
+ parts.push(' - LCP breakdown:');
154
+ parts.push(` - TTFB: ${serializeSubpart(subparts.ttfb)}`);
155
+ if (subparts.loadDelay !== undefined) {
156
+ parts.push(` - Load delay: ${serializeSubpart(subparts.loadDelay)}`);
157
+ }
158
+ if (subparts.loadDuration !== undefined) {
159
+ parts.push(` - Load duration: ${serializeSubpart(subparts.loadDuration)}`);
160
+ }
161
+ parts.push(` - Render delay: ${serializeSubpart(subparts.renderDelay)}`);
152
162
  }
153
- parts.push(` - Render delay: ${serializeSubpart(subparts.renderDelay)}`);
154
163
  }
164
+ if (inp) {
165
+ parts.push(` - INP: ${Math.round(inp.value / 1000)} ms, event: ${this.serializeEvent(inp.event)}`);
166
+ }
167
+ if (cls) {
168
+ const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
169
+ parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
170
+ }
171
+ } else {
172
+ parts.push('Metrics (lab / observed): n/a');
155
173
  }
156
- if (inp) {
157
- parts.push(` - INP: ${Math.round(inp.value / 1000)} ms, event: ${this.serializeEvent(inp.event)}`);
158
- }
159
- if (cls) {
160
- const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
161
- parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
162
- }
163
- } else {
164
- parts.push('Metrics (lab / observed): n/a');
165
- }
166
174
 
167
- const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
168
- if (cruxParts?.length) {
169
- parts.push(...cruxParts);
170
- } else {
171
- parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
172
- }
175
+ const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
176
+ if (cruxParts?.length) {
177
+ parts.push(...cruxParts);
178
+ } else {
179
+ parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
180
+ }
173
181
 
174
- if (insightSet) {
175
182
  parts.push('Available insights:');
176
183
  for (const [insightName, model] of Object.entries(insightSet.model)) {
177
184
  if (model.state === 'pass') {
@@ -202,8 +209,6 @@ export class PerformanceTraceFormatter {
202
209
  const insightPartsText = insightParts.join('\n ');
203
210
  parts.push(` - ${insightPartsText}`);
204
211
  }
205
- } else {
206
- parts.push('Available insights: none');
207
212
  }
208
213
 
209
214
  return parts.join('\n');
@@ -0,0 +1,151 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Host from '../../core/host/host.js';
6
+ import * as Root from '../../core/root/root.js';
7
+
8
+ import {debugLog} from './debug.js';
9
+
10
+ export const basePreamble =
11
+ `You are a highly skilled senior software engineer with deep expertise across multiple web technologies and programming languages, including JavaScript, TypeScript, HTML, and CSS.
12
+ Your role is to act as an expert pair programmer within the Chrome DevTools environment.
13
+
14
+ **Core Directives (Adhere to these strictly):**
15
+
16
+ 1. **Language and Quality:**
17
+ * Generate code that is modern, efficient, and idiomatic for the inferred language (e.g., modern JavaScript/ES6+, semantic HTML5, efficient CSS).
18
+ * Where appropriate, include basic error handling (e.g., for API calls).
19
+ `;
20
+
21
+ export const additionalContextForConsole = `
22
+ You are operating within the execution environment of the Chrome DevTools Console.
23
+ The console has direct access to the inspected page's \`window\` and \`document\`.
24
+
25
+ * **Utilize Console Utilities:** You have access to the Console Utilities API. You **should** use these helper functions and variables when they are the most direct way to accomplish the user's goal.
26
+ `;
27
+
28
+ interface Options {
29
+ aidaClient: Host.AidaClient.AidaClient;
30
+ serverSideLoggingEnabled?: boolean;
31
+ confirmSideEffectForTest?: typeof Promise.withResolvers;
32
+ }
33
+
34
+ interface RequestOptions {
35
+ temperature?: number;
36
+ modelId?: string;
37
+ }
38
+
39
+ /**
40
+ * The AiCodeGeneration class is responsible for fetching generated code suggestions
41
+ * from the AIDA backend.
42
+ */
43
+ export class AiCodeGeneration {
44
+ readonly #sessionId: string = crypto.randomUUID();
45
+ readonly #aidaClient: Host.AidaClient.AidaClient;
46
+ readonly #serverSideLoggingEnabled: boolean;
47
+
48
+ constructor(opts: Options) {
49
+ this.#aidaClient = opts.aidaClient;
50
+ this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
51
+ }
52
+
53
+ #buildRequest(
54
+ prompt: string,
55
+ preamble: string,
56
+ inferenceLanguage: Host.AidaClient.AidaInferenceLanguage = Host.AidaClient.AidaInferenceLanguage.JAVASCRIPT,
57
+ ): Host.AidaClient.GenerateCodeRequest {
58
+ const userTier = Host.AidaClient.convertToUserTierEnum(this.#userTier);
59
+ function validTemperature(temperature: number|undefined): number|undefined {
60
+ return typeof temperature === 'number' && temperature >= 0 ? temperature : undefined;
61
+ }
62
+ return {
63
+ client: Host.AidaClient.CLIENT_NAME,
64
+ preamble,
65
+ current_message: {
66
+ parts: [{
67
+ text: prompt,
68
+ }],
69
+ role: Host.AidaClient.Role.USER,
70
+ },
71
+ use_case: Host.AidaClient.UseCase.CODE_GENERATION,
72
+ options: {
73
+ inference_language: inferenceLanguage,
74
+ temperature: validTemperature(this.#options.temperature),
75
+ model_id: this.#options.modelId || undefined,
76
+ },
77
+ metadata: {
78
+ disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
79
+ string_session_id: this.#sessionId,
80
+ user_tier: userTier,
81
+ client_version: Root.Runtime.getChromeVersion(),
82
+ },
83
+ };
84
+ }
85
+
86
+ get #userTier(): string|undefined {
87
+ return Root.Runtime.hostConfig.devToolsAiCodeGeneration?.userTier;
88
+ }
89
+
90
+ get #options(): RequestOptions {
91
+ const temperature = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.temperature;
92
+ const modelId = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.modelId;
93
+
94
+ return {
95
+ temperature,
96
+ modelId,
97
+ };
98
+ }
99
+
100
+ registerUserImpression(rpcGlobalId: Host.AidaClient.RpcGlobalId, latency: number, sampleId?: number): void {
101
+ const seconds = Math.floor(latency / 1_000);
102
+ const remainingMs = latency % 1_000;
103
+ const nanos = Math.floor(remainingMs * 1_000_000);
104
+
105
+ void this.#aidaClient.registerClientEvent({
106
+ corresponding_aida_rpc_global_id: rpcGlobalId,
107
+ disable_user_content_logging: true,
108
+ generate_code_client_event: {
109
+ user_impression: {
110
+ sample: {
111
+ sample_id: sampleId,
112
+ },
113
+ latency: {
114
+ duration: {
115
+ seconds,
116
+ nanos,
117
+ },
118
+ }
119
+ },
120
+ },
121
+ });
122
+ debugLog('Registered user impression with latency {seconds:', seconds, ', nanos:', nanos, '}');
123
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionDisplayed);
124
+ }
125
+
126
+ registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId?: number): void {
127
+ void this.#aidaClient.registerClientEvent({
128
+ corresponding_aida_rpc_global_id: rpcGlobalId,
129
+ disable_user_content_logging: true,
130
+ generate_code_client_event: {
131
+ user_acceptance: {
132
+ sample: {
133
+ sample_id: sampleId,
134
+ }
135
+ },
136
+ },
137
+ });
138
+ debugLog('Registered user acceptance');
139
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionAccepted);
140
+ }
141
+
142
+ async generateCode(prompt: string, preamble: string, inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage):
143
+ Promise<Host.AidaClient.GenerateCodeResponse|null> {
144
+ const request = this.#buildRequest(prompt, preamble, inferenceLanguage);
145
+ const response = await this.#aidaClient.generateCode(request);
146
+
147
+ debugLog({request, response});
148
+
149
+ return response;
150
+ }
151
+ }
@@ -0,0 +1,6 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ export * from './debug.js';
6
+ export * as AiCodeGeneration from './AiCodeGeneration.js';