chrome-devtools-frontend 1.0.1519267 → 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 (90) 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/front_end/core/host/GdpClient.ts +12 -3
  5. package/front_end/core/sdk/NetworkManager.ts +1 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  8. package/front_end/entrypoints/main/main-meta.ts +3 -3
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +60 -34
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +100 -55
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
  15. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  16. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  17. package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
  19. package/front_end/models/badges/Badge.ts +6 -1
  20. package/front_end/models/badges/StarterBadge.ts +5 -0
  21. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  22. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  23. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  24. package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
  25. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  26. package/front_end/panels/ai_assistance/components/chatView.css +12 -0
  27. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  28. package/front_end/panels/animation/animationTimeline.css +4 -0
  29. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  30. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  31. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  32. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  33. package/front_end/panels/console/ConsoleView.ts +4 -2
  34. package/front_end/panels/coverage/CoverageListView.ts +133 -158
  35. package/front_end/panels/coverage/CoverageView.ts +39 -16
  36. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  37. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  38. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  39. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  40. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  41. package/front_end/panels/search/SearchView.ts +33 -30
  42. package/front_end/panels/search/searchView.css +0 -2
  43. package/front_end/panels/settings/components/SyncSection.ts +3 -3
  44. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  45. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  46. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  48. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  49. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  50. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/puppeteer/README.chromium +2 -2
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/package.json +3 -2
  80. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  81. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  82. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  83. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  85. package/front_end/tsconfig.json +12 -1
  86. package/front_end/ui/legacy/InspectorView.ts +86 -13
  87. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  88. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  89. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  90. 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
 
@@ -102,6 +137,26 @@ export class AgentFocus {
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,7 +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
+ clearCachedRequest: this.clearCachedRequest.bind(this),
336
336
  })
337
337
  });
338
338
 
@@ -415,10 +415,6 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
415
415
  this.#aidaRequestCache = {request, response};
416
416
  }
417
417
 
418
- #clearCachedRequest(): void {
419
- this.#aidaRequestCache = undefined;
420
- }
421
-
422
418
  #registerUserImpression(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId: number, latency: number): void {
423
419
  const seconds = Math.floor(latency / 1_000);
424
420
  const remainingMs = latency % 1_000;
@@ -461,6 +457,10 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
461
457
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionSuggestionAccepted);
462
458
  }
463
459
 
