chrome-devtools-frontend 1.0.1518653 → 1.0.1520139

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 (143) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/eslint.config.mjs +1 -0
  5. package/front_end/core/host/GdpClient.ts +12 -3
  6. package/front_end/core/sdk/EnhancedTracesParser.ts +5 -5
  7. package/front_end/core/sdk/NetworkManager.ts +1 -0
  8. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  9. package/front_end/core/sdk/RehydratingConnection.snapshot.txt +211 -0
  10. package/front_end/core/sdk/TargetManager.ts +4 -0
  11. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  12. package/front_end/entrypoints/main/main-meta.ts +3 -3
  13. package/front_end/generated/SupportedCSSProperties.js +19 -4
  14. package/front_end/models/ai_assistance/agents/AiAgent.ts +57 -10
  15. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +64 -87
  16. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  17. package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -31
  18. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
  19. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +106 -55
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
  22. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  23. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  24. package/front_end/models/ai_assistance/performance/AIContext.ts +63 -8
  25. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -0
  26. package/front_end/models/badges/AiExplorerBadge.ts +19 -3
  27. package/front_end/models/badges/Badge.ts +8 -1
  28. package/front_end/models/badges/CodeWhispererBadge.ts +1 -0
  29. package/front_end/models/badges/DOMDetectiveBadge.ts +1 -0
  30. package/front_end/models/badges/SpeedsterBadge.ts +1 -0
  31. package/front_end/models/badges/StarterBadge.ts +6 -0
  32. package/front_end/models/badges/badges.ts +1 -0
  33. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  34. package/front_end/models/trace/EventsSerializer.ts +4 -3
  35. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +101 -73
  36. package/front_end/models/trace/helpers/Timing.ts +1 -1
  37. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +18 -8
  38. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  39. package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
  40. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +63 -15
  41. package/front_end/panels/ai_assistance/components/chatView.css +12 -0
  42. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  43. package/front_end/panels/animation/animationTimeline.css +4 -0
  44. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
  45. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  46. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  47. package/front_end/panels/common/BadgeNotification.ts +3 -3
  48. package/front_end/panels/common/GdpSignUpDialog.ts +3 -4
  49. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  50. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  51. package/front_end/panels/console/ConsoleView.ts +4 -2
  52. package/front_end/panels/coverage/CoverageListView.ts +133 -158
  53. package/front_end/panels/coverage/CoverageView.ts +39 -16
  54. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
  55. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  56. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  57. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  58. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  59. package/front_end/panels/recorder/components/RecordingView.ts +2 -2
  60. package/front_end/panels/search/SearchResultsPane.ts +186 -134
  61. package/front_end/panels/search/SearchView.ts +42 -36
  62. package/front_end/panels/search/searchResultsPane.css +9 -0
  63. package/front_end/panels/search/searchView.css +0 -2
  64. package/front_end/panels/security/CookieControlsView.ts +2 -1
  65. package/front_end/panels/settings/AISettingsTab.ts +6 -3
  66. package/front_end/panels/settings/components/SyncSection.ts +26 -12
  67. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -1
  68. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -4
  69. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  70. package/front_end/panels/sources/SourcesPanel.ts +1 -1
  71. package/front_end/panels/sources/sourcesView.css +6 -1
  72. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  73. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  74. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  75. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  76. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  77. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +1 -1
  78. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +1 -1
  79. package/front_end/panels/timeline/components/RelatedInsightChips.ts +1 -1
  80. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/third_party/puppeteer/README.chromium +2 -2
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.d.ts.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +16 -25
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  104. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +19 -28
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.d.ts.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +16 -25
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  123. package/front_end/third_party/puppeteer/package/package.json +12 -4
  124. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  125. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  126. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  127. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
  128. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  129. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  130. package/front_end/third_party/puppeteer/package/src/util/Function.ts +22 -30
  131. package/front_end/tsconfig.json +12 -1
  132. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  133. package/front_end/ui/components/markdown_view/MarkdownImage.ts +4 -5
  134. package/front_end/ui/components/switch/SwitchImpl.ts +12 -1
  135. package/front_end/ui/components/text_editor/config.ts +16 -2
  136. package/front_end/ui/legacy/InspectorView.ts +86 -13
  137. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  138. package/front_end/ui/legacy/Treeoutline.ts +3 -1
  139. package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -11
  140. package/front_end/ui/lit/i18n-template.ts +5 -2
  141. package/front_end/ui/visual_logging/KnownContextValues.ts +15 -5
  142. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  143. package/package.json +1 -1
@@ -2,8 +2,6 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import '../../../ui/components/icon_button/icon_button.js';
6
-
7
5
  import * as Common from '../../../core/common/common.js';
8
6
  import * as Host from '../../../core/host/host.js';
9
7
  import * as i18n from '../../../core/i18n/i18n.js';
@@ -138,13 +136,23 @@ When referring to a trace event that has a corresponding \`eventKey\`, annotate
138
136
  When asking the user to make a choice between multiple options, output a list of choices at the end of your text response. The format is \`SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]\`. This MUST start on a newline, and be a single line.
139
137
  `;
140
138
 
