@hueest/xray 0.6.0 → 0.7.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/client.js CHANGED
@@ -1,45 +1,4 @@
1
- import { a as serializePlate, d as formatDiagnostic, f as makeDiagnostic, l as runPlatePasses, n as deriveBreakpoints, o as CaptureTooLargeError, p as emit, r as regimeFor, s as captureView, t as serializePlateIR, u as diagnosticsHeader } from "./project-BAXKpnfF.js";
2
- //#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/base64.js
3
- /** @type {(array_buffer: ArrayBuffer) => string} */
4
- function encode_native(array_buffer) {
5
- return new Uint8Array(array_buffer).toBase64();
6
- }
7
- /** @type {(base64: string) => ArrayBuffer} */
8
- function decode_native(base64) {
9
- return Uint8Array.fromBase64(base64).buffer;
10
- }
11
- /** @type {(array_buffer: ArrayBuffer) => string} */
12
- function encode_buffer(array_buffer) {
13
- return Buffer.from(array_buffer).toString("base64");
14
- }
15
- /** @type {(base64: string) => ArrayBuffer} */
16
- function decode_buffer(base64) {
17
- return Uint8Array.from(Buffer.from(base64, "base64")).buffer;
18
- }
19
- /** @type {(array_buffer: ArrayBuffer) => string} */
20
- function encode_legacy(array_buffer) {
21
- const array = new Uint8Array(array_buffer);
22
- let binary = "";
23
- const chunk_size = 32768;
24
- for (let i = 0; i < array.length; i += chunk_size) {
25
- const chunk = array.subarray(i, i + chunk_size);
26
- binary += String.fromCharCode.apply(null, chunk);
27
- }
28
- return btoa(binary);
29
- }
30
- /** @type {(base64: string) => ArrayBuffer} */
31
- function decode_legacy(base64) {
32
- const binary_string = atob(base64);
33
- const len = binary_string.length;
34
- const array = new Uint8Array(len);
35
- for (let i = 0; i < len; i++) array[i] = binary_string.charCodeAt(i);
36
- return array.buffer;
37
- }
38
- const native = typeof Uint8Array.fromBase64 === "function";
39
- const buffer = typeof process === "object" && process.versions?.node !== void 0;
40
- const encode64 = native ? encode_native : buffer ? encode_buffer : encode_legacy;
41
- const decode64 = native ? decode_native : buffer ? decode_buffer : decode_legacy;
42
- //#endregion
1
+ import { a as serializePlate, d as formatDiagnostic, f as makeDiagnostic, l as distilPlate, n as deriveBreakpoints, o as CaptureTooLargeError, p as emit, r as viewSpanFor, s as captureView, t as serializePlateIR, u as diagnosticsHeader } from "./serialize-Cj-EHvMv.js";
43
2
  //#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/constants.js
44
3
  const MAX_ARRAY_LEN = 2 ** 32 - 1;
45
4
  const MAX_ARRAY_INDEX = MAX_ARRAY_LEN - 1;
@@ -149,6 +108,47 @@ function valid_array_indices(array) {
149
108
  return keys;
150
109
  }
151
110
  //#endregion
111
+ //#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/base64.js
112
+ /** @type {(array_buffer: ArrayBuffer) => string} */
113
+ function encode_native(array_buffer) {
114
+ return new Uint8Array(array_buffer).toBase64();
115
+ }
116
+ /** @type {(base64: string) => ArrayBuffer} */
117
+ function decode_native(base64) {
118
+ return Uint8Array.fromBase64(base64).buffer;
119
+ }
120
+ /** @type {(array_buffer: ArrayBuffer) => string} */
121
+ function encode_buffer(array_buffer) {
122
+ return Buffer.from(array_buffer).toString("base64");
123
+ }
124
+ /** @type {(base64: string) => ArrayBuffer} */
125
+ function decode_buffer(base64) {
126
+ return Uint8Array.from(Buffer.from(base64, "base64")).buffer;
127
+ }
128
+ /** @type {(array_buffer: ArrayBuffer) => string} */
129
+ function encode_legacy(array_buffer) {
130
+ const array = new Uint8Array(array_buffer);
131
+ let binary = "";
132
+ const chunk_size = 32768;
133
+ for (let i = 0; i < array.length; i += chunk_size) {
134
+ const chunk = array.subarray(i, i + chunk_size);
135
+ binary += String.fromCharCode.apply(null, chunk);
136
+ }
137
+ return btoa(binary);
138
+ }
139
+ /** @type {(base64: string) => ArrayBuffer} */
140
+ function decode_legacy(base64) {
141
+ const binary_string = atob(base64);
142
+ const len = binary_string.length;
143
+ const array = new Uint8Array(len);
144
+ for (let i = 0; i < len; i++) array[i] = binary_string.charCodeAt(i);
145
+ return array.buffer;
146
+ }
147
+ const native = typeof Uint8Array.fromBase64 === "function";
148
+ const buffer = typeof process === "object" && process.versions?.node !== void 0;
149
+ const encode64 = native ? encode_native : buffer ? encode_buffer : encode_legacy;
150
+ const decode64 = native ? decode_native : buffer ? decode_buffer : decode_legacy;
151
+ //#endregion
152
152
  //#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/parse.js
