@oddsquad/vite-plugin-lit 0.2.3 → 0.3.0
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/CHANGELOG.md +64 -0
- package/LICENSE +28 -0
- package/README.md +153 -0
- package/bin.mjs +154 -0
- package/client.d.ts +61 -0
- package/dist/client/assets/browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js +69 -0
- package/dist/client/assets/browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js.map +1 -0
- package/dist/client/assets/index-CFrVgNfS.js +9740 -0
- package/dist/client/assets/index-CFrVgNfS.js.map +1 -0
- package/{panel → dist/client}/index.html +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -2
- package/index.js.map +1 -1
- package/lib/color-scheme.d.ts.map +1 -1
- package/lib/color-scheme.js +17 -21
- package/lib/color-scheme.js.map +1 -1
- package/lib/devframe/definition.d.ts +45 -0
- package/lib/devframe/definition.d.ts.map +1 -0
- package/lib/devframe/definition.js +547 -0
- package/lib/devframe/definition.js.map +1 -0
- package/lib/devframe/icon.d.ts +8 -0
- package/lib/devframe/icon.d.ts.map +1 -0
- package/lib/devframe/icon.js +21 -0
- package/lib/devframe/icon.js.map +1 -0
- package/lib/devframe/paths.d.ts +12 -0
- package/lib/devframe/paths.d.ts.map +1 -0
- package/lib/devframe/paths.js +38 -0
- package/lib/devframe/paths.js.map +1 -0
- package/lib/devframe/protocol.d.ts +218 -0
- package/lib/devframe/protocol.d.ts.map +1 -0
- package/lib/devframe/protocol.js +76 -0
- package/lib/devframe/protocol.js.map +1 -0
- package/lib/devframe/source.d.ts +49 -0
- package/lib/devframe/source.d.ts.map +1 -0
- package/lib/devframe/source.js +23 -0
- package/lib/devframe/source.js.map +1 -0
- package/lib/devframe/vite.d.ts +116 -0
- package/lib/devframe/vite.d.ts.map +1 -0
- package/lib/devframe/vite.js +291 -0
- package/lib/devframe/vite.js.map +1 -0
- package/lib/http.d.ts +29 -0
- package/lib/http.d.ts.map +1 -0
- package/lib/http.js +35 -0
- package/lib/http.js.map +1 -0
- package/lib/icons.d.ts +2 -0
- package/lib/icons.d.ts.map +1 -1
- package/lib/icons.js +10 -4
- package/lib/icons.js.map +1 -1
- package/lib/plugin.d.ts +15 -4
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +640 -574
- package/lib/plugin.js.map +1 -1
- package/lib/runtime/css.d.ts +2 -2
- package/lib/runtime/css.d.ts.map +1 -1
- package/lib/runtime/css.js +34 -34
- package/lib/runtime/css.js.map +1 -1
- package/lib/runtime/edge-panel.d.ts.map +1 -1
- package/lib/runtime/edge-panel.js +82 -91
- package/lib/runtime/edge-panel.js.map +1 -1
- package/lib/runtime/fonts.d.ts.map +1 -1
- package/lib/runtime/fonts.js +1 -1
- package/lib/runtime/fonts.js.map +1 -1
- package/lib/runtime/indicator.d.ts.map +1 -1
- package/lib/runtime/indicator.js +71 -74
- package/lib/runtime/indicator.js.map +1 -1
- package/lib/runtime/inspector/collect.d.ts.map +1 -1
- package/lib/runtime/inspector/collect.js +83 -90
- package/lib/runtime/inspector/collect.js.map +1 -1
- package/lib/runtime/inspector/highlight.d.ts +16 -0
- package/lib/runtime/inspector/highlight.d.ts.map +1 -0
- package/lib/runtime/inspector/highlight.js +66 -0
- package/lib/runtime/inspector/highlight.js.map +1 -0
- package/lib/runtime/inspector/install.d.ts.map +1 -1
- package/lib/runtime/inspector/install.js +180 -205
- package/lib/runtime/inspector/install.js.map +1 -1
- package/lib/runtime/inspector/serialize.d.ts.map +1 -1
- package/lib/runtime/inspector/serialize.js +86 -107
- package/lib/runtime/inspector/serialize.js.map +1 -1
- package/lib/runtime/intern.d.ts.map +1 -1
- package/lib/runtime/intern.js +31 -29
- package/lib/runtime/intern.js.map +1 -1
- package/lib/runtime/overrides.d.ts.map +1 -1
- package/lib/runtime/overrides.js +14 -13
- package/lib/runtime/overrides.js.map +1 -1
- package/lib/runtime/patch.d.ts +8 -0
- package/lib/runtime/patch.d.ts.map +1 -1
- package/lib/runtime/patch.js +321 -282
- package/lib/runtime/patch.js.map +1 -1
- package/lib/runtime/source-meta.d.ts.map +1 -1
- package/lib/runtime/source-meta.js +1 -1
- package/lib/runtime/source-meta.js.map +1 -1
- package/lib/runtime/source-overlay/editors.d.ts.map +1 -1
- package/lib/runtime/source-overlay/editors.js +27 -27
- package/lib/runtime/source-overlay/editors.js.map +1 -1
- package/lib/runtime/source-overlay/mask-path.d.ts.map +1 -1
- package/lib/runtime/source-overlay/mask-path.js +15 -15
- package/lib/runtime/source-overlay/mask-path.js.map +1 -1
- package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -1
- package/lib/runtime/source-overlay/overlay-element.js +347 -356
- package/lib/runtime/source-overlay/overlay-element.js.map +1 -1
- package/lib/runtime/source-overlay/source-host.d.ts.map +1 -1
- package/lib/runtime/source-overlay/source-host.js +64 -71
- package/lib/runtime/source-overlay/source-host.js.map +1 -1
- package/lib/runtime/source-overlay/template.d.ts.map +1 -1
- package/lib/runtime/source-overlay/template.js +3 -3
- package/lib/runtime/source-overlay/template.js.map +1 -1
- package/lib/runtime/source-overlay.d.ts.map +1 -1
- package/lib/runtime/source-overlay.js +5 -2
- package/lib/runtime/source-overlay.js.map +1 -1
- package/lib/runtime/timeline/clock.d.ts.map +1 -1
- package/lib/runtime/timeline/clock.js +2 -2
- package/lib/runtime/timeline/clock.js.map +1 -1
- package/lib/runtime/timeline/flash.d.ts +39 -0
- package/lib/runtime/timeline/flash.d.ts.map +1 -0
- package/lib/runtime/timeline/flash.js +169 -0
- package/lib/runtime/timeline/flash.js.map +1 -0
- package/lib/runtime/timeline/identity.d.ts.map +1 -1
- package/lib/runtime/timeline/identity.js +27 -27
- package/lib/runtime/timeline/identity.js.map +1 -1
- package/lib/runtime/timeline/input.d.ts.map +1 -1
- package/lib/runtime/timeline/input.js +54 -67
- package/lib/runtime/timeline/input.js.map +1 -1
- package/lib/runtime/timeline/install.d.ts.map +1 -1
- package/lib/runtime/timeline/install.js +38 -29
- package/lib/runtime/timeline/install.js.map +1 -1
- package/lib/runtime/timeline/lifecycle.d.ts +8 -0
- package/lib/runtime/timeline/lifecycle.d.ts.map +1 -1
- package/lib/runtime/timeline/lifecycle.js +149 -133
- package/lib/runtime/timeline/lifecycle.js.map +1 -1
- package/lib/runtime/timeline/public-api.d.ts.map +1 -1
- package/lib/runtime/timeline/public-api.js +9 -9
- package/lib/runtime/timeline/public-api.js.map +1 -1
- package/lib/runtime/timeline/render.d.ts.map +1 -1
- package/lib/runtime/timeline/render.js +70 -75
- package/lib/runtime/timeline/render.js.map +1 -1
- package/lib/runtime/timeline/transport.d.ts.map +1 -1
- package/lib/runtime/timeline/transport.js +39 -43
- package/lib/runtime/timeline/transport.js.map +1 -1
- package/lib/segmented-tabs.d.ts +3 -1
- package/lib/segmented-tabs.d.ts.map +1 -1
- package/lib/segmented-tabs.js +136 -107
- package/lib/segmented-tabs.js.map +1 -1
- package/lib/snapshot.d.ts +28 -0
- package/lib/snapshot.d.ts.map +1 -0
- package/lib/snapshot.js +57 -0
- package/lib/snapshot.js.map +1 -0
- package/lib/source-meta.d.ts.map +1 -1
- package/lib/source-meta.js +176 -162
- package/lib/source-meta.js.map +1 -1
- package/lib/timeline/derive.d.ts +152 -0
- package/lib/timeline/derive.d.ts.map +1 -0
- package/lib/timeline/derive.js +242 -0
- package/lib/timeline/derive.js.map +1 -0
- package/lib/timeline-plugin.d.ts.map +1 -1
- package/lib/timeline-plugin.js +304 -321
- package/lib/timeline-plugin.js.map +1 -1
- package/lib/tokens.d.ts.map +1 -1
- package/lib/tokens.js +17 -18
- package/lib/tokens.js.map +1 -1
- package/lib/transform.d.ts.map +1 -1
- package/lib/transform.js +62 -57
- package/lib/transform.js.map +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +1 -1
- package/lib/types.js.map +1 -1
- package/lib/wrap-table.d.ts.map +1 -1
- package/lib/wrap-table.js +19 -17
- package/lib/wrap-table.js.map +1 -1
- package/package.json +77 -13
- package/types/hmr-incompatibility.d.ts +59 -0
- package/types/hmr-incompatibility.d.ts.map +1 -0
- package/types/hmr-incompatibility.js +30 -0
- package/types/hmr-incompatibility.js.map +1 -0
- package/types/in-page.d.ts +49 -0
- package/types/in-page.d.ts.map +1 -0
- package/types/in-page.js +33 -0
- package/types/in-page.js.map +1 -0
- package/types/inspector.d.ts.map +1 -1
- package/types/inspector.js.map +1 -1
- package/types/snapshot.d.ts +44 -0
- package/types/snapshot.d.ts.map +1 -0
- package/types/snapshot.js +7 -0
- package/types/snapshot.js.map +1 -0
- package/types/timeline.d.ts +11 -0
- package/types/timeline.d.ts.map +1 -1
- package/types/timeline.js.map +1 -1
- package/panel/components-view.d.ts +0 -60
- package/panel/components-view.d.ts.map +0 -1
- package/panel/components-view.js +0 -587
- package/panel/components-view.js.map +0 -1
- package/panel/devtools-settings.d.ts +0 -41
- package/panel/devtools-settings.d.ts.map +0 -1
- package/panel/devtools-settings.js +0 -463
- package/panel/devtools-settings.js.map +0 -1
- package/panel/lit-devtools-panel.d.ts +0 -37
- package/panel/lit-devtools-panel.d.ts.map +0 -1
- package/panel/lit-devtools-panel.js +0 -185
- package/panel/lit-devtools-panel.js.map +0 -1
- package/panel/timeline-event-list.d.ts +0 -43
- package/panel/timeline-event-list.d.ts.map +0 -1
- package/panel/timeline-event-list.js +0 -416
- package/panel/timeline-event-list.js.map +0 -1
- package/panel/timeline-layers.d.ts +0 -25
- package/panel/timeline-layers.d.ts.map +0 -1
- package/panel/timeline-layers.js +0 -96
- package/panel/timeline-layers.js.map +0 -1
- package/panel/timeline-view.d.ts +0 -38
- package/panel/timeline-view.d.ts.map +0 -1
- package/panel/timeline-view.js +0 -234
- package/panel/timeline-view.js.map +0 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Derivation over recorded timeline events.
|
|
8
|
+
*
|
|
9
|
+
* The capture layers emit one event per phase boundary, which is the right
|
|
10
|
+
* wire format and the wrong thing to read: a single update tick of one
|
|
11
|
+
* component is five to ten rows whose durations the reader has to subtract by
|
|
12
|
+
* hand. Everything needed to do better is already on the events — `groupId`
|
|
13
|
+
* pairs a start with its end, `data.changed` names the reactive properties
|
|
14
|
+
* that caused the update — so this module turns a flat event list into the
|
|
15
|
+
* three shapes the panel and agents actually ask for:
|
|
16
|
+
*
|
|
17
|
+
* - {@link TimelineSpan} — a start/end pair collapsed into one entry with a
|
|
18
|
+
* duration ("`update` took 1.8ms").
|
|
19
|
+
* - {@link UpdateCycle} — one component's whole update tick, with the changed
|
|
20
|
+
* property keys that triggered it ("`<hmr-counter>` updated because
|
|
21
|
+
* `count` changed").
|
|
22
|
+
* - {@link ComponentRollup} — per-component totals over a recording ("this
|
|
23
|
+
* component updated 340 times for 120ms").
|
|
24
|
+
*
|
|
25
|
+
* Pure and dependency-free by design: it imports only the shared event types,
|
|
26
|
+
* so the panel derives client-side over its 5000-event buffer and
|
|
27
|
+
* `devframe/definition.ts` — which must stay framework-neutral — can derive
|
|
28
|
+
* server-side over the ring buffer for the agent-facing summary.
|
|
29
|
+
*/
|
|
30
|
+
import type { TimelineEvent } from '../../types/timeline.js';
|
|
31
|
+
/**
|
|
32
|
+
* A start/end pair collapsed into one entry, or a point event that never had
|
|
33
|
+
* an end to begin with.
|
|
34
|
+
*/
|
|
35
|
+
export interface TimelineSpan {
|
|
36
|
+
layerId: string;
|
|
37
|
+
/**
|
|
38
|
+
* Stable across re-derivation of the same events, which a Lit `repeat()`
|
|
39
|
+
* key and a selection comparison both need — the span objects themselves
|
|
40
|
+
* are rebuilt whenever the event buffer changes.
|
|
41
|
+
*/
|
|
42
|
+
key: string;
|
|
43
|
+
/** The event title with its `:start` / `:end` suffix removed. */
|
|
44
|
+
name: string;
|
|
45
|
+
/** Pairing id as emitted; `undefined` for point events. */
|
|
46
|
+
groupId?: number | string;
|
|
47
|
+
start: number;
|
|
48
|
+
/** Absent while the span is still open, or if its end was never recorded. */
|
|
49
|
+
end?: number;
|
|
50
|
+
/** `end - start`. Absent for open spans and for point events. */
|
|
51
|
+
duration?: number;
|
|
52
|
+
/** Reactive property keys that caused this update, where the phase has them. */
|
|
53
|
+
changed?: string[];
|
|
54
|
+
subtitle?: string;
|
|
55
|
+
logType?: TimelineEvent['logType'];
|
|
56
|
+
meta?: TimelineEvent['meta'];
|
|
57
|
+
/** The events this span was built from, for the detail pane. */
|
|
58
|
+
events: TimelineEvent[];
|
|
59
|
+
}
|
|
60
|
+
/** One component's complete update tick. */
|
|
61
|
+
export interface UpdateCycle {
|
|
62
|
+
/** The `${elementId}:${tick}` groupId the phases share. */
|
|
63
|
+
key: string;
|
|
64
|
+
elementId: number;
|
|
65
|
+
tagName: string;
|
|
66
|
+
source?: {
|
|
67
|
+
file: string;
|
|
68
|
+
line: number;
|
|
69
|
+
};
|
|
70
|
+
start: number;
|
|
71
|
+
/** The `performUpdate` span's duration — see {@link toUpdateCycles}. */
|
|
72
|
+
duration?: number;
|
|
73
|
+
/** Union of the changed reactive property keys across the tick's phases. */
|
|
74
|
+
changed: string[];
|
|
75
|
+
phases: TimelineSpan[];
|
|
76
|
+
/** The input event this update followed, if any. See {@link attributeInput}. */
|
|
77
|
+
cause?: {
|
|
78
|
+
layerId: string;
|
|
79
|
+
type: string;
|
|
80
|
+
detail?: string;
|
|
81
|
+
time: number;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** Per-component totals over a set of update cycles. */
|
|
85
|
+
export interface ComponentRollup {
|
|
86
|
+
tagName: string;
|
|
87
|
+
/** Every instance of this component that updated, in first-seen order. */
|
|
88
|
+
elementIds: number[];
|
|
89
|
+
updates: number;
|
|
90
|
+
/** Summed `performUpdate` durations; excludes cycles with no measured end. */
|
|
91
|
+
totalMs: number;
|
|
92
|
+
maxMs: number;
|
|
93
|
+
/** Changed-key frequency across the cycles, most frequent first. */
|
|
94
|
+
reasons: Array<{
|
|
95
|
+
key: string;
|
|
96
|
+
count: number;
|
|
97
|
+
}>;
|
|
98
|
+
source?: {
|
|
99
|
+
file: string;
|
|
100
|
+
line: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Collapses `:start` / `:end` event pairs into spans, passing anything else
|
|
105
|
+
* through as a point span.
|
|
106
|
+
*
|
|
107
|
+
* Pairing is keyed on **layer + groupId + phase name**, never on `groupId`
|
|
108
|
+
* alone: a whole update tick shares one groupId (`runtime/timeline/
|
|
109
|
+
* lifecycle.ts` builds it as `${elementId}:${tick}`), so matching on the id
|
|
110
|
+
* would marry `willUpdate:start` to `performUpdate:end`. Each phase occurs at
|
|
111
|
+
* most once per tick, which makes the triple unique.
|
|
112
|
+
*
|
|
113
|
+
* Unmatched halves survive rather than disappear — the buffer is a bounded
|
|
114
|
+
* ring, so its oldest end and its newest start routinely have no partner, and
|
|
115
|
+
* an update that is still running has a genuinely open span.
|
|
116
|
+
*/
|
|
117
|
+
export declare const toSpans: (events: readonly TimelineEvent[]) => TimelineSpan[];
|
|
118
|
+
/**
|
|
119
|
+
* Groups lifecycle spans into update ticks.
|
|
120
|
+
*
|
|
121
|
+
* `duration` is taken from the `performUpdate` span alone, never from a sum of
|
|
122
|
+
* the phases: `willUpdate` / `update` / `updated` all run *inside*
|
|
123
|
+
* `performUpdate`, so summing counts the same milliseconds several times over.
|
|
124
|
+
*
|
|
125
|
+
* `changed` is the union across the tick's phases because only the phases that
|
|
126
|
+
* receive a `PropertyValues` argument carry it — `performUpdate` takes none,
|
|
127
|
+
* so the bracketing span is always silent about the reason.
|
|
128
|
+
*/
|
|
129
|
+
export declare const toUpdateCycles: (spans: readonly TimelineSpan[]) => UpdateCycle[];
|
|
130
|
+
/**
|
|
131
|
+
* Totals update cycles per component tag.
|
|
132
|
+
*
|
|
133
|
+
* Grouped by tag rather than by instance because the question this answers is
|
|
134
|
+
* "which *component* re-renders too much" — a list of 200 rows each updating
|
|
135
|
+
* twice is one component's problem, not 200. The instance ids are kept so the
|
|
136
|
+
* caller can still drill in.
|
|
137
|
+
*/
|
|
138
|
+
export declare const rollup: (cycles: readonly UpdateCycle[]) => ComponentRollup[];
|
|
139
|
+
/**
|
|
140
|
+
* Attributes each update cycle to the input event that preceded it, within
|
|
141
|
+
* `windowMs`.
|
|
142
|
+
*
|
|
143
|
+
* This is what the mouse and keyboard layers are for. On their own they
|
|
144
|
+
* duplicate what the browser's own event log already shows; joined to the
|
|
145
|
+
* updates they precede they answer the question the raw layers only imply —
|
|
146
|
+
* an update with a cause is an interaction, and an update without one is a
|
|
147
|
+
* timer, a signal, or a stray `requestUpdate()`.
|
|
148
|
+
*
|
|
149
|
+
* Returns a new array; cycles with no cause are returned as-is.
|
|
150
|
+
*/
|
|
151
|
+
export declare const attributeInput: (cycles: readonly UpdateCycle[], events: readonly TimelineEvent[], windowMs?: number) => UpdateCycle[];
|
|
152
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/lib/timeline/derive.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAc3D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7B,gEAAgE;IAChE,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,gFAAgF;IAChF,KAAK,CAAC,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;CACxE;AAED,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;CACvC;AAwBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,WAAY,SAAS,aAAa,EAAE,KAAG,YAAY,EA0DtE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,UAClB,SAAS,YAAY,EAAE,KAC7B,WAAW,EAuCb,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,WAAY,SAAS,WAAW,EAAE,KAAG,eAAe,EAoDtE,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,WACjB,SAAS,WAAW,EAAE,UACtB,SAAS,aAAa,EAAE,wBAE/B,WAAW,EA4Bb,CAAC"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/** Layer whose events describe Lit update ticks. */
|
|
7
|
+
const LIFECYCLE_LAYER_ID = 'lit-lifecycle';
|
|
8
|
+
/** Layers whose events are developer input, for {@link attributeInput}. */
|
|
9
|
+
const INPUT_LAYER_IDS = ['mouse', 'keyboard'];
|
|
10
|
+
/** The phase that brackets an entire update tick. */
|
|
11
|
+
const ROOT_PHASE = 'performUpdate';
|
|
12
|
+
const START_SUFFIX = ':start';
|
|
13
|
+
const END_SUFFIX = ':end';
|
|
14
|
+
/** Reads `data.changed` defensively — `data` is `unknown` on the wire. */
|
|
15
|
+
const changedOf = (event) => {
|
|
16
|
+
const data = event.data;
|
|
17
|
+
if (data === null || typeof data !== 'object') return undefined;
|
|
18
|
+
const changed = data.changed;
|
|
19
|
+
if (!Array.isArray(changed)) return undefined;
|
|
20
|
+
const keys = changed.filter((key) => typeof key === 'string');
|
|
21
|
+
return keys.length > 0 ? keys : undefined;
|
|
22
|
+
};
|
|
23
|
+
const pointSpan = (event, index) => ({
|
|
24
|
+
layerId: event.layerId,
|
|
25
|
+
key: `${event.layerId}:point:${index}`,
|
|
26
|
+
name: event.title ?? event.layerId,
|
|
27
|
+
start: event.time,
|
|
28
|
+
changed: changedOf(event),
|
|
29
|
+
subtitle: event.subtitle,
|
|
30
|
+
logType: event.logType,
|
|
31
|
+
meta: event.meta,
|
|
32
|
+
events: [event],
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Collapses `:start` / `:end` event pairs into spans, passing anything else
|
|
36
|
+
* through as a point span.
|
|
37
|
+
*
|
|
38
|
+
* Pairing is keyed on **layer + groupId + phase name**, never on `groupId`
|
|
39
|
+
* alone: a whole update tick shares one groupId (`runtime/timeline/
|
|
40
|
+
* lifecycle.ts` builds it as `${elementId}:${tick}`), so matching on the id
|
|
41
|
+
* would marry `willUpdate:start` to `performUpdate:end`. Each phase occurs at
|
|
42
|
+
* most once per tick, which makes the triple unique.
|
|
43
|
+
*
|
|
44
|
+
* Unmatched halves survive rather than disappear — the buffer is a bounded
|
|
45
|
+
* ring, so its oldest end and its newest start routinely have no partner, and
|
|
46
|
+
* an update that is still running has a genuinely open span.
|
|
47
|
+
*/
|
|
48
|
+
export const toSpans = (events) => {
|
|
49
|
+
const spans = [];
|
|
50
|
+
const open = new Map();
|
|
51
|
+
events.forEach((event, index) => {
|
|
52
|
+
const title = event.title ?? event.layerId;
|
|
53
|
+
const isStart = title.endsWith(START_SUFFIX);
|
|
54
|
+
const isEnd = !isStart && title.endsWith(END_SUFFIX);
|
|
55
|
+
if (event.groupId === undefined || (!isStart && !isEnd)) {
|
|
56
|
+
spans.push(pointSpan(event, index));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const name = title.slice(
|
|
60
|
+
0,
|
|
61
|
+
-(isStart ? START_SUFFIX.length : END_SUFFIX.length)
|
|
62
|
+
);
|
|
63
|
+
const key = `${event.layerId}:${event.groupId}:${name}`;
|
|
64
|
+
if (isStart) {
|
|
65
|
+
const span = {
|
|
66
|
+
layerId: event.layerId,
|
|
67
|
+
key,
|
|
68
|
+
name,
|
|
69
|
+
groupId: event.groupId,
|
|
70
|
+
start: event.time,
|
|
71
|
+
changed: changedOf(event),
|
|
72
|
+
subtitle: event.subtitle,
|
|
73
|
+
logType: event.logType,
|
|
74
|
+
meta: event.meta,
|
|
75
|
+
events: [event],
|
|
76
|
+
};
|
|
77
|
+
open.set(key, span);
|
|
78
|
+
// Pushed on its start, so the list is already in start order and an
|
|
79
|
+
// open span keeps its place instead of appearing when it finally ends.
|
|
80
|
+
spans.push(span);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const span = open.get(key);
|
|
84
|
+
if (span === undefined) {
|
|
85
|
+
spans.push(pointSpan(event, index));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
open.delete(key);
|
|
89
|
+
span.events.push(event);
|
|
90
|
+
// A negative delta means the buffer straddles a clock reset (the runtime
|
|
91
|
+
// re-zeroes on the rising edge of recording). Report no duration rather
|
|
92
|
+
// than a negative one; the span stays open, which is what it honestly is.
|
|
93
|
+
if (event.time >= span.start) {
|
|
94
|
+
span.end = event.time;
|
|
95
|
+
span.duration = event.time - span.start;
|
|
96
|
+
}
|
|
97
|
+
span.changed ??= changedOf(event);
|
|
98
|
+
});
|
|
99
|
+
return spans.sort((a, b) => a.start - b.start);
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Groups lifecycle spans into update ticks.
|
|
103
|
+
*
|
|
104
|
+
* `duration` is taken from the `performUpdate` span alone, never from a sum of
|
|
105
|
+
* the phases: `willUpdate` / `update` / `updated` all run *inside*
|
|
106
|
+
* `performUpdate`, so summing counts the same milliseconds several times over.
|
|
107
|
+
*
|
|
108
|
+
* `changed` is the union across the tick's phases because only the phases that
|
|
109
|
+
* receive a `PropertyValues` argument carry it — `performUpdate` takes none,
|
|
110
|
+
* so the bracketing span is always silent about the reason.
|
|
111
|
+
*/
|
|
112
|
+
export const toUpdateCycles = (spans) => {
|
|
113
|
+
const byGroup = new Map();
|
|
114
|
+
for (const span of spans) {
|
|
115
|
+
const elementId = span.meta?.elementId;
|
|
116
|
+
if (
|
|
117
|
+
span.layerId !== LIFECYCLE_LAYER_ID ||
|
|
118
|
+
span.groupId === undefined ||
|
|
119
|
+
elementId === undefined
|
|
120
|
+
) {
|
|
121
|
+
// Point lifecycle events (connect/disconnect) have no groupId and are
|
|
122
|
+
// not update ticks; other layers are not this function's business.
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const key = String(span.groupId);
|
|
126
|
+
let cycle = byGroup.get(key);
|
|
127
|
+
if (cycle === undefined) {
|
|
128
|
+
cycle = {
|
|
129
|
+
key,
|
|
130
|
+
elementId,
|
|
131
|
+
tagName: span.meta?.tagName ?? 'unknown',
|
|
132
|
+
source: span.meta?.source,
|
|
133
|
+
start: span.start,
|
|
134
|
+
changed: [],
|
|
135
|
+
phases: [],
|
|
136
|
+
};
|
|
137
|
+
byGroup.set(key, cycle);
|
|
138
|
+
}
|
|
139
|
+
cycle.phases.push(span);
|
|
140
|
+
if (span.start < cycle.start) cycle.start = span.start;
|
|
141
|
+
if (span.name === ROOT_PHASE) cycle.duration = span.duration;
|
|
142
|
+
for (const changedKey of span.changed ?? []) {
|
|
143
|
+
if (!cycle.changed.includes(changedKey)) cycle.changed.push(changedKey);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return [...byGroup.values()].sort((a, b) => a.start - b.start);
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Totals update cycles per component tag.
|
|
150
|
+
*
|
|
151
|
+
* Grouped by tag rather than by instance because the question this answers is
|
|
152
|
+
* "which *component* re-renders too much" — a list of 200 rows each updating
|
|
153
|
+
* twice is one component's problem, not 200. The instance ids are kept so the
|
|
154
|
+
* caller can still drill in.
|
|
155
|
+
*/
|
|
156
|
+
export const rollup = (cycles) => {
|
|
157
|
+
const byTag = new Map();
|
|
158
|
+
for (const cycle of cycles) {
|
|
159
|
+
let record = byTag.get(cycle.tagName);
|
|
160
|
+
if (record === undefined) {
|
|
161
|
+
record = {
|
|
162
|
+
entry: {
|
|
163
|
+
tagName: cycle.tagName,
|
|
164
|
+
elementIds: [],
|
|
165
|
+
updates: 0,
|
|
166
|
+
totalMs: 0,
|
|
167
|
+
maxMs: 0,
|
|
168
|
+
reasons: [],
|
|
169
|
+
source: cycle.source,
|
|
170
|
+
},
|
|
171
|
+
reasons: new Map(),
|
|
172
|
+
};
|
|
173
|
+
byTag.set(cycle.tagName, record);
|
|
174
|
+
}
|
|
175
|
+
const {entry, reasons} = record;
|
|
176
|
+
entry.updates++;
|
|
177
|
+
if (!entry.elementIds.includes(cycle.elementId)) {
|
|
178
|
+
entry.elementIds.push(cycle.elementId);
|
|
179
|
+
}
|
|
180
|
+
entry.source ??= cycle.source;
|
|
181
|
+
// An open or clock-straddling cycle contributes a count but no time —
|
|
182
|
+
// better than inventing one, and the count is what flags a hot component.
|
|
183
|
+
if (cycle.duration !== undefined) {
|
|
184
|
+
entry.totalMs += cycle.duration;
|
|
185
|
+
if (cycle.duration > entry.maxMs) entry.maxMs = cycle.duration;
|
|
186
|
+
}
|
|
187
|
+
for (const key of cycle.changed) {
|
|
188
|
+
reasons.set(key, (reasons.get(key) ?? 0) + 1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const entries = [];
|
|
192
|
+
for (const {entry, reasons} of byTag.values()) {
|
|
193
|
+
entry.reasons = [...reasons]
|
|
194
|
+
.map(([key, count]) => ({key, count}))
|
|
195
|
+
.sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
196
|
+
entries.push(entry);
|
|
197
|
+
}
|
|
198
|
+
// Slowest first, then most frequent: a component with no measured durations
|
|
199
|
+
// still sorts above a quiet one.
|
|
200
|
+
return entries.sort((a, b) => b.totalMs - a.totalMs || b.updates - a.updates);
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Attributes each update cycle to the input event that preceded it, within
|
|
204
|
+
* `windowMs`.
|
|
205
|
+
*
|
|
206
|
+
* This is what the mouse and keyboard layers are for. On their own they
|
|
207
|
+
* duplicate what the browser's own event log already shows; joined to the
|
|
208
|
+
* updates they precede they answer the question the raw layers only imply —
|
|
209
|
+
* an update with a cause is an interaction, and an update without one is a
|
|
210
|
+
* timer, a signal, or a stray `requestUpdate()`.
|
|
211
|
+
*
|
|
212
|
+
* Returns a new array; cycles with no cause are returned as-is.
|
|
213
|
+
*/
|
|
214
|
+
export const attributeInput = (cycles, events, windowMs = 200) => {
|
|
215
|
+
const inputs = events
|
|
216
|
+
.filter((event) => INPUT_LAYER_IDS.includes(event.layerId))
|
|
217
|
+
.sort((a, b) => a.time - b.time);
|
|
218
|
+
if (inputs.length === 0) return [...cycles];
|
|
219
|
+
// Both sides are sorted by time, so one forward pass suffices.
|
|
220
|
+
let cursor = 0;
|
|
221
|
+
return cycles.map((cycle) => {
|
|
222
|
+
while (
|
|
223
|
+
cursor + 1 < inputs.length &&
|
|
224
|
+
inputs[cursor + 1].time <= cycle.start
|
|
225
|
+
) {
|
|
226
|
+
cursor++;
|
|
227
|
+
}
|
|
228
|
+
const candidate = inputs[cursor];
|
|
229
|
+
const delta = cycle.start - candidate.time;
|
|
230
|
+
if (delta < 0 || delta > windowMs) return cycle;
|
|
231
|
+
return {
|
|
232
|
+
...cycle,
|
|
233
|
+
cause: {
|
|
234
|
+
layerId: candidate.layerId,
|
|
235
|
+
type: candidate.title ?? candidate.layerId,
|
|
236
|
+
detail: candidate.subtitle,
|
|
237
|
+
time: candidate.time,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/lib/timeline/derive.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Derivation over recorded timeline events.\n *\n * The capture layers emit one event per phase boundary, which is the right\n * wire format and the wrong thing to read: a single update tick of one\n * component is five to ten rows whose durations the reader has to subtract by\n * hand. Everything needed to do better is already on the events — `groupId`\n * pairs a start with its end, `data.changed` names the reactive properties\n * that caused the update — so this module turns a flat event list into the\n * three shapes the panel and agents actually ask for:\n *\n * - {@link TimelineSpan} — a start/end pair collapsed into one entry with a\n * duration (\"`update` took 1.8ms\").\n * - {@link UpdateCycle} — one component's whole update tick, with the changed\n * property keys that triggered it (\"`<hmr-counter>` updated because\n * `count` changed\").\n * - {@link ComponentRollup} — per-component totals over a recording (\"this\n * component updated 340 times for 120ms\").\n *\n * Pure and dependency-free by design: it imports only the shared event types,\n * so the panel derives client-side over its 5000-event buffer and\n * `devframe/definition.ts` — which must stay framework-neutral — can derive\n * server-side over the ring buffer for the agent-facing summary.\n */\n\nimport type {TimelineEvent} from '../../types/timeline.js';\n\n/** Layer whose events describe Lit update ticks. */\nconst LIFECYCLE_LAYER_ID = 'lit-lifecycle';\n\n/** Layers whose events are developer input, for {@link attributeInput}. */\nconst INPUT_LAYER_IDS: readonly string[] = ['mouse', 'keyboard'];\n\n/** The phase that brackets an entire update tick. */\nconst ROOT_PHASE = 'performUpdate';\n\nconst START_SUFFIX = ':start';\nconst END_SUFFIX = ':end';\n\n/**\n * A start/end pair collapsed into one entry, or a point event that never had\n * an end to begin with.\n */\nexport interface TimelineSpan {\n layerId: string;\n /**\n * Stable across re-derivation of the same events, which a Lit `repeat()`\n * key and a selection comparison both need — the span objects themselves\n * are rebuilt whenever the event buffer changes.\n */\n key: string;\n /** The event title with its `:start` / `:end` suffix removed. */\n name: string;\n /** Pairing id as emitted; `undefined` for point events. */\n groupId?: number | string;\n start: number;\n /** Absent while the span is still open, or if its end was never recorded. */\n end?: number;\n /** `end - start`. Absent for open spans and for point events. */\n duration?: number;\n /** Reactive property keys that caused this update, where the phase has them. */\n changed?: string[];\n subtitle?: string;\n logType?: TimelineEvent['logType'];\n meta?: TimelineEvent['meta'];\n /** The events this span was built from, for the detail pane. */\n events: TimelineEvent[];\n}\n\n/** One component's complete update tick. */\nexport interface UpdateCycle {\n /** The `${elementId}:${tick}` groupId the phases share. */\n key: string;\n elementId: number;\n tagName: string;\n source?: {file: string; line: number};\n start: number;\n /** The `performUpdate` span's duration — see {@link toUpdateCycles}. */\n duration?: number;\n /** Union of the changed reactive property keys across the tick's phases. */\n changed: string[];\n phases: TimelineSpan[];\n /** The input event this update followed, if any. See {@link attributeInput}. */\n cause?: {layerId: string; type: string; detail?: string; time: number};\n}\n\n/** Per-component totals over a set of update cycles. */\nexport interface ComponentRollup {\n tagName: string;\n /** Every instance of this component that updated, in first-seen order. */\n elementIds: number[];\n updates: number;\n /** Summed `performUpdate` durations; excludes cycles with no measured end. */\n totalMs: number;\n maxMs: number;\n /** Changed-key frequency across the cycles, most frequent first. */\n reasons: Array<{key: string; count: number}>;\n source?: {file: string; line: number};\n}\n\n/** Reads `data.changed` defensively — `data` is `unknown` on the wire. */\nconst changedOf = (event: TimelineEvent): string[] | undefined => {\n const data = event.data;\n if (data === null || typeof data !== 'object') return undefined;\n const changed = (data as {changed?: unknown}).changed;\n if (!Array.isArray(changed)) return undefined;\n const keys = changed.filter((key): key is string => typeof key === 'string');\n return keys.length > 0 ? keys : undefined;\n};\n\nconst pointSpan = (event: TimelineEvent, index: number): TimelineSpan => ({\n layerId: event.layerId,\n key: `${event.layerId}:point:${index}`,\n name: event.title ?? event.layerId,\n start: event.time,\n changed: changedOf(event),\n subtitle: event.subtitle,\n logType: event.logType,\n meta: event.meta,\n events: [event],\n});\n\n/**\n * Collapses `:start` / `:end` event pairs into spans, passing anything else\n * through as a point span.\n *\n * Pairing is keyed on **layer + groupId + phase name**, never on `groupId`\n * alone: a whole update tick shares one groupId (`runtime/timeline/\n * lifecycle.ts` builds it as `${elementId}:${tick}`), so matching on the id\n * would marry `willUpdate:start` to `performUpdate:end`. Each phase occurs at\n * most once per tick, which makes the triple unique.\n *\n * Unmatched halves survive rather than disappear — the buffer is a bounded\n * ring, so its oldest end and its newest start routinely have no partner, and\n * an update that is still running has a genuinely open span.\n */\nexport const toSpans = (events: readonly TimelineEvent[]): TimelineSpan[] => {\n const spans: TimelineSpan[] = [];\n const open = new Map<string, TimelineSpan>();\n\n events.forEach((event, index) => {\n const title = event.title ?? event.layerId;\n const isStart = title.endsWith(START_SUFFIX);\n const isEnd = !isStart && title.endsWith(END_SUFFIX);\n\n if (event.groupId === undefined || (!isStart && !isEnd)) {\n spans.push(pointSpan(event, index));\n return;\n }\n\n const name = title.slice(\n 0,\n -(isStart ? START_SUFFIX.length : END_SUFFIX.length)\n );\n const key = `${event.layerId}:${event.groupId}:${name}`;\n\n if (isStart) {\n const span: TimelineSpan = {\n layerId: event.layerId,\n key,\n name,\n groupId: event.groupId,\n start: event.time,\n changed: changedOf(event),\n subtitle: event.subtitle,\n logType: event.logType,\n meta: event.meta,\n events: [event],\n };\n open.set(key, span);\n // Pushed on its start, so the list is already in start order and an\n // open span keeps its place instead of appearing when it finally ends.\n spans.push(span);\n return;\n }\n\n const span = open.get(key);\n if (span === undefined) {\n spans.push(pointSpan(event, index));\n return;\n }\n open.delete(key);\n span.events.push(event);\n // A negative delta means the buffer straddles a clock reset (the runtime\n // re-zeroes on the rising edge of recording). Report no duration rather\n // than a negative one; the span stays open, which is what it honestly is.\n if (event.time >= span.start) {\n span.end = event.time;\n span.duration = event.time - span.start;\n }\n span.changed ??= changedOf(event);\n });\n\n return spans.sort((a, b) => a.start - b.start);\n};\n\n/**\n * Groups lifecycle spans into update ticks.\n *\n * `duration` is taken from the `performUpdate` span alone, never from a sum of\n * the phases: `willUpdate` / `update` / `updated` all run *inside*\n * `performUpdate`, so summing counts the same milliseconds several times over.\n *\n * `changed` is the union across the tick's phases because only the phases that\n * receive a `PropertyValues` argument carry it — `performUpdate` takes none,\n * so the bracketing span is always silent about the reason.\n */\nexport const toUpdateCycles = (\n spans: readonly TimelineSpan[]\n): UpdateCycle[] => {\n const byGroup = new Map<string, UpdateCycle>();\n\n for (const span of spans) {\n const elementId = span.meta?.elementId;\n if (\n span.layerId !== LIFECYCLE_LAYER_ID ||\n span.groupId === undefined ||\n elementId === undefined\n ) {\n // Point lifecycle events (connect/disconnect) have no groupId and are\n // not update ticks; other layers are not this function's business.\n continue;\n }\n\n const key = String(span.groupId);\n let cycle = byGroup.get(key);\n if (cycle === undefined) {\n cycle = {\n key,\n elementId,\n tagName: span.meta?.tagName ?? 'unknown',\n source: span.meta?.source,\n start: span.start,\n changed: [],\n phases: [],\n };\n byGroup.set(key, cycle);\n }\n\n cycle.phases.push(span);\n if (span.start < cycle.start) cycle.start = span.start;\n if (span.name === ROOT_PHASE) cycle.duration = span.duration;\n for (const changedKey of span.changed ?? []) {\n if (!cycle.changed.includes(changedKey)) cycle.changed.push(changedKey);\n }\n }\n\n return [...byGroup.values()].sort((a, b) => a.start - b.start);\n};\n\n/**\n * Totals update cycles per component tag.\n *\n * Grouped by tag rather than by instance because the question this answers is\n * \"which *component* re-renders too much\" — a list of 200 rows each updating\n * twice is one component's problem, not 200. The instance ids are kept so the\n * caller can still drill in.\n */\nexport const rollup = (cycles: readonly UpdateCycle[]): ComponentRollup[] => {\n const byTag = new Map<\n string,\n {entry: ComponentRollup; reasons: Map<string, number>}\n >();\n\n for (const cycle of cycles) {\n let record = byTag.get(cycle.tagName);\n if (record === undefined) {\n record = {\n entry: {\n tagName: cycle.tagName,\n elementIds: [],\n updates: 0,\n totalMs: 0,\n maxMs: 0,\n reasons: [],\n source: cycle.source,\n },\n reasons: new Map(),\n };\n byTag.set(cycle.tagName, record);\n }\n\n const {entry, reasons} = record;\n entry.updates++;\n if (!entry.elementIds.includes(cycle.elementId)) {\n entry.elementIds.push(cycle.elementId);\n }\n entry.source ??= cycle.source;\n // An open or clock-straddling cycle contributes a count but no time —\n // better than inventing one, and the count is what flags a hot component.\n if (cycle.duration !== undefined) {\n entry.totalMs += cycle.duration;\n if (cycle.duration > entry.maxMs) entry.maxMs = cycle.duration;\n }\n for (const key of cycle.changed) {\n reasons.set(key, (reasons.get(key) ?? 0) + 1);\n }\n }\n\n const entries: ComponentRollup[] = [];\n for (const {entry, reasons} of byTag.values()) {\n entry.reasons = [...reasons]\n .map(([key, count]) => ({key, count}))\n .sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));\n entries.push(entry);\n }\n\n // Slowest first, then most frequent: a component with no measured durations\n // still sorts above a quiet one.\n return entries.sort((a, b) => b.totalMs - a.totalMs || b.updates - a.updates);\n};\n\n/**\n * Attributes each update cycle to the input event that preceded it, within\n * `windowMs`.\n *\n * This is what the mouse and keyboard layers are for. On their own they\n * duplicate what the browser's own event log already shows; joined to the\n * updates they precede they answer the question the raw layers only imply —\n * an update with a cause is an interaction, and an update without one is a\n * timer, a signal, or a stray `requestUpdate()`.\n *\n * Returns a new array; cycles with no cause are returned as-is.\n */\nexport const attributeInput = (\n cycles: readonly UpdateCycle[],\n events: readonly TimelineEvent[],\n windowMs = 200\n): UpdateCycle[] => {\n const inputs = events\n .filter((event) => INPUT_LAYER_IDS.includes(event.layerId))\n .sort((a, b) => a.time - b.time);\n if (inputs.length === 0) return [...cycles];\n\n // Both sides are sorted by time, so one forward pass suffices.\n let cursor = 0;\n return cycles.map((cycle) => {\n while (\n cursor + 1 < inputs.length &&\n inputs[cursor + 1]!.time <= cycle.start\n ) {\n cursor++;\n }\n const candidate = inputs[cursor]!;\n const delta = cycle.start - candidate.time;\n if (delta < 0 || delta > windowMs) return cycle;\n return {\n ...cycle,\n cause: {\n layerId: candidate.layerId,\n type: candidate.title ?? candidate.layerId,\n detail: candidate.subtitle,\n time: candidate.time,\n },\n };\n });\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-plugin.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"timeline-plugin.d.ts","sourceRoot":"","sources":["../src/lib/timeline-plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAC,MAAM,EAAgB,MAAM,MAAM,CAAC;AAEhD,OAAO,EAEL,KAAK,eAAe,EAIrB,MAAM,sBAAsB,CAAC;AAkB9B,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAC3C;AAED,4DAA4D;AAC5D,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAC3C;AAED,UAAU,WAAW;IACnB,KAAK,EAAE;QAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAA;KAAC,CAAC;IACjD,QAAQ,CAAC,EAAE;QAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAA;KAAC,CAAC;IAC1D,GAAG,EAAE;QACH,QAAQ,EAAE,CAAC,EAAE,EAAE,KAAK,KAAK,IAAI,CAAC;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;KAC7D,CAAC;IACF,KAAK,EAAE;QAAC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;KAAC,CAAC;IAChE,UAAU,CAAC,EAAE;QACX,WAAW,EAAE;YACX,GAAG,EAAE,CACH,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CACP,GAAG,EAAE;gBAAC,GAAG,CAAC,EAAE,MAAM,CAAA;aAAC,EACnB,GAAG,EAAE;gBACH,UAAU,EAAE,MAAM,CAAC;gBACnB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;gBAC1C,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;aAC7B,EACD,IAAI,EAAE,MAAM,IAAI,KACb,IAAI,KACN,IAAI,CAAC;SACX,CAAC;KACH,CAAC;IACF,UAAU,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;CAC7B;AAID,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,MAAM;QACd,QAAQ,CAAC,EAAE;YACT,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,KAAK,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;SACnD,CAAC;KACH;CACF;AA0ID,eAAO,MAAM,iBAAiB,iBACd,MAAM,eAAe,GAAG,SAAS,KAC9C,MAoTF,CAAC"}
|