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
@@ -9,7 +9,7 @@ import type {AgentFocus} from '../performance/AIContext.js';
9
9
  import {AIQueries} from '../performance/AIQueries.js';
10
10
 
11
11
  import {NetworkRequestFormatter} from './NetworkRequestFormatter.js';
12
- import {PerformanceInsightFormatter} from './PerformanceInsightFormatter.js';
12
+ import type {PerformanceInsightFormatter} from './PerformanceInsightFormatter.js';
13
13
  import {bytes, micros, millis} from './UnitFormatters.js';
14
14
 
15
15
  export interface NetworkRequestFormatOptions {
@@ -17,17 +17,27 @@ export interface NetworkRequestFormatOptions {
17
17
  customTitle?: string;
18
18
  }
19
19
 
20
+ type GetInsightFormatter = (focus: AgentFocus, model: Trace.Insights.Types.InsightModel) => PerformanceInsightFormatter;
21
+
20
22
  export class PerformanceTraceFormatter {
21
23
  #focus: AgentFocus;
22
24
  #parsedTrace: Trace.TraceModel.ParsedTrace;
23
25
  #insightSet: Trace.Insights.Types.InsightSet|null;
26
+ #getInsightFormatter: GetInsightFormatter|null = null;
24
27
  protected eventsSerializer: Trace.EventsSerializer.EventsSerializer;
25
28
 
26
- constructor(focus: AgentFocus) {
29
+ /**
30
+ * We inject the insight formatter because otherwise we get a circular
31
+ * dependency between PerformanceInsightFormatter and
32
+ * PerformanceTraceFormatter. This is OK in the browser build, but breaks when
33
+ * we reuse this code in NodeJS for DevTools MCP.
34
+ */
35
+ constructor(focus: AgentFocus, getInsightFormatter: GetInsightFormatter|null) {
27
36
  this.#focus = focus;
28
- this.#parsedTrace = focus.data.parsedTrace;
29
- this.#insightSet = focus.data.insightSet;
37
+ this.#parsedTrace = focus.parsedTrace;
38
+ this.#insightSet = focus.insightSet;
30
39
  this.eventsSerializer = focus.eventsSerializer;
40
+ this.#getInsightFormatter = getInsightFormatter;
31
41
  }
32
42
 
33
43
  serializeEvent(event: Trace.Types.Events.Event): string {
@@ -133,7 +143,10 @@ export class PerformanceTraceFormatter {
133
143
  if (lcp || cls || inp) {
134
144
  parts.push('Metrics (lab / observed):');
135
145
  if (lcp) {
136
- parts.push(` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}`);
146
+ const nodeId = insightSet?.model.LCPBreakdown.lcpEvent?.args.data?.nodeId;
147
+ const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
148
+ parts.push(
149
+ ` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
137
150
  const subparts = insightSet?.model.LCPBreakdown.subparts;
138
151
  if (subparts) {
139
152
  const serializeSubpart = (subpart: Trace.Insights.Models.LCPBreakdown.Subpart): string => {
@@ -175,7 +188,10 @@ export class PerformanceTraceFormatter {
175
188
  continue;
176
189
  }
177
190
 
178
- const formatter = new PerformanceInsightFormatter(this.#focus, model);
191
+ const formatter = this.#getInsightFormatter?.(this.#focus, model);
192
+ if (!formatter) {
193
+ continue;
194
+ }
179
195
  if (!formatter.insightIsSupported()) {
180
196
  continue;
181
197
  }
@@ -442,19 +458,7 @@ export class PerformanceTraceFormatter {
442
458
  }
443
459
 
444
460
  formatCallTree(tree: AICallTree, headerLevel = 1): string {
445
- const results = [tree.serialize(headerLevel), ''];
446
-
447
- // TODO(b/425270067): add eventKey to tree.serialize, but need to wait for other
448
- // performance agent to be consolidated.
449
- results.push('#'.repeat(headerLevel) + ' Node id to eventKey\n');
450
- results.push('These node ids correspond to the call tree nodes listed in the above section.\n');
451
- tree.breadthFirstWalk(tree.rootNode.children().values(), (node, nodeId) => {
452
- results.push(`${nodeId}: ${this.eventsSerializer.keyForEvent(node.event)}`);
453
- });
454
-
455
- results.push('\nIMPORTANT: Never show eventKey to the user.');
456
-
457
- return results.join('\n');
461
+ return `${tree.serialize(headerLevel)}\n\nIMPORTANT: Never show eventKey to the user.`;
458
462
  }
459
463
 
460
464
  formatNetworkRequests(
@@ -0,0 +1,75 @@
1
+ Title: AICallTree supports NodeJS traces that do not have a "main thread"
2
+ Content:
3
+ # All URLs:
4
+
5
+ * 0: node:internal/main/run_main_module
6
+ * 1: node:internal/modules/run_main
7
+ * 2: node:internal/modules/cjs/loader
8
+ * 3: file:///Users/andoli/Desktop/mocks/fixnodeinspector/app.js
9
+
10
+ # Call tree:
11
+
12
+ 1;p-1-1-0-2;(anonymous);2370;;0;2
13
+ 2;p-1-1-0-3;executeUserEntryPoint;2370;;1;3
14
+ 3;p-1-1-0-4;Module._load;2370;;2;4
15
+ 4;p-1-1-0-5;Module.load;2370;;2;5
16
+ 5;p-1-1-0-6;Module._extensions..js;2370;;2;6
17
+ 6;p-1-1-0-7;Module._compile;2370;;2;7
18
+ 7;p-1-1-0-8;callAndPauseOnStart;2370;;;8;S
19
+ 8;p-1-1-0-9;(anonymous);2370;2370;3;
20
+ === end content
21
+
22
+ Title: AICallTree serializes a simple tree
23
+ Content:
24
+ # All URLs:
25
+
26
+ * 0: https://www.gstatic.com/devrel-devsite/prod/vafe2e13ca17bb026e70df42a2ead1c8192750e86a12923a88eda839025dabf95/js/devsite_app_module.js
27
+
28
+ # Call tree:
29
+
30
+ 1;r-36071;Task;0.2;;;2
31
+ 2;r-36072;Timer fired;0.2;;;3
32
+ 3;r-36076;Function call;0.2;;0;4
33
+ 4;p-74406-259-16342-528;_ds.q.ns;0.2;;0;5;S
34
+ 5;p-74406-259-16342-529;clearTimeout;0.2;0;;6
35
+ 6;r-36082;Recalculate style;0.2;0.2;;
36
+ === end content
37
+
38
+ Title: AICallTree serializes a simple tree in a concise format
39
+ Content:
40
+ # All URLs:
41
+
42
+ * 0: https://www.gstatic.com/devrel-devsite/prod/vafe2e13ca17bb026e70df42a2ead1c8192750e86a12923a88eda839025dabf95/js/devsite_app_module.js
43
+
44
+ # Call tree:
45
+
46
+ 1;r-36071;Task;0.2;;;2
47
+ 2;r-36072;Timer fired;0.2;;;3
48
+ 3;r-36076;Function call;0.2;;0;4
49
+ 4;p-74406-259-16342-528;_ds.q.ns;0.2;;0;5;S
50
+ 5;p-74406-259-16342-529;clearTimeout;0.2;0;;6
51
+ 6;r-36082;Recalculate style;0.2;0.2;;
52
+ === end content
53
+
54
+ Title: AICallTree serializes a tree in a concise format
55
+ Content:
56
+ # All URLs:
57
+
58
+ * 0: https://www.gstatic.com/firebasejs/6.6.1/firebase-performance.js
59
+
60
+ # Call tree:
61
+
62
+ 1;r-5764;Task;0.9;0;;2;S
63
+ 2;r-5765;Timer fired;0.9;0;;3
64
+ 3;r-5766;Function call;0.9;0.1;0;4
65
+ 4;p-73704-775-2873-705;(anonymous);0.8;;0;5
66
+ 5;p-73704-775-2873-706;(anonymous);0.8;;0;6-8
67
+ 6;p-73704-775-2873-707;Ot.getEntriesByType;0.1;;0;8
68
+ 7;p-73704-775-2874-709;le.createOobTrace;0.6;0.2;0;9-11
69
+ 8;p-73704-775-2873-708;getEntriesByType;0.1;0.1;;
70
+ 9;p-73704-775-2875-710;le;0.1;0.1;0;
71
+ 10;p-73704-775-2877-711;ie;0.2;;0;11-13
72
+ 11;p-73704-775-2877-712;Ot.requiredApisAvailable;0.2;0.2;0;
73
+ 12;p-73704-775-2879-713;oe;0;;0;13
74
+ 13;p-73704-775-2879-714;setTimeout;0;0;;
75
+ === end content
@@ -24,6 +24,10 @@ export interface FromTimeOnThreadOptions {
24
24
  }
25
25
 
26
26
  export class AICallTree {
27
+ // Note: ideally this is passed in (or lived on ParsedTrace), but this class is
28
+ // stateless (mostly, there's a cache for some stuff) so it doesn't match much.
29
+ #eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
30
+
27
31
  constructor(
28
32
  public selectedNode: Trace.Extras.TraceTree.Node|null,
29
33
  public rootNode: Trace.Extras.TraceTree.TopDownRootNode,
@@ -307,7 +311,10 @@ export class AICallTree {
307
311
  // 1. ID
308
312
  const idStr = String(nodeId);
309
313
 
310
- // 2. Name
314
+ // 2. eventKey
315
+ const eventKey = this.#eventsSerializer.keyForEvent(node.event);
316
+
317
+ // 3. Name
311
318
  const name = Trace.Name.forEntry(event, parsedTrace);
312
319
 
313
320
  // Round milliseconds to one decimal place, return empty string if zero/undefined
@@ -318,13 +325,13 @@ export class AICallTree {
318
325
  return String(Math.round(num * 10) / 10);
319
326
  };
320
327
 
321
- // 3. Duration
328
+ // 4. Duration
322
329
  const durationStr = roundToTenths(node.totalTime);
323
330
 
324
- // 4. Self Time
331
+ // 5. Self Time
325
332
  const selfTimeStr = roundToTenths(node.selfTime);
326
333
 
327
- // 5. URL Index
334
+ // 6. URL Index
328
335
  const url = SourceMapsResolver.SourceMapsResolver.resolvedURLForEntry(parsedTrace, event);
329
336
  let urlIndexStr = '';
330
337
  if (url) {
@@ -336,7 +343,7 @@ export class AICallTree {
336
343
  }
337
344
  }
338
345
 
339
- // 6. Child Range
346
+ // 7. Child Range
340
347
  const children = Array.from(node.children().values());
341
348
  let childRangeStr = '';
342
349
  if (childStartingNodeIndex) {
@@ -344,11 +351,12 @@ export class AICallTree {
344
351
  `${childStartingNodeIndex}-${childStartingNodeIndex + children.length}`;
345
352
  }
346
353
 
347
- // 7. Selected Marker
354
+ // 8. Selected Marker
348
355
  const selectedMarker = selectedNode?.event === node.event ? 'S' : '';
349
356
 
350
357
  // Combine fields
351
358
  let line = idStr;
359
+ line += ';' + eventKey;
352
360
  line += ';' + name;
353
361
  line += ';' + durationStr;
354
362
  line += ';' + selfTimeStr;
@@ -4,11 +4,14 @@
4
4
 
5
5
  import * as Trace from '../../../models/trace/trace.js';
6
6
 
7
- import type {AICallTree} from './AICallTree.js';
7
+ import {AICallTree} from './AICallTree.js';
8
8
 
9
- export interface AgentFocusData {
9
+ interface AgentFocusData {
10
10
  parsedTrace: Trace.TraceModel.ParsedTrace;
11
11
  insightSet: Trace.Insights.Types.InsightSet|null;
12
+ /** Note: at most one of event or callTree is non-null. */
13
+ event: Trace.Types.Events.Event|null;
14
+ /** Note: at most one of event or callTree is non-null. */
12
15
  callTree: AICallTree|null;
13
16
  insight: Trace.Insights.Types.InsightModel|null;
14
17
  }
@@ -30,6 +33,7 @@ export class AgentFocus {
30
33
  return new AgentFocus({
31
34
  parsedTrace,
32
35
  insightSet,
36
+ event: null,
33
37
  callTree: null,
34
38
  insight: null,
35
39
  });
@@ -45,11 +49,22 @@ export class AgentFocus {
45
49
  return new AgentFocus({
46
50
  parsedTrace,
47
51
  insightSet,
52
+ event: null,
48
53
  callTree: null,
49
54
  insight,
50
55
  });
51
56
  }
52
57
 
58
+ static fromEvent(parsedTrace: Trace.TraceModel.ParsedTrace, event: Trace.Types.Events.Event): AgentFocus {
59
+ if (!parsedTrace.insights) {
60
+ throw new Error('missing insights');
61
+ }
62
+
63
+ const insightSet = getFirstInsightSet(parsedTrace.insights);
64
+ const result = AgentFocus.#getCallTreeOrEvent(parsedTrace, event);
65
+ return new AgentFocus({parsedTrace, insightSet, event: result.event, callTree: result.callTree, insight: null});
66
+ }
67
+
53
68
  static fromCallTree(callTree: AICallTree): AgentFocus {
54
69
  const insights = callTree.parsedTrace.insights;
55
70
 
@@ -65,7 +80,7 @@ export class AgentFocus {
65
80
  getFirstInsightSet(insights);
66
81
  }
67
82
 
68
- return new AgentFocus({parsedTrace: callTree.parsedTrace, insightSet, callTree, insight: null});
83
+ return new AgentFocus({parsedTrace: callTree.parsedTrace, insightSet, event: null, callTree, insight: null});
69
84
  }
70
85
 
71
86
  #data: AgentFocusData;
@@ -75,8 +90,26 @@ export class AgentFocus {
75
90
  this.#data = data;
76
91
  }
77
92
 
78
- get data(): AgentFocusData {
79
- return this.#data;
93
+ get parsedTrace(): Trace.TraceModel.ParsedTrace {
94
+ return this.#data.parsedTrace;
95
+ }
96
+
97
+ get insightSet(): Trace.Insights.Types.InsightSet|null {
98
+ return this.#data.insightSet;
99
+ }
100
+
101
+ /** Note: at most one of event or callTree is non-null. */
102
+ get event(): Trace.Types.Events.Event|null {
103
+ return this.#data.event;
104
+ }
105
+
106
+ /** Note: at most one of event or callTree is non-null. */
107
+ get callTree(): AICallTree|null {
108
+ return this.#data.callTree;
109
+ }
110
+
111
+ get insight(): Trace.Insights.Types.InsightModel|null {
112
+ return this.#data.insight;
80
113
  }
81
114
 
82
115
  withInsight(insight: Trace.Insights.Types.InsightModel|null): AgentFocus {
@@ -85,9 +118,11 @@ export class AgentFocus {
85
118
  return focus;
86
119
  }
87
120
 
88
- withCallTree(callTree: AICallTree|null): AgentFocus {
121
+ withEvent(event: Trace.Types.Events.Event|null): AgentFocus {
89
122
  const focus = new AgentFocus(this.#data);
90
- focus.#data.callTree = callTree;
123
+ const result = AgentFocus.#getCallTreeOrEvent(this.#data.parsedTrace, event);
124
+ focus.#data.callTree = result.callTree;
125
+ focus.#data.event = result.event;
91
126
  return focus;
92
127
  }
93
128
 
@@ -95,13 +130,33 @@ export class AgentFocus {
95
130
  try {
96
131
  return this.eventsSerializer.eventForKey(key, this.#data.parsedTrace);
97
132
  } catch (err) {
98
- if (err.toString().includes('Unknown trace event')) {
133
+ if (err.toString().includes('Unknown trace event') || err.toString().includes('Unknown profile call')) {
99
134
  return null;
100
135
  }
101
136
 
102
137
  throw err;
103
138
  }
104
139
  }
140
+
141
+ /**
142
+ * If an event is a call tree, this returns that call tree and a null event.
143
+ * If not a call tree, this only returns a non-null event if the event is a network
144
+ * request.
145
+ * This is an arbitrary limitation – it should be removed, but first we need to
146
+ * improve the agent's knowledge of events that are not main-thread or network
147
+ * events.
148
+ */
149
+ static #getCallTreeOrEvent(parsedTrace: Trace.TraceModel.ParsedTrace, event: Trace.Types.Events.Event|null):
150
+ {callTree: AICallTree|null, event: Trace.Types.Events.Event|null} {
151
+ const callTree = event && AICallTree.fromEvent(event, parsedTrace);
152
+ if (callTree) {
153
+ return {callTree, event: null};
154
+ }
155
+ if (event && Trace.Types.Events.isSyntheticNetworkRequest(event)) {
156
+ return {callTree: null, event};
157
+ }
158
+ return {callTree: null, event: null};
159
+ }
105
160
  }
106
161
 
107
162
  export function getPerformanceAgentFocusFromModel(model: Trace.TraceModel.Model): AgentFocus|null {
@@ -332,6 +332,7 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
332
332
  sampleId,
333
333
  startTime,
334
334
  onImpression: this.#registerUserImpression.bind(this),
335
+ clearCachedRequest: this.clearCachedRequest.bind(this),
335
336
  })
336
337
  });
337
338
 
@@ -456,6 +457,10 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
456
457
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionSuggestionAccepted);
457
458
  }
458
459
 
460
+ clearCachedRequest(): void {
461
+ this.#aidaRequestCache = undefined;
462
+ }
463
+
459
464
  onTextChanged(
460
465
  prefix: string, suffix: string, cursorPositionAtRequest: number,
461
466
  inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage): void {
@@ -2,20 +2,36 @@
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 {Badge, type BadgeAction} from './Badge.js';
5
+ import * as Common from '../../core/common/common.js';
6
+
7
+ import {Badge, BadgeAction} from './Badge.js';
6
8
 
7
9
  const AI_EXPLORER_BADGE_URI = new URL('../../Images/ai-explorer-badge.svg', import.meta.url).toString();
10
+ const AI_CONVERSATION_COUNT_SETTING_NAME = 'gdp.ai-conversation-count';
11
+ const AI_CONVERSATION_COUNT_LIMIT = 5;
12
+
8
13
  export class AiExplorerBadge extends Badge {
9
14
  override readonly name =
10
15
  'profiles/me/awards/developers.google.com%2Fprofile%2Fbadges%2Factivity%2Fchrome-devtools%2Fai-explorer';
11
16
  override readonly title = 'AI Explorer';
17
+ override readonly jslogContext = 'ai-explorer';
12
18
  override readonly imageUri = AI_EXPLORER_BADGE_URI;
19
+ #aiConversationCountSetting: Common.Settings.Setting<number> = Common.Settings.Settings.instance().createSetting(
20
+ AI_CONVERSATION_COUNT_SETTING_NAME, 0, Common.Settings.SettingStorageType.SYNCED);
13
21
 
14
22
  override readonly interestedActions = [
15
- // TODO(ergunsh): Instrument related actions.
23
+ BadgeAction.STARTED_AI_CONVERSATION,
16
24
  ] as const;
17
25
 
18
26
  handleAction(_action: BadgeAction): void {
19
- this.trigger();
27
+ const currentCount = this.#aiConversationCountSetting.get();
28
+ if (currentCount >= AI_CONVERSATION_COUNT_LIMIT) {
29
+ return;
30
+ }
31
+
32
+ this.#aiConversationCountSetting.set(currentCount + 1);
33
+ if (this.#aiConversationCountSetting.get() === AI_CONVERSATION_COUNT_LIMIT) {
34
+ this.trigger();
35
+ }
20
36
  }
21
37
  }
@@ -10,9 +10,15 @@ export enum BadgeAction {
10
10
  CSS_RULE_MODIFIED = 'css-rule-modified',
11
11
  DOM_ELEMENT_OR_ATTRIBUTE_EDITED = 'dom-element-or-attribute-edited',
12
12
  MODERN_DOM_BADGE_CLICKED = 'modern-dom-badge-clicked',
13
+ STARTED_AI_CONVERSATION = 'started-ai-conversation',
13
14
  // TODO(ergunsh): Instrument performance insight clicks.
14
15
  PERFORMANCE_INSIGHT_CLICKED = 'performance-insight-clicked',
15
- DEBUGGER_PAUSED = 'debugger-paused'
16
+ DEBUGGER_PAUSED = 'debugger-paused',
17
+ BREAKPOINT_ADDED = 'breakpoint-added',
18
+ CONSOLE_PROMPT_EXECUTED = 'console-prompt-executed',
19
+ PERFORMANCE_RECORDING_STARTED = 'performance-recording-started',
20
+ NETWORK_SPEED_THROTTLED = 'network-speed-throttled',
21
+ RECORDER_RECORDING_STARTED = 'recorder-recording-started',
16
22
  }
17
23
 
18
24
  export type BadgeActionEvents = Record<BadgeAction, void>;
@@ -36,6 +42,7 @@ export abstract class Badge {
36
42
  abstract readonly title: string;
37
43
  abstract readonly imageUri: string;
38
44
  abstract readonly interestedActions: readonly BadgeAction[];
45
+ abstract readonly jslogContext: string;
39
46
  readonly isStarterBadge: boolean = false;
40
47
 
41
48
  constructor(context: BadgeContext) {
@@ -9,6 +9,7 @@ export class CodeWhispererBadge extends Badge {
9
9
  override readonly name =
10
10
  'profiles/me/awards/developers.google.com%2Fprofile%2Fbadges%2Factivity%2Fchrome-devtools%2Fcode-whisperer';
11
11
  override readonly title = 'Code Whisperer';
12
+ override readonly jslogContext = 'code-whisperer';
12
13
  override readonly imageUri = CODE_WHISPERER_BADGE_IMAGE_URI;
13
14
 
14
15
  override readonly interestedActions = [BadgeAction.DEBUGGER_PAUSED] as const;
@@ -9,6 +9,7 @@ export class DOMDetectiveBadge extends Badge {
9
9
  override readonly name =
10
10
  'profiles/me/awards/developers.google.com%2Fprofile%2Fbadges%2Factivity%2Fchrome-devtools%2Fdom-detective';
11
11
  override readonly title = 'DOM Detective';
12
+ override readonly jslogContext = 'dom-detective';
12
13
  override readonly imageUri = DOM_DETECTIVE_BADGE_IMAGE_URI;
13
14
 
14
15
  override readonly interestedActions = [
@@ -9,6 +9,7 @@ export class SpeedsterBadge extends Badge {
9
9
  override readonly name =
10
10
  'profiles/me/awards/developers.google.com%2Fprofile%2Fbadges%2Factivity%2Fchrome-devtools%2Fspeedster';
11
11
  override readonly title = 'Speedster';
12
+ override readonly jslogContext = 'speedster';
12
13
  override readonly interestedActions = [
13
14
  BadgeAction.PERFORMANCE_INSIGHT_CLICKED,
14
15
  ] as const;
@@ -10,6 +10,7 @@ export class StarterBadge extends Badge {
10
10
  override readonly name =
11
11
  'profiles/me/awards/developers.google.com%2Fprofile%2Fbadges%2Factivity%2Fchrome-devtools%2Fchrome-devtools-user';
12
12
  override readonly title = 'Chrome DevTools User';
13
+ override readonly jslogContext = 'chrome-devtools-user';
13
14
  override readonly imageUri = STARTER_BADGE_IMAGE_URI;
14
15
 
15
16
  // TODO(ergunsh): Add remaining non-trivial event definitions
@@ -18,6 +19,11 @@ export class StarterBadge extends Badge {
18
19
  BadgeAction.RECEIVE_BADGES_SETTING_ENABLED,
19
20
  BadgeAction.CSS_RULE_MODIFIED,
20
21
  BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED,
22
+ BadgeAction.BREAKPOINT_ADDED,
23
+ BadgeAction.CONSOLE_PROMPT_EXECUTED,
24
+ BadgeAction.PERFORMANCE_RECORDING_STARTED,
25
+ BadgeAction.NETWORK_SPEED_THROTTLED,
26
+ BadgeAction.RECORDER_RECORDING_STARTED,
21
27
  ] as const;
22
28
 
23
29
  handleAction(action: BadgeAction): void {
@@ -2,6 +2,7 @@
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
+ export * from './AiExplorerBadge.js';
5
6
  export * from './SpeedsterBadge.js';
6
7
  export * from './StarterBadge.js';
7
8
  export * from './Badge.js';
@@ -7277,6 +7277,10 @@ export const NativeFunctions = [
7277
7277
  name: "unregisterTool",
7278
7278
  signatures: [["tool_name"]]
7279
7279
  },
7280
+ {
7281
+ name: "provideContext",
7282
+ signatures: [["params"]]
7283
+ },
7280
7284
  {
7281
7285
  name: "SnapEvent",
7282
7286
  signatures: [["type","?eventInitDict"]]
@@ -39,7 +39,7 @@ export class EventsSerializer {
39
39
  if (EventsSerializer.isLegacyTimelineFrameKey(eventValues)) {
40
40
  const event = parsedTrace.data.Frames.frames.at(eventValues.rawIndex);
41
41
  if (!event) {
42
- throw new Error(`Could not find frame with index ${eventValues.rawIndex}`);
42
+ throw new Error(`Unknown trace event. Could not find frame with index ${eventValues.rawIndex}`);
43
43
  }
44
44
  return event;
45
45
  }
@@ -48,7 +48,8 @@ export class EventsSerializer {
48
48
  const syntheticEvents = Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getSyntheticTraces();
49
49
  const syntheticEvent = syntheticEvents.at(eventValues.rawIndex);
50
50
  if (!syntheticEvent) {
51
- throw new Error(`Attempted to get a synthetic event from an unknown raw event index: ${eventValues.rawIndex}`);
51
+ throw new Error(`Unknown trace event. Attempted to get a synthetic event from an unknown raw event index: ${
52
+ eventValues.rawIndex}`);
52
53
  }
53
54
  return syntheticEvent;
54
55
  }
@@ -57,7 +58,7 @@ export class EventsSerializer {
57
58
  const rawEvents = Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getRawTraceEvents();
58
59
  return rawEvents[eventValues.rawIndex];
59
60
  }
60
- throw new Error(`Unknown trace event serializable key values: ${(eventValues as unknown[]).join('-')}`);
61
+ throw new Error(`Unknown trace event. Serializable key values: ${(eventValues as unknown[]).join('-')}`);
61
62
  }
62
63
 
63
64
  static isProfileCallKey(key: Types.File.SerializableKeyValues): key is Types.File.ProfileCallKeyValues {