153
153
  /**
154
154
  * Revive a value serialized with `devalue.stringify`
@@ -257,7 +257,8 @@ function unflatten(parsed, revivers) {
257
257
  case "ArrayBuffer": {
258
258
  const base64 = value[1];
259
259
  if (typeof base64 !== "string") throw new Error("Invalid ArrayBuffer encoding");
260
- hydrated[index] = decode64(base64);
260
+ const arraybuffer = decode64(base64);
261
+ hydrated[index] = arraybuffer;
261
262
  break;
262
263
  }
263
264
  case "Temporal.Duration":
@@ -272,12 +273,16 @@ function unflatten(parsed, revivers) {
272
273
  hydrated[index] = Temporal[temporalName].from(value[1]);
273
274
  break;
274
275
  }
275
- case "URL":
276
- hydrated[index] = new URL(value[1]);
276
+ case "URL": {
277
+ const url = new URL(value[1]);
278
+ hydrated[index] = url;
277
279
  break;
278
- case "URLSearchParams":
279
- hydrated[index] = new URLSearchParams(value[1]);
280
+ }
281
+ case "URLSearchParams": {
282
+ const url = new URLSearchParams(value[1]);
283
+ hydrated[index] = url;
280
284
  break;
285
+ }
281
286
  default: throw new Error(`Unknown type ${type}`);
282
287
  }
283
288
  } else if (value[0] === -7) {
@@ -526,17 +531,15 @@ function stringify_primitive(thing) {
526
531
  return String(thing);
527
532
  }
528
533
  //#endregion
529
- //#region src/flash.ts
534
+ //#region src/blink.ts
530
535
  /**
531
- * Dev-only radiographic capture effect pure delight. When a capture lands (and
532
- * actually CHANGES the stored result) we briefly "expose" the captured element: a
533
- * negative blink (an x-ray IS a negative `backdrop-filter: invert`) that enters
534
- * SHARPLY and fades a touch slower, framed by a soft shadow ring around the box so
535
- * you can see WHICH area was captured. Short and quiet it's feedback that a
536
- * resize/navigation (or the capture-all sweep) recorded something, not a light show.
537
- * Honors `prefers-reduced-motion` (then just a shadow pulse, no invert). A transient
538
- * `position: fixed`, `pointer-events: none` overlay just under the HUD; it never
539
- * touches the capture pipeline or the captured DOM.
536
+ * The BLINK: the dev-only visual that confirms a capture landed and actually CHANGED the stored
537
+ * result. It briefly "exposes" the captured element: a negative invert (`backdrop-filter: invert` —
538
+ * an x-ray is a negative) that enters sharply and fades slower, framed by a soft shadow ring so the
539
+ * captured area is visible. Feedback, not a light show. Under `prefers-reduced-motion` the invert
540
+ * is dropped and only the shadow pulses. Rendered as a transient `position: fixed`,
541
+ * `pointer-events: none` overlay just under the HUD; it never touches the capture pipeline or the
542
+ * captured DOM.
540
543
  */
541
544
  const STYLE_ID = "xr-flash-style";
542
545
  const DURATION_MS = 300;
@@ -585,8 +588,8 @@ function boxOf(el) {
585
588
  }
586
589
  return Number.isFinite(left) ? new DOMRect(left, top, right - left, bottom - top) : null;
587
590
  }
