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.
- package/config/owner/COMMON_OWNERS +1 -2
- package/config/typescript/tsconfig.eslint.json +12 -1
- package/docs/ui_engineering.md +1011 -0
- package/front_end/core/host/GdpClient.ts +12 -3
- package/front_end/core/sdk/NetworkManager.ts +1 -0
- package/front_end/core/sdk/NetworkRequest.ts +10 -0
- package/front_end/entrypoints/main/MainImpl.ts +6 -1
- package/front_end/entrypoints/main/main-meta.ts +3 -3
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +60 -34
- package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +100 -55
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
- package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
- package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
- package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
- package/front_end/models/badges/Badge.ts +6 -1
- package/front_end/models/badges/StarterBadge.ts +5 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
- package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
- package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
- package/front_end/panels/ai_assistance/components/chatView.css +12 -0
- package/front_end/panels/animation/AnimationTimeline.ts +1 -1
- package/front_end/panels/animation/animationTimeline.css +4 -0
- package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
- package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
- package/front_end/panels/console/ConsolePrompt.ts +6 -0
- package/front_end/panels/console/ConsoleView.ts +4 -2
- package/front_end/panels/coverage/CoverageListView.ts +133 -158
- package/front_end/panels/coverage/CoverageView.ts +39 -16
- package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
- package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
- package/front_end/panels/search/SearchResultsPane.ts +48 -15
- package/front_end/panels/search/SearchView.ts +33 -30
- package/front_end/panels/search/searchView.css +0 -2
- package/front_end/panels/settings/components/SyncSection.ts +3 -3
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
- package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
- package/front_end/panels/timeline/TimelinePanel.ts +2 -0
- package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/package.json +3 -2
- package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
- package/front_end/tsconfig.json +12 -1
- package/front_end/ui/legacy/InspectorView.ts +86 -13
- package/front_end/ui/legacy/TabbedPane.ts +2 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
- package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
- 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
|
-
|
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.
|
29
|
-
this.#insightSet = focus.
|
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
|
-
|
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 =
|
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
|
-
|
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.
|
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
|
-
//
|
328
|
+
// 4. Duration
|
322
329
|
const durationStr = roundToTenths(node.totalTime);
|
323
330
|
|
324
|
-
//
|
331
|
+
// 5. Self Time
|
325
332
|
const selfTimeStr = roundToTenths(node.selfTime);
|
326
333
|
|
327
|
-
//
|
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
|
-
//
|
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
|
-
//
|
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
|
7
|
+
import {AICallTree} from './AICallTree.js';
|
8
8
|
|
9
|
-
|
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
|
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
|
-
|
121
|
+
withEvent(event: Trace.Types.Events.Event|null): AgentFocus {
|
89
122
|
const focus = new AgentFocus(this.#data);
|
90
|
-
|
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
|
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"]
|
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(
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
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()
|
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
|
-
|
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
|
|