139
+ const extraPreambleWhenFreshTrace = `Additional notes:
140
+
141
+ When referring to an element for which you know the nodeId, annotate your output using markdown link syntax:
142
+ - For example, if nodeId is 23: [LCP element](#node-23)
143
+ - This link will reveal the element in the Elements panel
144
+ - Never mention node or nodeId when referring to the element, and especially not in the link text.
145
+ - When referring to the LCP, it's useful to also mention what the LCP element is via its nodeId. Use the markdown link syntax to do so.
146
+ `;
147
+
141
148
  const callFrameDataFormatDescription = `Each call frame is presented in the following format:
142
149
 
143
- 'id;name;duration;selfTime;urlIndex;childRange;[S]'
150
+ 'id;eventKey;name;duration;selfTime;urlIndex;childRange;[S]'
144
151
 
145
152
  Key definitions:
146
153
 
147
154
  * id: A unique numerical identifier for the call frame. Never mention this id in the output to the user.
155
+ * eventKey: String that uniquely identifies this event in the flame chart.
148
156
  * name: A concise string describing the call frame (e.g., 'Evaluate Script', 'render', 'fetchData').
149
157
  * duration: The total execution time of the call frame, including its children.
150
158
  * selfTime: The time spent directly within the call frame, excluding its children's execution.
@@ -154,11 +162,11 @@ Key definitions:
154
162
 
155
163
  Example Call Tree:
156
164
 
157
- 1;main;500;100;;
158
- 2;update;200;50;;3
159
- 3;animate;150;20;0;4-5;S
160
- 4;calculatePosition;80;80;;
161
- 5;applyStyles;50;50;;
165
+ 1;r-123;main;500;100;;
166
+ 2;r-124;update;200;50;;3
167
+ 3;p-49575-15428179-2834-374;animate;150;20;0;4-5;S
168
+ 4;p-49575-15428179-3505-1162;calculatePosition;80;80;;
169
+ 5;p-49575-15428179-5391-2767;applyStyles;50;50;;
162
170
  `;
163
171
 
164
172
  enum ScorePriority {
@@ -190,7 +198,7 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
190
198
  }
191
199
 
192
200
  override getOrigin(): string {
193
- const {min, max} = this.#focus.data.parsedTrace.data.Meta.traceBounds;
201
+ const {min, max} = this.#focus.parsedTrace.data.Meta.traceBounds;
194
202
  return `trace-${min}-${max}`;
195
203
  }
196
204
 
@@ -199,7 +207,7 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
199
207
  }
200
208
 
