@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/LICENSE +21 -0
- package/README.md +14 -12
- package/dist/client.d.ts +42 -51
- package/dist/client.js +134 -141
- package/dist/core.d.ts +34 -44
- package/dist/core.js +27 -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 +251 -284
- package/dist/index.js +292 -338
- package/dist/measure-CUQxB-Ic.d.ts +524 -0
- package/dist/{plate-BwJUgHDc.js → plate-CXIh5_OB.js} +52 -59
- 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 +54 -61
- package/dist/react.js +21 -21
- package/dist/{project-BAXKpnfF.js → serialize-Cj-EHvMv.js} +872 -1026
- package/package.json +8 -6
- package/virtual.d.ts +25 -32
- package/dist/css-escape-N7bOusGW.js +0 -52
- package/dist/react.core-DeEli0Lp.js +0 -307
- package/dist/react.core-sJfIcaXX.d.ts +0 -102
- package/dist/serialize-BqbQVxYl.d.ts +0 -564
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,16 +749,16 @@ function installXrayClient(options) {
|
|
|
750
749
|
breakpoints
|
|
751
750
|
});
|
|
752
751
|
};
|
|
753
|
-
const
|
|
754
|
-
const stored =
|
|
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
|
|
759
|
-
const entries = [...
|
|
760
|
-
|
|
757
|
+
const finalizeSession = async () => {
|
|
758
|
+
const entries = [...sessionBuffers];
|
|
759
|
+
sessionBuffers.clear();
|
|
761
760
|
await Promise.all(entries.map(async ([name, buf]) => {
|
|
762
|
-
const stored =
|
|
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 (!
|
|
773
|
-
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);
|
|
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
|
-
|
|
790
|
+
distilPlate({
|
|
792
791
|
name: site.name,
|
|
793
792
|
views: [view],
|
|
794
793
|
breakpoints: []
|
|
795
794
|
});
|
|
796
|
-
let buf =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
839
|
+
blinkCapture(site.el);
|
|
843
840
|
};
|
|
844
841
|
/**
|
|
845
|
-
* Finalize a site's accumulated sweep `PlateIR` and POST it ONCE (awaited, the
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
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
|
|
889
|
+
let sessionOn = captureEnabled.get();
|
|
893
890
|
const unsubscribeCapture = captureEnabled.subscribe(() => {
|
|
894
891
|
const on = captureEnabled.get();
|
|
895
|
-
if (on ===
|
|
896
|
-
|
|
892
|
+
if (on === sessionOn) return;
|
|
893
|
+
sessionOn = on;
|
|
897
894
|
if (on) {
|
|
898
|
-
|
|
895
|
+
finalizeSession();
|
|
899
896
|
for (const site of sites) scheduleCapture(site);
|
|
900
|
-
} else
|
|
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 [
|
|
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
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
* the
|
|
1089
|
-
*
|
|
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
|
-
*
|
|
1113
|
-
*
|
|
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
|
-
*
|
|
1145
|
-
*
|
|
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
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
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
|
|
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
|
-
* 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 (
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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:
|
|
14
|
+
declare function serializePlate(merged: GatedPlate): Plate;
|
|
18
15
|
/**
|
|
19
|
-
* Distinct plate names referenced by the tree's stitches, in first-seen order.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
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
|
-
*
|
|
53
|
-
*
|
|
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
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
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
|
|
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 };
|