chrome-devtools-frontend 1.0.1516909 → 1.0.1519267
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 +2 -2
- package/docs/checklist/README.md +2 -2
- package/docs/checklist/javascript.md +1 -1
- package/docs/contributing/README.md +1 -1
- package/docs/contributing/settings-experiments-features.md +9 -8
- package/docs/cookbook/devtools_on_devtools.md +2 -2
- package/docs/cookbook/localization.md +10 -10
- package/docs/devtools-protocol.md +9 -8
- package/docs/ecosystem/automatic_workspace_folders.md +3 -3
- package/docs/get_the_code.md +0 -2
- package/docs/styleguide/ux/components.md +166 -85
- package/docs/styleguide/ux/numbers.md +3 -4
- package/eslint.config.mjs +1 -0
- package/front_end/core/common/README.md +13 -12
- package/front_end/core/host/GdpClient.ts +16 -1
- package/front_end/core/host/UserMetrics.ts +4 -2
- package/front_end/core/root/Runtime.ts +13 -0
- package/front_end/core/sdk/CSSMatchedStyles.ts +5 -1
- package/front_end/core/sdk/EnhancedTracesParser.ts +5 -5
- package/front_end/core/sdk/RehydratingConnection.snapshot.txt +211 -0
- package/front_end/core/sdk/TargetManager.ts +4 -0
- package/front_end/entrypoints/main/MainImpl.ts +6 -3
- package/front_end/generated/InspectorBackendCommands.js +10 -7
- package/front_end/generated/SupportedCSSProperties.js +40 -11
- package/front_end/generated/protocol-mapping.d.ts +16 -1
- package/front_end/generated/protocol-proxy-api.d.ts +13 -1
- package/front_end/generated/protocol.ts +95 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +57 -10
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -51
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -31
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +14 -181
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +19 -315
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +224 -50
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +310 -11
- package/front_end/models/ai_assistance/performance/AIContext.ts +15 -2
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +22 -11
- package/front_end/models/badges/AiExplorerBadge.ts +19 -3
- package/front_end/models/badges/Badge.ts +10 -3
- package/front_end/models/badges/CodeWhispererBadge.ts +3 -4
- package/front_end/models/badges/DOMDetectiveBadge.ts +1 -0
- package/front_end/models/badges/SpeedsterBadge.ts +1 -0
- package/front_end/models/badges/StarterBadge.ts +3 -2
- package/front_end/models/badges/UserBadges.ts +21 -3
- package/front_end/models/badges/badges.ts +1 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
- package/front_end/models/trace/EventsSerializer.ts +4 -3
- package/front_end/models/trace/README.md +28 -1
- package/front_end/models/trace/handlers/UserInteractionsHandler.ts +101 -73
- package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
- package/front_end/models/trace/helpers/Timing.ts +1 -1
- package/front_end/models/trace/helpers/Trace.ts +99 -43
- package/front_end/models/trace/types/TraceEvents.ts +9 -0
- package/front_end/panels/accessibility/ARIAAttributesView.ts +113 -191
- package/front_end/panels/accessibility/AccessibilityNodeView.ts +9 -9
- package/front_end/panels/accessibility/AccessibilitySubPane.ts +6 -4
- package/front_end/panels/accessibility/accessibilityProperties.css +2 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +20 -3
- package/front_end/panels/ai_assistance/components/ChatView.ts +9 -10
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +44 -0
- package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +32 -9
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -1
- package/front_end/panels/common/BadgeNotification.ts +21 -5
- package/front_end/panels/common/GdpSignUpDialog.ts +20 -12
- package/front_end/panels/console/ConsolePrompt.ts +1 -1
- package/front_end/panels/console/ConsoleView.ts +6 -2
- package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
- package/front_end/panels/elements/ElementsPanel.ts +4 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +18 -0
- package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +21 -6
- package/front_end/panels/media/TickingFlameChart.ts +1 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +34 -19
- package/front_end/panels/recorder/components/RecordingView.ts +2 -2
- package/front_end/panels/search/SearchResultsPane.ts +167 -152
- package/front_end/panels/search/SearchView.ts +36 -26
- package/front_end/panels/search/searchResultsPane.css +9 -0
- package/front_end/panels/security/CookieControlsView.ts +2 -1
- package/front_end/panels/settings/AISettingsTab.ts +6 -3
- package/front_end/panels/settings/components/SyncSection.ts +39 -17
- package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -1
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +9 -1
- package/front_end/panels/sources/SourcesPanel.ts +4 -1
- package/front_end/panels/sources/sourcesView.css +6 -1
- package/front_end/panels/timeline/AppenderUtils.ts +2 -2
- package/front_end/panels/timeline/ExtensionTrackAppender.ts +13 -4
- package/front_end/panels/timeline/GPUTrackAppender.ts +2 -1
- package/front_end/panels/timeline/InteractionsTrackAppender.ts +5 -1
- package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +2 -1
- package/front_end/panels/timeline/ThreadAppender.ts +12 -3
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +9 -4
- package/front_end/panels/timeline/TimelinePanel.ts +3 -2
- package/front_end/panels/timeline/TimelineUIUtils.ts +5 -4
- package/front_end/panels/timeline/TimingsTrackAppender.ts +6 -1
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +95 -82
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +1 -1
- package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +1 -1
- package/front_end/panels/timeline/components/RelatedInsightChips.ts +1 -1
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
- package/front_end/panels/timeline/components/cpuThrottlingSelector.css +17 -15
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +3 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
- package/front_end/third_party/codemirror.next/codemirror.next.d.ts +6 -9
- package/front_end/third_party/codemirror.next/package.json +2 -1
- package/front_end/third_party/diff/README.chromium +1 -0
- 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/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
- 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/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +16 -25
- 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 +19 -28
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
- 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/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +16 -25
- 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 +10 -3
- package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/Function.ts +22 -30
- package/front_end/ui/components/dialogs/Dialog.ts +1 -1
- package/front_end/ui/components/markdown_view/MarkdownImage.ts +4 -5
- package/front_end/ui/components/switch/SwitchImpl.ts +12 -1
- package/front_end/ui/components/text_editor/config.ts +22 -9
- package/front_end/ui/components/tooltips/Tooltip.ts +70 -31
- package/front_end/ui/legacy/README.md +33 -24
- package/front_end/ui/legacy/SearchableView.ts +19 -26
- package/front_end/ui/legacy/TextPrompt.ts +166 -1
- package/front_end/ui/legacy/Treeoutline.ts +19 -3
- package/front_end/ui/legacy/UIUtils.ts +15 -2
- package/front_end/ui/legacy/XElement.ts +0 -43
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +20 -4
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -11
- package/front_end/ui/lit/i18n-template.ts +5 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +23 -6
- package/front_end/ui/visual_logging/README.md +43 -27
- package/package.json +1 -1
@@ -8,22 +8,30 @@ import type {AICallTree} from '../performance/AICallTree.js';
|
|
8
8
|
import type {AgentFocus} from '../performance/AIContext.js';
|
9
9
|
import {AIQueries} from '../performance/AIQueries.js';
|
10
10
|
|
11
|
-
import {
|
11
|
+
import {NetworkRequestFormatter} from './NetworkRequestFormatter.js';
|
12
|
+
import {PerformanceInsightFormatter} from './PerformanceInsightFormatter.js';
|
12
13
|
import {bytes, micros, millis} from './UnitFormatters.js';
|
13
14
|
|
15
|
+
export interface NetworkRequestFormatOptions {
|
16
|
+
verbose?: boolean;
|
17
|
+
customTitle?: string;
|
18
|
+
}
|
19
|
+
|
14
20
|
export class PerformanceTraceFormatter {
|
21
|
+
#focus: AgentFocus;
|
15
22
|
#parsedTrace: Trace.TraceModel.ParsedTrace;
|
16
23
|
#insightSet: Trace.Insights.Types.InsightSet|null;
|
17
|
-
|
24
|
+
protected eventsSerializer: Trace.EventsSerializer.EventsSerializer;
|
18
25
|
|
19
|
-
constructor(focus: AgentFocus
|
26
|
+
constructor(focus: AgentFocus) {
|
27
|
+
this.#focus = focus;
|
20
28
|
this.#parsedTrace = focus.data.parsedTrace;
|
21
29
|
this.#insightSet = focus.data.insightSet;
|
22
|
-
this
|
30
|
+
this.eventsSerializer = focus.eventsSerializer;
|
23
31
|
}
|
24
32
|
|
25
33
|
serializeEvent(event: Trace.Types.Events.Event): string {
|
26
|
-
const key = this
|
34
|
+
const key = this.eventsSerializer.keyForEvent(event);
|
27
35
|
return `(eventKey: ${key}, ts: ${event.ts})`;
|
28
36
|
}
|
29
37
|
|
@@ -167,7 +175,7 @@ export class PerformanceTraceFormatter {
|
|
167
175
|
continue;
|
168
176
|
}
|
169
177
|
|
170
|
-
const formatter = new PerformanceInsightFormatter(
|
178
|
+
const formatter = new PerformanceInsightFormatter(this.#focus, model);
|
171
179
|
if (!formatter.insightIsSupported()) {
|
172
180
|
continue;
|
173
181
|
}
|
@@ -199,7 +207,6 @@ export class PerformanceTraceFormatter {
|
|
199
207
|
}
|
200
208
|
|
201
209
|
formatCriticalRequests(): string {
|
202
|
-
const parsedTrace = this.#parsedTrace;
|
203
210
|
const insightSet = this.#insightSet;
|
204
211
|
const criticalRequests: Trace.Types.Events.SyntheticNetworkRequest[] = [];
|
205
212
|
|
@@ -213,8 +220,7 @@ export class PerformanceTraceFormatter {
|
|
213
220
|
return '';
|
214
221
|
}
|
215
222
|
|
216
|
-
return 'Critical network requests:\n' +
|
217
|
-
TraceEventFormatter.networkRequests(criticalRequests, parsedTrace, {verbose: false});
|
223
|
+
return 'Critical network requests:\n' + this.formatNetworkRequests(criticalRequests, {verbose: false});
|
218
224
|
}
|
219
225
|
|
220
226
|
#serializeBottomUpRootNode(rootNode: Trace.Extras.TraceTree.BottomUpRootNode, limit: number): string {
|
@@ -421,7 +427,7 @@ export class PerformanceTraceFormatter {
|
|
421
427
|
|
422
428
|
const requests = this.#parsedTrace.data.NetworkRequests.byTime.filter(
|
423
429
|
request => Trace.Helpers.Timing.eventIsInBounds(request, bounds));
|
424
|
-
const requestsText =
|
430
|
+
const requestsText = this.formatNetworkRequests(requests, {verbose: false});
|
425
431
|
results.push('# Network requests summary');
|
426
432
|
results.push(requestsText || 'No requests in the given bounds');
|
427
433
|
|
@@ -443,11 +449,304 @@ export class PerformanceTraceFormatter {
|
|
443
449
|
results.push('#'.repeat(headerLevel) + ' Node id to eventKey\n');
|
444
450
|
results.push('These node ids correspond to the call tree nodes listed in the above section.\n');
|
445
451
|
tree.breadthFirstWalk(tree.rootNode.children().values(), (node, nodeId) => {
|
446
|
-
results.push(`${nodeId}: ${this
|
452
|
+
results.push(`${nodeId}: ${this.eventsSerializer.keyForEvent(node.event)}`);
|
447
453
|
});
|
448
454
|
|
449
455
|
results.push('\nIMPORTANT: Never show eventKey to the user.');
|
450
456
|
|
451
457
|
return results.join('\n');
|
452
458
|
}
|
459
|
+
|
460
|
+
formatNetworkRequests(
|
461
|
+
requests: readonly Trace.Types.Events.SyntheticNetworkRequest[], options?: NetworkRequestFormatOptions): string {
|
462
|
+
if (requests.length === 0) {
|
463
|
+
return '';
|
464
|
+
}
|
465
|
+
|
466
|
+
let verbose;
|
467
|
+
if (options?.verbose !== undefined) {
|
468
|
+
verbose = options.verbose;
|
469
|
+
} else {
|
470
|
+
verbose = requests.length === 1;
|
471
|
+
}
|
472
|
+
|
473
|
+
// Use verbose format for a single network request. With the compressed format, a format description
|
474
|
+
// needs to be provided, which is not worth sending if only one network request is being stringified.
|
475
|
+
if (verbose) {
|
476
|
+
return requests.map(request => this.#networkRequestVerbosely(request, options)).join('\n');
|
477
|
+
}
|
478
|
+
|
479
|
+
return this.#networkRequestsArrayCompressed(requests);
|
480
|
+
}
|
481
|
+
|
482
|
+
#getOrAssignUrlIndex(urlIdToIndex: Map<string, number>, url: string): number {
|
483
|
+
let index = urlIdToIndex.get(url);
|
484
|
+
if (index !== undefined) {
|
485
|
+
return index;
|
486
|
+
}
|
487
|
+
index = urlIdToIndex.size;
|
488
|
+
urlIdToIndex.set(url, index);
|
489
|
+
return index;
|
490
|
+
}
|
491
|
+
|
492
|
+
#getInitiatorChain(parsedTrace: Trace.TraceModel.ParsedTrace, request: Trace.Types.Events.SyntheticNetworkRequest):
|
493
|
+
Trace.Types.Events.SyntheticNetworkRequest[] {
|
494
|
+
const initiators: Trace.Types.Events.SyntheticNetworkRequest[] = [];
|
495
|
+
|
496
|
+
let cur: Trace.Types.Events.SyntheticNetworkRequest|undefined = request;
|
497
|
+
while (cur) {
|
498
|
+
const initiator = parsedTrace.data.NetworkRequests.eventToInitiator.get(cur);
|
499
|
+
if (initiator) {
|
500
|
+
// Should never happen, but if it did that would be an infinite loop.
|
501
|
+
if (initiators.includes(initiator)) {
|
502
|
+
return [];
|
503
|
+
}
|
504
|
+
|
505
|
+
initiators.unshift(initiator);
|
506
|
+
}
|
507
|
+
|
508
|
+
cur = initiator;
|
509
|
+
}
|
510
|
+
|
511
|
+
return initiators;
|
512
|
+
}
|
513
|
+
|
514
|
+
/**
|
515
|
+
* This is the data passed to a network request when the Performance Insights
|
516
|
+
* agent is asking for information. It is a slimmed down version of the
|
517
|
+
* request's data to avoid using up too much of the context window.
|
518
|
+
* IMPORTANT: these set of fields have been reviewed by Chrome Privacy &
|
519
|
+
* Security; be careful about adding new data here. If you are in doubt please
|
520
|
+
* talk to jacktfranklin@.
|
521
|
+
*/
|
522
|
+
#networkRequestVerbosely(request: Trace.Types.Events.SyntheticNetworkRequest, options?: NetworkRequestFormatOptions):
|
523
|
+
string {
|
524
|
+
const {
|
525
|
+
url,
|
526
|
+
statusCode,
|
527
|
+
initialPriority,
|
528
|
+
priority,
|
529
|
+
fromServiceWorker,
|
530
|
+
mimeType,
|
531
|
+
responseHeaders,
|
532
|
+
syntheticData,
|
533
|
+
protocol
|
534
|
+
} = request.args.data;
|
535
|
+
const parsedTrace = this.#parsedTrace;
|
536
|
+
|
537
|
+
const titlePrefix = `## ${options?.customTitle ?? 'Network request'}`;
|
538
|
+
|
539
|
+
// Note: unlike other agents, we do have the ability to include
|
540
|
+
// cross-origins, hence why we do not sanitize the URLs here.
|
541
|
+
const navigationForEvent = Trace.Helpers.Trace.getNavigationForTraceEvent(
|
542
|
+
request,
|
543
|
+
request.args.data.frame,
|
544
|
+
parsedTrace.data.Meta.navigationsByFrameId,
|
545
|
+
);
|
546
|
+
const baseTime = navigationForEvent?.ts ?? parsedTrace.data.Meta.traceBounds.min;
|
547
|
+
|
548
|
+
// Gets all the timings for this request, relative to the base time.
|
549
|
+
// Note that this is the start time, not total time. E.g. "queuedAt: X"
|
550
|
+
// means that the request was queued at Xms, not that it queued for Xms.
|
551
|
+
const startTimesForLifecycle = {
|
552
|
+
queuedAt: request.ts - baseTime,
|
553
|
+
requestSentAt: syntheticData.sendStartTime - baseTime,
|
554
|
+
downloadCompletedAt: syntheticData.finishTime - baseTime,
|
555
|
+
processingCompletedAt: request.ts + request.dur - baseTime,
|
556
|
+
} as const;
|
557
|
+
|
558
|
+
const mainThreadProcessingDuration =
|
559
|
+
startTimesForLifecycle.processingCompletedAt - startTimesForLifecycle.downloadCompletedAt;
|
560
|
+
const downloadTime = syntheticData.finishTime - syntheticData.downloadStart;
|
561
|
+
|
562
|
+
const renderBlocking = Trace.Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(request);
|
563
|
+
const initiator = parsedTrace.data.NetworkRequests.eventToInitiator.get(request);
|
564
|
+
|
565
|
+
const priorityLines = [];
|
566
|
+
if (initialPriority === priority) {
|
567
|
+
priorityLines.push(`Priority: ${priority}`);
|
568
|
+
} else {
|
569
|
+
priorityLines.push(`Initial priority: ${initialPriority}`);
|
570
|
+
priorityLines.push(`Final priority: ${priority}`);
|
571
|
+
}
|
572
|
+
|
573
|
+
const redirects = request.args.data.redirects.map((redirect, index) => {
|
574
|
+
const startTime = redirect.ts - baseTime;
|
575
|
+
return `#### Redirect ${index + 1}: ${redirect.url}
|
576
|
+
- Start time: ${micros(startTime)}
|
577
|
+
- Duration: ${micros(redirect.dur)}`;
|
578
|
+
});
|
579
|
+
|
580
|
+
const initiators = this.#getInitiatorChain(parsedTrace, request);
|
581
|
+
const initiatorUrls = initiators.map(initiator => initiator.args.data.url);
|
582
|
+
|
583
|
+
const eventKey = this.eventsSerializer.keyForEvent(request);
|
584
|
+
const eventKeyLine = eventKey ? `eventKey: ${eventKey}\n` : '';
|
585
|
+
|
586
|
+
return `${titlePrefix}: ${url}
|
587
|
+
${eventKeyLine}Timings:
|
588
|
+
- Queued at: ${micros(startTimesForLifecycle.queuedAt)}
|
589
|
+
- Request sent at: ${micros(startTimesForLifecycle.requestSentAt)}
|
590
|
+
- Download complete at: ${micros(startTimesForLifecycle.downloadCompletedAt)}
|
591
|
+
- Main thread processing completed at: ${micros(startTimesForLifecycle.processingCompletedAt)}
|
592
|
+
Durations:
|
593
|
+
- Download time: ${micros(downloadTime)}
|
594
|
+
- Main thread processing time: ${micros(mainThreadProcessingDuration)}
|
595
|
+
- Total duration: ${micros(request.dur)}${initiator ? `\nInitiator: ${initiator.args.data.url}` : ''}
|
596
|
+
Redirects:${redirects.length ? '\n' + redirects.join('\n') : ' no redirects'}
|
597
|
+
Status code: ${statusCode}
|
598
|
+
MIME Type: ${mimeType}
|
599
|
+
Protocol: ${protocol}
|
600
|
+
${priorityLines.join('\n')}
|
601
|
+
Render blocking: ${renderBlocking ? 'Yes' : 'No'}
|
602
|
+
From a service worker: ${fromServiceWorker ? 'Yes' : 'No'}
|
603
|
+
Initiators (root request to the request that directly loaded this one): ${initiatorUrls.join(', ') || 'none'}
|
604
|
+
${NetworkRequestFormatter.formatHeaders('Response headers', responseHeaders ?? [], true)}`;
|
605
|
+
}
|
606
|
+
|
607
|
+
// A compact network requests format designed to save tokens when sending multiple network requests to the model.
|
608
|
+
// It creates a map that maps request URLs to IDs and references the IDs in the compressed format.
|
609
|
+
//
|
610
|
+
// Important: Do not use this method for stringifying a single network request. With this format, a format description
|
611
|
+
// needs to be provided, which is not worth sending if only one network request is being stringified.
|
612
|
+
// For a single request, use `formatRequestVerbosely`, which formats with all fields specified and does not require a
|
613
|
+
// format description.
|
614
|
+
#networkRequestsArrayCompressed(requests: readonly Trace.Types.Events.SyntheticNetworkRequest[]): string {
|
615
|
+
const networkDataString = `
|
616
|
+
Network requests data:
|
617
|
+
|
618
|
+
`;
|
619
|
+
const urlIdToIndex = new Map<string, number>();
|
620
|
+
const allRequestsText = requests
|
621
|
+
.map(request => {
|
622
|
+
const urlIndex = this.#getOrAssignUrlIndex(urlIdToIndex, request.args.data.url);
|
623
|
+
return this.#networkRequestCompressedFormat(urlIndex, request, urlIdToIndex);
|
624
|
+
})
|
625
|
+
.join('\n');
|
626
|
+
|
627
|
+
const urlsMapString = 'allUrls = ' +
|
628
|
+
`[${
|
629
|
+
Array.from(urlIdToIndex.entries())
|
630
|
+
.map(([url, index]) => {
|
631
|
+
return `${index}: ${url}`;
|
632
|
+
})
|
633
|
+
.join(', ')}]`;
|
634
|
+
|
635
|
+
return networkDataString + '\n\n' + urlsMapString + '\n\n' + allRequestsText;
|
636
|
+
}
|
637
|
+
|
638
|
+
/**
|
639
|
+
* Network requests format description that is sent to the model as a fact.
|
640
|
+
*/
|
641
|
+
static networkDataFormatDescription = `Network requests are formatted like this:
|
642
|
+
\`urlIndex;eventKey;queuedTime;requestSentTime;downloadCompleteTime;processingCompleteTime;totalDuration;downloadDuration;mainThreadProcessingDuration;statusCode;mimeType;priority;initialPriority;finalPriority;renderBlocking;protocol;fromServiceWorker;initiators;redirects:[[redirectUrlIndex|startTime|duration]];responseHeaders:[header1Value|header2Value|...]\`
|
643
|
+
|
644
|
+
- \`urlIndex\`: Numerical index for the request's URL, referencing the "All URLs" list.
|
645
|
+
- \`eventKey\`: String that uniquely identifies this request's trace event.
|
646
|
+
Timings (all in milliseconds, relative to navigation start):
|
647
|
+
- \`queuedTime\`: When the request was queued.
|
648
|
+
- \`requestSentTime\`: When the request was sent.
|
649
|
+
- \`downloadCompleteTime\`: When the download completed.
|
650
|
+
- \`processingCompleteTime\`: When main thread processing finished.
|
651
|
+
Durations (all in milliseconds):
|
652
|
+
- \`totalDuration\`: Total time from the request being queued until its main thread processing completed.
|
653
|
+
- \`downloadDuration\`: Time spent actively downloading the resource.
|
654
|
+
- \`mainThreadProcessingDuration\`: Time spent on the main thread after the download completed.
|
655
|
+
- \`statusCode\`: The HTTP status code of the response (e.g., 200, 404).
|
656
|
+
- \`mimeType\`: The MIME type of the resource (e.g., "text/html", "application/javascript").
|
657
|
+
- \`priority\`: The final network request priority (e.g., "VeryHigh", "Low").
|
658
|
+
- \`initialPriority\`: The initial network request priority.
|
659
|
+
- \`finalPriority\`: The final network request priority (redundant if \`priority\` is always final, but kept for clarity if \`initialPriority\` and \`priority\` differ).
|
660
|
+
- \`renderBlocking\`: 't' if the request was render-blocking, 'f' otherwise.
|
661
|
+
- \`protocol\`: The network protocol used (e.g., "h2", "http/1.1").
|
662
|
+
- \`fromServiceWorker\`: 't' if the request was served from a service worker, 'f' otherwise.
|
663
|
+
- \`initiators\`: A list (separated by ,) of URL indices for the initiator chain of this request. Listed in order starting from the root request to the request that directly loaded this one. This represents the network dependencies necessary to load this request. If there is no initiator, this is empty.
|
664
|
+
- \`redirects\`: A comma-separated list of redirects, enclosed in square brackets. Each redirect is formatted as
|
665
|
+
\`[redirectUrlIndex|startTime|duration]\`, where: \`redirectUrlIndex\`: Numerical index for the redirect's URL. \`startTime\`: The start time of the redirect in milliseconds, relative to navigation start. \`duration\`: The duration of the redirect in milliseconds.
|
666
|
+
- \`responseHeaders\`: A list (separated by '|') of values for specific, pre-defined response headers, enclosed in square brackets.
|
667
|
+
The order of headers corresponds to an internal fixed list. If a header is not present, its value will be empty.
|
668
|
+
`;
|
669
|
+
|
670
|
+
/**
|
671
|
+
* This is the network request data passed to the Performance agent.
|
672
|
+
*
|
673
|
+
* The `urlIdToIndex` Map is used to map URLs to numerical indices in order to not need to pass whole url every time it's mentioned.
|
674
|
+
* The map content is passed in the response together will all the requests data.
|
675
|
+
*
|
676
|
+
* See `networkDataFormatDescription` above for specifics.
|
677
|
+
*/
|
678
|
+
#networkRequestCompressedFormat(
|
679
|
+
urlIndex: number, request: Trace.Types.Events.SyntheticNetworkRequest,
|
680
|
+
urlIdToIndex: Map<string, number>): string {
|
681
|
+
const {
|
682
|
+
statusCode,
|
683
|
+
initialPriority,
|
684
|
+
priority,
|
685
|
+
fromServiceWorker,
|
686
|
+
mimeType,
|
687
|
+
responseHeaders,
|
688
|
+
syntheticData,
|
689
|
+
protocol,
|
690
|
+
} = request.args.data;
|
691
|
+
const parsedTrace = this.#parsedTrace;
|
692
|
+
|
693
|
+
const navigationForEvent = Trace.Helpers.Trace.getNavigationForTraceEvent(
|
694
|
+
request,
|
695
|
+
request.args.data.frame,
|
696
|
+
parsedTrace.data.Meta.navigationsByFrameId,
|
697
|
+
);
|
698
|
+
const baseTime = navigationForEvent?.ts ?? parsedTrace.data.Meta.traceBounds.min;
|
699
|
+
const queuedTime = micros(request.ts - baseTime);
|
700
|
+
const requestSentTime = micros(syntheticData.sendStartTime - baseTime);
|
701
|
+
const downloadCompleteTime = micros(syntheticData.finishTime - baseTime);
|
702
|
+
const processingCompleteTime = micros(request.ts + request.dur - baseTime);
|
703
|
+
const totalDuration = micros(request.dur);
|
704
|
+
const downloadDuration = micros(syntheticData.finishTime - syntheticData.downloadStart);
|
705
|
+
const mainThreadProcessingDuration = micros(request.ts + request.dur - syntheticData.finishTime);
|
706
|
+
const renderBlocking = Trace.Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(request) ? 't' : 'f';
|
707
|
+
const finalPriority = priority;
|
708
|
+
const headerValues = responseHeaders
|
709
|
+
?.map(header => {
|
710
|
+
const value =
|
711
|
+
NetworkRequestFormatter.allowHeader(header.name) ? header.value : '<redacted>';
|
712
|
+
return `${header.name}: ${value}`;
|
713
|
+
})
|
714
|
+
.join('|');
|
715
|
+
const redirects = request.args.data.redirects
|
716
|
+
.map(redirect => {
|
717
|
+
const urlIndex = this.#getOrAssignUrlIndex(urlIdToIndex, redirect.url);
|
718
|
+
const redirectStartTime = micros(redirect.ts - baseTime);
|
719
|
+
const redirectDuration = micros(redirect.dur);
|
720
|
+
return `[${urlIndex}|${redirectStartTime}|${redirectDuration}]`;
|
721
|
+
})
|
722
|
+
.join(',');
|
723
|
+
|
724
|
+
const initiators = this.#getInitiatorChain(parsedTrace, request);
|
725
|
+
const initiatorUrlIndices =
|
726
|
+
initiators.map(initiator => this.#getOrAssignUrlIndex(urlIdToIndex, initiator.args.data.url));
|
727
|
+
|
728
|
+
const parts = [
|
729
|
+
urlIndex,
|
730
|
+
this.eventsSerializer.keyForEvent(request) ?? '',
|
731
|
+
queuedTime,
|
732
|
+
requestSentTime,
|
733
|
+
downloadCompleteTime,
|
734
|
+
processingCompleteTime,
|
735
|
+
totalDuration,
|
736
|
+
downloadDuration,
|
737
|
+
mainThreadProcessingDuration,
|
738
|
+
statusCode,
|
739
|
+
mimeType,
|
740
|
+
priority,
|
741
|
+
initialPriority,
|
742
|
+
finalPriority,
|
743
|
+
renderBlocking,
|
744
|
+
protocol,
|
745
|
+
fromServiceWorker ? 't' : 'f',
|
746
|
+
initiatorUrlIndices.join(','),
|
747
|
+
`[${redirects}]`,
|
748
|
+
`[${headerValues ?? ''}]`,
|
749
|
+
];
|
750
|
+
return parts.join(';');
|
751
|
+
}
|
453
752
|
}
|
@@ -21,7 +21,7 @@ function getFirstInsightSet(insights: Trace.Insights.Types.TraceInsightSets): Tr
|
|
21
21
|
}
|
22
22
|
|
23
23
|
export class AgentFocus {
|
24
|
-
static
|
24
|
+
static fromParsedTrace(parsedTrace: Trace.TraceModel.ParsedTrace): AgentFocus {
|
25
25
|
if (!parsedTrace.insights) {
|
26
26
|
throw new Error('missing insights');
|
27
27
|
}
|
@@ -69,6 +69,7 @@ export class AgentFocus {
|
|
69
69
|
}
|
70
70
|
|
71
71
|
#data: AgentFocusData;
|
72
|
+
readonly eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
|
72
73
|
|
73
74
|
constructor(data: AgentFocusData) {
|
74
75
|
this.#data = data;
|
@@ -89,6 +90,18 @@ export class AgentFocus {
|
|
89
90
|
focus.#data.callTree = callTree;
|
90
91
|
return focus;
|
91
92
|
}
|
93
|
+
|
94
|
+
lookupEvent(key: Trace.Types.File.SerializableKey): Trace.Types.Events.Event|null {
|
95
|
+
try {
|
96
|
+
return this.eventsSerializer.eventForKey(key, this.#data.parsedTrace);
|
97
|
+
} catch (err) {
|
98
|
+
if (err.toString().includes('Unknown trace event') || err.toString().includes('Unknown profile call')) {
|
99
|
+
return null;
|
100
|
+
}
|
101
|
+
|
102
|
+
throw err;
|
103
|
+
}
|
104
|
+
}
|
92
105
|
}
|
93
106
|
|
94
107
|
export function getPerformanceAgentFocusFromModel(model: Trace.TraceModel.Model): AgentFocus|null {
|
@@ -97,5 +110,5 @@ export function getPerformanceAgentFocusFromModel(model: Trace.TraceModel.Model)
|
|
97
110
|
return null;
|
98
111
|
}
|
99
112
|
|
100
|
-
return AgentFocus.
|
113
|
+
return AgentFocus.fromParsedTrace(parsedTrace);
|
101
114
|
}
|
@@ -143,13 +143,14 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
143
143
|
#stopSequences: string[];
|
144
144
|
#renderingTimeout?: number;
|
145
145
|
#aidaRequestCache?: CachedRequest;
|
146
|
-
#panel:
|
146
|
+
#panel: ContextFlavor;
|
147
147
|
|
148
148
|
readonly #sessionId: string = crypto.randomUUID();
|
149
149
|
readonly #aidaClient: Host.AidaClient.AidaClient;
|
150
150
|
readonly #serverSideLoggingEnabled: boolean;
|
151
151
|
|
152
|
-
constructor(
|
152
|
+
constructor(
|
153
|
+
opts: AgentOptions, editor: TextEditor.TextEditor.TextEditor, panel: ContextFlavor, stopSequences?: string[]) {
|
153
154
|
super();
|
154
155
|
this.#aidaClient = opts.aidaClient;
|
155
156
|
this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
|
@@ -177,12 +178,12 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
177
178
|
// As a temporary fix for b/441221870 we are prepending a newline for each prefix.
|
178
179
|
prefix = '\n' + prefix;
|
179
180
|
|
180
|
-
const additionalFiles = this.#panel ===
|
181
|
+
const additionalFiles = this.#panel === ContextFlavor.CONSOLE ? [{
|
181
182
|
path: 'devtools-console-context.js',
|
182
183
|
content: consoleAdditionalContextFileContent,
|
183
184
|
included_reason: Host.AidaClient.Reason.RELATED_FILE,
|
184
185
|
}] :
|
185
|
-
|
186
|
+
undefined;
|
186
187
|
|
187
188
|
return {
|
188
189
|
client: Host.AidaClient.CLIENT_NAME,
|
@@ -276,6 +277,11 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
276
277
|
return null;
|
277
278
|
}
|
278
279
|
|
280
|
+
const isRepetitive = this.#checkIfSuggestionRepeatsExistingText(suggestionSample.generationString, request);
|
281
|
+
if (isRepetitive) {
|
282
|
+
return null;
|
283
|
+
}
|
284
|
+
|
279
285
|
const suggestionText = this.#trimSuggestionOverlap(suggestionSample.generationString, request);
|
280
286
|
if (suggestionText.length === 0) {
|
281
287
|
return null;
|
@@ -326,6 +332,7 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
326
332
|
sampleId,
|
327
333
|
startTime,
|
328
334
|
onImpression: this.#registerUserImpression.bind(this),
|
335
|
+
clearCachedRequest: this.#clearCachedRequest.bind(this),
|
329
336
|
})
|
330
337
|
});
|
331
338
|
|
@@ -333,11 +340,6 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
333
340
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionResponseServedFromCache);
|
334
341
|
}
|
335
342
|
|
336
|
-
if (rpcGlobalId) {
|
337
|
-
const latency = performance.now() - startTime;
|
338
|
-
this.#registerUserImpression(rpcGlobalId, sampleId, latency);
|
339
|
-
}
|
340
|
-
|
341
343
|
debugLog('Suggestion dispatched to the editor', suggestionText, 'at cursor position', cursorPositionAtRequest);
|
342
344
|
this.dispatchEventToListeners(Events.RESPONSE_RECEIVED, {citations});
|
343
345
|
}, remainingDelay);
|
@@ -381,6 +383,11 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
381
383
|
return generationString;
|
382
384
|
}
|
383
385
|
|
386
|
+
#checkIfSuggestionRepeatsExistingText(generationString: string, request: Host.AidaClient.CompletionRequest): boolean {
|
387
|
+
const {prefix, suffix} = request;
|
388
|
+
return Boolean(prefix.includes(generationString.trim()) || suffix?.includes(generationString.trim()));
|
389
|
+
}
|
390
|
+
|
384
391
|
#checkCachedRequestForResponse(request: Host.AidaClient.CompletionRequest): Host.AidaClient.CompletionResponse|null {
|
385
392
|
if (!this.#aidaRequestCache || this.#aidaRequestCache.request.suffix !== request.suffix ||
|
386
393
|
JSON.stringify(this.#aidaRequestCache.request.options) !== JSON.stringify(request.options)) {
|
@@ -408,6 +415,10 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
408
415
|
this.#aidaRequestCache = {request, response};
|
409
416
|
}
|
410
417
|
|
418
|
+
#clearCachedRequest(): void {
|
419
|
+
this.#aidaRequestCache = undefined;
|
420
|
+
}
|
421
|
+
|
411
422
|
#registerUserImpression(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId: number, latency: number): void {
|
412
423
|
const seconds = Math.floor(latency / 1_000);
|
413
424
|
const remainingMs = latency % 1_000;
|
@@ -467,8 +478,8 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
467
478
|
}
|
468
479
|
}
|
469
480
|
|
470
|
-
export const enum
|
471
|
-
CONSOLE = 'console',
|
481
|
+
export const enum ContextFlavor {
|
482
|
+
CONSOLE = 'console', // generated code can contain console specific APIs like `$0`.
|
472
483
|
SOURCES = 'sources',
|
473
484
|
}
|
474
485
|
|
@@ -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
|
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
|
-
|
23
|
+
BadgeAction.STARTED_AI_CONVERSATION,
|
16
24
|
] as const;
|
17
25
|
|
18
26
|
handleAction(_action: BadgeAction): void {
|
19
|
-
this.
|
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,8 +10,10 @@ 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',
|
16
|
+
DEBUGGER_PAUSED = 'debugger-paused'
|
15
17
|
}
|
16
18
|
|
17
19
|
export type BadgeActionEvents = Record<BadgeAction, void>;
|
@@ -21,8 +23,12 @@ export interface BadgeContext {
|
|
21
23
|
badgeActionEventTarget: Common.ObjectWrapper.ObjectWrapper<BadgeActionEvents>;
|
22
24
|
}
|
23
25
|
|
26
|
+
export interface TriggerOptions {
|
27
|
+
immediate?: boolean;
|
28
|
+
}
|
29
|
+
|
24
30
|
export abstract class Badge {
|
25
|
-
#onTriggerBadge: (badge: Badge) => void;
|
31
|
+
#onTriggerBadge: (badge: Badge, opts?: TriggerOptions) => void;
|
26
32
|
#badgeActionEventTarget: Common.ObjectWrapper.ObjectWrapper<BadgeActionEvents>;
|
27
33
|
#eventListeners: Common.EventTarget.EventDescriptor[] = [];
|
28
34
|
#triggeredBefore = false;
|
@@ -31,6 +37,7 @@ export abstract class Badge {
|
|
31
37
|
abstract readonly title: string;
|
32
38
|
abstract readonly imageUri: string;
|
33
39
|
abstract readonly interestedActions: readonly BadgeAction[];
|
40
|
+
abstract readonly jslogContext: string;
|
34
41
|
readonly isStarterBadge: boolean = false;
|
35
42
|
|
36
43
|
constructor(context: BadgeContext) {
|
@@ -39,14 +46,14 @@ export abstract class Badge {
|
|
39
46
|
}
|
40
47
|
|
41
48
|
abstract handleAction(action: BadgeAction): void;
|
42
|
-
protected trigger(): void {
|
49
|
+
protected trigger(opts?: TriggerOptions): void {
|
43
50
|
if (this.#triggeredBefore) {
|
44
51
|
return;
|
45
52
|
}
|
46
53
|
|
47
54
|
this.#triggeredBefore = true;
|
48
55
|
this.deactivate();
|
49
|
-
this.#onTriggerBadge(this);
|
56
|
+
this.#onTriggerBadge(this, opts);
|
50
57
|
}
|
51
58
|
|
52
59
|
activate(): void {
|
@@ -2,18 +2,17 @@
|
|
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,
|
5
|
+
import {Badge, BadgeAction} from './Badge.js';
|
6
6
|
|
7
7
|
const CODE_WHISPERER_BADGE_IMAGE_URI = new URL('../../Images/code-whisperer-badge.svg', import.meta.url).toString();
|
8
8
|
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
|
-
override readonly interestedActions = [
|
15
|
-
// TODO(ergunsh): Instrument related actions.
|
16
|
-
] as const;
|
15
|
+
override readonly interestedActions = [BadgeAction.DEBUGGER_PAUSED] as const;
|
17
16
|
|
18
17
|
handleAction(_action: BadgeAction): void {
|
19
18
|
this.trigger();
|
@@ -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
|
@@ -20,7 +21,7 @@ export class StarterBadge extends Badge {
|
|
20
21
|
BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED,
|
21
22
|
] as const;
|
22
23
|
|
23
|
-
handleAction(
|
24
|
-
this.trigger();
|
24
|
+
handleAction(action: BadgeAction): void {
|
25
|
+
this.trigger({immediate: action === BadgeAction.GDP_SIGN_UP_COMPLETE});
|
25
26
|
}
|
26
27
|
}
|