@hueest/xray 0.5.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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/client.d.ts +42 -51
- package/dist/client.js +136 -142
- package/dist/core.d.ts +42 -34
- package/dist/core.js +39 -33
- package/dist/css-escape-DJ-BsrNm.js +47 -0
- package/dist/hud.d.ts +7 -8
- package/dist/hud.js +5 -5
- package/dist/index.d.ts +268 -250
- package/dist/index.js +369 -240
- package/dist/measure-CUQxB-Ic.d.ts +524 -0
- package/dist/{plate-BRR6d8Se.js → plate-CXIh5_OB.js} +61 -45
- package/dist/react.core-BS2yvqeh.js +301 -0
- package/dist/react.core-BkJoCgWJ.d.ts +115 -0
- package/dist/react.d.ts +11 -13
- package/dist/react.dev.d.ts +4 -6
- package/dist/react.dev.js +58 -64
- package/dist/react.js +27 -25
- package/dist/{project-BZosujs9.js → serialize-Cj-EHvMv.js} +877 -1027
- package/package.json +8 -6
- package/virtual.d.ts +25 -32
- package/dist/css-escape-N7bOusGW.js +0 -52
- package/dist/react.core-BC02ukac.js +0 -294
- package/dist/react.core-NhWc9qan.d.ts +0 -89
- package/dist/serialize-BgdGt34A.d.ts +0 -501
package/dist/client.js
CHANGED
|
@@ -1,45 +1,4 @@
|
|
|
1
|
-
import { a as serializePlate, d as formatDiagnostic, f as makeDiagnostic, l as
|
|
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
|
-
|
|
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
|
-
|
|
276
|
+
case "URL": {
|
|
277
|
+
const url = new URL(value[1]);
|
|
278
|
+
hydrated[index] = url;
|
|
277
279
|
break;
|
|
278
|
-
|
|
279
|
-
|
|
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/
|
|
534
|
+
//#region src/blink.ts
|
|
530
535
|
/**
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
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
|
-
/**
|
|
589
|
-
function
|
|
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
|
-
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
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
|
|
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
|
-
*
|
|
697
|
-
*
|
|
698
|
-
* `
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
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
|
-
*
|
|
710
|
-
*
|
|
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
|
|
740
|
-
const
|
|
741
|
-
const
|
|
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 =
|
|
742
|
+
const breakpoints = sessionBreakpoints(buf);
|
|
744
743
|
const perBand = /* @__PURE__ */ new Map();
|
|
745
|
-
for (const view of buf.views) perBand.set(
|
|
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,15 +749,16 @@ function installXrayClient(options) {
|
|
|
750
749
|
breakpoints
|
|
751
750
|
});
|
|
752
751
|
};
|
|
753
|
-
const
|
|
754
|
-
const stored =
|
|
755
|
-
|
|
752
|
+
const previewSession = (buf) => {
|
|
753
|
+
const stored = projectSessionBuffer(buf);
|
|
754
|
+
if (!stored) return false;
|
|
755
|
+
return updatePlate(buf.name, serializePlate(stored));
|
|
756
756
|
};
|
|
757
|
-
const
|
|
758
|
-
const entries = [...
|
|
759
|
-
|
|
757
|
+
const finalizeSession = async () => {
|
|
758
|
+
const entries = [...sessionBuffers];
|
|
759
|
+
sessionBuffers.clear();
|
|
760
760
|
await Promise.all(entries.map(async ([name, buf]) => {
|
|
761
|
-
const stored =
|
|
761
|
+
const stored = projectSessionBuffer(buf);
|
|
762
762
|
if (!stored) return;
|
|
763
763
|
const diagnostics = buf.views.flatMap((v) => v.diagnostics ?? []);
|
|
764
764
|
const envelope = {
|
|
@@ -768,8 +768,8 @@ function installXrayClient(options) {
|
|
|
768
768
|
try {
|
|
769
769
|
await options.sink(envelope);
|
|
770
770
|
} catch (error) {
|
|
771
|
-
if (!
|
|
772
|
-
console.warn(`[xray]
|
|
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);
|
|
773
773
|
}
|
|
774
774
|
}));
|
|
775
775
|
};
|
|
@@ -787,40 +787,38 @@ function installXrayClient(options) {
|
|
|
787
787
|
}
|
|
788
788
|
if (!captureEnabled.get()) return;
|
|
789
789
|
view.bpSeed = deriveBreakpoints(emit(view.bundle).rules, []);
|
|
790
|
-
|
|
790
|
+
distilPlate({
|
|
791
791
|
name: site.name,
|
|
792
792
|
views: [view],
|
|
793
793
|
breakpoints: []
|
|
794
794
|
});
|
|
795
|
-
let buf =
|
|
795
|
+
let buf = sessionBuffers.get(site.name);
|
|
796
796
|
if (!buf) {
|
|
797
797
|
buf = {
|
|
798
798
|
name: site.name,
|
|
799
799
|
views: [],
|
|
800
800
|
breakpoints: []
|
|
801
801
|
};
|
|
802
|
-
|
|
802
|
+
sessionBuffers.set(site.name, buf);
|
|
803
803
|
}
|
|
804
804
|
buf.views = buf.views.filter((v) => v.width !== view.width);
|
|
805
805
|
buf.views.push(view);
|
|
806
|
-
if (
|
|
806
|
+
if (previewSession(buf)) blinkCapture(site.el);
|
|
807
807
|
};
|
|
808
808
|
/**
|
|
809
|
-
* Project a per-site `PlateIR` to the structured `StoredPlate` IN THE BROWSER
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
* ONLY when `awaitSink`, rethrows so capture-all still fails loudly (pieces
|
|
817
|
-
* 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.
|
|
818
816
|
*/
|
|
819
817
|
const sendPlate = async (site, plate, awaitSink) => {
|
|
820
818
|
if (plate.views.length === 0) return;
|
|
821
819
|
plate.name = site.name;
|
|
822
820
|
plate.breakpoints = deriveBreakpoints(plate.views.flatMap((v) => emit(v.bundle).rules), [...matchMediaQueries]);
|
|
823
|
-
|
|
821
|
+
distilPlate(plate);
|
|
824
822
|
const stored = serializePlateIR(plate);
|
|
825
823
|
const diagnostics = plate.views.flatMap((v) => v.diagnostics ?? []);
|
|
826
824
|
const envelope = {
|
|
@@ -838,13 +836,13 @@ function installXrayClient(options) {
|
|
|
838
836
|
console.warn("[xray] capture send failed for", site.name, error);
|
|
839
837
|
return;
|
|
840
838
|
}
|
|
841
|
-
|
|
839
|
+
blinkCapture(site.el);
|
|
842
840
|
};
|
|
843
841
|
/**
|
|
844
|
-
* Finalize a site's accumulated sweep `PlateIR` and POST it ONCE (awaited, the
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
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.
|
|
848
846
|
*/
|
|
849
847
|
const finalizeSessionPlate = async (site) => {
|
|
850
848
|
const session = site.sessionPlate;
|
|
@@ -888,15 +886,15 @@ function installXrayClient(options) {
|
|
|
888
886
|
}, RESIZE_DEBOUNCE);
|
|
889
887
|
};
|
|
890
888
|
win.addEventListener("resize", onResize);
|
|
891
|
-
let
|
|
889
|
+
let sessionOn = captureEnabled.get();
|
|
892
890
|
const unsubscribeCapture = captureEnabled.subscribe(() => {
|
|
893
891
|
const on = captureEnabled.get();
|
|
894
|
-
if (on ===
|
|
895
|
-
|
|
892
|
+
if (on === sessionOn) return;
|
|
893
|
+
sessionOn = on;
|
|
896
894
|
if (on) {
|
|
897
|
-
|
|
895
|
+
finalizeSession();
|
|
898
896
|
for (const site of sites) scheduleCapture(site);
|
|
899
|
-
} else
|
|
897
|
+
} else finalizeSession();
|
|
900
898
|
});
|
|
901
899
|
const extAcks = /* @__PURE__ */ new Map();
|
|
902
900
|
let nextExtReqId = 1;
|
|
@@ -998,7 +996,7 @@ function installXrayClient(options) {
|
|
|
998
996
|
const breakpoints = [...set].toSorted((a, b) => a - b);
|
|
999
997
|
const min = breakpoints[0];
|
|
1000
998
|
const first = min === void 0 ? 390 : Math.max(1, Math.min(390, min - 1));
|
|
1001
|
-
return [
|
|
999
|
+
return [.../* @__PURE__ */ new Set([first, ...breakpoints])].toSorted((a, b) => a - b);
|
|
1002
1000
|
};
|
|
1003
1001
|
const MAX_SWEEP_PASSES = 8;
|
|
1004
1002
|
const captureAllViews = async () => {
|
|
@@ -1081,12 +1079,11 @@ function installXrayClient(options) {
|
|
|
1081
1079
|
};
|
|
1082
1080
|
}
|
|
1083
1081
|
/**
|
|
1084
|
-
* Surface a capture's dev-only diagnostics as ONE grouped `console.warn` keyed
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
* the
|
|
1088
|
-
*
|
|
1089
|
-
* 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.
|
|
1090
1087
|
*/
|
|
1091
1088
|
function reportDiagnostics(name, diagnostics) {
|
|
1092
1089
|
if (!diagnostics || diagnostics.length === 0) return;
|
|
@@ -1107,10 +1104,9 @@ const CAPTURE_PARAM = "xray-capture";
|
|
|
1107
1104
|
const REPLAY_KEY = "xray:replay";
|
|
1108
1105
|
const REPLAY_PARAM = "xray-replay";
|
|
1109
1106
|
/**
|
|
1110
|
-
* A sticky boolean toggle: per-tab in sessionStorage, seedable to `true` via
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
* 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`.
|
|
1114
1110
|
*/
|
|
1115
1111
|
function createToggleStore(win, key, param, initial) {
|
|
1116
1112
|
let value = initial;
|
|
@@ -1139,17 +1135,15 @@ function createToggleStore(win, key, param, initial) {
|
|
|
1139
1135
|
};
|
|
1140
1136
|
}
|
|
1141
1137
|
/**
|
|
1142
|
-
* Resolve once the subtree has been quiet (no mutations, no child resizes)
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
* 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.
|
|
1146
1141
|
*/
|
|
1147
1142
|
/**
|
|
1148
|
-
* Force lazy images in the subtree to load and await their decode, so the walk
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
* 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.
|
|
1153
1147
|
*/
|
|
1154
1148
|
function awaitImages(root, cap) {
|
|
1155
1149
|
const imgs = Array.from(root.querySelectorAll("img"));
|
package/dist/core.d.ts
CHANGED
|
@@ -1,48 +1,56 @@
|
|
|
1
|
-
import { _ as
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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.
|
|
7
|
+
*
|
|
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.
|
|
9
13
|
*/
|
|
10
|
-
declare function serializePlate(merged:
|
|
14
|
+
declare function serializePlate(merged: GatedPlate): Plate;
|
|
11
15
|
/**
|
|
12
|
-
* Distinct plate names referenced by the tree's stitches, in first-seen order.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `mergeViews`/`addCapture` removal (the multi-View merge/gate now lives in the
|
|
18
|
-
* 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).
|
|
19
21
|
*/
|
|
20
22
|
declare function collectRefs(tree: RenderNode | null): string[];
|
|
21
23
|
//#endregion
|
|
22
24
|
//#region src/core.d.ts
|
|
23
25
|
/**
|
|
24
|
-
* Capture a live DOM subtree straight to
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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.
|
|
30
33
|
*
|
|
31
|
-
* `options.walker` is the public programmable capture walker (ADR 0018
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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.
|
|
35
38
|
*/
|
|
36
|
-
declare function captureElement(roots: readonly Element[], options: CaptureOptions):
|
|
39
|
+
declare function captureElement(roots: readonly Element[], options: CaptureOptions): {
|
|
40
|
+
plate: Plate;
|
|
41
|
+
css: string;
|
|
42
|
+
};
|
|
37
43
|
/**
|
|
38
|
-
* Render a stitch-free Plate to
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
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
|
|
47
|
+
* `dangerouslySetInnerHTML`.
|
|
48
|
+
*
|
|
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.
|
|
45
53
|
*/
|
|
46
|
-
declare function renderPlateHtml(plate: Plate): string;
|
|
54
|
+
declare function renderPlateHtml(plate: Plate, css: string): string;
|
|
47
55
|
//#endregion
|
|
48
|
-
export { type CaptureOptions, type CaptureSize, CaptureTooLargeError, type CaptureWalkerContext, type CollectAxis, type CollectLimits, type
|
|
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 };
|