588
- /** Flash the radiographic effect over `el`'s box. No-op outside a laid-out DOM. */
589
- function flashCapture(el) {
591
+ /** Blink the radiographic effect over `el`'s box. No-op outside a laid-out DOM. */
592
+ function blinkCapture(el) {
590
593
  if (typeof document === "undefined") return;
591
594
  const box = boxOf(el);
592
595
  if (!box) return;
@@ -612,17 +615,14 @@ const DEFAULT_MAX_BREADTH = 512;
612
615
  const deriveMaxNodes = (maxBreadth, maxDepth) => Math.round(maxBreadth * maxDepth / 4);
613
616
  const RESIZE_DEBOUNCE = 300;
614
617
  /**
615
- * The capture roots for a site, the ONE place the auto/manual root models
616
- * diverge (ADR 0014, finding #6).
618
+ * The capture roots for a site, the ONE place the auto/manual root models diverge (ADR 0014).
617
619
  *
618
- * - AUTO: `site.el` is the `display:contents` wrapper, a layout-neutral box, so
619
- * the content is its CHILDREN — the synthetic root (id 0) stands in for the
620
- * wrapper and the children become the plate's top level (today's behavior).
621
- * `<STYLE>` children are dropped (a stitched-in scoped sheet, never content).
622
- * - MANUAL: `site.el` is the consumer's OWN styled element, so the element
623
- * ITSELF is the root. Capturing its children would drop its box, classes,
624
- * padding, border, background, and radius — the exact bug this fixes — so the
625
- * element is the single root and its own box is serialized.
620
+ * - AUTO: `site.el` is the `display:contents` wrapper, a layout-neutral box, so the content is its
621
+ * CHILDREN — the synthetic root (id 0) stands in for the wrapper and the children become the
622
+ * plate's top level. `<STYLE>` children are dropped (a stitched-in scoped sheet, never content).
623
+ * - MANUAL: `site.el` is the consumer's OWN styled element, so the element ITSELF is the root.
624
+ * Capturing its children would drop its box, classes, padding, border, background, and radius —
625
+ * so the element is the single root and its own box is serialized.
626
626
  */
627
627
  function rootsFor(site) {
628
628
  if (site.mode === "manual") return [site.el];
@@ -681,7 +681,8 @@ function installXrayClient(options) {
681
681
  };
682
682
  const lightbox = createToggleStore(win, LIGHTBOX_KEY, LIGHTBOX_PARAM, false);
683
683
  const captureEnabled = createToggleStore(win, CAPTURE_KEY, CAPTURE_PARAM, options.capture ?? false);
684
- const replayEnabled = createToggleStore(win, REPLAY_KEY, REPLAY_PARAM, options.replay === "prefer" || options.replay === "missing-only");
684
+ const replayBoot = options.replay === "prefer" || options.replay === "missing-only";
685
+ const replayEnabled = createToggleStore(win, REPLAY_KEY, REPLAY_PARAM, replayBoot);
685
686
  const replayMode = options.replay === "missing-only" ? "missing-only" : "prefer";
686
687
  const recordMode = options.record ?? "manual";
687
688
  const isPrimaryForName = (site) => {
@@ -692,22 +693,20 @@ function installXrayClient(options) {
692
693
  return true;
693
694
  };
694
695
  /**
695
- * Measure one View into a LOCAL, fresh 1-View `PlateIR` (no shared bundle) and
696
- * return it only if the generation guard still holds AFTER the settle the
697
- * heart of the ADR 0022 generation-guard preservation (plan §4). Because
698
- * `captureView(roots, plate)` MUTATES the passed bundle in place
699
- * (`plate.views.push`), a stale capture appended to a SHARED session PlateIR
700
- * would corrupt the whole Plate's projection (one width's geometry under
701
- * another's island/gate), and post-hoc rollback is impossible. So we capture
702
- * into a local bundle FIRST, re-check the guard, and only the caller splices the
703
- * survivor into the session bundle. Returns the local View on success, or
704
- * `undefined` when the guard moved / the capture was refused / the site was torn
705
- * down — the caller then DISCARDS it (never appends).
696
+ * Measure one View into a LOCAL, fresh 1-View `PlateIR` (no shared bundle) and return it only if
697
+ * the generation guard still holds AFTER the settle (the generation guard, ADR 0025; ADR 0022
698
+ * §4). Because `captureView(roots, plate)` MUTATES the passed bundle in place
699
+ * (`plate.views.push`), a stale capture appended to a SHARED session PlateIR would corrupt the
700
+ * whole Plate's projection (one width's geometry under another's island/gate), and post-hoc
701
+ * rollback is impossible. So the capture goes into a local bundle FIRST, the guard is re-checked,
702
+ * and only the caller splices the survivor into the session bundle. Returns the local View on
703
+ * success, or `undefined` when the guard moved / the capture was refused / the site was torn down
704
+ * the caller then DISCARDS it (never appends).
706
705
  *
707
706
  * `generation` is the token snapshotted by the caller BEFORE the settle; a
708
- * width-change/disposal/uninstall mid-settle moves `captureGeneration` and this
709
- * returns undefined. A `CaptureTooLargeError` marks the site and warns. Any
710
- * other failure warns and returns undefined.
707
+ * width-change/disposal/uninstall mid-settle moves `captureGeneration` and this returns
708
+ * undefined. A `CaptureTooLargeError` marks the site and warns. Any other failure warns and
709
+ * returns undefined.
711
710
  */
712
711
  const captureLocalView = async (site, generation) => {
713
712
  await settle(site.el, site.delay, settleCap, site.mode);
@@ -736,13 +735,13 @@ function installXrayClient(options) {
736
735
  if (generation !== captureGeneration || site.disposed) return void 0;
737
736
  return view;
738
737
  };
739
- const recording = /* @__PURE__ */ new Map();
740
- const recordingBreakpoints = (buf) => [...new Set([...buf.views.flatMap((v) => v.bpSeed ?? []), ...deriveBreakpoints([], [...matchMediaQueries])])].toSorted((a, b) => a - b);
741
- const projectRecording = (buf) => {
738
+ const sessionBuffers = /* @__PURE__ */ new Map();
739
+ const sessionBreakpoints = (buf) => [.../* @__PURE__ */ new Set([...buf.views.flatMap((v) => v.bpSeed ?? []), ...deriveBreakpoints([], [...matchMediaQueries])])].toSorted((a, b) => a - b);
740
+ const projectSessionBuffer = (buf) => {
742
741
  if (buf.views.length === 0) return void 0;
743
- const breakpoints = recordingBreakpoints(buf);
742
+ const breakpoints = sessionBreakpoints(buf);
744
743
  const perBand = /* @__PURE__ */ new Map();
745
- for (const view of buf.views) perBand.set(regimeFor(view.width, breakpoints).min ?? 0, view);
744
+ for (const view of buf.views) perBand.set(viewSpanFor(view.width, breakpoints).min ?? 0, view);
746
745
  const views = [...perBand.values()].toSorted((a, b) => a.width - b.width);
747
746
  return serializePlateIR({
748
747
  name: buf.name,
@@ -750,16 +749,16 @@ function installXrayClient(options) {
750
749
  breakpoints
751
750
  });
752
751
  };
753
- const previewRecording = (buf) => {
754
- const stored = projectRecording(buf);
752
+ const previewSession = (buf) => {
753
+ const stored = projectSessionBuffer(buf);
755
754
  if (!stored) return false;
756
755
  return updatePlate(buf.name, serializePlate(stored));
757
756
  };
758
- const finalizeRecording = async () => {
759
- const entries = [...recording];
760
- recording.clear();
757
+ const finalizeSession = async () => {
758
+ const entries = [...sessionBuffers];
759
+ sessionBuffers.clear();
761
760
  await Promise.all(entries.map(async ([name, buf]) => {
762
- const stored = projectRecording(buf);
761
+ const stored = projectSessionBuffer(buf);
763
762
  if (!stored) return;
764
763
  const diagnostics = buf.views.flatMap((v) => v.diagnostics ?? []);
765
764
  const envelope = {
@@ -769,8 +768,8 @@ function installXrayClient(options) {
769
768
  try {
770
769
  await options.sink(envelope);
771
770
  } catch (error) {
772
- if (!recording.has(name)) recording.set(name, buf);
773
- console.warn(`[xray] recording write failed for "${name}" — kept in memory; retried when you next start a recording`, error);
771
+ if (!sessionBuffers.has(name)) sessionBuffers.set(name, buf);
772
+ console.warn(`[xray] capture-session write failed for "${name}" — kept in memory; retried when you next start a session`, error);
774
773
  }
775
774
  }));
776
775
  };
@@ -788,40 +787,38 @@ function installXrayClient(options) {
788
787
  }
789
788
  if (!captureEnabled.get()) return;
790
789
  view.bpSeed = deriveBreakpoints(emit(view.bundle).rules, []);
791
- runPlatePasses({
790
+ distilPlate({
792
791
  name: site.name,
793
792
  views: [view],
794
793
  breakpoints: []
795
794
  });
796
- let buf = recording.get(site.name);
795
+ let buf = sessionBuffers.get(site.name);
797
796
  if (!buf) {
798
797
  buf = {
799
798
  name: site.name,
800
799
  views: [],
801
800
  breakpoints: []
802
801
  };
803
- recording.set(site.name, buf);
802
+ sessionBuffers.set(site.name, buf);
804
803
  }
805
804
  buf.views = buf.views.filter((v) => v.width !== view.width);
806
805
  buf.views.push(view);
807
- if (previewRecording(buf)) flashCapture(site.el);
806
+ if (previewSession(buf)) blinkCapture(site.el);
808
807
  };
809
808
  /**
810
- * Project a per-site `PlateIR` to the structured `StoredPlate` IN THE BROWSER
811
- * (the ADR 0022 Serialize) and hand the envelope to the sink. Derives the
812
- * breakpoint union ONCE over every View's rules + the recorded matchMedia
813
- * queries (relocating the old per-View derivation), runs the reduction passes
814
- * once over the whole bundle, then `serializePlateIR`. Dedupes per-PLATE by the
815
- * serialized StoredPlate string. ALWAYS catches so neither mode leaks an
816
- * unhandled rejection; on failure it clears the dedupe so a retry re-sends and,
817
- * ONLY when `awaitSink`, rethrows so capture-all still fails loudly (pieces
818
- * 3/8). The radiographic flash runs only AFTER a successful send.
809
+ * Project a per-site `PlateIR` to the structured `StoredPlate` IN THE BROWSER (the ADR 0022
810
+ * Serialize) and hand the envelope to the sink. Derives the breakpoint union ONCE over every
811
+ * View's rules + the recorded matchMedia queries, runs the Distil passes once over the whole
812
+ * bundle, then `serializePlateIR`. Dedupes per-PLATE by the serialized StoredPlate string. ALWAYS
813
+ * catches so neither mode leaks an unhandled rejection; on failure it clears the dedupe so a
814
+ * retry re-sends and, ONLY when `awaitSink`, rethrows so capture-all still fails loudly. The
815
+ * Blink runs only AFTER a successful send.
819
816
  */
820
817
  const sendPlate = async (site, plate, awaitSink) => {
821
818
  if (plate.views.length === 0) return;
822
819
  plate.name = site.name;
823
820
  plate.breakpoints = deriveBreakpoints(plate.views.flatMap((v) => emit(v.bundle).rules), [...matchMediaQueries]);
824
- runPlatePasses(plate);
821
+ distilPlate(plate);
825
822
  const stored = serializePlateIR(plate);
826
823
  const diagnostics = plate.views.flatMap((v) => v.diagnostics ?? []);
827
824
  const envelope = {
@@ -839,13 +836,13 @@ function installXrayClient(options) {
839
836
  console.warn("[xray] capture send failed for", site.name, error);
840
837
  return;
841
838
  }
842
- flashCapture(site.el);
839
+ blinkCapture(site.el);
843
840
  };
844
841
  /**
845
- * Finalize a site's accumulated sweep `PlateIR` and POST it ONCE (awaited, the
846
- * backpressure point). Clears `site.sessionPlate` first so any straggler capture
847
- * for the site falls back to the browse path rather than mutating the bundle
848
- * mid-flight. A session that captured no surviving View posts nothing.
842
+ * Finalize a site's accumulated sweep `PlateIR` and POST it ONCE (awaited, the backpressure
843
+ * point). Clears `site.sessionPlate` first so any straggler capture for the site falls back to
844
+ * the browse path rather than mutating the bundle mid-flight. A session that captured no
845
+ * surviving View posts nothing.
849
846
  */
850
847
  const finalizeSessionPlate = async (site) => {
851
848
  const session = site.sessionPlate;
@@ -889,15 +886,15 @@ function installXrayClient(options) {
889
886
  }, RESIZE_DEBOUNCE);
890
887
  };
891
888
  win.addEventListener("resize", onResize);
892
- let recordingOn = captureEnabled.get();
889
+ let sessionOn = captureEnabled.get();
893
890
  const unsubscribeCapture = captureEnabled.subscribe(() => {
894
891
  const on = captureEnabled.get();
895
- if (on === recordingOn) return;
896
- recordingOn = on;
892
+ if (on === sessionOn) return;
893
+ sessionOn = on;
897
894
  if (on) {
898
- finalizeRecording();
895
+ finalizeSession();
899
896
  for (const site of sites) scheduleCapture(site);
900
- } else finalizeRecording();
897
+ } else finalizeSession();
901
898
  });
902
899
  const extAcks = /* @__PURE__ */ new Map();
903
900
  let nextExtReqId = 1;
@@ -999,7 +996,7 @@ function installXrayClient(options) {
999
996
  const breakpoints = [...set].toSorted((a, b) => a - b);
1000
997
  const min = breakpoints[0];
1001
998
  const first = min === void 0 ? 390 : Math.max(1, Math.min(390, min - 1));
1002
- return [...new Set([first, ...breakpoints])].toSorted((a, b) => a - b);
999
+ return [.../* @__PURE__ */ new Set([first, ...breakpoints])].toSorted((a, b) => a - b);
1003
1000
  };
1004
1001
  const MAX_SWEEP_PASSES = 8;
1005
1002
  const captureAllViews = async () => {
@@ -1082,12 +1079,11 @@ function installXrayClient(options) {
1082
1079
  };
1083
1080
  }
1084
1081
  /**
1085
- * Surface a capture's dev-only diagnostics as ONE grouped `console.warn` keyed
1086
- * by plate name: a header line, then one formatted line per diagnostic via the
1087
- * shared formatter (diagnostics.ts) — the same text the Vite server prints, so
1088
- * the two never drift. `console.group` collapses the lines under the header;
1089
- * we fall back to a single multi-line warn where it's unavailable. No-op when
1090
- * there are no diagnostics, so a clean capture is silent.
1082
+ * Surface a capture's dev-only diagnostics as ONE grouped `console.warn` keyed by plate name: a
1083
+ * header line, then one formatted line per diagnostic via the shared formatter (diagnostics.ts) —
1084
+ * the same text the Vite server prints, so the two never drift. `console.group` collapses the lines
1085
+ * under the header, falling back to a single multi-line warn where unavailable. No-op when there
1086
+ * are no diagnostics, so a clean capture is silent.
1091
1087
  */
1092
1088
  function reportDiagnostics(name, diagnostics) {
1093
1089
  if (!diagnostics || diagnostics.length === 0) return;
@@ -1108,10 +1104,9 @@ const CAPTURE_PARAM = "xray-capture";
1108
1104
  const REPLAY_KEY = "xray:replay";
1109
1105
  const REPLAY_PARAM = "xray-replay";
1110
1106
  /**
1111
- * A sticky boolean toggle: per-tab in sessionStorage, seedable to `true` via
1112
- * `?<param>` in the URL, flipped from the HUD or the console
1113
- * (`__XRAY__.lightbox.set(true)`). A stored value wins over `initial`; the
1114
- * URL param forces `true`.
1107
+ * A sticky boolean toggle: per-tab in sessionStorage, seedable to `true` via `?<param>` in the URL,
1108
+ * flipped from the HUD or the console (`__XRAY__.lightbox.set(true)`). A stored value wins over
1109
+ * `initial`; the URL param forces `true`.
1115
1110
  */
1116
1111
  function createToggleStore(win, key, param, initial) {
1117
1112
  let value = initial;
@@ -1140,17 +1135,15 @@ function createToggleStore(win, key, param, initial) {
1140
1135
  };
1141
1136
  }
1142
1137
  /**
1143
- * Resolve once the subtree has been quiet (no mutations, no child resizes)
1144
- * for `delay` ms — fonts loaded first. Charts, lazy images and late embeds
1145
- * made single-shot capture non-deterministic in the M1 harness; this is the
1146
- * cure. Hard-capped so a perpetually-animating subtree still captures.
1138
+ * Resolve once the subtree has been quiet (no mutations, no child resizes) for `delay` ms — fonts
1139
+ * loaded first. Charts, lazy images and late embeds made single-shot capture non-deterministic in
1140
+ * the M1 harness; this is the cure. Hard-capped so a perpetually-animating subtree still captures.
1147
1141
  */
1148
1142
  /**
1149
- * Force lazy images in the subtree to load and await their decode, so the walk
1150
- * measures their real boxes. A `loading="lazy"` image below the fold never enters
1151
- * the viewport during capture, so its IntersectionObserver never fires flip it
1152
- * to eager to trigger the load. Capped: a slow or broken image resolves the race
1153
- * via the timeout instead of blocking the capture.
1143
+ * Force lazy images in the subtree to load and await their decode, so the walk measures their real
1144
+ * boxes. A `loading="lazy"` image below the fold never enters the viewport during capture, so its
1145
+ * IntersectionObserver never fires flip it to eager to trigger the load. Capped: a slow or broken
1146
+ * image resolves the race via the timeout instead of blocking the capture.
1154
1147
  */
1155
1148
  function awaitImages(root, cap) {
1156
1149
  const imgs = Array.from(root.querySelectorAll("img"));
package/dist/core.d.ts CHANGED
@@ -1,66 +1,56 @@
1
- import { _ as ViewCapture, a as CollectAxis, c as XrayCaptureWalker, d as LeafKind, f as MergedPlate, h as RenderNode, i as CaptureWalkerContext, l as defineXrayCaptureWalker, m as PlateNode, n as CaptureSize, o as CollectLimits, p as Plate, r as CaptureTooLargeError, s as WalkerDecision, t as CaptureOptions, u as BASE_CSS, v as composeCss, y as emptyPlate } from "./serialize-BqbQVxYl.js";
2
-
1
+ import { _ as ViewCapture, a as CollectAxis, c as XrayCaptureWalker, d as GatedPlate, f as LeafKind, h as RenderNode, i as CaptureWalkerContext, l as defineXrayCaptureWalker, m as PlateNode, n as CaptureSize, o as CollectLimits, p as Plate, r as CaptureTooLargeError, s as WalkerDecision, t as CaptureOptions, u as BASE_CSS, v as composeCss, y as emptyPlate } from "./measure-CUQxB-Ic.js";
3
2
  //#region src/chunk.d.ts
4
3
  /**
5
- * Turn a merged plate (RenderNode tree) into the shipped plate (chunks). The
6
- * root's own content classes travel as `rootCls` (the adapter puts them on the
7
- * root element); its children become `chunks`. A capture-less plate has null
8
- * chunks and renders the fallback.
4
+ * Turn a merged plate (RenderNode tree) into the shipped plate (chunks). The root's own content
5
+ * classes travel as `rootCls` (the adapter puts them on the root element); its children become
6
+ * `chunks`. A capture-less plate has null chunks and renders the fallback.
9
7
  *
10
- * Two-artifact contract (plan 005 round 2): the returned `Plate` is the TREE
11
- * artifact only — the input's `css` is deliberately NOT copied onto it. The
12
- * css string stays the producer's (`MergedPlate`/`StoredPlate`) field; each
13
- * lowering site decides independently how its css travels (the plugin's
14
- * virtual `.css` modules + `export const css`, `captureElement`'s
15
- * `{ plate, css }` pair, or the `xray:plate` dev event, which ships no css).
8
+ * Two-artifact contract (ADR 0026): the returned `Plate` is the TREE artifact only; the input's
9
+ * `css` is deliberately NOT copied onto it. The css string stays the producer's
10
+ * (`GatedPlate`/`StoredPlate`) field, and each lowering site decides independently how its css
11
+ * travels: the plugin's virtual `.css` modules + `export const css`, `captureElement`'s `{ plate,
12
+ * css }` pair, or the `xray:plate` dev event, which ships no css.
16
13
  */
17
- declare function serializePlate(merged: MergedPlate): Plate;
14
+ declare function serializePlate(merged: GatedPlate): Plate;
18
15
  /**
19
- * Distinct plate names referenced by the tree's stitches, in first-seen order.
20
- * Walks a `RenderNode` tree (the stored, post-classify shape), so it drives the
21
- * gen-side stitch import graph straight off a `StoredPlate.tree`
22
- * (`renderPlateModule`, index.ts). Relocated here from the now-deleted `merge.ts`
23
- * in the ADR 0022 cutover — it is the one node-side primitive that survived the
24
- * `mergeViews`/`addCapture` removal (the multi-View merge/gate now lives in the
25
- * in-browser `serializePlateIR`, project.ts).
16
+ * Distinct plate names referenced by the tree's stitches, in first-seen order. Walks a `RenderNode`
17
+ * tree (the stored, post-classify shape), so it drives the gen-side stitch import graph straight
18
+ * off a `StoredPlate.tree` (`renderPlateModule`, index.ts). The one node-side primitive needed at
19
+ * build time; multi-View gating happens in the in-browser `serializePlateIR` (serialize.ts, ADR
20
+ * 0022).
26
21
  */
27
22
  declare function collectRefs(tree: RenderNode | null): string[];
28
23
  //#endregion
29
24
  //#region src/core.d.ts
30
25
  /**
31
- * Capture a live DOM subtree straight to the two renderable artifacts: the
32
- * structural `plate` (tree, lowered to chunks) and its scoped `css` string —
33
- * independent parts, per the two-artifact contract. Runs the same uniform
34
- * pipeline the dev client does for a single View — measure (`captureView`),
35
- * reduce (`runPlatePasses`), project (`serializePlateIR` the structured
36
- * `StoredPlate`), then lower to chunks (`serializePlate`) in one call.
37
- * `roots` are the top-level elements to capture (a component's rendered
38
- * roots). Render however suits: `renderPlateHtml(plate, composeCss(css))`,
39
- * or `<Skeleton plate={plate} css={composeCss(css)}>` with the React adapter.
26
+ * Capture a live DOM subtree straight to the two renderable artifacts: the structural `plate`
27
+ * (tree, lowered to chunks) and its scoped `css` string — independent parts, per the two-artifact
28
+ * contract. Runs the same uniform pipeline the dev client does for a single View — measure
29
+ * (`captureView`), distil (`distilPlate`), project (`serializePlateIR` the structured
30
+ * `StoredPlate`), then lower to chunks (`serializePlate`) in one call. `roots` are the top-level
31
+ * elements to capture (a component's rendered roots). Render however suits: `renderPlateHtml(plate,
32
+ * composeCss(css))`, or `<Skeleton plate={plate} css={composeCss(css)}>` with the React adapter.
40
33
  *
41
- * `options.walker` is the public programmable capture walker (ADR 0018, Q2
42
- * yes, public): the same `XrayCaptureWalker` the `<Skeleton captureWalker>` prop
43
- * threads through in dev, available here for driving capture directly. It is
44
- * capture-only and never persisted into the returned artifacts.
34
+ * `options.walker` is the public programmable capture walker (ADR 0018): the same
35
+ * `XrayCaptureWalker` the `<Skeleton captureWalker>` prop threads through in dev, available here
36
+ * for driving capture directly. It is capture-only and never persisted into the returned
37
+ * artifacts.
45
38
  */
46
39
  declare function captureElement(roots: readonly Element[], options: CaptureOptions): {
47
40
  plate: Plate;
48
41
  css: string;
49
42
  };
50
43
  /**
51
- * Render a stitch-free Plate to an HTML string — the framework-neutral
52
- * equivalent of `<Skeleton plate loading />`, for when you don't want to pull
53
- * a framework adapter (demos, plain DOM, server strings). Drop it in with
54
- * `el.innerHTML = renderPlateHtml(plate, composeCss(css))` or React's
44
+ * Render a stitch-free Plate to an HTML string — the framework-neutral equivalent of `<Skeleton
45
+ * plate loading />`, for when you don't want to pull a framework adapter (demos, plain DOM, server
46
+ * strings). Drop it in with `el.innerHTML = renderPlateHtml(plate, composeCss(css))` or React's
55
47
  * `dangerouslySetInnerHTML`.
56
48
  *
57
- * `css` is the caller's explicit input (two-artifact contract): pass
58
- * `composeCss(css)` for the standard BASE_CSS + plate-rules composition
59
- * (BASE_CSS is what makes bones paint and pulse), the raw capture css if the
60
- * base rules are already on the page, or your own composition. Stitched
61
- * plates (a nested `<Skeleton>`) need an adapter to mount their child plates,
62
- * so they throw here.
49
+ * `css` is the caller's explicit input (two-artifact contract): pass `composeCss(css)` for the
50
+ * standard BASE_CSS + plate-rules composition (BASE_CSS is what makes bones paint and pulse), the
51
+ * raw capture css if the base rules are already on the page, or your own composition. Stitched
52
+ * plates (a nested `<Skeleton>`) need an adapter to mount their child plates, so they throw here.
63
53
  */
64
54
  declare function renderPlateHtml(plate: Plate, css: string): string;
65
55
  //#endregion
66
- export { BASE_CSS, type CaptureOptions, type CaptureSize, CaptureTooLargeError, type CaptureWalkerContext, type CollectAxis, type CollectLimits, type LeafKind, type MergedPlate, type Plate, type PlateNode, type ViewCapture, type WalkerDecision, type XrayCaptureWalker, captureElement, collectRefs, composeCss, defineXrayCaptureWalker, emptyPlate, renderPlateHtml, serializePlate };
56
+ export { BASE_CSS, type CaptureOptions, type CaptureSize, CaptureTooLargeError, type CaptureWalkerContext, type CollectAxis, type CollectLimits, type GatedPlate, type LeafKind, type Plate, type PlateNode, type ViewCapture, type WalkerDecision, type XrayCaptureWalker, captureElement, collectRefs, composeCss, defineXrayCaptureWalker, emptyPlate, renderPlateHtml, serializePlate };