@flemo/devtools 0.3.0 → 0.5.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/dist/index.mjs CHANGED
@@ -1,53 +1,80 @@
1
1
  //#region src/anomalies.ts
2
- var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = (e) => {
3
- let n = [], { t0Ms: r, driver: i, frameSamples: a, playerGaps: o, longTasks: s, holdLongTasks: c, landing: l } = e, u = r + (e.releasedAtMs ?? 0);
4
- if (o && o.over30Count > 0 && n.push(`player frame gap up to ${o.maxMs}ms ×${o.over30Count} during flight (the rAF player missed frames — convergence jank on this navigation)`), a.released.over30Count > 0) {
2
+ var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = 48, o = (e) => {
3
+ let n = [], { t0Ms: r, driver: i, frameSamples: a, longTasks: o, holdLongTasks: s, landing: c } = e, l = r + (e.releasedAtMs ?? 0);
4
+ if (a.released.over30Count > 0) {
5
5
  let e = i === "compiled" ? " (compiled/compositor flights can still present cleanly through main-thread gaps)" : "";
6
6
  n.push(`main-thread rAF gap up to ${a.released.maxGapMs}ms ×${a.released.over30Count} during visible motion${e}`);
7
7
  }
8
- for (let e of s) e.startMs <= u + 120 && e.startMs + e.durationMs >= u - 50 ? n.push(`long task ${Math.round(e.durationMs)}ms overlapped the visible-motion start (opening-swallow risk: the first presented frames of the transition may have been lost)`) : e.durationMs >= 100 && n.push(`long task ${Math.round(e.durationMs)}ms mid-flight`);
9
- for (let e of c) e.durationMs >= 100 && n.push(`long task ${Math.round(e.durationMs)}ms absorbed by the hold (screen posed, not yet moving — the hold doing its job, not user-visible jank)`);
10
- let { motion: d, images: f } = e;
11
- return d.holdReassertedAtMs !== null && n.push(`hold re-asserted ${d.holdReassertedAtMs}ms into the flight, after it had already released (an interleaved commit wrote the stale paused hold attribute over a running flight — the flemo 2026-08-18 release-race signature; the motion pauses while every timing metric stays clean)`), d.pausedAfterRelease && n.push("compiled animation reported playState=paused after its release (the flight was posed and then stopped mid-motion, not merely starved of frames)"), d.longestStallMs >= 48 && n.push(`motion stalled ${d.longestStallMs}ms mid-flight (${d.stalledFrames}/${d.sampledFrames} released frames advanced neither the animation clock nor the pose — freeze, or freeze-then-leap if the flight still landed on time)`), d.sampledFrames === 0 && e.releasedAtMs !== null && n.push("no released frames were sampled (the flight ended at or before its own hold release — the visible motion, if any, was never observed)"), f.completedUnheld > 0 && n.push(`${f.completedUnheld} image(s) finished loading mid-flight without a hold (${f.completedDuringFlight} completed, ${f.heldDuringFlight} held; ${f.loadingAtStart} were loading at t0, ${f.addedDuringFlight} arrived mid-flight) — each decode rasters on the moving layer and costs a present; this is the warm-side image-hold regression`), l.orphanedHolds.length > 0 && n.push(`hold markers left on the page at rest: ${l.orphanedHolds.join("; ")} (whatever they hide has no owner left to reveal it — the permanently-blank-avatar class)`), l.residualInlineTransforms.length > 0 && n.push(`residual inline style after COMPLETED: ${l.residualInlineTransforms.join("; ")} (landing cleanup failure — the landed scope belongs to the compiled rest rules)`), l.offViewportAtRest && n.push("screen resting at from-pose while COMPLETED+active (blank-viewport signature — the flemo PR #259 class: a residual pose left the landed screen parked off-viewport)"), l.stuckStatuses.length > 0 && n.push(`transitional status stuck >${t / 1e3}s: ${l.stuckStatuses.join(", ")} (navigation queue lock or missed animationend — later navigations will be swallowed)`), i === "unknown" && n.push("driver could not be classified (no running flemo-* CSSAnimation and no player inline-style signature observed — zero-duration flight, or the sampler attached after motion ended)"), n;
12
- }, o = (e) => {
8
+ for (let e of o) e.startMs <= l + 120 && e.startMs + e.durationMs >= l - 50 ? n.push(`long task ${Math.round(e.durationMs)}ms overlapped the visible-motion start (opening-swallow risk: the first presented frames of the transition may have been lost)`) : e.durationMs >= 100 && n.push(`long task ${Math.round(e.durationMs)}ms mid-flight`);
9
+ for (let e of s) e.durationMs >= 100 && n.push(`long task ${Math.round(e.durationMs)}ms absorbed by the hold (screen posed, not yet moving — the hold doing its job, not user-visible jank)`);
10
+ let { motion: u, images: d } = e;
11
+ u.holdReassertedAtMs !== null && n.push(`hold re-asserted ${u.holdReassertedAtMs}ms into the flight, after it had already released (an interleaved commit wrote the stale paused hold attribute over a running flight — the flemo 2026-08-18 release-race signature; the motion pauses while every timing metric stays clean)`), u.pausedAfterRelease && n.push("compiled animation reported playState=paused after its release (the flight was posed and then stopped mid-motion, not merely starved of frames)"), u.longestStallMs >= 48 && n.push(`motion stalled ${u.longestStallMs}ms mid-flight (${u.stalledFrames}/${u.sampledFrames} released frames advanced neither the animation clock nor the pose — freeze, or freeze-then-leap if the flight still landed on time)`), u.sampledFrames === 0 && e.releasedAtMs !== null && n.push("no released frames were sampled (the flight ended at or before its own hold release — the visible motion, if any, was never observed)"), d.completedUnheld > 0 && n.push(`${d.completedUnheld} image(s) finished loading mid-flight without a hold (${d.completedDuringFlight} completed, ${d.heldDuringFlight} held; ${d.loadingAtStart} were loading at t0, ${d.addedDuringFlight} arrived mid-flight) — each decode rasters on the moving layer and costs a present; this is the warm-side image-hold regression`), c.orphanedHolds.length > 0 && n.push(`hold markers left on the page at rest: ${c.orphanedHolds.join("; ")} (whatever they hide has no owner left to reveal it — the permanently-blank-avatar class)`), c.residualInlineTransforms.length > 0 && n.push(`residual inline style after COMPLETED: ${c.residualInlineTransforms.join("; ")} (landing cleanup failure — the landed scope belongs to the compiled rest rules)`), c.offViewportAtRest && n.push("screen resting at from-pose while COMPLETED+active (blank-viewport signature — the flemo PR #259 class: a residual pose left the landed screen parked off-viewport)"), c.stuckStatuses.length > 0 && n.push(`transitional status stuck >${t / 1e3}s: ${c.stuckStatuses.join(", ")} (navigation queue lock or missed animationend — later navigations will be swallowed)`);
12
+ let { morphs: f, tripwires: p } = e;
13
+ for (let e of p) n.push(`tripwire ${e.kind} at +${e.atMs}ms: ${e.detail}`);
14
+ return f.skipped.length > 0 && n.push(`shared element(s) did not fly: ${f.skipped.join(", ")} (${f.pairable.length} pairable key(s), ${f.flew.length} flew) — both ends were registered on two different screens and neither was stamped with a flight role, so the pair was never made`), f.duplicatedKeys.length > 0 && n.push(`pairing key(s) used twice inside one screen: ${f.duplicatedKeys.join(", ")} (two ends under one screen are not a pair, so one of them can never fly — this is in the consuming app)`), f.strandedRoles > 0 && n.push(`${f.strandedRoles} morph element(s) still carry a flight role at rest (the stranded participant class: a role that outlives its flight stays in the layer and poisons the NEXT pairing, which is how one interrupted gesture turned into every later pop losing its camera)`), (f.strandedStandIns > 0 || f.strandedGhosts > 0 || f.layerResidue > 0) && n.push(`morph residue at rest: ${f.strandedStandIns} stand-in(s), ${f.strandedGhosts} ghost(s), ${f.layerResidue} element(s) left in a flight layer (a stand-in is a hole in the layout and a layer element is a corpse the next flight will pair against)`), f.leakedSheetRules > 0 && n.push(`${f.leakedSheetRules} morph keyframe rule(s) were left in the per-flight sheet (a flight that never landed keeps its keyframes; they accumulate for the life of the page)`), i === "unknown" && n.push("driver could not be classified (no running flemo-* CSSAnimation and no player inline-style signature observed — zero-duration flight, or the sampler attached after motion ended)"), n;
15
+ }, s = (e) => {
13
16
  let n = [];
14
17
  if (e.emulationSuspected) {
15
18
  let t = /Win/i.test(e.platform) ? " (Windows touch hardware makes this signal ambiguous — confirm the DevTools device toolbar state)" : "";
16
19
  n.push("DevTools device emulation suspected — the page composites to a rescaled surface, so visual reports from this session are untrustworthy; judge motion in a plain window or on a real device" + t);
17
20
  }
18
21
  return e.stuckFlightOpen && n.push(`a flight is still transitional after ${t / 1e3}s — the navigation queue is likely locked; subsequent navigations will be ignored`), e.flightAnomalies.some((e) => e.some((e) => e.includes("blank-viewport"))) && n.push("at least one flight landed with the blank-viewport signature (see that flight's anomalies)"), n;
19
- }, s = [
22
+ }, c = [
20
23
  "macOS Chrome present-pipeline pacing: on 120Hz ProMotion displays Chrome can present frames unevenly even when rAF cadence and this report read perfectly clean (Chromium issues 40062488 / 345275139; proven with a no-script pure-CSS control page that still juddered). Not diagnosable or fixable from page code.",
21
24
  "Display-hardware effects: local dimming, backlight modulation, and panel overdrive alter perceived motion and are invisible to every web API.",
22
25
  "Compositor-internal present skips: a frame can be produced on time (clean rAF, clean player gaps) yet dropped or delayed inside the compositor's present queue; only platform tools (about:tracing, Instruments) see that layer.",
23
26
  "DevTools device emulation composites the page to a rescaled surface: every in-page instrument reads the pre-scale surface and reports clean while the eye watches the post-scale one, so emulated sessions grow shimmer/flash artifacts that do not exist in a plain window or on a real device."
24
- ], c = "data-flemo-screen", l = "data-flemo-status", u = "data-flemo-active", d = "data-flemo-anim-hold", f = "data-flemo-img-hold", p = "data-flemo-held-arrival", ee = "data-flemo-part-name", m = "data-flemo-decorator", h = "data-flemo-bar", g = "data-flemo-devtools-panel", _ = [
27
+ ], l = (e) => {
28
+ /* v8 ignore next -- unreachable: a group exists only because a flight was
29
+ put in it, so nothing ever asks for the median of nothing. */
30
+ if (e.length === 0) return 0;
31
+ let t = [...e].sort((e, t) => e - t);
32
+ return Math.round(t[Math.floor(t.length / 2)] * 10) / 10;
33
+ }, u = (e) => {
34
+ let t = e.filter((e) => typeof e.bucket == "string");
35
+ if (t.length === 0) return [];
36
+ let n = /* @__PURE__ */ new Map();
37
+ for (let e of t) {
38
+ let t = e.bucket, r = n.get(t);
39
+ r ? r.push(e) : n.set(t, [e]);
40
+ }
41
+ return [...n.entries()].map(([e, t]) => ({
42
+ bucket: e,
43
+ flights: t.length,
44
+ medianDurationMs: l(t.map((e) => e.durationMs)),
45
+ medianReleasedGapMs: l(t.map((e) => e.frameSamples.released.medianGapMs)),
46
+ worstReleasedGapMs: t.reduce((e, t) => Math.max(e, t.frameSamples.released.maxGapMs), 0),
47
+ longGapCount: t.reduce((e, t) => e + t.frameSamples.released.over30Count, 0),
48
+ anomalyCount: t.reduce((e, t) => e + t.anomalies.length, 0),
49
+ stalledFlights: t.filter((e) => e.motion.stalledFrames > 0).length
50
+ })).sort((e, t) => e.bucket.localeCompare(t.bucket));
51
+ }, d = "data-flemo-screen", f = "data-flemo-status", p = "data-flemo-active", m = "data-flemo-anim-hold", h = "data-flemo-img-hold", g = "data-flemo-held-arrival", _ = "data-flemo-part-name", v = "data-flemo-decorator", y = "data-flemo-bar", b = "data-flemo-morph", x = "data-flemo-morph-id", S = "data-flemo-morph-layer", C = "data-flemo-morph-stand-in", w = "data-flemo-morph-ghost", T = "data-flemo-morph-camera", E = ["enter", "exit"], D = "data-flemo-devtools-panel", O = [
25
52
  "PUSHING",
26
53
  "POPPING",
27
54
  "REPLACING"
28
- ], v = [
55
+ ], k = [
29
56
  "true",
30
57
  "park",
31
58
  "park-under",
32
59
  "park-over"
33
- ], y = (e) => `[${e}]`, te = (e, t) => `[${e}="${t}"]`, b = () => {
60
+ ], A = (e) => `[${e}]`, j = (e, t) => `[${e}="${t}"]`, M = () => {
34
61
  if (typeof navigator > "u") return null;
35
62
  let e = navigator.userAgentData;
36
63
  return !e || !Array.isArray(e.brands) ? null : e.brands.map((e) => ({
37
64
  brand: String(e.brand ?? ""),
38
65
  version: String(e.version ?? "")
39
66
  }));
40
- }, x = () => {
67
+ }, N = () => {
41
68
  if (typeof navigator > "u") return "unknown";
42
- let e = b();
69
+ let e = M();
43
70
  if (e?.some((e) => /Chromium/i.test(e.brand))) return "blink";
44
71
  let t = navigator.userAgent ?? "";
45
72
  return /Firefox|FxiOS/i.test(t) ? "gecko" : e || /AppleWebKit/i.test(t) && !/Chrome|Chromium|Android/i.test(t) ? "webkit" : /Android/i.test(t) ? "blink" : "unknown";
46
- }, S = () => {
47
- if (typeof navigator > "u" || x() !== "blink") return !1;
73
+ }, P = () => {
74
+ if (typeof navigator > "u" || N() !== "blink") return !1;
48
75
  let e = navigator.platform ?? "";
49
76
  return /Mac|Win|Linux/i.test(e) ? /Android/i.test(navigator.userAgent ?? "") && !/Mac|Win/i.test(e) ? /Linux (x86_64|i686)/i.test(e) : (navigator.maxTouchPoints ?? 0) > 0 : !1;
50
- }, ne = (e = 20) => new Promise((t) => {
77
+ }, ee = (e = 20) => new Promise((t) => {
51
78
  let n = Number.isFinite(e) ? Math.max(1, Math.floor(e)) : 20;
52
79
  if (typeof requestAnimationFrame != "function" || typeof performance > "u") {
53
80
  t({
@@ -69,50 +96,348 @@ var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = (e) => {
69
96
  requestAnimationFrame(a);
70
97
  };
71
98
  requestAnimationFrame(a);
72
- }), C = () => {
99
+ }), te = [
100
+ "__vite_plugin_react_preamble_installed__",
101
+ "__vite__",
102
+ "__NEXT_HMR_CB",
103
+ "__NEXT_HMR_LATENCY_CB",
104
+ "__webpack_hot_middleware_reporter__",
105
+ "webpackHotUpdate",
106
+ "__REACT_REFRESH_RUNTIME__",
107
+ "$RefreshReg$"
108
+ ], F = () => {
109
+ if (typeof window > "u") return [];
110
+ let e = [];
111
+ for (let t of te) try {
112
+ window[t] !== void 0 && e.push(t);
113
+ } catch {}
114
+ return e;
115
+ }, I = () => {
73
116
  try {
74
117
  return typeof PerformanceObserver < "u" && Array.isArray(PerformanceObserver.supportedEntryTypes) && PerformanceObserver.supportedEntryTypes.includes("longtask");
75
118
  } catch {
76
119
  return !1;
77
120
  }
78
- }, w = (e) => {
79
- let t = typeof navigator < "u" ? navigator : null, n = typeof window < "u" ? window : null, r = !1;
121
+ }, ne = (e, t = !1) => {
122
+ let n = typeof navigator < "u" ? navigator : null, r = typeof window < "u" ? window : null, i = !1;
80
123
  try {
81
- r = n?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? !1;
124
+ i = r?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? !1;
82
125
  } catch {
83
- r = !1;
126
+ i = !1;
84
127
  }
85
128
  return {
86
- userAgent: t?.userAgent ?? "",
87
- uaBrands: b(),
88
- engine: x(),
89
- platform: t?.platform ?? "",
90
- maxTouchPoints: t?.maxTouchPoints ?? 0,
91
- devicePixelRatio: n?.devicePixelRatio ?? 1,
129
+ userAgent: n?.userAgent ?? "",
130
+ uaBrands: M(),
131
+ engine: N(),
132
+ platform: n?.platform ?? "",
133
+ maxTouchPoints: n?.maxTouchPoints ?? 0,
134
+ devicePixelRatio: r?.devicePixelRatio ?? 1,
135
+ hardwareConcurrency: n?.hardwareConcurrency ?? 0,
92
136
  screen: {
93
- width: n?.screen?.width ?? 0,
94
- height: n?.screen?.height ?? 0
137
+ width: r?.screen?.width ?? 0,
138
+ height: r?.screen?.height ?? 0
95
139
  },
96
140
  viewport: {
97
- width: n?.innerWidth ?? 0,
98
- height: n?.innerHeight ?? 0
141
+ width: r?.innerWidth ?? 0,
142
+ height: r?.innerHeight ?? 0
99
143
  },
100
- visualViewportScale: n?.visualViewport?.scale ?? null,
144
+ visualViewportScale: r?.visualViewport?.scale ?? null,
101
145
  rafCadence: e,
102
- reducedMotion: r,
103
- emulationSuspected: S(),
146
+ reducedMotion: i,
147
+ developmentHints: F(),
148
+ emulationSuspected: P(),
104
149
  observation: {
105
- longTasks: C(),
150
+ longTasks: I(),
106
151
  elementAnimations: typeof Element < "u" && typeof Element.prototype.getAnimations == "function",
107
- playerGapMirror: Array.isArray(n?.__flemoPlayerGaps)
152
+ animationEvents: t
108
153
  }
109
154
  };
110
- }, T = [
155
+ }, re = 2, L = new Set(k), ie = (e) => Math.round(e * 10) / 10, ae = () => ({
156
+ heldGaps: [],
157
+ releasedGaps: [],
158
+ lastFrameAt: null,
159
+ evidence: {
160
+ compiledAnimation: !1,
161
+ inlineSuppression: !1,
162
+ inlineAdvance: !1
163
+ },
164
+ lastPose: /* @__PURE__ */ new Map(),
165
+ clocks: /* @__PURE__ */ new Map(),
166
+ lastClock: /* @__PURE__ */ new Map(),
167
+ releasedFrames: 0,
168
+ stalledFrames: 0,
169
+ tailFrames: 0,
170
+ stallRunMs: 0,
171
+ longestStallMs: 0,
172
+ pausedAfterRelease: !1,
173
+ holdReassertedAtMs: null
174
+ }), oe = (e) => e.some((e) => {
175
+ let t = e.getAttribute(m);
176
+ return t !== null && L.has(t);
177
+ }), se = (e) => `${e.transform}/${e.opacity}`, ce = (e, t, n) => {
178
+ e.releasedFrames += 1;
179
+ let r = !1, i = !1;
180
+ for (let n of t) {
181
+ let t = e.clocks.get(n);
182
+ if (t) {
183
+ t.playState === "paused" && (e.pausedAfterRelease = !0), t.playState !== "finished" && (i = !0);
184
+ let a = typeof t.currentTime == "number" ? t.currentTime : null;
185
+ if (a !== null) {
186
+ let t = e.lastClock.get(n);
187
+ t !== void 0 && a !== t && (r = !0), e.lastClock.set(n, a);
188
+ }
189
+ }
190
+ let a = n.style;
191
+ a && a.animation !== "" && e.lastPose.get(n) !== se(a) && (r = !0);
192
+ }
193
+ if (!(e.releasedFrames < re)) {
194
+ if (r) {
195
+ e.stallRunMs = 0;
196
+ return;
197
+ }
198
+ if (!i && e.clocks.size > 0) {
199
+ e.tailFrames += 1, e.stallRunMs = 0;
200
+ return;
201
+ }
202
+ e.stalledFrames += 1, e.stallRunMs += n, e.stallRunMs > e.longestStallMs && (e.longestStallMs = e.stallRunMs);
203
+ }
204
+ }, le = (e, t) => {
205
+ let n = t.style;
206
+ if (!n || n.animation === "") return;
207
+ e.evidence.inlineSuppression = !0;
208
+ let r = se(n), i = e.lastPose.get(t);
209
+ i !== void 0 && i !== r && (e.evidence.inlineAdvance = !0), e.lastPose.set(t, r);
210
+ }, ue = (e, t) => {
211
+ for (let n of t) {
212
+ let t = n.getAnimations, r = e.clocks.get(n);
213
+ if (r !== void 0 && r.playState !== "finished") {
214
+ r.playState === "running" && (e.evidence.compiledAnimation = !0), le(e, n);
215
+ continue;
216
+ }
217
+ if (typeof t == "function") try {
218
+ for (let r of t.call(n)) {
219
+ let t = r.animationName;
220
+ typeof t == "string" && t.startsWith("flemo-") && (e.clocks.set(n, r), r.playState === "running" && (e.evidence.compiledAnimation = !0));
221
+ }
222
+ } catch {}
223
+ le(e, n);
224
+ }
225
+ }, de = (e) => ({
226
+ sampledFrames: e.releasedFrames,
227
+ stalledFrames: e.stalledFrames,
228
+ longestStallMs: ie(e.longestStallMs),
229
+ pausedAfterRelease: e.pausedAfterRelease,
230
+ holdReassertedAtMs: e.holdReassertedAtMs,
231
+ tailFrames: e.tailFrames
232
+ }), R = 200, z = (e) => {
233
+ let t = [];
234
+ for (let n of e) for (let e of Array.from(n.querySelectorAll("img"))) t.push(e);
235
+ return t;
236
+ }, fe = (e) => {
237
+ let t = new Set(z(e).filter((e) => !e.complete));
238
+ return {
239
+ tracked: t,
240
+ loadingAtStart: t.size,
241
+ addedDuringFlight: 0,
242
+ held: /* @__PURE__ */ new Set()
243
+ };
244
+ }, pe = (e, t, n) => {
245
+ if (!(e.tracked.size >= R)) for (let r of Array.from(n)) {
246
+ if (!(r instanceof Element) || !t.some((e) => e === r || e.contains(r))) continue;
247
+ let n = r instanceof HTMLImageElement ? [r] : Array.from(r.querySelectorAll("img"));
248
+ for (let t of n) if (!(t.complete || e.tracked.has(t))) {
249
+ if (e.tracked.size >= R) return;
250
+ e.tracked.add(t), e.addedDuringFlight += 1;
251
+ }
252
+ }
253
+ }, me = (e, t) => {
254
+ for (let n of t) for (let t of Array.from(n.querySelectorAll(`img[${h}]`))) e.held.add(t);
255
+ for (let t of e.tracked) t.hasAttribute("data-flemo-img-hold") && e.held.add(t);
256
+ }, he = (e) => {
257
+ let t = 0, n = 0;
258
+ for (let r of e.tracked) r.complete && (t += 1, e.held.has(r) || (n += 1));
259
+ return {
260
+ loadingAtStart: e.loadingAtStart,
261
+ addedDuringFlight: e.addedDuringFlight,
262
+ completedDuringFlight: t,
263
+ heldDuringFlight: e.held.size,
264
+ completedUnheld: n
265
+ };
266
+ }, ge = [
111
267
  "DevTools must be CLOSED while judging motion. An open inspector serializes requests and repaints its own panels on the same machine; the 2026-08 campaign's entire residual 'stutter' was this, and it is invisible to every in-page metric.",
112
268
  "No screen recording or display capture while judging. A capture client forces WindowServer to composite every vsync, which SUPPRESSES the symptom — a capture that looks smooth proves nothing about the uncaptured session.",
113
269
  "Judge with real input (a finger, a mouse). Synthetic dispatch — evaluate().click() and friends — never fires pointerdown, so it bypasses the swipe/gesture machinery that a real navigation goes through.",
114
270
  "Establish the viewing configuration before believing any verdict: device emulation off, which physical display, its refresh rate and HiDPI scaling, Low Power Mode. Ask for a photo of the setup if it is not certain."
115
- ], E = "flemo:devtools-panel-height", D = [{
271
+ ], B = (e) => Math.round(e * 10) / 10, V = (e) => {
272
+ if (e.length === 0) return {
273
+ count: 0,
274
+ medianGapMs: 0,
275
+ maxGapMs: 0,
276
+ over30Count: 0
277
+ };
278
+ let t = [...e].sort((e, t) => e - t);
279
+ return {
280
+ count: e.length,
281
+ medianGapMs: B(t[Math.floor(t.length / 2)]),
282
+ maxGapMs: B(t[t.length - 1]),
283
+ over30Count: e.filter((e) => e >= 30).length
284
+ };
285
+ }, _e = (e, t = []) => {
286
+ let n = [...e, ...t];
287
+ return {
288
+ ...n.length === 0 ? {
289
+ count: 0,
290
+ medianGapMs: 0,
291
+ maxGapMs: 0
292
+ } : (() => {
293
+ let e = [...n].sort((e, t) => e - t);
294
+ return {
295
+ count: n.length,
296
+ medianGapMs: B(e[Math.floor(e.length / 2)]),
297
+ maxGapMs: B(e[e.length - 1])
298
+ };
299
+ })(),
300
+ longGaps: n.filter((e) => e >= 30).map(B),
301
+ held: V(e),
302
+ released: V(t)
303
+ };
304
+ }, ve = (e) => {
305
+ let t = e.inlineSuppression || e.inlineAdvance;
306
+ return t && e.compiledAnimation ? "mixed" : t ? "inline" : e.compiledAnimation ? "compiled" : "unknown";
307
+ }, ye = (e) => e === "PUSHING" ? "PUSH" : e === "POPPING" ? "POP" : e === "REPLACING" ? "REPLACE" : null, be = (e) => {
308
+ let t = /^matrix3d\(([^)]+)\)$/.exec(e.trim());
309
+ if (t) {
310
+ let e = t[1].split(",").map((e) => Number(e.trim()));
311
+ return e.length === 16 && Number.isFinite(e[12]) ? e[12] : null;
312
+ }
313
+ let n = /^matrix\(([^)]+)\)$/.exec(e.trim());
314
+ if (n) {
315
+ let e = n[1].split(",").map((e) => Number(e.trim()));
316
+ return e.length === 6 && Number.isFinite(e[4]) ? e[4] : null;
317
+ }
318
+ return null;
319
+ }, H = (e) => {
320
+ if (e) return [];
321
+ let t = [];
322
+ for (let [e, n] of [[h, "image reveal hold"], [g, "arrival hold"]]) {
323
+ let r = document.querySelectorAll(`[${e}]`).length;
324
+ r > 0 && t.push(`${r} × ${n} (${e}) still marked at rest`);
325
+ }
326
+ return t;
327
+ }, xe = (e, t) => {
328
+ let n = [], r = !1, i = window.visualViewport?.width ?? window.innerWidth ?? 0;
329
+ return e.forEach((e, t) => {
330
+ if (!(e instanceof HTMLElement) || !e.isConnected) return;
331
+ let a = "", o = "";
332
+ try {
333
+ let t = getComputedStyle(e);
334
+ a = t.transform ?? "", o = t.display ?? "";
335
+ } catch {}
336
+ if (o === "none") return;
337
+ let s = e.getAttribute(p) === "true", c = `screen[${t}]${s ? " (active)" : ""}`;
338
+ if (e.style.transform !== "" && e.style.transform !== "none" && n.push(`${c} transform=${e.style.transform}`), e.style.opacity !== "" && e.style.opacity !== "1" && n.push(`${c} opacity=${e.style.opacity}`), s && e.getAttribute("data-flemo-status") === "COMPLETED" && i > 0) {
339
+ let e = be(a);
340
+ e !== null && Math.abs(e) >= i * .5 && (r = !0);
341
+ }
342
+ }), {
343
+ residualInlineTransforms: n,
344
+ offViewportAtRest: r,
345
+ orphanedHolds: H(t)
346
+ };
347
+ }, Se = E.map((e) => `[${b}="${e}"]`).join(","), Ce = A(d), we = (e) => Math.round(e * 10) / 10, Te = (e) => {
348
+ let t = e.getAttribute(x);
349
+ return t === null || t === "" ? null : t;
350
+ }, Ee = (e) => {
351
+ let t = e.getAttribute(b);
352
+ return t !== null && E.includes(t);
353
+ }, De = () => {
354
+ try {
355
+ return document.querySelector("style[data-flemo-morph-sheet]")?.sheet?.cssRules.length ?? 0;
356
+ } catch {
357
+ return 0;
358
+ }
359
+ }, Oe = (e) => {
360
+ let t = {
361
+ registered: 0,
362
+ pairable: /* @__PURE__ */ new Set(),
363
+ duplicated: /* @__PURE__ */ new Set(),
364
+ flew: /* @__PURE__ */ new Set(),
365
+ camera: document.querySelector(A(T)) !== null,
366
+ ghosts: 0,
367
+ ghostBornAt: /* @__PURE__ */ new Map(),
368
+ shortestGhostLifeMs: null,
369
+ sheetRulesAtStart: De()
370
+ }, n = new Set(e), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
371
+ for (let e of Array.from(document.querySelectorAll(A(b)))) {
372
+ t.registered += 1;
373
+ let a = Te(e);
374
+ if (a === null) continue;
375
+ if (Ee(e)) {
376
+ t.flew.add(a);
377
+ continue;
378
+ }
379
+ let o = e.closest(Ce);
380
+ if (o !== null && !n.has(o)) continue;
381
+ let s = o, c = r.get(a);
382
+ c || r.set(a, c = /* @__PURE__ */ new Set()), c.add(s);
383
+ let l = i.get(a);
384
+ l || i.set(a, l = /* @__PURE__ */ new Map()), l.set(s, (l.get(s) ?? 0) + 1);
385
+ }
386
+ for (let [e, n] of r) {
387
+ n.size >= 2 && t.pairable.add(e);
388
+ let r = i.get(e);
389
+ r && [...r.values()].some((e) => e > 1) && t.duplicated.add(e);
390
+ }
391
+ return t;
392
+ }, ke = (e, t) => {
393
+ if (t.hasAttribute("data-flemo-morph-camera") && (e.camera = !0), !Ee(t)) return;
394
+ let n = Te(t);
395
+ n !== null && e.flew.add(n);
396
+ }, Ae = (e, t, n) => {
397
+ for (let r of Array.from(t.addedNodes)) if (r instanceof Element) for (let t of je(r)) e.ghostBornAt.has(t) || (e.ghosts += 1, e.ghostBornAt.set(t, n));
398
+ for (let r of Array.from(t.removedNodes)) if (r instanceof Element) for (let t of je(r)) {
399
+ let r = e.ghostBornAt.get(t);
400
+ if (r === void 0) continue;
401
+ e.ghostBornAt.delete(t);
402
+ let i = n - r;
403
+ (e.shortestGhostLifeMs === null || i < e.shortestGhostLifeMs) && (e.shortestGhostLifeMs = i);
404
+ }
405
+ }, je = (e) => [...e.hasAttribute("data-flemo-morph-ghost") ? [e] : [], ...Array.from(e.querySelectorAll(A(w)))], Me = (e) => {
406
+ let t = e.shortestGhostLifeMs;
407
+ return t === null || t >= 34 ? [] : [{
408
+ kind: "ghost-cut",
409
+ atMs: we(t),
410
+ detail: `a morph ghost was removed ${we(t)}ms after it was created (under one frame) — the departing content it stands for was never presented; this is the first-frame blade signature`
411
+ }];
412
+ }, Ne = (e, t) => {
413
+ let n = [...e.pairable].sort(), r = [...e.flew].sort(), i = new Set(r), a = t ? {
414
+ strandedRoles: 0,
415
+ strandedStandIns: 0,
416
+ strandedGhosts: 0,
417
+ layerResidue: 0,
418
+ leaked: 0
419
+ } : {
420
+ strandedRoles: document.querySelectorAll(Se).length,
421
+ strandedStandIns: document.querySelectorAll(A(C)).length,
422
+ strandedGhosts: document.querySelectorAll(A(w)).length,
423
+ layerResidue: Array.from(document.querySelectorAll(A(S))).reduce((e, t) => e + t.childElementCount, 0),
424
+ leaked: Math.max(0, De() - e.sheetRulesAtStart)
425
+ };
426
+ return {
427
+ registered: e.registered,
428
+ pairable: n,
429
+ flew: r,
430
+ skipped: n.filter((e) => !i.has(e)),
431
+ camera: e.camera,
432
+ ghosts: e.ghosts,
433
+ strandedRoles: a.strandedRoles,
434
+ strandedStandIns: a.strandedStandIns,
435
+ strandedGhosts: a.strandedGhosts,
436
+ leakedSheetRules: a.leaked,
437
+ layerResidue: a.layerResidue,
438
+ duplicatedKeys: [...e.duplicated].sort()
439
+ };
440
+ }, U = "flemo:devtools-panel-height", Pe = [{
116
441
  key: "flemo:devtools",
117
442
  storage: "session",
118
443
  kind: "opt-in-diagnostic",
@@ -120,118 +445,88 @@ var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = (e) => {
120
445
  fallback: "off",
121
446
  effect: "arms this flight recorder in the playground (?devtools=on)"
122
447
  }, {
123
- key: E,
448
+ key: U,
124
449
  storage: "session",
125
450
  kind: "production-state",
126
451
  values: "a pixel height",
127
452
  fallback: "(the panel's default height)",
128
453
  effect: "the drawer height this panel was last dragged to"
129
- }], O = [
454
+ }], Fe = [], W = [...Fe, ...Pe], G = [
130
455
  {
131
456
  key: "flemo:sixty",
132
457
  storage: "session",
133
- kind: "production-state",
134
- values: "\"high\" / a streak count",
135
- fallback: "(learned)",
136
- effect: "steady-60 desktop verdict seed — owned by steadySixtyCadence.ts"
458
+ retiredWith: "the steady-60 verdict seed, now document-scoped module state (2026-08-31)"
137
459
  },
138
460
  {
139
461
  key: "flemo:settle-gate",
140
462
  storage: "session",
141
- kind: "production-default-with-override",
142
- values: "\"on\" / \"off\"",
143
- fallback: "touch WebKit + touch Blink + desktop macOS WebKit + steady-60 desktop",
144
- effect: "render-settle entry gate, shared by the engine's routing and the binding"
463
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
145
464
  },
146
465
  {
147
466
  key: "flemo:arrivalhold",
148
467
  storage: "session",
149
- kind: "production-default-with-override",
150
- values: "\"off\"",
151
- fallback: "on",
152
- effect: "the whole in-flight hold set (arrival, response, invisible animations, images)"
468
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
153
469
  },
154
470
  {
155
471
  key: "flemo:deskflip",
156
472
  storage: "session",
157
- kind: "production-default-with-override",
158
- values: "\"on\" / \"off\"",
159
- fallback: "desktop macOS WebKit",
160
- effect: "atomic release flip: the hold attribute flips on the DOM inside the readiness rAF"
473
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
161
474
  },
162
475
  {
163
476
  key: "flemo:deskhead",
164
477
  storage: "session",
165
- kind: "production-default-with-override",
166
- values: "\"on\" / \"off\"",
167
- fallback: "desktop macOS WebKit",
168
- effect: "desktop flat-head keyframes; arming it retires the desktop birth anchor"
478
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
169
479
  },
170
480
  {
171
481
  key: "flemo:creep",
172
482
  storage: "session",
173
- kind: "production-default-with-override",
174
- values: "\"on\" / \"off\"",
175
- fallback: "touch WebKit",
176
- effect: "creep head: its end keyframe carries a hair of motion, so the compositor is already carrying the animation at the boundary"
483
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
177
484
  },
178
485
  {
179
486
  key: "flemo:relcommit",
180
487
  storage: "session",
181
- kind: "production-default-with-override",
182
- values: "\"defer\" / \"sync\"",
183
- fallback: "touch WebKit",
184
- effect: "the release's reconcile lands next frame instead of synchronously"
488
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
185
489
  },
186
490
  {
187
491
  key: "flemo:imgoffload",
188
492
  storage: "session",
189
- kind: "production-default-with-override",
190
- values: "\"on\" / \"off\"",
191
- fallback: "on — the offloader decides per image, from the source's own size",
192
- effect: "image decode offloader: an oversized source is fetched and downscaled in a worker so its decode never lands in a paint"
493
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
193
494
  },
194
495
  {
195
496
  key: "flemo:governed",
196
497
  storage: "session",
197
- kind: "production-default-with-override",
198
- values: "\"on\" / \"off\"",
199
- fallback: "legacy Android Blink (an old browser, not a slow device)",
200
- effect: "the governed head kit on touch Blink — a flat head covering a compiled flight's opening. The auto-detection reads browser AGE, so a modern-but-weak phone falls through it; this is how such a device gets measured"
498
+ retiredWith: "the engine diagnostic surface (2026-08-31)"
201
499
  },
202
500
  {
203
501
  key: "flemo:imghold",
204
502
  storage: "session",
205
- kind: "opt-in-diagnostic",
206
- values: "\"on\" / \"off\"",
207
- fallback: "off",
208
- effect: "flight-scoped <img> reveal hold"
503
+ retiredWith: "the image reveal hold, whose only arming path it was (2026-08-31)"
209
504
  },
210
505
  {
211
506
  key: "flemo:preraster",
212
507
  storage: "session",
213
- kind: "opt-in-diagnostic",
214
- values: "\"on\"",
215
- fallback: "off",
216
- effect: "REST-time scope promotion, and the park-over hold variant. Flight-time promotion is the engine's own stamp and needs no flag"
508
+ retiredWith: "the REST-time scope promotion, whose only arming path it was (2026-08-31)"
509
+ },
510
+ {
511
+ key: "flemo:parkhead",
512
+ storage: "session",
513
+ retiredWith: "the engine diagnostic surface — the park head is unconditional (2026-08-31)"
217
514
  },
218
515
  {
219
516
  key: "flemo:layers",
220
517
  storage: "session",
221
- kind: "opt-in-diagnostic",
222
- values: "\"resident\" / \"off\"",
223
- fallback: "off",
224
- effect: "keep screen layers resident at rest — a resident layer is a PERMANENT stacking context over the consumer's screen"
518
+ retiredWith: "the resident-layer experiment (2026-08-31)"
519
+ },
520
+ {
521
+ key: "flemo:morph",
522
+ storage: "session",
523
+ retiredWith: "the morph decision trace (2026-08-31)"
225
524
  },
226
525
  {
227
526
  key: "flemo:freeze",
228
527
  storage: "session",
229
- kind: "opt-in-diagnostic",
230
- values: "\"shallow\"",
231
- fallback: "off",
232
- effect: "keep the direct prev screen live instead of freezing it"
233
- }
234
- ], k = [...O, ...D], A = [
528
+ retiredWith: "the shallow-freeze experiment (2026-08-31)"
529
+ },
235
530
  {
236
531
  key: "flemo:motion-driver",
237
532
  storage: "local",
@@ -277,407 +572,535 @@ var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = (e) => {
277
572
  storage: "session",
278
573
  retiredWith: "the Low Power Mode release-latency probe (2026-08-22)"
279
574
  }
280
- ], j = "(retired — the library no longer reads this)", M = (e) => {
575
+ ], Ie = "(retired — the library no longer reads this)", Le = (e) => {
281
576
  try {
282
577
  let t = e === "session" ? sessionStorage : localStorage;
283
578
  return t.length, t;
284
579
  } catch {
285
580
  return null;
286
581
  }
287
- }, N = (e, t) => {
582
+ }, Re = (e, t) => {
288
583
  try {
289
584
  return e ? e.getItem(t) : null;
290
585
  } catch {
291
586
  return null;
292
587
  }
293
- }, re = new Set(k.map((e) => e.key)), ie = new Set(A.map((e) => e.key)), P = new Set(D.map((e) => e.key)), F = () => {
294
- let e = {}, t = M("session"), n = M("local");
295
- for (let r of k) {
296
- let i = N(r.storage === "session" ? t : n, r.key);
588
+ }, ze = new Set(W.map((e) => e.key)), Be = new Set(G.map((e) => e.key)), Ve = () => {
589
+ let e = {}, t = Le("session"), n = Le("local");
590
+ for (let r of W) {
591
+ let i = Re(r.storage === "session" ? t : n, r.key);
297
592
  i !== null && (e[r.key] = i);
298
593
  }
299
- for (let r of A) for (let [i, a] of [[t, "sessionStorage"], [n, "localStorage"]]) {
300
- let t = N(i, r.key);
301
- t !== null && (e[`${r.key} (${a}) ${j}`] = t);
594
+ for (let r of G) for (let [i, a] of [[t, "sessionStorage"], [n, "localStorage"]]) {
595
+ let t = Re(i, r.key);
596
+ t !== null && (e[`${r.key} (${a}) ${Ie}`] = t);
302
597
  }
303
598
  for (let [r, i] of [[t, "sessionStorage"], [n, "localStorage"]]) if (r) try {
304
599
  for (let t = 0; t < r.length; t += 1) {
305
600
  let n = r.key(t);
306
- !n || !n.startsWith("flemo:") || re.has(n) || ie.has(n) || (e[`${n} (${i}, unknown key)`] = r.getItem(n) ?? "");
601
+ !n || !n.startsWith("flemo:") || ze.has(n) || Be.has(n) || (e[`${n} (${i}, unknown key)`] = r.getItem(n) ?? "");
307
602
  }
308
603
  } catch {}
309
604
  return e;
310
- }, ae = (e) => {
605
+ }, He = (e) => {
311
606
  let t = [];
312
- for (let [n, r] of Object.entries(e)) {
313
- if (n.includes("(retired the library no longer reads this)")) {
314
- let e = n.slice(0, n.indexOf(" (")), i = A.find((t) => t.key === e);
315
- t.push(`${n}=${r} — RETIRED residue: this key went with ${i?.retiredWith ?? "a removed feature"}. The library never reads it, so it cannot explain anything you are seeing. Safe to delete; it is listed only so it does not read as an unexplained lead.`);
316
- continue;
317
- }
318
- let e = k.find((e) => n === e.key || n.startsWith(`${e.key} `));
319
- !e || P.has(e.key) || (e.kind === "opt-in-diagnostic" ? t.push(`${n}=${r} — opt-in diagnostic active (${e.effect}); behavior differs from stock. Possible left-over A/B toggle from an earlier debugging session.`) : e.kind === "production-default-with-override" && t.push(`${n}=${r} — production default overridden for this session (${e.effect}). Stock behavior here is ${e.fallback}.`));
607
+ for (let [n, r] of Object.entries(e)) if (n.includes("(retired — the library no longer reads this)")) {
608
+ let e = n.slice(0, n.indexOf(" (")), i = G.find((t) => t.key === e);
609
+ t.push(`${n}=${r} RETIRED residue: this key went with ${i?.retiredWith ?? "a removed feature"}. The library never reads it, so it cannot explain anything you are seeing. Safe to delete; it is listed only so it does not read as an unexplained lead.`);
610
+ continue;
320
611
  }
321
612
  return t;
322
- }, I = (e) => Math.round(e * 10) / 10, L = (e) => {
323
- if (e.length === 0) return {
324
- count: 0,
325
- medianGapMs: 0,
326
- maxGapMs: 0,
327
- over30Count: 0
613
+ }, Ue = "flemo:devtools-trace", We = () => {
614
+ try {
615
+ let e = sessionStorage;
616
+ return e.length, e;
617
+ } catch {
618
+ return null;
619
+ }
620
+ }, Ge = (e, t) => {
621
+ let n = We();
622
+ if (!n) return;
623
+ let r = e.slice(-10);
624
+ for (; r.length > 0;) {
625
+ let e = JSON.stringify({
626
+ version: t,
627
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
628
+ flights: r
629
+ });
630
+ if (e.length <= 64e3) {
631
+ try {
632
+ n.setItem(Ue, e);
633
+ } catch {}
634
+ return;
635
+ }
636
+ r = r.slice(1);
637
+ }
638
+ Ke();
639
+ }, Ke = () => {
640
+ try {
641
+ We()?.removeItem(Ue);
642
+ } catch {}
643
+ }, qe = (e) => {
644
+ let t = We();
645
+ if (!t) return null;
646
+ let n = null;
647
+ try {
648
+ n = t.getItem(Ue);
649
+ } catch {
650
+ return null;
651
+ }
652
+ if (n === null) return null;
653
+ let r = null;
654
+ try {
655
+ r = JSON.parse(n);
656
+ } catch {
657
+ return Ke(), null;
658
+ }
659
+ return !r || r.version !== e || !Array.isArray(r.flights) ? (Ke(), null) : {
660
+ savedAt: typeof r.savedAt == "string" ? r.savedAt : "",
661
+ flights: r.flights,
662
+ note: "These flights were recorded by the page instance BEFORE the last full load. They are kept apart from the live ones because they may have come from a different build."
328
663
  };
329
- let t = [...e].sort((e, t) => e - t);
330
- return {
331
- count: e.length,
332
- medianGapMs: I(t[Math.floor(t.length / 2)]),
333
- maxGapMs: I(t[t.length - 1]),
334
- over30Count: e.filter((e) => e >= 30).length
664
+ }, Je = 12, Ye = 20, Xe = 40, Ze = 500, Qe = 200, $e = (e) => {
665
+ let t = e.rafCadence.medianGapMs;
666
+ if (t === null) return {
667
+ id: "display-cadence",
668
+ status: "unknown",
669
+ detail: "the idle frame cadence could not be sampled, so the display's rate is unknown"
335
670
  };
336
- }, R = (e, t = []) => {
337
- let n = [...e, ...t];
338
- return {
339
- ...n.length === 0 ? {
340
- count: 0,
341
- medianGapMs: 0,
342
- maxGapMs: 0
343
- } : (() => {
344
- let e = [...n].sort((e, t) => e - t);
671
+ let n = { medianIdleFrameMs: t };
672
+ return t < Je ? {
673
+ id: "display-cadence",
674
+ status: "ok",
675
+ detail: `idle frames arrive every ${t}ms — a display above 60Hz. Frame budgets in this report are still quoted against 60Hz (a gap of 30ms or more)`,
676
+ metrics: n
677
+ } : t <= Ye ? {
678
+ id: "display-cadence",
679
+ status: "ok",
680
+ detail: `idle frames arrive every ${t}ms — a 60Hz clock`,
681
+ metrics: n
682
+ } : t <= Xe ? {
683
+ id: "display-cadence",
684
+ status: "violated",
685
+ detail: `idle frames arrive every ${t}ms — the page is being served roughly HALF rate. On iOS this is Low Power Mode, whose main-thread ceiling is ~30Hz while the compositor keeps 60: motion judged here is judged against a clock no transition can beat, and the ceiling is not a library defect. Turn it off, or judge elsewhere`,
686
+ metrics: n
687
+ } : {
688
+ id: "display-cadence",
689
+ status: "violated",
690
+ detail: `idle frames arrive every ${t}ms — the page is barely being served frames at all while idle. Something outside the transition owns this machine; no motion verdict taken here means anything`,
691
+ metrics: n
692
+ };
693
+ }, et = (e) => {
694
+ if (!e.environment.observation.longTasks) return {
695
+ id: "machine-idle",
696
+ status: "unknown",
697
+ detail: "this browser does not report long tasks, so the recorder cannot tell whether anything else was competing for the main thread"
698
+ };
699
+ let t = e.idleLongTasks.length, n = Math.round(e.idleLongTasks.reduce((e, t) => e + t.durationMs, 0)), r = Math.round(e.idleLongTasks.reduce((e, t) => Math.max(e, t.durationMs), 0)), i = {
700
+ idleLongTasks: t,
701
+ idleLongTaskMs: n,
702
+ worstIdleTaskMs: r
703
+ };
704
+ return n >= Ze || r >= Qe ? {
705
+ id: "machine-idle",
706
+ status: "violated",
707
+ detail: `${t} long task(s) totalling ${n}ms ran while NO navigation was in flight (worst ${r}ms). Something else on this page or this machine was busy, and a motion verdict taken under someone else's load measures the load. A whole tremble campaign was invalidated by exactly this, with a build running in another window`,
708
+ metrics: i
709
+ } : {
710
+ id: "machine-idle",
711
+ status: "ok",
712
+ detail: `${t} idle long task(s), ${n}ms total — the page was quiet between navigations`,
713
+ metrics: i
714
+ };
715
+ }, tt = (e) => {
716
+ let { trusted: t, synthetic: n, pointerTypes: r } = e.input;
717
+ return n > 0 ? {
718
+ id: "real-input",
719
+ status: "violated",
720
+ detail: `${n} script-dispatched (untrusted) input event(s) drove this session. Synthetic dispatch never fires the gesture machinery a real navigation goes through, so a clean run proves nothing about a finger`,
721
+ metrics: {
722
+ trusted: t,
723
+ synthetic: n
724
+ }
725
+ } : t === 0 ? {
726
+ id: "real-input",
727
+ status: "unknown",
728
+ detail: "no input event preceded any recorded flight. These navigations were driven programmatically or by the browser's own back/forward, which is a different path from the one a user takes",
729
+ metrics: {
730
+ trusted: t,
731
+ synthetic: n
732
+ }
733
+ } : {
734
+ id: "real-input",
735
+ status: "ok",
736
+ detail: `${t} trusted input event(s) drove this session (${r.join(", ") || "type unreported"})`,
737
+ metrics: {
738
+ trusted: t,
739
+ synthetic: n
740
+ }
741
+ };
742
+ }, nt = (e) => e.input.pointerTypes.includes("touch") ? {
743
+ id: "touch-path",
744
+ status: "ok",
745
+ detail: "at least one navigation was driven by touch"
746
+ } : e.environment.maxTouchPoints === 0 ? {
747
+ id: "touch-path",
748
+ status: "unknown",
749
+ detail: "this device reports no touch points, so there is no touch path to exercise here"
750
+ } : {
751
+ id: "touch-path",
752
+ status: "unknown",
753
+ detail: "no touch-driven navigation was recorded on a touch-capable device. The swipe and pointer-capture paths are only reached by a finger; a build that had broken them outright once passed every automated layer green because every probe used a mouse"
754
+ }, rt = [
755
+ {
756
+ id: "devtools-closed",
757
+ status: "unknown",
758
+ detail: "whether an inspector is open cannot be seen from inside the page. An open one serializes requests and repaints its own panels on the same machine; a whole campaign's residual stutter was this and nothing else. Confirm it with the user"
759
+ },
760
+ {
761
+ id: "no-screen-capture",
762
+ status: "unknown",
763
+ detail: "whether the screen is being captured cannot be seen from inside the page. A capture client forces the compositor to present every vsync, which SUPPRESSES the symptom — a recording that looks smooth proves nothing about the uncaptured session"
764
+ },
765
+ {
766
+ id: "viewing-configuration",
767
+ status: "unknown",
768
+ detail: "which physical display this window is on, its refresh rate and scaling, cannot be read from the page. Establish it with the user before believing any verdict"
769
+ }
770
+ ], it = (e) => {
771
+ let { environment: t } = e, n = [];
772
+ return n.push(t.emulationSuspected ? {
773
+ id: "device-emulation",
774
+ status: "violated",
775
+ detail: "DevTools device emulation is suspected (Blink on a desktop platform reporting touch points). Emulation composites the page to a rescaled surface: every in-page instrument reads the pre-scale surface and reports clean while the eye watches the post-scale one" + (/Win/i.test(t.platform) ? ". Windows touch hardware makes this signal ambiguous — confirm the device toolbar state" : "")
776
+ } : {
777
+ id: "device-emulation",
778
+ status: "ok",
779
+ detail: "no device-emulation signature on this session"
780
+ }), n.push($e(t)), n.push(e.documentHidden || e.wentHidden ? {
781
+ id: "page-foreground",
782
+ status: "violated",
783
+ detail: e.documentHidden ? "this document is HIDDEN right now — a backgrounded page is throttled to a fraction of its frames" : "this document was hidden at least once while recording; any flight that ran then was throttled"
784
+ } : {
785
+ id: "page-foreground",
786
+ status: "ok",
787
+ detail: "the document stayed visible throughout"
788
+ }), n.push(et(e)), n.push(t.developmentHints.length > 0 ? {
789
+ id: "build-mode",
790
+ status: "violated",
791
+ detail: `development-server globals are present (${t.developmentHints.join(", ")}). A development build is a different program: unminified, double-invoking, and hot-reload instrumented. A day of ladder measurements once turned out to be measuring the build itself`
792
+ } : {
793
+ id: "build-mode",
794
+ status: "unknown",
795
+ detail: "no development-server global was found. That is consistent with a production build but does not prove one — confirm how this page was built"
796
+ }), n.push(tt(e)), n.push(nt(e)), n.push(t.reducedMotion ? {
797
+ id: "reduced-motion",
798
+ status: "violated",
799
+ detail: "this session prefers reduced motion, so the transitions under test are being suppressed or shortened. Whatever is on screen is not the motion being judged"
800
+ } : {
801
+ id: "reduced-motion",
802
+ status: "ok",
803
+ detail: "reduced motion is not requested"
804
+ }), [...n, ...rt];
805
+ }, at = 2e3, ot = 40, st = (e) => Math.round(e * 10) / 10, ct = (e) => typeof e.animationName == "string" && e.animationName.startsWith("flemo-"), lt = (e) => e instanceof Element ? `<${e.tagName.toLowerCase()}>${e.getAttribute("data-flemo-screen") === null ? "" : " (screen)"}` : "an unidentified node", ut = (e) => {
806
+ if (typeof document > "u") return {
807
+ detach: () => {},
808
+ sawAnimationEvent: () => !1,
809
+ inputBetween: () => ({
810
+ trusted: 0,
811
+ synthetic: 0,
812
+ pointerTypes: []
813
+ })
814
+ };
815
+ let t = [], n = !1, r = (t) => {
816
+ ct(t) && (n = !0, e.onAnimationStart(performance.now()));
817
+ }, i = (t) => {
818
+ ct(t) && (n = !0, e.onHit({
819
+ kind: "animation-cancel",
820
+ atMs: performance.now(),
821
+ detail: `${t.animationName} was CANCELLED on ${lt(t.target)} — the element was re-parented, re-styled or removed mid-flight. A cancelled animation loses its start time, and whatever restarts it is free to overwrite the authored delay`
822
+ }));
823
+ }, a = (t) => {
824
+ ct(t) && (n = !0, t.elapsedTime === 0 && e.onHit({
825
+ kind: "zero-length-animation-end",
826
+ atMs: performance.now(),
827
+ detail: `${t.animationName} reported animationend with elapsedTime 0 on ${lt(t.target)} — the animation ended without ever running. Anything landing on this event lands before the motion it was waiting for`
828
+ }));
829
+ }, o = (e) => {
830
+ let n = e.pointerType ?? "";
831
+ t.push({
832
+ atMs: performance.now(),
833
+ trusted: e.isTrusted === !0,
834
+ pointerType: n === "" ? e.type : n
835
+ }), t.length > ot && t.splice(0, t.length - ot);
836
+ }, s = {
837
+ capture: !0,
838
+ passive: !0
839
+ };
840
+ return document.addEventListener("animationstart", r, s), document.addEventListener("animationcancel", i, s), document.addEventListener("animationend", a, s), document.addEventListener("pointerdown", o, s), document.addEventListener("click", o, s), {
841
+ detach: () => {
842
+ document.removeEventListener("animationstart", r, s), document.removeEventListener("animationcancel", i, s), document.removeEventListener("animationend", a, s), document.removeEventListener("pointerdown", o, s), document.removeEventListener("click", o, s);
843
+ },
844
+ sawAnimationEvent: () => n,
845
+ inputBetween: (e, n) => {
846
+ let r = e - at, i = t.filter((e) => e.atMs >= r && e.atMs <= n), a = i.filter((e) => e.trusted).length, o = new Set(i.map((e) => e.pointerType));
345
847
  return {
346
- count: n.length,
347
- medianGapMs: I(e[Math.floor(e.length / 2)]),
348
- maxGapMs: I(e[e.length - 1])
848
+ trusted: a,
849
+ synthetic: i.length - a,
850
+ pointerTypes: [...o].sort()
349
851
  };
350
- })(),
351
- longGaps: n.filter((e) => e >= 30).map(I),
352
- held: L(e),
353
- released: L(t)
852
+ }
354
853
  };
355
- }, oe = (e) => e.length === 0 ? null : {
356
- maxMs: I(Math.max(...e)),
357
- over30Count: e.filter((e) => e >= 30).length
358
- }, z = (e) => {
359
- let t = e.playerSuppression || e.playerAdvance;
360
- return t && e.compiledAnimation ? "mixed" : t ? "player" : e.compiledAnimation ? "compiled" : "unknown";
361
- }, se = (e) => e === "PUSHING" ? "PUSH" : e === "POPPING" ? "POP" : e === "REPLACING" ? "REPLACE" : null, ce = (e) => {
362
- let t = /^matrix3d\(([^)]+)\)$/.exec(e.trim());
363
- if (t) {
364
- let e = t[1].split(",").map((e) => Number(e.trim()));
365
- return e.length === 16 && Number.isFinite(e[12]) ? e[12] : null;
854
+ }, dt = (e, t) => ({
855
+ kind: e.kind,
856
+ atMs: st(e.atMs - t),
857
+ detail: e.detail
858
+ }), ft = (e) => {
859
+ /* v8 ignore next -- unreachable: deriveVerdict returns before this on an
860
+ empty flight list, so nothing ever asks for the median of nothing. */
861
+ if (e.length === 0) return 0;
862
+ let t = [...e].sort((e, t) => e - t);
863
+ return t[Math.floor(t.length / 2)];
864
+ }, pt = (e) => {
865
+ let t = [], n = e.preconditions.filter((e) => e.status === "violated");
866
+ if (n.length > 0 && t.push(`NOT EVIDENCE: ${n.length} judging precondition(s) failed — ${n.map((e) => e.id).join(", ")}. Everything below was measured under those conditions; fix them and measure again before drawing a conclusion from it.`), e.flights.length === 0) return t.push("No flights were recorded. Either no navigation happened while the recorder was attached, or it attached after the one you meant to measure — the recorder only sees flights that start after it does."), t;
867
+ e.observation.animationEvents || t.push("The animation channel observed NOTHING across this whole session: not one flemo keyframe reported a start, an end or a cancel, though flights were recorded. Treat every animation-derived field here as unmeasured rather than as clean — a probe that never fires reads exactly like a page with nothing to report.");
868
+ let r = e.flights, i = r.reduce((e, t) => Math.max(e, t.frameSamples.released.maxGapMs), 0), a = r.filter((e) => e.anomalies.length > 0).length;
869
+ t.push(`${r.length} flight(s) recorded. Median duration ${ft(r.map((e) => e.durationMs))}ms, worst frame gap during visible motion ${i}ms, ${a} flight(s) carrying at least one anomaly.`);
870
+ let o = r.filter((e) => e.motion.longestStallMs >= 48);
871
+ o.length > 0 && t.push(`${o.length} flight(s) STOPPED MOVING mid-flight (longest ${Math.max(...o.map((e) => e.motion.longestStallMs))}ms). Frames kept arriving; the picture did not change. Look at the hold and the animation's play state, not at the frame budget.`);
872
+ let s = r.filter((e) => e.morphs.skipped.length > 0);
873
+ if (s.length > 0) {
874
+ let e = [...new Set(s.flatMap((e) => e.morphs.skipped))];
875
+ t.push(`Shared elements did NOT fly on ${s.length} flight(s): ${e.join(", ")}. Both ends were registered on two different screens and neither was ever stamped with a flight role, so the pair was never made. This is silent by nature — nothing else on the page says it happened.`);
366
876
  }
367
- let n = /^matrix\(([^)]+)\)$/.exec(e.trim());
368
- if (n) {
369
- let e = n[1].split(",").map((e) => Number(e.trim()));
370
- return e.length === 6 && Number.isFinite(e[4]) ? e[4] : null;
877
+ let c = [...new Set(r.flatMap((e) => e.morphs.duplicatedKeys))];
878
+ c.length > 0 && t.push(`Pairing key(s) used twice inside one screen: ${c.join(", ")}. Two ends under one screen are not a pair, so one of them can never fly. This is in the consuming app, not in the library.`);
879
+ let l = r.flatMap((e) => e.tripwires);
880
+ if (l.length > 0) {
881
+ let e = [...new Set(l.map((e) => e.kind))];
882
+ t.push(`${l.length} tripwire hit(s) across the session (${e.join(", ")}). These are one-frame events reported by the browser itself, not sampled — read the flights' \`tripwires\` arrays for the exact animations.`);
371
883
  }
372
- return null;
373
- }, le = "2", B = new Set(_), V = new Set(v), ue = y(c), H = d, de = 2, fe = 2e3, pe = 2, me = 200, he = 1e3, U = (e) => Math.round(e * 10) / 10, ge = null, _e = () => window, ve = (e = {}) => {
374
- if (ge) return ge;
884
+ return r.filter((e) => e.frameSamples.released.over30Count > 0).length === 0 && a === 0 && n.length === 0 && t.push("No flight dropped a frame during visible motion (nothing at or over 30ms) and no anomaly fired. Under the preconditions above, this session is clean — what remains is in `blindSpots`, and none of it is reachable from page code."), t;
885
+ }, mt = "3", ht = new Set(O), gt = new Set(k), _t = A(d), vt = 2e3, yt = 1e3, bt = 4e3, K = (e) => Math.round(e * 10) / 10, xt = null, St = () => ({
886
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
887
+ version: "3",
888
+ verdict: ["No DOM was available, so nothing could be observed. The recorder ran inert."],
889
+ environment: ne({
890
+ medianGapMs: null,
891
+ sampleCount: 0
892
+ }),
893
+ preconditions: [],
894
+ overrides: {
895
+ active: {},
896
+ warnings: ["no DOM available — recorder ran inert"]
897
+ },
898
+ flights: [],
899
+ comparison: [],
900
+ previousSession: null,
901
+ anomalies: [],
902
+ blindSpots: [...c],
903
+ judgingProtocol: [...ge]
904
+ }), Ct = (e = {}) => {
905
+ if (xt) return xt;
375
906
  if (typeof window > "u" || typeof document > "u") return {
376
907
  detach: () => {},
377
- report: () => ({
378
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
379
- version: "2",
380
- environment: w({
381
- medianGapMs: null,
382
- sampleCount: 0
383
- }),
384
- overrides: {
385
- active: {},
386
- warnings: ["no DOM available — recorder ran inert"]
387
- },
388
- flights: [],
389
- anomalies: [],
390
- blindSpots: [...s],
391
- judgingProtocol: [...T]
392
- })
908
+ report: St,
909
+ mark: () => null
393
910
  };
394
- let n = e.maxFlights ?? 50, r = e.log ?? !1, i = e.installGlobal ?? !0, c = F(), d = {
911
+ let n = e.maxFlights ?? 50, r = e.log ?? !1, i = e.installGlobal ?? !0, a = e.persist ?? !0, l = Ve(), d = performance.now(), h = a ? qe("3") : null, g = {
395
912
  medianGapMs: null,
396
913
  sampleCount: 0
397
914
  };
398
- ne().then((e) => {
399
- d = e;
915
+ ee().then((e) => {
916
+ g = e;
400
917
  });
401
- let g = [], _ = null;
918
+ let x = [], S = null;
402
919
  try {
403
- typeof PerformanceObserver < "u" && PerformanceObserver.supportedEntryTypes?.includes("longtask") && (_ = new PerformanceObserver((e) => {
404
- for (let t of e.getEntries()) g.push({
405
- startMs: U(t.startTime),
406
- durationMs: U(t.duration)
920
+ typeof PerformanceObserver < "u" && PerformanceObserver.supportedEntryTypes?.includes("longtask") && (S = new PerformanceObserver((e) => {
921
+ for (let t of e.getEntries()) x.push({
922
+ startMs: K(t.startTime),
923
+ durationMs: K(t.duration)
407
924
  });
408
- g.length > he && g.splice(0, g.length - he);
409
- }), _.observe({
925
+ x.length > yt && x.splice(0, x.length - yt);
926
+ }), S.observe({
410
927
  type: "longtask",
411
928
  buffered: !0
412
929
  }));
413
930
  } catch {
414
- _ = null;
931
+ S = null;
415
932
  }
416
- let v = [], te = 0, b = null, x = [], S = !1, C = !1, E = () => Array.from(document.querySelectorAll(ue)).filter((e) => B.has(e.getAttribute("data-flemo-status") ?? "")), D = (e) => {
417
- let t = Array.from(document.querySelectorAll(y(h))).filter((e) => B.has(e.getAttribute("data-flemo-bar-status") ?? "") || e.getAttribute("data-flemo-bar-riding") === "true").length, n = Array.from(document.querySelectorAll(y(m))).filter((e) => B.has(e.getAttribute("data-flemo-status") ?? "")).length, r = Array.from(document.querySelectorAll(y(ee))).filter((e) => B.has(e.getAttribute("data-flemo-status") ?? "")).length;
933
+ let C = [], w = 0, E = null, D = null, O = [], k = !1, j = !1, M = document.visibilityState === "hidden", N = 0, P = () => Array.from(document.querySelectorAll(_t)).filter((e) => ht.has(e.getAttribute("data-flemo-status") ?? "")), te = () => E !== null || P().length > 0, F = (e) => {
934
+ let t = Array.from(document.querySelectorAll(A(y))).filter((e) => ht.has(e.getAttribute("data-flemo-bar-status") ?? "") || e.getAttribute("data-flemo-bar-riding") === "true").length, n = Array.from(document.querySelectorAll(A(v))).filter((e) => ht.has(e.getAttribute("data-flemo-status") ?? "")).length, r = Array.from(document.querySelectorAll(A(_))).filter((e) => ht.has(e.getAttribute("data-flemo-status") ?? "")).length;
418
935
  return {
419
936
  screens: e.length,
420
937
  bars: t,
421
938
  decorators: n,
422
939
  parts: r
423
940
  };
424
- }, O = (e) => {
425
- let t = [];
426
- for (let n of e) for (let e of Array.from(n.querySelectorAll("img"))) t.push(e);
427
- return t;
428
- }, k = (e) => {
429
- let t = 0, n = 0;
430
- for (let r of e.imagesTracked) r.complete && (t += 1, e.imagesHeld.has(r) || (n += 1));
431
- return {
432
- loadingAtStart: e.imagesLoadingAtStart,
433
- addedDuringFlight: e.imagesAddedDuringFlight,
434
- completedDuringFlight: t,
435
- heldDuringFlight: e.imagesHeld.size,
436
- completedUnheld: n
437
- };
438
- }, A = (e) => {
439
- let t = b;
440
- if (!(!t || t.imagesTracked.size >= me)) for (let n of Array.from(e)) {
441
- if (!(n instanceof Element) || !t.elements.some((e) => e === n || e.contains(n))) continue;
442
- let e = n instanceof HTMLImageElement ? [n] : Array.from(n.querySelectorAll("img"));
443
- for (let n of e) if (!(n.complete || t.imagesTracked.has(n))) {
444
- if (t.imagesTracked.size >= me) return;
445
- t.imagesTracked.add(n), t.imagesAddedDuringFlight += 1;
446
- }
447
- }
448
- }, j = (e) => {
449
- for (let t of e.elements) for (let n of Array.from(t.querySelectorAll(`img[${f}]`))) e.imagesHeld.add(n);
450
- for (let t of e.imagesTracked) t.hasAttribute("data-flemo-img-hold") && e.imagesHeld.add(t);
451
- }, M = (e, t) => {
452
- e.releasedFrames += 1;
453
- let n = !1, r = !1;
454
- for (let t of e.elements) {
455
- let i = e.clocks.get(t);
456
- if (i) {
457
- i.playState === "paused" && (e.pausedAfterRelease = !0), i.playState !== "finished" && (r = !0);
458
- let a = typeof i.currentTime == "number" ? i.currentTime : null;
459
- if (a !== null) {
460
- let r = e.lastClock.get(t);
461
- r !== void 0 && a !== r && (n = !0), e.lastClock.set(t, a);
462
- }
463
- }
464
- let a = t.style;
465
- if (a && a.animation !== "") {
466
- let r = `${a.transform}/${a.opacity}`;
467
- e.lastPose.get(t) !== r && (n = !0);
468
- }
469
- }
470
- if (!(e.releasedFrames < pe)) {
471
- if (n) {
472
- e.stallRunMs = 0;
473
- return;
474
- }
475
- if (!r && e.clocks.size > 0) {
476
- e.tailFrames += 1, e.stallRunMs = 0;
477
- return;
478
- }
479
- e.stalledFrames += 1, e.stallRunMs += t, e.stallRunMs > e.longestStallMs && (e.longestStallMs = e.stallRunMs);
480
- }
481
- }, N = (e) => {
482
- for (let t of e.elements) {
483
- let n = t.style, r = t.getAnimations, i = e.clocks.get(t);
484
- if (i !== void 0 && i.playState !== "finished") {
485
- if (i.playState === "running" && (e.evidence.compiledAnimation = !0), n && n.animation !== "") {
486
- e.evidence.playerSuppression = !0;
487
- let r = `${n.transform}/${n.opacity}`, i = e.lastPose.get(t);
488
- i !== void 0 && i !== r && (e.evidence.playerAdvance = !0), e.lastPose.set(t, r);
489
- }
490
- continue;
491
- }
492
- if (typeof r == "function") try {
493
- for (let n of r.call(t)) {
494
- let r = n.animationName;
495
- typeof r == "string" && r.startsWith("flemo-") && (e.clocks.set(t, n), n.playState === "running" && (e.evidence.compiledAnimation = !0));
496
- }
497
- } catch {}
498
- if (n && n.animation !== "") {
499
- e.evidence.playerSuppression = !0;
500
- let r = `${n.transform}/${n.opacity}`, i = e.lastPose.get(t);
501
- i !== void 0 && i !== r && (e.evidence.playerAdvance = !0), e.lastPose.set(t, r);
502
- }
503
- }
504
- }, re = (e) => e.elements.some((e) => {
505
- let t = e.getAttribute(H);
506
- return t !== null && V.has(t);
507
- }), ie = () => {
508
- let e = b;
509
- if (!e || S) return;
510
- let t = performance.now();
511
- if (e.lastFrameAt !== null && e.heldGaps.length + e.releasedGaps.length < fe) {
512
- let n = t - e.lastFrameAt, r = re(e);
513
- (r ? e.heldGaps : e.releasedGaps).push(n), r || (M(e, n), e.releasedFrames === 1 && j(e));
514
- }
515
- if (e.lastFrameAt = t, N(e), t - e.t0Ms > 1e4) {
516
- x = [...e.elements], ye(t, P(e));
517
- return;
941
+ }, I = ut({
942
+ onHit: (e) => {
943
+ let t = E;
944
+ t && t.tripwires.push(dt(e, t.t0Ms));
945
+ },
946
+ onAnimationStart: (e) => {
947
+ let t = E;
948
+ !t || t.firstAnimationAtMs !== null || (t.firstAnimationAtMs = K(e - t.t0Ms));
518
949
  }
519
- e.rafId = requestAnimationFrame(ie);
520
- }, P = (e) => {
950
+ }), re = (e) => {
521
951
  let t = /* @__PURE__ */ new Set();
522
952
  for (let n of e.elements) {
523
953
  let e = n.getAttribute("data-flemo-status") ?? "";
524
- B.has(e) && t.add(e);
954
+ ht.has(e) && t.add(e);
525
955
  }
526
956
  return [...t];
527
- }, I = (e) => {
957
+ }, L = () => {
958
+ let e = E;
959
+ if (!e || k) return;
960
+ let t = performance.now(), n = e.frames;
961
+ if (n.lastFrameAt !== null && n.heldGaps.length + n.releasedGaps.length < vt) {
962
+ let r = t - n.lastFrameAt, i = oe(e.elements);
963
+ (i ? n.heldGaps : n.releasedGaps).push(r), i || (ce(n, e.elements, r), n.releasedFrames === 1 && me(e.images, e.elements));
964
+ }
965
+ if (n.lastFrameAt = t, ue(n, e.elements), t - e.t0Ms > 1e4) {
966
+ O = [...e.elements], z(t, re(e));
967
+ return;
968
+ }
969
+ e.rafId = requestAnimationFrame(L);
970
+ }, ie = (e) => {
528
971
  let t = performance.now();
529
- te += 1;
530
- let n = e.find((e) => e.getAttribute("data-flemo-active") === "true") ?? e[0], r = se(n.getAttribute("data-flemo-status") ?? "") ?? "PUSH", i = null;
972
+ w += 1;
973
+ let n = e.find((e) => e.getAttribute("data-flemo-active") === "true") ?? e[0], r = ye(n.getAttribute("data-flemo-status") ?? "") ?? "PUSH", i = null;
531
974
  for (let t of e) {
532
- let e = t.getAttribute(H);
533
- if (e !== null && V.has(e)) {
975
+ let e = t.getAttribute(m);
976
+ if (e !== null && gt.has(e)) {
534
977
  i = e;
535
978
  break;
536
979
  }
537
980
  }
538
- b = {
539
- id: `flight-${te}`,
981
+ E = {
982
+ id: `flight-${w}`,
540
983
  kind: r,
541
984
  routerId: n.getAttribute("data-flemo-router") ?? void 0,
985
+ bucket: D,
542
986
  t0Ms: t,
543
987
  t0Iso: (/* @__PURE__ */ new Date()).toISOString(),
544
988
  elements: [...e],
545
- participants: D(e),
989
+ participants: F(e),
546
990
  holdKind: i,
547
991
  holdReleasedAtMs: null,
548
- playerGapStartIndex: _e().__flemoPlayerGaps?.length ?? 0,
549
- heldGaps: [],
550
- releasedGaps: [],
551
- lastFrameAt: null,
552
- evidence: {
553
- compiledAnimation: !1,
554
- playerSuppression: !1,
555
- playerAdvance: !1
556
- },
557
- lastPose: /* @__PURE__ */ new Map(),
558
- clocks: /* @__PURE__ */ new Map(),
559
- lastClock: /* @__PURE__ */ new Map(),
560
- releasedFrames: 0,
561
- tailFrames: 0,
562
- stalledFrames: 0,
563
- stallRunMs: 0,
564
- longestStallMs: 0,
565
- pausedAfterRelease: !1,
566
- holdReassertedAtMs: null,
567
- imagesTracked: new Set(O(e).filter((e) => !e.complete)),
568
- imagesLoadingAtStart: 0,
569
- imagesAddedDuringFlight: 0,
570
- imagesHeld: /* @__PURE__ */ new Set(),
992
+ firstAnimationAtMs: null,
993
+ frames: ae(),
994
+ images: fe(e),
995
+ morphs: Oe(e),
996
+ tripwires: [],
571
997
  rafId: null
572
- }, b.imagesLoadingAtStart = b.imagesTracked.size, N(b), b.rafId = requestAnimationFrame(ie);
573
- }, L = (e, t) => {
574
- let n = de, r = () => {
575
- if (!S) {
998
+ }, ue(E.frames, E.elements), E.rafId = requestAnimationFrame(L);
999
+ }, se = (e, t) => {
1000
+ let n = 2, r = () => {
1001
+ if (k) return;
1002
+ if (--n, n > 0) {
1003
+ requestAnimationFrame(r);
1004
+ return;
1005
+ }
1006
+ let i = xe(t, te());
1007
+ e.landing.residualInlineTransforms = i.residualInlineTransforms, e.landing.offViewportAtRest = i.offViewportAtRest, e.landing.orphanedHolds = i.orphanedHolds;
1008
+ };
1009
+ requestAnimationFrame(r);
1010
+ }, le = (e, t) => {
1011
+ let n = 2, r = () => {
1012
+ if (!k) {
576
1013
  if (--n, n > 0) {
577
1014
  requestAnimationFrame(r);
578
1015
  return;
579
1016
  }
580
- ve(e, t);
1017
+ e.morphs = Ne(t.morphs, te());
581
1018
  }
582
1019
  };
583
1020
  requestAnimationFrame(r);
584
- }, le = () => {
585
- if (b !== null || E().length > 0) return [];
586
- let e = [];
587
- for (let [t, n] of [[f, "image reveal hold"], [p, "arrival hold"]]) {
588
- let r = document.querySelectorAll(`[${t}]`).length;
589
- r > 0 && e.push(`${r} × ${n} (${t}) still marked at rest`);
590
- }
591
- return e;
592
- }, ve = (e, t) => {
593
- let n = [], r = !1, i = window.visualViewport?.width ?? window.innerWidth ?? 0;
594
- t.forEach((e, t) => {
595
- if (!(e instanceof HTMLElement) || !e.isConnected) return;
596
- let a = "", o = "";
597
- try {
598
- let t = getComputedStyle(e);
599
- a = t.transform ?? "", o = t.display ?? "";
600
- } catch {}
601
- if (o === "none") return;
602
- let s = e.getAttribute(u) === "true", c = `screen[${t}]${s ? " (active)" : ""}`;
603
- if (e.style.transform !== "" && e.style.transform !== "none" && n.push(`${c} transform=${e.style.transform}`), e.style.opacity !== "" && e.style.opacity !== "1" && n.push(`${c} opacity=${e.style.opacity}`), s && e.getAttribute("data-flemo-status") === "COMPLETED" && i > 0) {
604
- let e = ce(a);
605
- e !== null && Math.abs(e) >= i * .5 && (r = !0);
606
- }
607
- }), e.landing.residualInlineTransforms = n, e.landing.offViewportAtRest = r, e.landing.orphanedHolds = le();
608
- }, ye = (e, t) => {
609
- let i = b;
1021
+ }, R = (e, t, n, r) => ({
1022
+ id: r ? `${e.id} (in flight)` : e.id,
1023
+ ...e.routerId === void 0 ? {} : { routerId: e.routerId },
1024
+ ...e.bucket === null ? {} : { bucket: e.bucket },
1025
+ kind: e.kind,
1026
+ t0: {
1027
+ ms: K(e.t0Ms),
1028
+ iso: e.t0Iso
1029
+ },
1030
+ t1: {
1031
+ ms: K(t),
1032
+ iso: (/* @__PURE__ */ new Date()).toISOString()
1033
+ },
1034
+ durationMs: K(t - e.t0Ms),
1035
+ driver: ve(e.frames.evidence),
1036
+ participants: e.participants,
1037
+ holds: {
1038
+ kind: e.holdKind,
1039
+ releasedAtMs: e.holdReleasedAtMs
1040
+ },
1041
+ frameSamples: _e(e.frames.heldGaps, e.frames.releasedGaps),
1042
+ motion: {
1043
+ ...de(e.frames),
1044
+ firstAnimationAtMs: e.firstAnimationAtMs
1045
+ },
1046
+ images: he(e.images),
1047
+ morphs: Ne(e.morphs, !0),
1048
+ tripwires: [...e.tripwires, ...Me(e.morphs)],
1049
+ input: I.inputBetween(e.t0Ms, t),
1050
+ longTasks: [],
1051
+ holdLongTasks: [],
1052
+ landing: {
1053
+ residualInlineTransforms: [],
1054
+ offViewportAtRest: !1,
1055
+ stuckStatuses: n,
1056
+ orphanedHolds: []
1057
+ },
1058
+ anomalies: []
1059
+ }), z = (e, t) => {
1060
+ let i = E;
1061
+ /* v8 ignore next -- unreachable: both callers hold a flight. sampleFrame
1062
+ returns at its own top when `current` is null, and evaluate only calls
1063
+ this inside `if (current && ...)`. The narrowing stays for the type. */
610
1064
  if (!i) return;
611
- j(i), b = null, i.rafId !== null && cancelAnimationFrame(i.rafId);
612
- let a = oe((_e().__flemoPlayerGaps ?? []).slice(i.playerGapStartIndex)), o = {
613
- id: i.id,
614
- ...i.routerId === void 0 ? {} : { routerId: i.routerId },
615
- kind: i.kind,
616
- t0: {
617
- ms: U(i.t0Ms),
618
- iso: i.t0Iso
619
- },
620
- t1: {
621
- ms: U(e),
622
- iso: (/* @__PURE__ */ new Date()).toISOString()
623
- },
624
- durationMs: U(e - i.t0Ms),
625
- driver: z(i.evidence),
626
- participants: i.participants,
627
- holds: {
628
- kind: i.holdKind,
629
- releasedAtMs: i.holdReleasedAtMs
630
- },
631
- frameSamples: R(i.heldGaps, i.releasedGaps),
632
- motion: {
633
- sampledFrames: i.releasedFrames,
634
- stalledFrames: i.stalledFrames,
635
- longestStallMs: U(i.longestStallMs),
636
- pausedAfterRelease: i.pausedAfterRelease,
637
- holdReassertedAtMs: i.holdReassertedAtMs,
638
- tailFrames: i.tailFrames
639
- },
640
- images: k(i),
641
- ...a ? { playerGaps: a } : {},
642
- longTasks: [],
643
- holdLongTasks: [],
644
- landing: {
645
- residualInlineTransforms: [],
646
- offViewportAtRest: !1,
647
- stuckStatuses: t,
648
- orphanedHolds: []
649
- },
650
- anomalies: []
651
- };
652
- v.push(o), v.length > n && v.splice(0, v.length - n), t.length === 0 && L(o, i.elements), r && console.info(`[flemo devtools] ${o.id} ${o.kind} driver=${o.driver} ${o.durationMs}ms screens=${o.participants.screens}`);
653
- }, W = (e) => {
654
- let t = b;
1065
+ me(i.images, i.elements), E = null, i.rafId !== null && cancelAnimationFrame(i.rafId);
1066
+ let a = R(i, e, t, !1);
1067
+ C.push(a), C.length > n && C.splice(0, C.length - n), t.length === 0 && (se(a, i.elements), le(a, i)), r && console.info(`[flemo devtools] ${a.id} ${a.kind} driver=${a.driver} ${a.durationMs}ms screens=${a.participants.screens}`);
1068
+ }, B = (e) => {
1069
+ let t = E;
655
1070
  if (!t || !(e.target instanceof Element)) return;
656
- let n = e.target.getAttribute(H);
657
- n !== null && V.has(n) && t.holdKind === null && (t.holdKind = n), n !== null && V.has(n) && t.holdReleasedAtMs !== null && t.holdReassertedAtMs === null && (t.holdReassertedAtMs = U(performance.now() - t.t0Ms)), (n === "false" || n === null) && e.oldValue !== null && V.has(e.oldValue) && t.holdReleasedAtMs === null && !re(t) && (t.holdReleasedAtMs = U(performance.now() - t.t0Ms));
658
- }, be = () => {
659
- let e = E();
660
- if (x.length > 0) {
661
- if (e.some((e) => x.includes(e))) return;
662
- x = [];
1071
+ let n = e.target.getAttribute(m);
1072
+ if (n !== null && gt.has(n) && t.holdKind === null && (t.holdKind = n), n !== null && gt.has(n) && t.holdReleasedAtMs !== null && t.frames.holdReassertedAtMs === null) {
1073
+ let e = K(performance.now() - t.t0Ms);
1074
+ t.frames.holdReassertedAtMs = e, t.tripwires.push({
1075
+ kind: "hold-reassert",
1076
+ atMs: e,
1077
+ detail: "an animation hold was re-asserted after this flight had already released it — the motion pauses here while every timing metric stays clean"
1078
+ });
1079
+ }
1080
+ (n === "false" || n === null) && e.oldValue !== null && gt.has(e.oldValue) && t.holdReleasedAtMs === null && !oe(t.elements) && (t.holdReleasedAtMs = K(performance.now() - t.t0Ms));
1081
+ }, V = () => {
1082
+ let e = P();
1083
+ if (O.length > 0) {
1084
+ if (e.some((e) => O.includes(e))) return;
1085
+ O = [];
663
1086
  }
664
- if (!b && e.length > 0) {
665
- I(e);
1087
+ if (!E && e.length > 0) {
1088
+ ie(e);
666
1089
  return;
667
1090
  }
668
- if (b && e.length === 0) {
669
- ye(performance.now(), []);
1091
+ if (E && e.length === 0) {
1092
+ z(performance.now(), []);
670
1093
  return;
671
1094
  }
672
- if (b) for (let t of e) b.elements.includes(t) || (b.elements.push(t), A(t.querySelectorAll("img")), b.participants = D(b.elements));
673
- }, G = null, K = null, q = () => {
1095
+ if (E) for (let t of e) E.elements.includes(t) || (E.elements.push(t), pe(E.images, E.elements, t.querySelectorAll("img")), E.participants = F(E.elements));
1096
+ }, be = null, H = null, Se = () => {
674
1097
  let e = document.documentElement;
675
1098
  if (!e) return !1;
676
1099
  let t = new MutationObserver((e) => {
677
- if (!S) {
678
- for (let t of e) t.type === "attributes" && t.attributeName === H ? W(t) : t.type === "childList" && t.addedNodes.length > 0 && A(t.addedNodes);
679
- be();
680
- }
1100
+ if (k) return;
1101
+ let t = performance.now();
1102
+ for (let n of e) n.type === "attributes" ? n.attributeName === "data-flemo-anim-hold" ? B(n) : E && (n.attributeName === "data-flemo-morph" || n.attributeName === "data-flemo-morph-camera") && n.target instanceof Element && ke(E.morphs, n.target) : (E && Ae(E.morphs, n, t), E && n.addedNodes.length > 0 && pe(E.images, E.elements, n.addedNodes));
1103
+ V();
681
1104
  });
682
1105
  try {
683
1106
  t.observe(e, {
@@ -686,412 +1109,498 @@ var e = 30, t = 1e4, n = 50, r = 120, i = 100, a = (e) => {
686
1109
  attributes: !0,
687
1110
  attributeOldValue: !0,
688
1111
  attributeFilter: [
689
- l,
690
- u,
691
- H
1112
+ f,
1113
+ p,
1114
+ m,
1115
+ b,
1116
+ T
692
1117
  ]
693
1118
  });
694
1119
  } catch {
695
1120
  return !1;
696
1121
  }
697
- return G = t, be(), !0;
1122
+ return be = t, V(), !0;
698
1123
  };
699
- if (!q()) {
1124
+ if (!Se()) {
700
1125
  let e = () => {
701
- K = null, S || q();
1126
+ H = null, k || Se();
702
1127
  };
703
- K = e, document.addEventListener("DOMContentLoaded", e, { once: !0 });
1128
+ H = e, document.addEventListener("DOMContentLoaded", e, { once: !0 });
704
1129
  }
705
- let J = (e) => {
706
- let t = g.filter((t) => t.startMs + t.durationMs >= e.t0.ms - 120 && t.startMs <= e.t1.ms), n = e.holds.kind === null ? e.t0.ms : e.holds.releasedAtMs === null ? e.t1.ms : e.t0.ms + e.holds.releasedAtMs;
1130
+ let Ce = () => {
1131
+ document.visibilityState === "hidden" && (M = !0, a && E === null && Ge(C, "3"));
1132
+ };
1133
+ document.addEventListener("visibilitychange", Ce);
1134
+ let we = () => {
1135
+ N = 0, !k && (E === null && Ge(C, "3"), N = window.setTimeout(we, bt));
1136
+ };
1137
+ a && (N = window.setTimeout(we, bt));
1138
+ let Te = (e) => {
1139
+ let t = x.filter((t) => t.startMs + t.durationMs >= e.t0.ms - 120 && t.startMs <= e.t1.ms), n = e.holds.kind === null ? e.t0.ms : e.holds.releasedAtMs === null ? e.t1.ms : e.t0.ms + e.holds.releasedAtMs;
707
1140
  return {
708
1141
  released: t.filter((e) => e.startMs + e.durationMs > n),
709
1142
  held: t.filter((e) => e.startMs + e.durationMs <= n)
710
1143
  };
711
- }, Y = () => {
712
- let e = performance.now(), n = v.map((e) => {
713
- let t = J(e), n = {
714
- ...e,
715
- longTasks: t.released,
716
- holdLongTasks: t.held,
717
- landing: {
718
- residualInlineTransforms: [...e.landing.residualInlineTransforms],
719
- offViewportAtRest: e.landing.offViewportAtRest,
720
- stuckStatuses: [...e.landing.stuckStatuses],
721
- orphanedHolds: [...e.landing.orphanedHolds]
722
- }
723
- };
724
- return n.anomalies = a({
725
- t0Ms: n.t0.ms,
726
- t1Ms: n.t1.ms,
727
- driver: n.driver,
728
- frameSamples: n.frameSamples,
729
- playerGaps: n.playerGaps ?? null,
730
- longTasks: n.longTasks,
731
- holdLongTasks: n.holdLongTasks,
732
- releasedAtMs: n.holds.releasedAtMs,
733
- landing: n.landing,
734
- motion: n.motion,
735
- images: n.images
736
- }), n;
737
- }), r = b;
738
- if (!r) return n;
739
- let i = e - r.t0Ms > t, o = {
740
- id: `${r.id} (in flight)`,
741
- ...r.routerId === void 0 ? {} : { routerId: r.routerId },
742
- kind: r.kind,
743
- t0: {
744
- ms: U(r.t0Ms),
745
- iso: r.t0Iso
746
- },
747
- t1: {
748
- ms: U(e),
749
- iso: (/* @__PURE__ */ new Date()).toISOString()
750
- },
751
- durationMs: U(e - r.t0Ms),
752
- driver: z(r.evidence),
753
- participants: r.participants,
754
- holds: {
755
- kind: r.holdKind,
756
- releasedAtMs: r.holdReleasedAtMs
757
- },
758
- frameSamples: R(r.heldGaps, r.releasedGaps),
759
- motion: {
760
- sampledFrames: r.releasedFrames,
761
- stalledFrames: r.stalledFrames,
762
- longestStallMs: U(r.longestStallMs),
763
- pausedAfterRelease: r.pausedAfterRelease,
764
- holdReassertedAtMs: r.holdReassertedAtMs,
765
- tailFrames: r.tailFrames
766
- },
767
- images: k(r),
768
- longTasks: [],
769
- holdLongTasks: [],
1144
+ }, Ee = (e) => x.filter((t) => t.startMs >= d && !e.some((e) => t.startMs + t.durationMs >= e.t0.ms && t.startMs <= e.t1.ms)), De = (e) => {
1145
+ let t = Te(e), n = {
1146
+ ...e,
1147
+ longTasks: t.released,
1148
+ holdLongTasks: t.held,
770
1149
  landing: {
771
- residualInlineTransforms: [],
772
- offViewportAtRest: !1,
773
- stuckStatuses: i ? P(r) : [],
774
- orphanedHolds: []
1150
+ residualInlineTransforms: [...e.landing.residualInlineTransforms],
1151
+ offViewportAtRest: e.landing.offViewportAtRest,
1152
+ stuckStatuses: [...e.landing.stuckStatuses],
1153
+ orphanedHolds: [...e.landing.orphanedHolds]
775
1154
  },
776
- anomalies: []
777
- }, s = J(o);
778
- return o.longTasks = s.released, o.holdLongTasks = s.held, o.anomalies = a({
779
- t0Ms: o.t0.ms,
780
- t1Ms: o.t1.ms,
781
- driver: o.driver,
782
- frameSamples: o.frameSamples,
783
- playerGaps: o.playerGaps ?? null,
784
- longTasks: o.longTasks,
785
- holdLongTasks: o.holdLongTasks,
786
- releasedAtMs: o.holds.releasedAtMs,
787
- landing: o.landing,
788
- motion: o.motion,
789
- images: o.images
790
- }), [...n, o];
791
- }, X = () => {
792
- let e = F(), t = { ...e };
793
- for (let [n, r] of Object.entries(c)) n in e || (t[`${n} (at attach, since cleared)`] = r);
794
- let n = ae(t), r = w(d), i = Y(), a = b;
1155
+ morphs: { ...e.morphs },
1156
+ tripwires: [...e.tripwires]
1157
+ };
1158
+ return n.anomalies = o({
1159
+ t0Ms: n.t0.ms,
1160
+ t1Ms: n.t1.ms,
1161
+ driver: n.driver,
1162
+ frameSamples: n.frameSamples,
1163
+ longTasks: n.longTasks,
1164
+ holdLongTasks: n.holdLongTasks,
1165
+ releasedAtMs: n.holds.releasedAtMs,
1166
+ landing: n.landing,
1167
+ motion: n.motion,
1168
+ images: n.images,
1169
+ morphs: n.morphs,
1170
+ tripwires: n.tripwires
1171
+ }), n;
1172
+ }, je = () => {
1173
+ let e = C.map(De), n = E;
1174
+ if (!n) return e;
1175
+ let r = performance.now(), i = r - n.t0Ms > t;
1176
+ return [...e, De(R(n, r, i ? re(n) : [], !0))];
1177
+ }, U = (e) => {
1178
+ let t = /* @__PURE__ */ new Set(), n = 0, r = 0;
1179
+ for (let i of e) {
1180
+ n += i.input.trusted, r += i.input.synthetic;
1181
+ for (let e of i.input.pointerTypes) t.add(e);
1182
+ }
1183
+ return {
1184
+ trusted: n,
1185
+ synthetic: r,
1186
+ pointerTypes: [...t].sort()
1187
+ };
1188
+ }, Pe = () => {
1189
+ let e = Ve(), t = { ...e };
1190
+ for (let [n, r] of Object.entries(l)) n in e || (t[`${n} (at attach, since cleared)`] = r);
1191
+ let n = He(t), r = ne(g, I.sawAnimationEvent()), i = je(), a = E, o = it({
1192
+ environment: r,
1193
+ idleLongTasks: Ee(i),
1194
+ observedMs: performance.now() - d,
1195
+ wentHidden: M,
1196
+ documentHidden: document.visibilityState === "hidden",
1197
+ input: U(i)
1198
+ });
795
1199
  return {
796
1200
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
797
- version: "2",
1201
+ version: "3",
1202
+ verdict: pt({
1203
+ preconditions: o,
1204
+ flights: i,
1205
+ observation: r.observation
1206
+ }),
798
1207
  environment: r,
1208
+ preconditions: o,
799
1209
  overrides: {
800
1210
  active: t,
801
1211
  warnings: n
802
1212
  },
803
1213
  flights: i,
804
- anomalies: o({
1214
+ comparison: u(i),
1215
+ previousSession: h,
1216
+ anomalies: s({
805
1217
  emulationSuspected: r.emulationSuspected,
806
1218
  platform: r.platform,
807
1219
  stuckFlightOpen: a !== null && performance.now() - a.t0Ms > 1e4,
808
1220
  flightAnomalies: i.map((e) => e.anomalies)
809
1221
  }),
810
- blindSpots: [...s],
811
- judgingProtocol: [...T]
1222
+ blindSpots: [...c],
1223
+ judgingProtocol: [...ge]
812
1224
  };
813
- }, xe = () => {
814
- if (!S) {
815
- if (S = !0, G?.disconnect(), K !== null && (document.removeEventListener("DOMContentLoaded", K), K = null), _?.disconnect(), b?.rafId != null && cancelAnimationFrame(b.rafId), b = null, C) {
816
- let e = _e();
1225
+ }, Fe = (e) => (D = e === null || e === "" ? null : e, D), W = () => {
1226
+ if (!k) {
1227
+ if (k = !0, be?.disconnect(), H !== null && (document.removeEventListener("DOMContentLoaded", H), H = null), document.removeEventListener("visibilitychange", Ce), I.detach(), S?.disconnect(), window.clearTimeout(N), N = 0, E?.rafId != null && cancelAnimationFrame(E.rafId), E = null, j) {
1228
+ let e = window;
817
1229
  e.flemo?.__flemoDevtools === !0 && delete e.flemo;
818
1230
  }
819
- ge = null;
1231
+ xt = null;
820
1232
  }
821
- }, Se = {
822
- detach: xe,
823
- report: X
1233
+ }, G = {
1234
+ detach: W,
1235
+ report: Pe,
1236
+ mark: Fe
824
1237
  };
825
1238
  if (i) {
826
- let e = _e(), t = e.flemo;
1239
+ let e = window, t = e.flemo;
827
1240
  (t === void 0 || t?.__flemoDevtools === !0) && (e.flemo = {
828
1241
  __flemoDevtools: !0,
829
- report: X,
830
- flights: Y,
831
- detach: xe
832
- }, C = !0);
1242
+ report: Pe,
1243
+ flights: je,
1244
+ mark: Fe,
1245
+ detach: W
1246
+ }, j = !0);
833
1247
  }
834
- return ge = Se, Se;
835
- }, ye = "http://www.w3.org/2000/svg", W = (e, t, n) => {
1248
+ return xt = G, G;
1249
+ }, wt = O.map((e) => A(d) + j(f, e)).join(","), Tt = () => document.querySelector(wt) !== null, Et = (e) => {
1250
+ let t = document.createElement("div");
1251
+ t.setAttribute(D, ""), t.style.position = "fixed", t.style.top = "0", t.style.left = "0", t.style.width = "0", t.style.height = "0", t.style.zIndex = "2147483000";
1252
+ let n = t.attachShadow({ mode: "open" }), r = document.createElement("style");
1253
+ r.textContent = e, n.appendChild(r);
1254
+ let i = document.createElement("div");
1255
+ return i.className = "root", n.appendChild(i), {
1256
+ host: t,
1257
+ root: i
1258
+ };
1259
+ }, Dt = () => {}, Ot = (e) => {
1260
+ if (e) return {
1261
+ recorder: e,
1262
+ ownsRecorder: !1
1263
+ };
1264
+ let t = window.flemo;
1265
+ return t?.__flemoDevtools === !0 && typeof t.report == "function" ? {
1266
+ recorder: {
1267
+ report: t.report,
1268
+ detach: Dt,
1269
+ mark: t.mark ?? (() => null)
1270
+ },
1271
+ ownsRecorder: !1
1272
+ } : {
1273
+ recorder: Ct(),
1274
+ ownsRecorder: !0
1275
+ };
1276
+ }, kt = "http://www.w3.org/2000/svg", q = (e, t, n) => {
836
1277
  let r = document.createElement(e);
837
1278
  return t !== void 0 && (r.className = t), n !== void 0 && (r.textContent = n), r;
838
- }, be = (e, t) => {
839
- let n = document.createElementNS(ye, e);
1279
+ }, At = (e, t) => {
1280
+ let n = document.createElementNS(kt, e);
840
1281
  for (let [e, r] of Object.entries(t)) n.setAttribute(e, r);
841
1282
  return n;
842
- }, G = (e) => {
1283
+ }, jt = (e) => {
843
1284
  for (; e.firstChild !== null;) e.removeChild(e.firstChild);
844
- }, K = (e, t) => {
1285
+ }, Mt = (e, t) => {
845
1286
  e.textContent !== t && (e.textContent = t);
846
- }, q = (e) => e == null || e === "" ? "—" : String(e), J = (e) => typeof e == "number" && Number.isFinite(e) ? `${Math.round(e)}ms` : "—", Y = (e) => typeof e == "number" && Number.isFinite(e) ? `${Math.round(e * 10) / 10}ms` : "—", X = (e) => typeof e == "number" && Number.isFinite(e) ? String(e) : "—", xe = (e) => e === !0 ? "yes" : e === !1 ? "no" : "—", Se = (e) => {
1287
+ }, J = (e) => e == null || e === "" ? "—" : String(e), Y = (e) => typeof e == "number" && Number.isFinite(e) ? `${Math.round(e)}ms` : "—", Nt = (e) => typeof e == "number" && Number.isFinite(e) ? `${Math.round(e * 10) / 10}ms` : "—", X = (e) => typeof e == "number" && Number.isFinite(e) ? String(e) : "—", Pt = (e) => e === !0 ? "yes" : e === !1 ? "no" : "—", Ft = (e) => {
847
1288
  let t = e?.environment;
848
1289
  if (!t) return "—";
849
1290
  let n = t.viewport, r = n && typeof n.width == "number" && typeof n.height == "number" ? `${Math.round(n.width)}×${Math.round(n.height)}` : "—";
850
1291
  return [
851
- q(t.engine),
852
- `dpr ${q(t.devicePixelRatio)}`,
1292
+ J(t.engine),
1293
+ `dpr ${J(t.devicePixelRatio)}`,
853
1294
  r,
854
- `rAF ${Y(t.rafCadence?.medianGapMs)}`
1295
+ `rAF ${Nt(t.rafCadence?.medianGapMs)}`
855
1296
  ].join(" · ");
856
- }, Ce = (e, t) => `${t ?? ""}#${e.map((e) => `${e?.id ?? ""}|${e?.kind ?? ""}|${e?.driver ?? ""}|${Math.round(e?.durationMs ?? 0)}|${e?.participants?.screens ?? 0}|${e?.anomalies?.length ?? 0}`).join(",")}`, we = (e) => {
1297
+ }, It = (e, t) => `${t ?? ""}#${e.map((e) => `${e?.id ?? ""}|${e?.kind ?? ""}|${e?.driver ?? ""}|${Math.round(e?.durationMs ?? 0)}|${e?.participants?.screens ?? 0}|${e?.anomalies?.length ?? 0}`).join(",")}`, Lt = (e) => {
857
1298
  let t = e?.released?.gaps;
858
1299
  if (!Array.isArray(t)) return null;
859
1300
  let n = t.filter((e) => typeof e == "number" && Number.isFinite(e));
860
1301
  return n.length >= 2 ? n : null;
861
- }, Te = "\n:host {\n all: initial;\n color-scheme: dark;\n}\n* { box-sizing: border-box; }\n.root {\n --bg: #14161a;\n --bg-soft: #1b1e24;\n --line: #2b3038;\n --fg: #e6e9ef;\n --fg-dim: #99a1b0;\n --accent: #7cc4ff;\n --warn: #ffb454;\n --bad: #ff6b6b;\n font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: var(--fg);\n}\n@media (prefers-color-scheme: light) {\n .root {\n --bg: #ffffff;\n --bg-soft: #f4f6f9;\n --line: #dde1e8;\n --fg: #1a1d22;\n --fg-dim: #5c6472;\n --accent: #0b6bcb;\n --warn: #a35a00;\n --bad: #c2261f;\n }\n}\n.toggle {\n position: fixed;\n bottom: 12px;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 6px 10px;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--bg);\n color: var(--fg);\n font: inherit;\n cursor: pointer;\n}\n.toggle:hover { background: var(--bg-soft); }\n.toggle[data-corner=\"bottom-right\"] { right: 12px; }\n.toggle[data-corner=\"bottom-left\"] { left: 12px; }\n.mark { font-weight: 700; letter-spacing: 0.04em; }\n.count {\n padding: 0 6px;\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n}\n.dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--bad);\n}\n.dot[hidden] { display: none; }\n.panel {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147482999;\n display: flex;\n flex-direction: column;\n max-width: 100%;\n border-top: 1px solid var(--line);\n background: var(--bg);\n overflow: hidden;\n}\n.panel[hidden] { display: none; }\n.grip {\n flex: 0 0 6px;\n height: 6px;\n background: var(--line);\n cursor: ns-resize;\n touch-action: none;\n}\n.head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line);\n}\n.env { color: var(--fg-dim); }\n.spacer { flex: 1 1 auto; }\n.act {\n padding: 4px 8px;\n border: 1px solid var(--line);\n border-radius: 4px;\n background: var(--bg-soft);\n color: var(--fg);\n font: inherit;\n cursor: pointer;\n}\n.chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n flex: 1 1 100%;\n}\n.chips:empty { display: none; }\n.chip {\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n max-width: 100%;\n overflow-wrap: anywhere;\n}\n.chip.warn { border-color: var(--warn); color: var(--warn); }\n.chip.bad { border-color: var(--bad); color: var(--bad); }\n.body {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n.list {\n flex: 0 0 40%;\n max-width: 340px;\n min-width: 180px;\n overflow: auto;\n border-right: 1px solid var(--line);\n}\n.row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-bottom: 1px solid var(--line);\n background: none;\n color: var(--fg);\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.row[aria-selected=\"true\"] { background: var(--bg-soft); }\n.row .kind { font-weight: 700; }\n.row .driver { color: var(--accent); }\n.row .dur, .row .screens { color: var(--fg-dim); }\n.row .n {\n margin-left: auto;\n padding: 0 6px;\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n}\n.row .n.bad { background: var(--bad); color: #fff; }\n.detail { flex: 1 1 auto; overflow: auto; padding: 8px 10px; }\n.section { margin: 0 0 10px; }\n.section > h2 {\n margin: 0 0 4px;\n font: inherit;\n font-weight: 700;\n color: var(--fg-dim);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.kv { display: flex; gap: 8px; }\n.kv > .k { flex: 0 0 34%; max-width: 180px; color: var(--fg-dim); }\n.kv > .v { flex: 1 1 auto; overflow-wrap: anywhere; }\n/* A value that IS the finding — a stall, a re-asserted hold, an orphan. */\n.kv > .v.bad { color: var(--bad); }\n.li { overflow-wrap: anywhere; }\n.li.bad { color: var(--bad); }\n.dim { color: var(--fg-dim); }\n.spark { display: block; width: 100%; height: 28px; }\n.foot { border-top: 1px solid var(--line); padding: 6px 10px; color: var(--fg-dim); }\n.foot summary { cursor: pointer; }\n.foot .li { margin-top: 4px; }\n", Ee = 48, Z = (e, t, n, r) => {
862
- let i = W("div", "kv");
863
- i.appendChild(W("span", "k", t)), i.appendChild(W("span", r ? `v ${r}` : "v", n)), e.appendChild(i);
1302
+ }, Rt = "\n:host {\n all: initial;\n color-scheme: dark;\n}\n* { box-sizing: border-box; }\n.root {\n --bg: #14161a;\n --bg-soft: #1b1e24;\n --line: #2b3038;\n --fg: #e6e9ef;\n --fg-dim: #99a1b0;\n --accent: #7cc4ff;\n --warn: #ffb454;\n --bad: #ff6b6b;\n font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: var(--fg);\n}\n@media (prefers-color-scheme: light) {\n .root {\n --bg: #ffffff;\n --bg-soft: #f4f6f9;\n --line: #dde1e8;\n --fg: #1a1d22;\n --fg-dim: #5c6472;\n --accent: #0b6bcb;\n --warn: #a35a00;\n --bad: #c2261f;\n }\n}\n.toggle {\n position: fixed;\n bottom: 12px;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 6px 10px;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--bg);\n color: var(--fg);\n font: inherit;\n cursor: pointer;\n}\n.toggle:hover { background: var(--bg-soft); }\n.toggle[data-corner=\"bottom-right\"] { right: 12px; }\n.toggle[data-corner=\"bottom-left\"] { left: 12px; }\n.mark { font-weight: 700; letter-spacing: 0.04em; }\n.count {\n padding: 0 6px;\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n}\n.dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--bad);\n}\n.dot[hidden] { display: none; }\n.panel {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147482999;\n display: flex;\n flex-direction: column;\n max-width: 100%;\n border-top: 1px solid var(--line);\n background: var(--bg);\n overflow: hidden;\n}\n.panel[hidden] { display: none; }\n.grip {\n flex: 0 0 6px;\n height: 6px;\n background: var(--line);\n cursor: ns-resize;\n touch-action: none;\n}\n.head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line);\n}\n.env { color: var(--fg-dim); }\n.spacer { flex: 1 1 auto; }\n.act {\n padding: 4px 8px;\n border: 1px solid var(--line);\n border-radius: 4px;\n background: var(--bg-soft);\n color: var(--fg);\n font: inherit;\n cursor: pointer;\n}\n.chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n flex: 1 1 100%;\n}\n.chips:empty { display: none; }\n.chip {\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n max-width: 100%;\n overflow-wrap: anywhere;\n}\n/* The verdict reads as prose, not as a tag: it is a sentence about the whole\n session and it is the first thing anybody should read. */\n.chip.lead {\n border-color: var(--fg);\n color: var(--fg);\n white-space: normal;\n max-width: 100%;\n line-height: 1.5;\n}\n.chip.warn { border-color: var(--warn); color: var(--warn); }\n.chip.bad { border-color: var(--bad); color: var(--bad); }\n.body {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n.list {\n flex: 0 0 40%;\n max-width: 340px;\n min-width: 180px;\n overflow: auto;\n border-right: 1px solid var(--line);\n}\n.row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-bottom: 1px solid var(--line);\n background: none;\n color: var(--fg);\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.row[aria-selected=\"true\"] { background: var(--bg-soft); }\n.row .kind { font-weight: 700; }\n.row .driver { color: var(--accent); }\n.row .dur, .row .screens { color: var(--fg-dim); }\n.row .n {\n margin-left: auto;\n padding: 0 6px;\n border-radius: 999px;\n background: var(--bg-soft);\n color: var(--fg-dim);\n}\n.row .n.bad { background: var(--bad); color: #fff; }\n.detail { flex: 1 1 auto; overflow: auto; padding: 8px 10px; }\n.section { margin: 0 0 10px; }\n.section > h2 {\n margin: 0 0 4px;\n font: inherit;\n font-weight: 700;\n color: var(--fg-dim);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.kv { display: flex; gap: 8px; }\n.kv > .k { flex: 0 0 34%; max-width: 180px; color: var(--fg-dim); }\n.kv > .v { flex: 1 1 auto; overflow-wrap: anywhere; }\n/* A value that IS the finding — a stall, a re-asserted hold, an orphan. */\n.kv > .v.bad { color: var(--bad); }\n.li { overflow-wrap: anywhere; }\n.li.bad { color: var(--bad); }\n.dim { color: var(--fg-dim); }\n.spark { display: block; width: 100%; height: 28px; }\n.foot { border-top: 1px solid var(--line); padding: 6px 10px; color: var(--fg-dim); }\n.foot summary { cursor: pointer; }\n.foot .li { margin-top: 4px; }\n", zt = 48, Z = (e, t, n, r) => {
1303
+ let i = q("div", "kv");
1304
+ i.appendChild(q("span", "k", t)), i.appendChild(q("span", r ? `v ${r}` : "v", n)), e.appendChild(i);
864
1305
  }, Q = (e, t) => {
865
- let n = W("section", "section");
866
- return n.appendChild(W("h2", void 0, t)), e.appendChild(n), n;
1306
+ let n = q("section", "section");
1307
+ return n.appendChild(q("h2", void 0, t)), e.appendChild(n), n;
867
1308
  }, $ = (e, t, n = "li") => {
868
- e.appendChild(W("div", n, t));
869
- }, De = (e, t) => {
870
- G(e);
871
- for (let n of t?.overrides?.warnings ?? []) e.appendChild(W("span", "chip warn", q(n)));
872
- for (let n of t?.anomalies ?? []) e.appendChild(W("span", "chip bad", q(n)));
873
- for (let [n, r] of Object.entries(t?.overrides?.active ?? {})) e.appendChild(W("span", "chip", `${n}=${String(r)}`));
874
- }, Oe = (e, t, n, r) => {
875
- if (G(e), t.length === 0) {
1309
+ e.appendChild(q("div", n, t));
1310
+ }, Bt = (e, t) => {
1311
+ jt(e);
1312
+ for (let n of t?.verdict ?? []) e.appendChild(q("span", "chip lead", J(n)));
1313
+ for (let n of t?.preconditions ?? []) n?.status === "violated" && e.appendChild(q("span", "chip bad", `${n.id}: ${J(n.detail)}`));
1314
+ for (let n of t?.overrides?.warnings ?? []) e.appendChild(q("span", "chip warn", J(n)));
1315
+ for (let n of t?.anomalies ?? []) e.appendChild(q("span", "chip bad", J(n)));
1316
+ for (let [n, r] of Object.entries(t?.overrides?.active ?? {})) e.appendChild(q("span", "chip", `${n}=${String(r)}`));
1317
+ }, Vt = (e, t, n, r) => {
1318
+ if (jt(e), t.length === 0) {
876
1319
  $(e, "no flights recorded yet — navigate once", "li dim");
877
1320
  return;
878
1321
  }
879
1322
  for (let i = t.length - 1; i >= 0; --i) {
880
- let a = t[i], o = q(a?.id), s = W("button", "row");
881
- s.type = "button", s.setAttribute("data-flight-id", o), s.setAttribute("aria-selected", String(o === n)), s.appendChild(W("span", "kind", q(a?.kind))), s.appendChild(W("span", "driver", q(a?.driver))), s.appendChild(W("span", "dur", J(a?.durationMs))), s.appendChild(W("span", "screens", `${X(a?.participants?.screens)} scr`));
1323
+ let a = t[i], o = J(a?.id), s = q("button", "row");
1324
+ s.type = "button", s.setAttribute("data-flight-id", o), s.setAttribute("aria-selected", String(o === n)), s.appendChild(q("span", "kind", J(a?.kind))), s.appendChild(q("span", "driver", J(a?.driver))), s.appendChild(q("span", "dur", Y(a?.durationMs))), s.appendChild(q("span", "screens", `${X(a?.participants?.screens)} scr`));
882
1325
  let c = a?.anomalies?.length ?? 0;
883
- s.appendChild(W("span", c > 0 ? "n bad" : "n", String(c))), s.addEventListener("click", () => r(o)), e.appendChild(s);
1326
+ s.appendChild(q("span", c > 0 ? "n bad" : "n", String(c))), s.addEventListener("click", () => r(o)), e.appendChild(s);
884
1327
  }
885
- }, ke = (e, t, n) => {
1328
+ }, Ht = (e, t, n) => {
886
1329
  if (!n) {
887
1330
  Z(e, t, "—");
888
1331
  return;
889
1332
  }
890
1333
  let r = [
891
1334
  `${X(n.count)} frames`,
892
- `median ${Y(n.medianGapMs)}`,
893
- `max ${Y(n.maxGapMs)}`
1335
+ `median ${Nt(n.medianGapMs)}`,
1336
+ `max ${Nt(n.maxGapMs)}`
894
1337
  ];
895
1338
  typeof n.over30Count == "number" && r.push(`>30ms ×${n.over30Count}`), Z(e, t, r.join(" · "));
896
- }, Ae = (e) => {
897
- let t = Math.max(...e), n = t > 0 ? t : 1, r = 100 / (e.length - 1), i = e.map((e, t) => `${Math.round(t * r * 10) / 10},${Math.round((27 - e / n * 26) * 10) / 10}`).join(" "), a = be("svg", {
1339
+ }, Ut = (e) => {
1340
+ let t = Math.max(...e), n = t > 0 ? t : 1, r = 100 / (e.length - 1), i = e.map((e, t) => `${Math.round(t * r * 10) / 10},${Math.round((27 - e / n * 26) * 10) / 10}`).join(" "), a = At("svg", {
898
1341
  class: "spark",
899
1342
  viewBox: "0 0 100 28",
900
1343
  preserveAspectRatio: "none",
901
1344
  "aria-hidden": "true"
902
1345
  });
903
- return a.appendChild(be("polyline", {
1346
+ return a.appendChild(At("polyline", {
904
1347
  points: i,
905
1348
  fill: "none",
906
1349
  stroke: "currentColor",
907
1350
  "stroke-width": "1",
908
1351
  "vector-effect": "non-scaling-stroke"
909
1352
  })), a;
910
- }, je = (e, t) => {
1353
+ }, Wt = (e, t) => {
911
1354
  let n = t.longTasks ?? [], r = t.holdLongTasks ?? [];
912
1355
  if (n.length === 0 && r.length === 0) {
913
1356
  $(e, "none", "li dim");
914
1357
  return;
915
1358
  }
916
- let i = (e) => `${J(e?.startMs)} + ${J(e?.durationMs)}`;
1359
+ let i = (e) => `${Y(e?.startMs)} + ${Y(e?.durationMs)}`;
917
1360
  for (let t of n) $(e, i(t), "li bad");
918
1361
  for (let t of r) $(e, `${i(t)} (absorbed by hold)`, "li dim");
919
- }, Me = (e, t) => {
920
- if (G(e), !t) {
1362
+ }, Gt = (e, t) => {
1363
+ if (jt(e), !t) {
921
1364
  $(e, "select a flight", "li dim");
922
1365
  return;
923
1366
  }
924
1367
  let n = Q(e, "timing");
925
- Z(n, "id", q(t.id)), Z(n, "kind / driver", `${q(t.kind)} · ${q(t.driver)}`), Z(n, "router", q(t.routerId)), Z(n, "t0", q(t.t0?.iso)), Z(n, "duration", J(t.durationMs)), Z(n, "participants", `${X(t.participants?.screens)} screens · ${X(t.participants?.bars)} bars · ${X(t.participants?.decorators)} decorators · ${X(t.participants?.parts)} parts`), Z(n, "hold", `${q(t.holds?.kind)} · released ${J(t.holds?.releasedAtMs)}`);
1368
+ Z(n, "id", J(t.id)), Z(n, "kind / driver", `${J(t.kind)} · ${J(t.driver)}`), Z(n, "router", J(t.routerId)), Z(n, "t0", J(t.t0?.iso)), Z(n, "duration", Y(t.durationMs)), Z(n, "participants", `${X(t.participants?.screens)} screens · ${X(t.participants?.bars)} bars · ${X(t.participants?.decorators)} decorators · ${X(t.participants?.parts)} parts`), Z(n, "hold", `${J(t.holds?.kind)} · released ${Y(t.holds?.releasedAtMs)}`);
926
1369
  let r = Q(e, "frames");
927
- ke(r, "held (absorbed)", t.frameSamples?.held), ke(r, "released (visible)", t.frameSamples?.released), t.playerGaps && Z(r, "player mirror", `max ${Y(t.playerGaps.maxMs)} · >30ms ×${X(t.playerGaps.over30Count)}`);
928
- let i = we(t.frameSamples);
929
- i && r.appendChild(Ae(i));
1370
+ Ht(r, "held (absorbed)", t.frameSamples?.held), Ht(r, "released (visible)", t.frameSamples?.released);
1371
+ let i = Lt(t.frameSamples);
1372
+ i && r.appendChild(Ut(i));
930
1373
  let a = Q(e, "motion (did it move)"), o = t.motion?.longestStallMs, s = t.motion?.holdReassertedAtMs;
931
- Z(a, "released frames", X(t.motion?.sampledFrames)), Z(a, "stalled frames", X(t.motion?.stalledFrames), (t.motion?.stalledFrames ?? 0) > 0 ? "bad" : void 0), Z(a, "longest stall", J(o), (o ?? 0) >= Ee ? "bad" : void 0), Z(a, "paused after release", xe(t.motion?.pausedAfterRelease), t.motion?.pausedAfterRelease === !0 ? "bad" : void 0), Z(a, "hold re-asserted", s == null ? "no" : J(s), typeof s == "number" ? "bad" : void 0);
932
- let c = Q(e, "images"), l = t.images?.completedDuringFlight ?? 0, u = t.images?.heldDuringFlight ?? 0, d = t.images?.completedUnheld, f = typeof d == "number" ? d > 0 : l > u;
933
- Z(c, "loading at t0", X(t.images?.loadingAtStart)), Z(c, "added mid-flight", X(t.images?.addedDuringFlight)), Z(c, "completed mid-flight", X(t.images?.completedDuringFlight), f ? "bad" : void 0), Z(c, "held by the engine", X(t.images?.heldDuringFlight)), Z(c, "completed without hold", X(d), f ? "bad" : void 0), je(Q(e, "long tasks"), t);
934
- let p = Q(e, "landing"), ee = t.landing?.residualInlineTransforms ?? [];
935
- if (ee.length === 0) Z(p, "inline residue", "clean");
936
- else for (let e of ee) Z(p, "inline residue", q(e));
937
- Z(p, "off-viewport at rest", xe(t.landing?.offViewportAtRest));
938
- let m = t.landing?.stuckStatuses ?? [];
939
- Z(p, "stuck statuses", m.length === 0 ? "none" : m.join(", "));
940
- let h = t.landing?.orphanedHolds ?? [];
941
- if (h.length === 0) Z(p, "orphaned holds", "none");
942
- else for (let e of h) Z(p, "orphaned holds", q(e), "bad");
943
- let g = Q(e, "anomalies"), _ = t.anomalies ?? [];
944
- if (_.length === 0) {
945
- $(g, "none", "li dim");
1374
+ Z(a, "released frames", X(t.motion?.sampledFrames)), Z(a, "stalled frames", X(t.motion?.stalledFrames), (t.motion?.stalledFrames ?? 0) > 0 ? "bad" : void 0), Z(a, "longest stall", Y(o), (o ?? 0) >= zt ? "bad" : void 0), Z(a, "paused after release", Pt(t.motion?.pausedAfterRelease), t.motion?.pausedAfterRelease === !0 ? "bad" : void 0), Z(a, "hold re-asserted", s == null ? "no" : Y(s), typeof s == "number" ? "bad" : void 0), Z(a, "first animation", t.motion?.firstAnimationAtMs === null || t.motion?.firstAnimationAtMs === void 0 ? "not observed" : `+${Y(t.motion.firstAnimationAtMs)} after t0`);
1375
+ let c = t.morphs;
1376
+ if (c) {
1377
+ let t = Q(e, "shared elements");
1378
+ Z(t, "registered", X(c.registered)), Z(t, "pairable / flew", `${X(c.pairable?.length)} · ${X(c.flew?.length)}`);
1379
+ let n = c.skipped ?? [];
1380
+ Z(t, "did not fly", n.length === 0 ? "none" : n.join(", "), n.length > 0 ? "bad" : void 0);
1381
+ let r = c.duplicatedKeys ?? [];
1382
+ r.length > 0 && Z(t, "duplicate keys in one screen", r.join(", "), "bad"), Z(t, "camera", Pt(c.camera)), Z(t, "ghosts", X(c.ghosts));
1383
+ let i = (c.strandedRoles ?? 0) + (c.strandedStandIns ?? 0) + (c.strandedGhosts ?? 0) + (c.layerResidue ?? 0) + (c.leakedSheetRules ?? 0);
1384
+ Z(t, "residue at rest", i === 0 ? "clean" : `${X(c.strandedRoles)} roles · ${X(c.strandedStandIns)} stand-ins · ${X(c.strandedGhosts)} ghosts · ${X(c.layerResidue)} in layer · ${X(c.leakedSheetRules)} rules`, i > 0 ? "bad" : void 0);
1385
+ }
1386
+ let l = t.tripwires ?? [];
1387
+ if (l.length > 0) {
1388
+ let t = Q(e, "tripwires (one-frame events)");
1389
+ for (let e of l) $(t, `+${Y(e.atMs)} ${e.kind}: ${e.detail}`, "li bad");
1390
+ }
1391
+ let u = t.input;
1392
+ if (u) {
1393
+ let t = Q(e, "what drove it");
1394
+ Z(t, "input", `${X(u.trusted)} trusted · ${X(u.synthetic)} synthetic`, u.synthetic > 0 ? "bad" : void 0), Z(t, "pointer types", u.pointerTypes?.join(", ") || "none observed");
1395
+ }
1396
+ let d = Q(e, "images"), f = t.images?.completedDuringFlight ?? 0, p = t.images?.heldDuringFlight ?? 0, m = t.images?.completedUnheld, h = typeof m == "number" ? m > 0 : f > p;
1397
+ Z(d, "loading at t0", X(t.images?.loadingAtStart)), Z(d, "added mid-flight", X(t.images?.addedDuringFlight)), Z(d, "completed mid-flight", X(t.images?.completedDuringFlight), h ? "bad" : void 0), Z(d, "held by the engine", X(t.images?.heldDuringFlight)), Z(d, "completed without hold", X(m), h ? "bad" : void 0), Wt(Q(e, "long tasks"), t);
1398
+ let g = Q(e, "landing"), _ = t.landing?.residualInlineTransforms ?? [];
1399
+ if (_.length === 0) Z(g, "inline residue", "clean");
1400
+ else for (let e of _) Z(g, "inline residue", J(e));
1401
+ Z(g, "off-viewport at rest", Pt(t.landing?.offViewportAtRest));
1402
+ let v = t.landing?.stuckStatuses ?? [];
1403
+ Z(g, "stuck statuses", v.length === 0 ? "none" : v.join(", "));
1404
+ let y = t.landing?.orphanedHolds ?? [];
1405
+ if (y.length === 0) Z(g, "orphaned holds", "none");
1406
+ else for (let e of y) Z(g, "orphaned holds", J(e), "bad");
1407
+ let b = Q(e, "anomalies"), x = t.anomalies ?? [];
1408
+ if (x.length === 0) {
1409
+ $(b, "none", "li dim");
946
1410
  return;
947
1411
  }
948
- for (let e of _) $(g, q(e), "li bad");
949
- }, Ne = (e, t) => {
950
- G(e);
951
- for (let n of t ?? []) $(e, q(n));
952
- }, Pe = _.map((e) => y(c) + te(l, e)).join(","), Fe = 320, Ie = 2e3, Le = 120, Re = "Copy report JSON", ze = () => {}, Be = null, Ve = (e = {}) => {
953
- if (Be) return Be;
954
- if (typeof document > "u") return { detach: ze };
955
- let t = !1, n, r = window.flemo;
956
- e.recorder ? n = e.recorder : r?.__flemoDevtools === !0 && typeof r.report == "function" ? n = {
957
- report: r.report,
958
- detach: ze
959
- } : (n = ve(), t = !0);
960
- let i = W("div");
961
- i.setAttribute(g, ""), i.style.position = "fixed", i.style.top = "0", i.style.left = "0", i.style.width = "0", i.style.height = "0", i.style.zIndex = "2147483000";
962
- let a = i.attachShadow({ mode: "open" }), o = W("style");
963
- o.textContent = Te, a.appendChild(o);
964
- let s = W("div", "root");
965
- a.appendChild(s);
966
- let c = W("button", "toggle");
967
- c.type = "button", c.setAttribute("data-corner", e.position ?? "bottom-right"), c.setAttribute("aria-label", "flemo devtools");
968
- let l = W("span", "count", "0"), u = W("span", "dot");
969
- u.hidden = !0, c.appendChild(W("span", "mark", "flemo")), c.appendChild(l), c.appendChild(u);
970
- let d = W("section", "panel");
971
- d.hidden = !0;
972
- let f = W("div", "grip");
973
- f.setAttribute("role", "separator"), f.setAttribute("aria-label", "Resize flemo devtools panel");
974
- let p = W("header", "head"), ee = W("div", "env", "—"), m = W("button", "act", Re);
1412
+ for (let e of x) $(b, J(e), "li bad");
1413
+ }, Kt = (e, t) => {
1414
+ jt(e);
1415
+ for (let n of t ?? []) $(e, J(n));
1416
+ }, qt = 320, Jt = 2e3, Yt = 120, Xt = "Copy report JSON", Zt = () => {}, Qt = null, $t = (e = {}) => {
1417
+ if (Qt) return Qt;
1418
+ if (typeof document > "u") return { detach: Zt };
1419
+ let { recorder: t, ownsRecorder: n } = Ot(e.recorder), { host: r, root: i } = Et(Rt), a = q("button", "toggle");
1420
+ a.type = "button", a.setAttribute("data-corner", e.position ?? "bottom-right"), a.setAttribute("aria-label", "flemo devtools");
1421
+ let o = q("span", "count", "0"), s = q("span", "dot");
1422
+ s.hidden = !0, a.appendChild(q("span", "mark", "flemo")), a.appendChild(o), a.appendChild(s);
1423
+ let c = q("section", "panel");
1424
+ c.hidden = !0;
1425
+ let l = q("div", "grip");
1426
+ l.setAttribute("role", "separator"), l.setAttribute("aria-label", "Resize flemo devtools panel");
1427
+ let u = q("header", "head"), d = q("div", "env", "—"), f = q("button", "act", Xt);
1428
+ f.type = "button";
1429
+ let p = q("button", "act", "Close");
1430
+ p.type = "button";
1431
+ let m = q("button", "act", "Detach");
975
1432
  m.type = "button";
976
- let h = W("button", "act", "Close");
1433
+ let h = q("button", "act", "A/B: off");
977
1434
  h.type = "button";
978
- let _ = W("button", "act", "Detach");
979
- _.type = "button";
980
- let v = W("div", "chips");
981
- p.appendChild(ee), p.appendChild(W("div", "spacer")), p.appendChild(m), p.appendChild(h), p.appendChild(_), p.appendChild(v);
982
- let y = W("div", "body"), te = W("div", "list"), b = W("div", "detail");
983
- y.appendChild(te), y.appendChild(b);
984
- let x = W("div", "foot"), S = W("details");
985
- S.appendChild(W("summary", void 0, "What this cannot see"));
986
- let ne = W("div");
987
- S.appendChild(ne), x.appendChild(S), d.appendChild(f), d.appendChild(p), d.appendChild(y), d.appendChild(x), s.appendChild(c), s.appendChild(d);
988
- let C = !1, w = e.initialOpen === !0, T = null, D = "", O = "", k = "", A = !1, j = 0, M = 0, N = (e) => Math.min(Math.max(e, Le), Math.max(Le, window.innerHeight * .9)), re = () => {
1435
+ let g = q("div", "chips");
1436
+ u.appendChild(d), u.appendChild(q("div", "spacer")), u.appendChild(h), u.appendChild(f), u.appendChild(p), u.appendChild(m), u.appendChild(g);
1437
+ let _ = q("div", "body"), v = q("div", "list"), y = q("div", "detail");
1438
+ _.appendChild(v), _.appendChild(y);
1439
+ let b = q("div", "foot"), x = q("details");
1440
+ x.appendChild(q("summary", void 0, "What this cannot see"));
1441
+ let S = q("div");
1442
+ x.appendChild(S), b.appendChild(x), c.appendChild(l), c.appendChild(u), c.appendChild(_), c.appendChild(b), i.appendChild(a), i.appendChild(c);
1443
+ let C = !1, w = e.initialOpen === !0, T = null, E = "", D = "", O = "", k = !1, A = 0, j = 0, M = -1, N = e.buckets && e.buckets.length > 0 ? e.buckets : ["A", "B"], P = (e) => Math.min(Math.max(e, Yt), Math.max(Yt, window.innerHeight * .9)), ee = () => {
989
1444
  try {
990
- let e = sessionStorage.getItem(E);
1445
+ let e = sessionStorage.getItem(U);
991
1446
  if (e === null) return null;
992
1447
  let t = Number.parseFloat(e);
993
1448
  return Number.isFinite(t) ? t : null;
994
1449
  } catch {
995
1450
  return null;
996
1451
  }
997
- }, ie = (e) => {
1452
+ }, te = (e) => {
998
1453
  try {
999
- sessionStorage.setItem(E, String(Math.round(e)));
1454
+ sessionStorage.setItem(U, String(Math.round(e)));
1000
1455
  } catch {}
1001
- }, P = N(re() ?? window.innerHeight * .4);
1002
- d.style.height = `${Math.round(P)}px`;
1003
- let F = () => document.querySelector(Pe) !== null, ae = () => {
1456
+ }, F = P(ee() ?? window.innerHeight * .4);
1457
+ c.style.height = `${Math.round(F)}px`;
1458
+ let I = () => {
1004
1459
  try {
1005
- return n.report();
1460
+ return t.report();
1006
1461
  } catch {
1007
1462
  return null;
1008
1463
  }
1009
- }, I = (e) => {
1010
- e !== T && (T = e, R());
1011
- }, L = () => {
1012
- let e = ae(), t = e?.flights ?? [];
1013
- K(l, String(t.length));
1464
+ }, ne = (e) => {
1465
+ e !== T && (T = e, L());
1466
+ }, re = () => {
1467
+ let e = I(), t = e?.flights ?? [];
1468
+ Mt(o, String(t.length));
1014
1469
  let n = t.some((e) => (e?.anomalies?.length ?? 0) > 0);
1015
- if (u.hidden = !n, d.hidden = !w, !w) return;
1016
- K(ee, Se(e));
1470
+ if (s.hidden = !n, c.hidden = !w, !w) return;
1471
+ Mt(d, Ft(e));
1017
1472
  let r = JSON.stringify([
1018
1473
  e?.overrides?.warnings ?? [],
1019
1474
  e?.anomalies ?? [],
1020
1475
  e?.overrides?.active ?? {}
1021
1476
  ]);
1022
- r !== k && (k = r, De(v, e)), T === null && t.length > 0 && (T = t[t.length - 1]?.id ?? null);
1023
- let i = Ce(t, T);
1024
- i !== D && (D = i, Oe(te, t, T, I));
1025
- let a = t.find((e) => e?.id === T) ?? null, o = JSON.stringify(a);
1026
- o !== O && (O = o, Me(b, a)), A || (A = !0, Ne(ne, e?.blindSpots));
1477
+ r !== O && (O = r, Bt(g, e)), T === null && t.length > 0 && (T = t[t.length - 1]?.id ?? null);
1478
+ let i = It(t, T);
1479
+ i !== E && (E = i, Vt(v, t, T, ne));
1480
+ let a = t.find((e) => e?.id === T) ?? null, l = JSON.stringify(a);
1481
+ l !== D && (D = l, Gt(y, a)), k || (k = !0, Kt(S, e?.blindSpots));
1027
1482
  };
1028
- function R() {
1029
- C || F() || L();
1483
+ function L() {
1484
+ C || Tt() || re();
1030
1485
  }
1031
- function oe() {
1032
- j = 0, R(), z();
1486
+ function ie() {
1487
+ A = 0, L(), ae();
1033
1488
  }
1034
- function z() {
1035
- j = window.setTimeout(oe, w ? Fe : Ie);
1489
+ function ae() {
1490
+ A = window.setTimeout(ie, w ? qt : Jt);
1036
1491
  }
1037
- let se = () => {
1038
- window.clearTimeout(j), z();
1039
- }, ce = (e) => {
1040
- w = e, se(), R();
1492
+ let oe = () => {
1493
+ window.clearTimeout(A), ae();
1494
+ }, se = (e) => {
1495
+ w = e, oe(), L();
1041
1496
  };
1042
- c.addEventListener("click", () => ce(!w)), h.addEventListener("click", () => ce(!1)), _.addEventListener("click", () => de());
1043
- let le = () => {
1497
+ a.addEventListener("click", () => se(!w)), p.addEventListener("click", () => se(!1)), m.addEventListener("click", () => z()), h.addEventListener("click", () => {
1498
+ M = M + 1 >= N.length ? -1 : M + 1;
1499
+ let e = M === -1 ? null : N[M];
1500
+ t.mark(e), Mt(h, `A/B: ${e ?? "off"}`), O = "", L();
1501
+ });
1502
+ let ce = () => {
1044
1503
  if (!C) {
1045
- if (F()) {
1046
- window.clearTimeout(M), M = window.setTimeout(le, Fe);
1504
+ if (Tt()) {
1505
+ window.clearTimeout(j), j = window.setTimeout(ce, qt);
1047
1506
  return;
1048
1507
  }
1049
- K(m, "Copied ✓"), window.clearTimeout(M), M = window.setTimeout(B, 1200);
1508
+ Mt(f, "Copied ✓"), window.clearTimeout(j), j = window.setTimeout(le, 1200);
1050
1509
  }
1051
1510
  };
1052
- function B() {
1053
- if (F()) {
1054
- M = window.setTimeout(B, Fe);
1511
+ function le() {
1512
+ if (Tt()) {
1513
+ j = window.setTimeout(le, qt);
1055
1514
  return;
1056
1515
  }
1057
- M = 0, K(m, Re);
1516
+ j = 0, Mt(f, Xt);
1058
1517
  }
1059
- let V = (e) => {
1518
+ let ue = (e) => {
1060
1519
  if (C) return;
1061
- if (F()) {
1062
- window.clearTimeout(M), M = window.setTimeout(() => V(e), Fe);
1520
+ if (Tt()) {
1521
+ window.clearTimeout(j), j = window.setTimeout(() => ue(e), qt);
1063
1522
  return;
1064
1523
  }
1065
- let t = W("textarea");
1524
+ let t = q("textarea");
1066
1525
  t.value = e, t.setAttribute("aria-hidden", "true"), t.style.position = "fixed", t.style.opacity = "0", t.style.pointerEvents = "none", document.body.appendChild(t), t.select();
1067
1526
  let n = document;
1068
1527
  try {
1069
1528
  n.execCommand?.("copy");
1070
1529
  } catch {}
1071
- t.remove(), le();
1530
+ t.remove(), ce();
1072
1531
  };
1073
- m.addEventListener("click", () => {
1074
- let e = JSON.stringify(ae(), null, 2), t = navigator.clipboard;
1532
+ f.addEventListener("click", () => {
1533
+ let e = JSON.stringify(I(), null, 2), t = navigator.clipboard;
1075
1534
  if (t && typeof t.writeText == "function") {
1076
- t.writeText(e).then(le, () => V(e));
1535
+ t.writeText(e).then(ce, () => ue(e));
1077
1536
  return;
1078
1537
  }
1079
- V(e);
1538
+ ue(e);
1080
1539
  });
1081
- let ue = (e) => {
1082
- P = N(window.innerHeight - e.clientY), d.style.height = `${Math.round(P)}px`;
1083
- }, H = () => {
1084
- window.removeEventListener("pointermove", ue), window.removeEventListener("pointerup", H), ie(P);
1540
+ let de = (e) => {
1541
+ F = P(window.innerHeight - e.clientY), c.style.height = `${Math.round(F)}px`;
1542
+ }, R = () => {
1543
+ window.removeEventListener("pointermove", de), window.removeEventListener("pointerup", R), te(F);
1085
1544
  };
1086
- f.addEventListener("pointerdown", (e) => {
1087
- e.preventDefault(), window.addEventListener("pointermove", ue), window.addEventListener("pointerup", H);
1545
+ l.addEventListener("pointerdown", (e) => {
1546
+ e.preventDefault(), window.addEventListener("pointermove", de), window.addEventListener("pointerup", R);
1088
1547
  });
1089
- function de() {
1090
- C || (C = !0, window.clearTimeout(j), j = 0, window.clearTimeout(M), M = 0, window.removeEventListener("pointermove", ue), window.removeEventListener("pointerup", H), i.remove(), t && n.detach(), Be = null);
1548
+ function z() {
1549
+ C || (C = !0, window.clearTimeout(A), A = 0, window.clearTimeout(j), j = 0, window.removeEventListener("pointermove", de), window.removeEventListener("pointerup", R), r.remove(), n && t.detach(), Qt = null);
1091
1550
  }
1092
- document.body.appendChild(i), R(), z();
1093
- let fe = { detach: de };
1094
- return Be = fe, fe;
1551
+ document.body.appendChild(r), L(), ae();
1552
+ let fe = { detach: z };
1553
+ return Qt = fe, fe;
1554
+ }, en = 500, tn = 450, nn = "\n:host { all: initial; }\n.root { all: initial; }\n.hud {\n position: fixed;\n left: 50%;\n transform: translateX(-50%);\n max-width: calc(100vw - 16px);\n box-sizing: border-box;\n padding: 6px 10px;\n border-radius: 8px;\n background: #0b0f14;\n color: #e8f0f8;\n border: 1px solid #2b3a4a;\n font: 500 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n white-space: pre;\n text-align: left;\n overflow-x: auto;\n -webkit-user-select: none;\n user-select: none;\n touch-action: manipulation;\n /* No transition and no keyframe anywhere in this sheet: the readout must\n never be a moving thing on a screen whose motion is under test. */\n}\n.hud[data-edge=\"top\"] { top: max(8px, env(safe-area-inset-top)); }\n.hud[data-edge=\"bottom\"] { bottom: max(8px, env(safe-area-inset-bottom)); }\n.hud[data-alarm=\"true\"] { border-color: #ff6b6b; color: #ffd9d9; }\n.hud[data-blocked=\"true\"] { border-color: #ffb020; color: #ffe9c2; }\n", rn = (e) => Math.round(e * 10) / 10, an = (e) => {
1555
+ if (!e) return "flemo no flight yet";
1556
+ let t = e.frameSamples?.released, n = e.anomalies?.length ?? 0;
1557
+ return `${e.kind} ${Math.round(e.durationMs)}ms gap ${rn(t?.maxGapMs ?? 0)} drop ${t?.over30Count ?? 0}` + (n > 0 ? ` !${n}` : " ok") + (e.bucket ? ` [${e.bucket}]` : "");
1558
+ }, on = (e, t) => {
1559
+ let n = [], r = (e?.preconditions ?? []).filter((e) => e.status === "violated");
1560
+ if (r.length > 0 && n.push(`BLOCKED ${r.map((e) => e.id).join(" ")}`), !t) return n.push(`rAF ${rn(e?.environment?.rafCadence?.medianGapMs ?? 0)}ms`), n;
1561
+ let i = t.frameSamples;
1562
+ n.push(`frames n${i?.released?.count ?? 0} med ${rn(i?.released?.medianGapMs ?? 0)} max ${rn(i?.released?.maxGapMs ?? 0)}`), n.push(`motion stall ${rn(t.motion?.longestStallMs ?? 0)}ms tail ${t.motion?.tailFrames ?? 0} start +${t.motion?.firstAnimationAtMs ?? "?"}ms`), n.push(`hold ${t.holds?.kind ?? "none"} rel ${t.holds?.releasedAtMs ?? "-"}ms task ${t.longTasks?.length ?? 0}`);
1563
+ let a = t.morphs;
1564
+ a && (a.pairable.length > 0 || a.flew.length > 0) && n.push(`morph ${a.flew.length} flew` + (a.camera ? " cam" : "") + (a.skipped.length > 0 ? ` SKIPPED ${a.skipped.length} (${a.skipped.join(",")})` : ""));
1565
+ let o = t.input;
1566
+ o && n.push(`input ${o.pointerTypes.join("/") || "none"}` + (o.synthetic > 0 ? ` SYNTHETIC ${o.synthetic}` : ""));
1567
+ for (let e of t.anomalies ?? []) n.push(`! ${e.slice(0, 120)}`);
1568
+ return n;
1569
+ }, sn = null, cn = (e = {}) => {
1570
+ if (sn) return sn;
1571
+ if (typeof document > "u") return { detach: () => {} };
1572
+ let { recorder: t, ownsRecorder: n } = Ot(e.recorder), r = e.buckets && e.buckets.length > 0 ? e.buckets : ["A", "B"], { host: i, root: a } = Et(nn), o = document.createElement("div");
1573
+ o.className = "hud", o.setAttribute("data-edge", e.position ?? "top"), o.setAttribute("role", "status"), o.textContent = "flemo attaching", a.appendChild(o);
1574
+ let s = !1, c = e.initialExpanded === !0, l = -1, u = 0, d = 0, f = !1, p = "", m = () => {
1575
+ try {
1576
+ return t.report();
1577
+ } catch {
1578
+ return null;
1579
+ }
1580
+ }, h = () => {
1581
+ if (s || Tt()) return;
1582
+ let e = m(), t = e?.flights ?? [], n = t[t.length - 1], r = [an(n)];
1583
+ c && r.push(...on(e, n));
1584
+ let i = r.join("\n"), a = (e?.preconditions ?? []).some((e) => e.status === "violated"), l = (n?.anomalies?.length ?? 0) > 0;
1585
+ i !== p && (p = i, o.textContent = i), o.setAttribute("data-alarm", l ? "true" : "false"), o.setAttribute("data-blocked", a ? "true" : "false");
1586
+ }, g = () => {
1587
+ u = 0, h(), u = window.setTimeout(g, en);
1588
+ }, _ = () => {
1589
+ l = l + 1 >= r.length ? -1 : l + 1, t.mark(l === -1 ? null : r[l]), p = "", h();
1590
+ };
1591
+ o.addEventListener("pointerdown", () => {
1592
+ f = !1, window.clearTimeout(d), d = window.setTimeout(() => {
1593
+ f = !0, _();
1594
+ }, tn);
1595
+ }), o.addEventListener("pointerup", () => {
1596
+ window.clearTimeout(d), d = 0, !f && (c = !c, p = "", h());
1597
+ }), o.addEventListener("pointercancel", () => window.clearTimeout(d));
1598
+ let v = () => {
1599
+ s || (s = !0, window.clearTimeout(u), u = 0, window.clearTimeout(d), d = 0, i.remove(), n && t.detach(), sn = null);
1600
+ };
1601
+ document.body.appendChild(i), h(), u = window.setTimeout(g, en);
1602
+ let y = { detach: v };
1603
+ return sn = y, y;
1095
1604
  };
1096
1605
  //#endregion
1097
- export { s as BLIND_SPOTS, O as CORE_FLAGS, D as DEVTOOLS_OWNED_FLAGS, k as FLAG_REGISTRY, e as LONG_GAP_MS, i as MID_FLIGHT_TASK_MS, n as OPENING_WINDOW_LEAD_MS, r as OPENING_WINDOW_TAIL_MS, E as PANEL_HEIGHT_KEY, le as REPORT_SCHEMA_VERSION, A as RETIRED_FLAGS, j as RETIRED_MARKER, t as STUCK_STATUS_MS, Ve as attachDevtoolsPanel, ve as attachFlightRecorder, w as captureEnvironment, z as classifyDriver, R as computeFrameStats, L as computePhaseStats, oe as computePlayerGapStats, a as deriveFlightAnomalies, ae as deriveOverrideWarnings, o as deriveReportAnomalies, x as detectEngine, S as isEmulationSuspected, se as kindFromStatus, ce as parseTranslateX, ne as sampleRafCadence, F as snapshotOverrides };
1606
+ export { c as BLIND_SPOTS, Fe as CORE_FLAGS, Pe as DEVTOOLS_OWNED_FLAGS, W as FLAG_REGISTRY, ge as JUDGING_PROTOCOL, e as LONG_GAP_MS, i as MID_FLIGHT_TASK_MS, n as OPENING_WINDOW_LEAD_MS, r as OPENING_WINDOW_TAIL_MS, U as PANEL_HEIGHT_KEY, mt as REPORT_SCHEMA_VERSION, G as RETIRED_FLAGS, Ie as RETIRED_MARKER, a as STALL_MS, t as STUCK_STATUS_MS, Ue as TRACE_KEY, cn as attachDevtoolsHud, $t as attachDevtoolsPanel, Ct as attachFlightRecorder, ne as captureEnvironment, ve as classifyDriver, Ke as clearTrace, _e as computeFrameStats, V as computePhaseStats, o as deriveFlightAnomalies, He as deriveOverrideWarnings, it as derivePreconditions, s as deriveReportAnomalies, pt as deriveVerdict, N as detectEngine, F as developmentHints, P as isEmulationSuspected, ye as kindFromStatus, qe as loadTrace, be as parseTranslateX, ee as sampleRafCadence, Ge as saveTrace, Ve as snapshotOverrides, u as summariseBuckets };