460
+ clearCachedRequest(): void {
461
+ this.#aidaRequestCache = undefined;
462
+ }
463
+
464
464
  onTextChanged(
465
465
  prefix: string, suffix: string, cursorPositionAtRequest: number,
466
466
  inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage): void {
@@ -13,7 +13,12 @@ export enum BadgeAction {
13
13
  STARTED_AI_CONVERSATION = 'started-ai-conversation',
14
14
  // TODO(ergunsh): Instrument performance insight clicks.
15
15
  PERFORMANCE_INSIGHT_CLICKED = 'performance-insight-clicked',
16
- 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',
17
22
  }
18
23
 
19
24
  export type BadgeActionEvents = Record<BadgeAction, void>;
@@ -19,6 +19,11 @@ export class StarterBadge extends Badge {
19
19
  BadgeAction.RECEIVE_BADGES_SETTING_ENABLED,
20
20
  BadgeAction.CSS_RULE_MODIFIED,
21
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,
22
27
  ] as const;
23
28
 
24
29
  handleAction(action: BadgeAction): void {
@@ -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"]]
@@ -7978,7 +7982,7 @@ export const NativeFunctions = [
7978
7982
  },
7979
7983
  {
7980
7984
  name: "constant",
7981
- signatures: [["tensor"],["desc","buffer"],["type","value"]]
7985
+ signatures: [["tensor"],["desc","buffer"]]
7982
7986
  },
7983
7987
  {
7984
7988
  name: "argMin",
@@ -266,11 +266,18 @@ async function getEmptyStateSuggestions(
266
266
  }
267
267
  }
268
268
 
269
- function getMarkdownRenderer(context: AiAssistanceModel.ConversationContext<unknown>|null):
270
- MarkdownRendererWithCodeBlock {
271
- if (context instanceof AiAssistanceModel.PerformanceTraceContext && !context.external) {
272
- const focus = context.getItem();
273
- return new PerformanceAgentMarkdownRenderer(focus.lookupEvent.bind(focus));
269
+ function getMarkdownRenderer(
270
+ context: AiAssistanceModel.ConversationContext<unknown>|null,
271
+ conversation?: AiAssistanceModel.Conversation): MarkdownRendererWithCodeBlock {
272
+ if (context instanceof AiAssistanceModel.PerformanceTraceContext) {
273
+ if (!context.external) {
274
+ const focus = context.getItem();
275
+ return new PerformanceAgentMarkdownRenderer(
276
+ focus.parsedTrace.data.Meta.mainFrameId, focus.lookupEvent.bind(focus));
277
+ }
278
+ } else if (conversation?.type === AiAssistanceModel.ConversationType.PERFORMANCE) {
279
+ // Handle historical conversations (can't linkify anything).
280
+ return new PerformanceAgentMarkdownRenderer();
274
281
  }
275
282
 
276
283
  return new MarkdownRendererWithCodeBlock();
@@ -850,7 +857,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
850
857
 
851
858
  override async performUpdate(): Promise<void> {
852
859
  const emptyStateSuggestions = await getEmptyStateSuggestions(this.#selectedContext, this.#conversation);
853
- const markdownRenderer = getMarkdownRenderer(this.#selectedContext);
860
+ const markdownRenderer = getMarkdownRenderer(this.#selectedContext, this.#conversation);
854
861
 
855
862
  this.view(
856
863
  {
@@ -1066,7 +1073,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1066
1073
  return Common.Revealer.reveal(context.getItem().uiLocation(0, 0));
1067
1074
  }
1068
1075
  if (context instanceof AiAssistanceModel.PerformanceTraceContext) {
1069
- const focus = context.getItem().data;
1076
+ const focus = context.getItem();
1070
1077
  if (focus.callTree) {
1071
1078
  const event = focus.callTree.selectedNode?.event ?? focus.callTree.rootNode.event;
1072
1079
  const revealable = new SDK.TraceObject.RevealableEvent(event);
@@ -182,7 +182,6 @@ export interface ViewInput {
182
182
  }
183
183
 
184
184
  export interface ViewOutput {
185
- tooltipRef?: Directives.Ref<HTMLElement>;
186
185
  changeRef?: Directives.Ref<HTMLElement>;
187
186
  summaryRef?: Directives.Ref<HTMLElement>;
188
187
  }
@@ -211,7 +210,6 @@ export class PatchWidget extends UI.Widget.Widget {
211
210
  #automaticFileSystem =
212
211
  Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager.instance().automaticFileSystem;
213
212
  #applyToDisconnectedAutomaticWorkspace = false;
214
- #popoverHelper: UI.PopoverHelper.PopoverHelper|null = null;
215
213
  // `rpcId` from the `applyPatch` request
216
214
  #rpcId: Host.AidaClient.RpcGlobalId|null = null;
217
215
 
@@ -228,7 +226,7 @@ export class PatchWidget extends UI.Widget.Widget {
228
226
  if (!input.changeSummary && input.patchSuggestionState === PatchSuggestionState.INITIAL) {
229
227
  return;
230
228
  }
231
- output.tooltipRef = output.tooltipRef ?? Directives.createRef<HTMLElement>();
229
+
232
230
  output.changeRef = output.changeRef ?? Directives.createRef<HTMLElement>();
233
231
  output.summaryRef = output.summaryRef ?? Directives.createRef<HTMLElement>();
234
232
 
@@ -390,8 +388,23 @@ export class PatchWidget extends UI.Widget.Widget {
390
388
  .jslogContext=${'patch-widget.info-tooltip-trigger'}
391
389
  .iconName=${'info'}
392
390
  .variant=${Buttons.Button.Variant.ICON}
393
- .title=${input.applyToWorkspaceTooltipText}
394
391
  ></devtools-button>
392
+ <devtools-tooltip
393
+ id="info-tooltip"
394
+ variant=${'rich'}
395
+ >
396
+ <div class="info-tooltip-container">
397
+ ${input.applyToWorkspaceTooltipText}
398
+ <button
399
+ class="link tooltip-link"
400
+ role="link"
401
+ jslog=${VisualLogging.link('open-ai-settings').track({
402
+ click: true,
403
+ })}
404
+ @click=${input.onLearnMoreTooltipClick}
405
+ >${lockedString(UIStringsNotTranslate.learnMore)}</button>
406
+ </div>
407
+ </devtools-tooltip>
395
408
  </div>
396
409
  </div>`;
397
410
  }
@@ -417,62 +430,11 @@ export class PatchWidget extends UI.Widget.Widget {
417
430
 
418
431
  render(template, target, {host: target});
419
432
  });
420
- // We're using PopoverHelper as a workaround instead of using <devtools-tooltip>. See the bug for more details.
421
- // TODO: Update here when b/409965560 is fixed.
422
- this.#popoverHelper = new UI.PopoverHelper.PopoverHelper(this.contentElement, event => {
423
- // There are two ways this event is received for showing a popover case:
424
- // * The latest element on the composed path is `<devtools-button>`
425
- // * The 2nd element on the composed path is `<devtools-button>` (the last element is the `<button>` inside it.)
426
- const hoveredNode = event.composedPath()[0];
427
- const maybeDevToolsButton = event.composedPath()[2];
428
-
429
- const popoverShownNode = hoveredNode instanceof HTMLElement && hoveredNode.getAttribute('aria-details') === 'info-tooltip' ? hoveredNode
430
- : maybeDevToolsButton instanceof HTMLElement && maybeDevToolsButton.getAttribute('aria-details') === 'info-tooltip' ? maybeDevToolsButton
431
- : null;
432
- if (!popoverShownNode) {
433
- return null;
434
- }
435
- return {
436
- box: popoverShownNode.boxInWindow(),
437
- show: async (popover: UI.GlassPane.GlassPane) => {
438
- // clang-format off
439
- render(html`
440
- <style>
441
- .info-tooltip-container {
442
- max-width: var(--sys-size-28);
443
- padding: var(--sys-size-4) var(--sys-size-5);
444
-
445
- .tooltip-link {
446
- display: block;
447
- margin-top: var(--sys-size-4);
448
- color: var(--sys-color-primary);
449
- padding-left: 0;
450
- }
451
- }
452
- </style>
453
- <div class="info-tooltip-container">
454
- ${UIStringsNotTranslate.applyToWorkspaceTooltip}
455
- <button
456
- class="link tooltip-link"
457
- role="link"
458
- jslog=${VisualLogging.link('open-ai-settings').track({
459
- click: true,
460
- })}
461
- @click=${this.#onLearnMoreTooltipClick}
462
- >${lockedString(UIStringsNotTranslate.learnMore)}</button>
463
- </div>`, popover.contentElement, {host: this});
464
- // clang-forat on
465
- return true;
466
- },
467
- };
468
- }, 'patch-widget.info-tooltip');
469
- this.#popoverHelper.setTimeout(0);
470
433
  // clang-format on
471
434
  this.requestUpdate();
472
435
  }
473
436
 
474
437
  #onLearnMoreTooltipClick(): void {
475
- this.#viewOutput.tooltipRef?.value?.hidePopover();
476
438
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
477
439
  }
478
440