201
209
  override getTitle(): string {
202
- const focus = this.#focus.data;
210
+ const focus = this.#focus;
203
211
 
204
212
  let url = focus.insightSet?.url;
205
213
  if (!url) {
@@ -210,6 +218,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
210
218
  if (focus.insight) {
211
219
  parts.push(focus.insight.title);
212
220
  }
221
+ if (focus.event) {
222
+ parts.push(Trace.Name.forEntry(focus.event));
223
+ }
213
224
  if (focus.callTree) {
214
225
  const node = focus.callTree.selectedNode ?? focus.callTree.rootNode;
215
226
  parts.push(Trace.Name.forEntry(node.event));
@@ -222,9 +233,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
222
233
  * "Ask AI".
223
234
  */
224
235
  override async getSuggestions(): Promise<ConversationSuggestions|undefined> {
225
- const data = this.#focus.data;
236
+ const focus = this.#focus;
226
237
 
227
- if (data.callTree) {
238
+ if (focus.callTree) {
228
239
  return [
229
240
  {title: 'What\'s the purpose of this work?', jslogContext: 'performance-default'},
230
241
  {title: 'Where is time being spent?', jslogContext: 'performance-default'},
@@ -232,17 +243,17 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
232
243
  ];
233
244
  }
234
245
 
235
- if (data.insight) {
236
- return new PerformanceInsightFormatter(this.#focus, data.insight).getSuggestions();
246
+ if (focus.insight) {
247
+ return new PerformanceInsightFormatter(focus, focus.insight).getSuggestions();
237
248
  }
238
249
 
239
250
  const suggestions: ConversationSuggestions =
240
251
  [{title: 'What performance issues exist with my page?', jslogContext: 'performance-default'}];
241
252
 
242
- if (data.insightSet) {
243
- const lcp = data.insightSet ? Trace.Insights.Common.getLCP(data.insightSet) : null;
244
- const cls = data.insightSet ? Trace.Insights.Common.getCLS(data.insightSet) : null;
245
- const inp = data.insightSet ? Trace.Insights.Common.getINP(data.insightSet) : null;
253
+ if (focus.insightSet) {
254
+ const lcp = focus.insightSet ? Trace.Insights.Common.getLCP(focus.insightSet) : null;
255
+ const cls = focus.insightSet ? Trace.Insights.Common.getCLS(focus.insightSet) : null;
256
+ const inp = focus.insightSet ? Trace.Insights.Common.getINP(focus.insightSet) : null;
246
257
 
247
258
  const ModelHandlers = Trace.Handlers.ModelHandlers;
248
259
  const GOOD = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.GOOD;
@@ -259,9 +270,9 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
259
270
 
260
271
  // Add up to 3 suggestions from the top failing insights.
261
272
  const top3FailingInsightSuggestions =
262
- Object.values(data.insightSet.model)
273
+ Object.values(focus.insightSet.model)
263
274
  .filter(model => model.state !== 'pass')
264
- .map(model => new PerformanceInsightFormatter(this.#focus, model).getSuggestions().at(-1))
275
+ .map(model => new PerformanceInsightFormatter(focus, model).getSuggestions().at(-1))
265
276
  .filter(suggestion => !!suggestion)
266
277
  .slice(0, 3);
267
278
  suggestions.push(...top3FailingInsightSuggestions);
@@ -280,6 +291,7 @@ const MAX_FUNCTION_RESULT_BYTE_LENGTH = 16384 * 4;
280
291
  */
281
292
  export class PerformanceAgent extends AiAgent<AgentFocus> {
282
293
  #formatter: PerformanceTraceFormatter|null = null;
294
+ #lastEventForEnhancedQuery: Trace.Types.Events.Event|undefined;
283
295
  #lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
284
296
  #hasShownAnalyzeTraceContext = false;
285
297
 
@@ -300,6 +312,10 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
300
312
  text: extraPreambleWhenNotExternal,
301
313
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
302
314
  };
315
+ #freshTraceExtraPreambleFact: Host.AidaClient.RequestFact = {
316
+ text: extraPreambleWhenFreshTrace,
317
+ metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
318
+ };
303
319
  #networkDataDescriptionFact: Host.AidaClient.RequestFact = {
304
320
  text: PerformanceTraceFormatter.networkDataFormatDescription,
305
321
  metadata: {source: 'devtools', score: ScorePriority.CRITICAL}
@@ -373,10 +389,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
373
389
  * 1. go to paulirish.com, record a trace
374
390
  * 2. say "What performance issues exist with my page?"
375
391
  * 3. then say "images"
376
- *
377
- * TODO(cjamcl): reduce the reliance on this by making sure all URL references
378
- * (such as the insight formatters) add the "eventKey" as a suffix, just like all
379
- * other events.
380
392
  */
381
393
  #parseForKnownUrls(response: string): string {
382
394
  const focus = this.context?.getItem();
@@ -405,8 +417,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
405
417
  return match;
406
418
  }
407
419
 
408
- const request =
409
- focus.data.parsedTrace.data.NetworkRequests.byTime.find(request => request.args.data.url === urlText);
420
+ const request = focus.parsedTrace.data.NetworkRequests.byTime.find(request => request.args.data.url === urlText);
410
421
  if (!request) {
411
422
  return match;
412
423
  }
@@ -420,53 +431,6 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
420
431
  });
421
432
  }
422
433
 
423
- #parseSuggestions(text: string): ParsedResponse {
424
- if (!text) {
425
- return {answer: ''};
426
- }
427
-
428
- const lines = text.split('\n');
429
- const answerLines: string[] = [];
430
- let suggestions: [string, ...string[]]|undefined;
431
-
432
- for (const line of lines) {
433
- const trimmed = line.trim();
434
- if (trimmed.startsWith('SUGGESTIONS:')) {
435
- try {
436
- // TODO: Do basic validation this is an array with strings
437
- suggestions = JSON.parse(trimmed.substring('SUGGESTIONS:'.length).trim());
438
- } catch {
439
- }
440
- } else {
441
- answerLines.push(line);
442
- }
443
- }
444
-
445
- // Sometimes the model fails to put the SUGGESTIONS text on its own line. Handle
446
- // the case where the suggestions are part of the last line of the answer.
447
- if (!suggestions && answerLines.at(-1)?.includes('SUGGESTIONS:')) {
448
- const [answer, suggestionsText] = answerLines[answerLines.length - 1].split('SUGGESTIONS:', 2);
449
- try {
450
- // TODO: Do basic validation this is an array with strings
451
- suggestions = JSON.parse(suggestionsText.trim().substring('SUGGESTIONS:'.length).trim());
452
- } catch {
453
- }
454
- answerLines[answerLines.length - 1] = answer;
455
- }
456
-
457
- const response: ParsedResponse = {
458
- // If we could not parse the parts, consider the response to be an
459
- // answer.
460
- answer: answerLines.join('\n'),
461
- };
462
-
463
- if (suggestions) {
464
- response.suggestions = suggestions;
465
- }
466
-
467
- return response;
468
- }
469
-
470
434
  #parseMarkdown(response: string): string {
471
435
  /**
472
436
  * Sometimes the LLM responds with code chunks that wrap a text based markdown response.
@@ -482,10 +446,10 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
482
446
  }
483
447
 
484
448
  override parseTextResponse(response: string): ParsedResponse {
485
- response = response.trim();
486
- response = this.#parseForKnownUrls(response);
487
- response = this.#parseMarkdown(response);
488
- return this.#parseSuggestions(response);
449
+ const parsedResponse = super.parseTextResponse(response);
450
+ parsedResponse.answer = this.#parseForKnownUrls(parsedResponse.answer);
451
+ parsedResponse.answer = this.#parseMarkdown(parsedResponse.answer);
452
+ return parsedResponse;
489
453
  }
490
454
 
491
455
  override async enhanceQuery(query: string, context: PerformanceTraceContext|null): Promise<string> {
@@ -500,13 +464,21 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
500
464
  const focus = context.getItem();
501
465
  const selected: string[] = [];
502
466
 
503
- if (focus.data.callTree) {
467
+ if (focus.event) {
468
+ const includeEventInfo = focus.event !== this.#lastEventForEnhancedQuery;
469
+ this.#lastEventForEnhancedQuery = focus.event;
470
+ if (includeEventInfo) {
471
+ selected.push(`User selected an event ${this.#formatter?.serializeEvent(focus.event)}.\n\n`);
472
+ }
473
+ }
474
+
475
+ if (focus.callTree) {
504
476
  // If this is a followup chat about the same call tree, don't include the call tree serialization again.
505
477
  // We don't need to repeat it and we'd rather have more the context window space.
506
478
  let contextString = '';
507
- if (!this.#callTreeContextSet.has(focus.data.callTree)) {
508
- contextString = focus.data.callTree.serialize();
509
- this.#callTreeContextSet.add(focus.data.callTree);
479
+ if (!this.#callTreeContextSet.has(focus.callTree)) {
480
+ contextString = focus.callTree.serialize();
481
+ this.#callTreeContextSet.add(focus.callTree);
510
482
  }
511
483
 
512
484
  if (contextString) {
@@ -514,17 +486,17 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
514
486
  }
515
487
  }
516
488
 
517
- if (focus.data.insight) {
489
+ if (focus.insight) {
518
490
  // We only need to add Insight info to a prompt when the context changes. For example:
519
491
  // User clicks Insight A. We need to send info on Insight A with the prompt.
520
492
  // User asks follow up question. We do not need to resend Insight A with the prompt.
521
493
  // User clicks Insight B. We now need to send info on Insight B with the prompt.
522
494
  // User clicks Insight A. We should resend the Insight info with the prompt.
523
- const includeInsightInfo = focus.data.insight !== this.#lastInsightForEnhancedQuery;
524
- this.#lastInsightForEnhancedQuery = focus.data.insight;
495
+ const includeInsightInfo = focus.insight !== this.#lastInsightForEnhancedQuery;
496
+ this.#lastInsightForEnhancedQuery = focus.insight;
525
497
 
526
498
  if (includeInsightInfo) {
527
- selected.push(`User selected the ${focus.data.insight.insightKey} insight.\n\n`);
499
+ selected.push(`User selected the ${focus.insight.insightKey} insight.\n\n`);
528
500
  }
529
501
  }
530
502
 
@@ -636,11 +608,16 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
636
608
  this.addFact(this.#notExternalExtraPreambleFact);
637
609
  }
638
610
 
611
+ const isFresh = Tracing.FreshRecording.Tracker.instance().recordingIsFresh(focus.parsedTrace);
612
+ if (isFresh) {
613
+ this.addFact(this.#freshTraceExtraPreambleFact);
614
+ }
615
+
639
616
  this.addFact(this.#callFrameDataDescriptionFact);
640
617
  this.addFact(this.#networkDataDescriptionFact);
641
618
 
642
619
  if (!this.#traceFacts.length) {
643
- this.#formatter = new PerformanceTraceFormatter(focus);
620
+ this.#formatter = new PerformanceTraceFormatter(focus, PerformanceInsightFormatter.create);
644
621
  this.#createFactForTraceSummary();
645
622
  this.#createFactForCriticalRequests();
646
623
  this.#createFactForMainThreadBottomUpSummary();
@@ -672,7 +649,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
672
649
 
673
650
  #declareFunctions(context: PerformanceTraceContext): void {
674
651
  const focus = context.getItem();
675
- const {parsedTrace, insightSet} = focus.data;
652
+ const {parsedTrace, insightSet} = focus;
676
653
 
677
654
  this.declareFunction<{insightName: string}, {details: string}>('getInsightDetails', {
678
655
  description:
@@ -116,11 +116,11 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
116
116
  }
117
117
 
118
118
  const focus = context.getItem();
119
- if (!focus.data.callTree) {
119
+ if (!focus.callTree) {
120
120
  throw new Error('unexpected context');
121
121
  }
122
122
 
123
- const callTree = focus.data.callTree;
123
+ const callTree = focus.callTree;
124
124
 
125
125
  yield {
126
126
  type: ResponseType.CONTEXT,
@@ -140,11 +140,11 @@ export class PerformanceAnnotationsAgent extends AiAgent<AgentFocus> {
140
140
  }
141
141
 
142
142
  const focus = context.getItem();
143
- if (!focus.data.callTree) {
143
+ if (!focus.callTree) {
144
144
  throw new Error('unexpected context');
145
145
  }
146
146
 
147
- const callTree = focus.data.callTree;
147
+ const callTree = focus.callTree;
148
148
  const contextString = callTree.serialize();
149
149
  return `${contextString}\n\n# User request\n\n${query}`;
150
150
  }
@@ -21,7 +21,6 @@ import {
21
21
  type ConversationSuggestions,
22
22
  type FunctionCallHandlerResult,
23
23
  MultimodalInputType,
24
- type ParsedResponse,
25
24
  type RequestOptions,
26
25
  ResponseType,
27
26
  } from './AiAgent.js';
@@ -265,36 +264,6 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
265
264
  override preambleFeatures(): string[] {
266
265
  return ['function_calling'];
267
266
  }
268
- override parseTextResponse(text: string): ParsedResponse {
269
- // We're returning an empty answer to denote the erroneous case.
270
- if (!text.trim()) {
271
- return {answer: ''};
272
- }
273
-
274
- const lines = text.split('\n');
275
- const answerLines: string[] = [];
276
- let suggestions: [string, ...string[]]|undefined;
277
-
278
- for (const line of lines) {
279
- const trimmed = line.trim();
280
- if (trimmed.startsWith('SUGGESTIONS:')) {
281
- try {
282
- // TODO: Do basic validation this is an array with strings
283
- suggestions = JSON.parse(trimmed.substring('SUGGESTIONS:'.length).trim());
284
- } catch {
285
- }
286
- } else {
287
- answerLines.push(line);
288
- }
289
- }
290
-
291
- return {
292
- // If we could not parse the parts, consider the response to be an
293
- // answer.
294
- answer: answerLines.join('\n'),
295
- suggestions,
296
- };
297
- }
298
267
 
299
268
  #execJs: typeof executeJsCode;
300
269
 
@@ -7,7 +7,7 @@ This insight is used to analyze the time spent that contributed to the final LCP
7
7
 
8
8
  ## Detailed analysis:
9
9
  The Largest Contentful Paint (LCP) time for this navigation was 240 ms.
10
- The LCP element (IMG) is an image fetched from `https://creativetouchrotherham.co.uk/images/creative-touch-home/creative_touch_rotherham_homehero/creative_touch_rotherham_homehero_700.webp`.
10
+ The LCP element (IMG, nodeId: 23) is an image fetched from https://creativetouchrotherham.co.uk/images/creative-touch-home/creative_touch_rotherham_homehero/creative_touch_rotherham_homehero_700.webp (eventKey: s-1418).
11
11
  ## LCP resource network request: https://creativetouchrotherham.co.uk/images/creative-touch-home/creative_touch_rotherham_homehero/creative_touch_rotherham_homehero_700.webp
12
12
  eventKey: s-1418
13
13
  Timings:
@@ -68,7 +68,7 @@ This insight is used to analyze the time spent that contributed to the final LCP
68
68
 
69
69
  ## Detailed analysis:
70
70
  The Largest Contentful Paint (LCP) time for this navigation was 129 ms.
71
- The LCP element is an image fetched from `https://web-dev.imgix.net/image/kheDArv5csY6rvQUJDbWRscckLr1/4i7JstVZvgTFk9dxCe4a.svg`.
71
+ The LCP element is an image fetched from https://web-dev.imgix.net/image/kheDArv5csY6rvQUJDbWRscckLr1/4i7JstVZvgTFk9dxCe4a.svg (eventKey: s-1314).
72
72
  ## LCP resource network request: https://web-dev.imgix.net/image/kheDArv5csY6rvQUJDbWRscckLr1/4i7JstVZvgTFk9dxCe4a.svg
73
73
  eventKey: s-1314
74
74
  Timings:
@@ -186,7 +186,7 @@ It is important that all of these checks pass to minimize the delay between the
186
186
 
187
187
  ## Detailed analysis:
188
188
  The Largest Contentful Paint (LCP) time for this navigation was 1,077 ms.
189
- The LCP element is an image fetched from `http://localhost:8787/lcp-discovery-delay/lcp-image.jpg`.
189
+ The LCP element is an image fetched from http://localhost:8787/lcp-discovery-delay/lcp-image.jpg (eventKey: s-25281).
190
190
  ## LCP resource network request: http://localhost:8787/lcp-discovery-delay/lcp-image.jpg
191
191
  eventKey: s-25281
192
192
  Timings:
@@ -310,7 +310,7 @@ Layout shifts in this cluster:
310
310
  - Start time: 472 ms
311
311
  - Score: 0.0003
312
312
  - Potential root causes:
313
- - A font that was loaded over the network (https://fonts.gstatic.com/s/specialgothicexpandedone/v2/IurO6Zxk74-YaYk1r3HOet4g75ENmBxUmOK61tA0Iu5QmJF_.woff2).
313
+ - A font that was loaded over the network: https://fonts.gstatic.com/s/specialgothicexpandedone/v2/IurO6Zxk74-YaYk1r3HOet4g75ENmBxUmOK61tA0Iu5QmJF_.woff2 (eventKey: s-1158).
314
314
  ### Layout shift 2:
315
315
  - Start time: 857 ms
316
316
  - Score: 0.0844
@@ -320,12 +320,12 @@ Layout shifts in this cluster:
320
320
  - Start time: 1,352 ms
321
321
  - Score: 0.0068
322
322
  - Potential root causes:
323
- - An unsized image (IMG) (url: http://localhost:8000/unsized-image.png).
323
+ - An unsized image (IMG) (url: http://localhost:8000/unsized-image.png (eventKey: s-4487)).
324
324
  ### Layout shift 4:
325
325
  - Start time: 1,537 ms
326
326
  - Score: 0.3344
327
327
  - Potential root causes:
328
- - An unsized image (IMG) (url: http://localhost:8000/unsized-image.png).
328
+ - An unsized image (IMG) (url: http://localhost:8000/unsized-image.png (eventKey: s-4487)).
329
329
  ### Layout shift 5:
330
330
  - Start time: 2,343 ms
331
331
  - Score: 0.3396
@@ -338,6 +338,104 @@ Layout shifts in this cluster:
338
338
  - https://web.dev/articles/optimize-cls
339
339
  === end content
340
340
 
341
+ Title: PerformanceInsightFormatter CLS serializes correctly when there are no layout shifts
342
+ Content:
343
+ ## Insight Title: Layout shift culprits
344
+
345
+ ## Insight Summary:
346
+ Cumulative Layout Shifts (CLS) is a measure of the largest burst of layout shifts for every unexpected layout shift that occurs during the lifecycle of a page. This is a Core Web Vital and the thresholds for categorizing a score are:
347
+ - Good: 0.1 or less
348
+ - Needs improvement: more than 0.1 and less than or equal to 0.25
349
+ - Bad: over 0.25
350
+
351
+ ## Detailed analysis:
352
+ No layout shifts were found.
353
+
354
+ ## Estimated savings: none
355
+
356
+ ## External resources:
357
+ - https://wdeb.dev/articles/cls
358
+ - https://web.dev/articles/optimize-cls
359
+ === end content
360
+
361
+ Title: PerformanceInsightFormatter CLS outputs information on non-composited animations
362
+ Content:
363
+ ## Insight Title: Layout shift culprits
364
+
365
+ ## Insight Summary:
366
+ Cumulative Layout Shifts (CLS) is a measure of the largest burst of layout shifts for every unexpected layout shift that occurs during the lifecycle of a page. This is a Core Web Vital and the thresholds for categorizing a score are:
367
+ - Good: 0.1 or less
368
+ - Needs improvement: more than 0.1 and less than or equal to 0.25
369
+ - Bad: over 0.25
370
+
371
+ ## Detailed analysis:
372
+ The worst layout shift cluster was the cluster that started at 60 ms and ended at 1,243 ms, with a duration of 1,183 ms.
373
+ The score for this cluster is 0.0140.
374
+
375
+ Layout shifts in this cluster:
376
+ ### Layout shift 1:
377
+ - Start time: 60 ms
378
+ - Score: 0.0012
379
+ - No potential root causes identified
380
+ ### Layout shift 2:
381
+ - Start time: 76 ms
382
+ - Score: 0.0012
383
+ - No potential root causes identified
384
+ ### Layout shift 3:
385
+ - Start time: 93 ms
386
+ - Score: 0.0012
387
+ - No potential root causes identified
388
+ ### Layout shift 4:
389
+ - Start time: 110 ms
390
+ - Score: 0.0012
391
+ - No potential root causes identified
392
+ ### Layout shift 5:
393
+ - Start time: 126 ms
394
+ - Score: 0.0012
395
+ - No potential root causes identified
396
+ ### Layout shift 6:
397
+ - Start time: 143 ms
398
+ - Score: 0.0012
399
+ - No potential root causes identified
400
+ ### Layout shift 7:
401
+ - Start time: 160 ms
402
+ - Score: 0.0012
403
+ - No potential root causes identified
404
+ ### Layout shift 8:
405
+ - Start time: 176 ms
406
+ - Score: 0.0012
407
+ - No potential root causes identified
408
+ ### Layout shift 9:
409
+ - Start time: 193 ms
410
+ - Score: 0.0012
411
+ - No potential root causes identified
412
+ ### Layout shift 10:
413
+ - Start time: 210 ms
414
+ - Score: 0.0012
415
+ - No potential root causes identified
416
+ ### Layout shift 11:
417
+ - Start time: 226 ms
418
+ - Score: 0.0012
419
+ - No potential root causes identified
420
+ ### Layout shift 12:
421
+ - Start time: 243 ms
422
+ - Score: 0.0012
423
+ - Potential root causes:
424
+ - A non-composited animation:
425
+ - non-composited animation: `change-height`
426
+ Animation name: change-height
427
+ Unsupported CSS properties:
428
+ - height
429
+ Failure reasons:
430
+ - TARGET_HAS_INVALID_COMPOSITING_STATE, UNSUPPORTED_CSS_PROPERTY
431
+
432
+ ## Estimated savings: none
433
+
434
+ ## External resources:
435
+ - https://wdeb.dev/articles/cls
436
+ - https://web.dev/articles/optimize-cls
437
+ === end content
438
+
341
439
  Title: PerformanceInsightFormatter INP breakdown serializes the correct details
342
440
  Content:
343
441
  ## Insight Title: INP breakdown
@@ -589,16 +687,16 @@ Total legacy JavaScript: 8 files.
589
687
 
590
688
  Legacy JavaScript by file:
591
689
 
592
- - Script: https://s.yimg.com/aaq/benji/benji-2.2.99.js - Wasted bytes: 37204 bytes
690
+ - Script: https://s.yimg.com/aaq/benji/benji-2.2.99.js (eventKey: s-3387) - Wasted bytes: 37204 bytes
593
691
  Matches:
594
692
  Line: 0, Column: 133, Name: Promise.allSettled
595
693
 
596
- - Script: https://s.yimg.com/aaq/c/25fa214.caas-news_web.min.js - Wasted bytes: 36084 bytes
694
+ - Script: https://s.yimg.com/aaq/c/25fa214.caas-news_web.min.js (eventKey: s-3412) - Wasted bytes: 36084 bytes
597
695
  Matches:
598
696
  Line: 0, Column: 13310, Name: Array.from
599
697
  Line: 0, Column: 14623, Name: Object.assign
600
698
 
601
- - Script: https://s.yimg.com/du/ay/wnsrvbjmeprtfrnfx.js - Wasted bytes: 12850 bytes
699
+ - Script: https://s.yimg.com/du/ay/wnsrvbjmeprtfrnfx.js (eventKey: s-6273) - Wasted bytes: 12850 bytes
602
700
  Matches:
603
701
  Line: 111, Column: 7829, Name: @babel/plugin-transform-spread
604
702
  Line: 111, Column: 1794, Name: Array.prototype.find
@@ -607,21 +705,21 @@ Line: 111, Column: 2748, Name: Object.values
607
705
  Line: 111, Column: 2473, Name: String.prototype.includes
608
706
  Line: 111, Column: 2627, Name: String.prototype.startsWith
609
707
 
610
- - Script: https://static.criteo.net/js/ld/publishertag.prebid.144.js - Wasted bytes: 10751 bytes
708
+ - Script: https://static.criteo.net/js/ld/publishertag.prebid.144.js (eventKey: s-257378) - Wasted bytes: 10751 bytes
611
709
  Matches:
612
710
  Line: 1, Column: 74871, Name: Array.isArray
613
711
  Line: 1, Column: 75344, Name: Array.prototype.filter
614
712
  Line: 1, Column: 75013, Name: Array.prototype.indexOf
615
713
 
616
- - Script: https://s.yimg.com/oa/consent.js - Wasted bytes: 8157 bytes
714
+ - Script: https://s.yimg.com/oa/consent.js (eventKey: s-3384) - Wasted bytes: 8157 bytes
617
715
  Matches:
618
716
  Line: 1, Column: 132267, Name: Array.prototype.includes
619
717
 
620
- - Script: https://pm-widget.taboola.com/yahooweb-network/pmk-20220605.1.js - Wasted bytes: 7625 bytes
718
+ - Script: https://pm-widget.taboola.com/yahooweb-network/pmk-20220605.1.js (eventKey: s-52229) - Wasted bytes: 7625 bytes
621
719
  Matches:
622
720
  Line: 181, Column: 26, Name: Object.keys
623
721
 
624
- - Script: https://news.yahoo.com/ - Wasted bytes: 7141 bytes
722
+ - Script: https://news.yahoo.com/ (eventKey: s-2116) - Wasted bytes: 7141 bytes
625
723
  Matches:
626
724
  Line: 0, Column: 8382, Name: @babel/plugin-transform-classes
627
725
  Line: 0, Column: 107712, Name: Array.prototype.filter
@@ -629,7 +727,7 @@ Line: 0, Column: 107393, Name: Array.prototype.forEach
629
727
  Line: 0, Column: 108005, Name: Array.prototype.map
630
728
  Line: 0, Column: 108358, Name: String.prototype.includes
631
729
 
632
- - Script: https://cdn.taboola.com/libtrc/yahooweb-network/loader.js - Wasted bytes: 7061 bytes
730
+ - Script: https://cdn.taboola.com/libtrc/yahooweb-network/loader.js (eventKey: s-6352) - Wasted bytes: 7061 bytes
633
731
  Matches:
634
732
  Line: 0, Column: 390544, Name: Object.entries
635
733
  Line: 0, Column: 390688, Name: Object.values
@@ -669,12 +767,12 @@ This insight identifies font issues when a webpage uses custom fonts, for exampl
669
767
  ## Detailed analysis:
670
768
  The following font display issues were found:
671
769
 
672
- - Font name: jizaRExUiTo99u79D0KExcOPIDU.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizaRExUiTo99u79D0KExcOPIDU.woff2, Property 'font-display' set to: 'auto', Wasted time: 20 ms.
673
- - Font name: SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2, Property 'font-display' set to: 'auto', Wasted time: 15 ms.
674
- - Font name: jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2, Property 'font-display' set to: 'auto', Wasted time: 15 ms.
675
- - Font name: SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2, Property 'font-display' set to: 'auto', Wasted time: 15 ms.
676
- - Font name: EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2, URL: https://fonts.gstatic.com/s/ptserif/v18/EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2, Property 'font-display' set to: 'auto', Wasted time: 15 ms.
677
- - Font name: S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2, URL: https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2, Property 'font-display' set to: 'auto', Wasted time: 10 ms.
770
+ - Font name: jizaRExUiTo99u79D0KExcOPIDU.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizaRExUiTo99u79D0KExcOPIDU.woff2 (eventKey: s-5246), Property 'font-display' set to: 'auto', Wasted time: 20 ms.
771
+ - Font name: SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2 (eventKey: s-5232), Property 'font-display' set to: 'auto', Wasted time: 15 ms.
772
+ - Font name: jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2 (eventKey: s-5259), Property 'font-display' set to: 'auto', Wasted time: 15 ms.
773
+ - Font name: SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2 (eventKey: s-5269), Property 'font-display' set to: 'auto', Wasted time: 15 ms.
774
+ - Font name: EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2, URL: https://fonts.gstatic.com/s/ptserif/v18/EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2 (eventKey: s-5325), Property 'font-display' set to: 'auto', Wasted time: 15 ms.
775
+ - Font name: S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2, URL: https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2 (eventKey: s-5238), Property 'font-display' set to: 'auto', Wasted time: 10 ms.
678
776
 
679
777
  Consider setting [`font-display`](https://developer.chrome.com/blog/font-display) to `swap` or `optional` to ensure text is consistently visible. `swap` can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).
680
778
 
@@ -714,28 +812,28 @@ Total potential savings: 2 MB
714
812
 
715
813
  The following images could be optimized:
716
814
 
717
- ### https://images.ctfassets.net/u275ja1nivmq/6T6z40ay5GFCUtwV7DONgh/0e23606ed1692d9721ab0f39a8d8a99e/yeti_cover.jpg
815
+ ### https://images.ctfassets.net/u275ja1nivmq/6T6z40ay5GFCUtwV7DONgh/0e23606ed1692d9721ab0f39a8d8a99e/yeti_cover.jpg (eventKey: s-1212)
718
816
  - Potential savings: 1.1 MB
719
817
  - Optimizations:
720
818
  Using a modern image format (WebP, AVIF) or increasing the image compression could improve this image's download size. (Est 1.1 MB)
721
819
 
722
- ### https://raw.githubusercontent.com/GoogleChrome/lighthouse/refs/heads/main/cli/test/fixtures/dobetterweb/lighthouse-rotating.gif
820
+ ### https://raw.githubusercontent.com/GoogleChrome/lighthouse/refs/heads/main/cli/test/fixtures/dobetterweb/lighthouse-rotating.gif (eventKey: s-1216)
723
821
  - Potential savings: 682 kB
724
822
  - Optimizations:
725
823
  Using video formats instead of GIFs can improve the download size of animated content. (Est 682 kB)
726
824
 
727
- ### https://onlinepngtools.com/images/examples-onlinepngtools/elephant-hd-quality.png
825
+ ### https://onlinepngtools.com/images/examples-onlinepngtools/elephant-hd-quality.png (eventKey: s-1228)
728
826
  - Potential savings: 176 kB
729
827
  - Optimizations:
730
828
  Using a modern image format (WebP, AVIF) or increasing the image compression could improve this image's download size. (Est 134.1 kB)
731
829
  This image file is larger than it needs to be (640x436) for its displayed dimensions (200x136). Use responsive images to reduce the image download size. (Est 162.9 kB)
732
830
 
733
- ### https://images.ctfassets.net/u275ja1nivmq/6T6z40ay5GFCUtwV7DONgh/0e23606ed1692d9721ab0f39a8d8a99e/yeti_cover.jpg?fm=webp
831
+ ### https://images.ctfassets.net/u275ja1nivmq/6T6z40ay5GFCUtwV7DONgh/0e23606ed1692d9721ab0f39a8d8a99e/yeti_cover.jpg?fm=webp (eventKey: s-1224)
734
832
  - Potential savings: 49.8 kB
735
833
  - Optimizations:
736
834
  Increasing the image compression factor could improve this image's download size. (Est 49.8 kB)
737
835
 
738
- ### https://raw.githubusercontent.com/GoogleChrome/lighthouse/refs/heads/main/cli/test/fixtures/byte-efficiency/lighthouse-2048x1356.webp
836
+ ### https://raw.githubusercontent.com/GoogleChrome/lighthouse/refs/heads/main/cli/test/fixtures/byte-efficiency/lighthouse-2048x1356.webp (eventKey: s-1226)
739
837
  - Potential savings: 41.4 kB
740
838
  - Optimizations:
741
839
  This image file is larger than it needs to be (2048x1356) for its displayed dimensions (200x132). Use responsive images to reduce the image download size. (Est 41.4 kB)
@@ -852,9 +950,9 @@ The network dependency tree checks found one or more problems.
852
950
  Max critical path latency is 5,015 ms
853
951
 
854
952
  The following is the critical request chain:
855
- - http://localhost:8787/lcp-iframes/index.html (5,006 ms) (longest chain)
856
- - http://localhost:8787/lcp-iframes/app.js (5,015 ms) (longest chain)
857
- - http://localhost:8787/lcp-iframes/app.css (5,010 ms)
953
+ - http://localhost:8787/lcp-iframes/index.html (eventKey: s-7103) (5,006 ms) (longest chain)
954
+ - http://localhost:8787/lcp-iframes/app.js (eventKey: s-7225) (5,015 ms) (longest chain)
955
+ - http://localhost:8787/lcp-iframes/app.css (eventKey: s-7213) (5,010 ms)
858
956
 
859
957
  no origins were preconnected.
860
958
 
@@ -979,7 +1077,7 @@ This insight identifies static resources that are not cached effectively by the
979
1077
  ## Detailed analysis:
980
1078
  The following resources were associated with ineffficient cache policies:
981
1079
 
982
- - https://chromedevtools.github.io/performance-stories/lcp-large-image/app.css
1080
+ - https://chromedevtools.github.io/performance-stories/lcp-large-image/app.css (eventKey: s-1106)
983
1081
  - Cache Time to Live (TTL): 600 seconds
984
1082
  - Wasted bytes: 0 B
985
1083