@hueest/xray 0.1.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.
@@ -0,0 +1,88 @@
1
+ import { n as MergedPlate, s as ViewCapture, t as LeafKind } from "./plate-DboxMKg-.js";
2
+
3
+ //#region src/serialize.d.ts
4
+ /**
5
+ * Browser-side capture: walk a live rendered subtree into a Plate — one node
6
+ * tree plus one self-contained, scoped CSS string (ADR 0003). The CSS is
7
+ * lifted from the author rules that matched each node, rewritten to
8
+ * plate-local selectors — it carries `@media` for free and keeps layout live
9
+ * (the M1 bake-off winner; ADR 0005).
10
+ */
11
+ interface CaptureOptions {
12
+ /** Plate name; comes from the virtual-module specifier. */
13
+ name: string;
14
+ }
15
+ /**
16
+ * Capture the rendered subtree(s) under a Skeleton into a Plate. `roots` are
17
+ * the component's top-level elements (the children of the layout-neutral
18
+ * wrapper); the synthetic root node (id 0) stands in for the wrapper itself.
19
+ */
20
+ declare function capture(roots: readonly Element[], options: CaptureOptions): MergedPlate;
21
+ /**
22
+ * Thrown when a subtree has more nodes than the capture limit — almost always
23
+ * a `<Skeleton>` sitting too high in the tree. Raised after the cheap walk and
24
+ * before the O(rules × nodes) CSS extraction, so an oversized capture is
25
+ * refused without freezing the main thread on it.
26
+ */
27
+ declare class CaptureTooLargeError extends Error {
28
+ readonly nodeCount: number;
29
+ constructor(nodeCount: number);
30
+ }
31
+ interface CaptureRegimeOptions {
32
+ /** Refuse (throw `CaptureTooLargeError`) before CSS extraction if the walk exceeds this. */
33
+ maxNodes?: number;
34
+ }
35
+ /**
36
+ * Capture one view's worth of a component: the tree plus id-form rules,
37
+ * with the viewport width it was taken at. The caller (the dev client)
38
+ * derives the view interval and ships it off; `capture` is the
39
+ * single-view convenience on top.
40
+ */
41
+ declare function captureRegime(roots: readonly Element[], options?: CaptureRegimeOptions): ViewCapture;
42
+ /** One measurement per walked node, ids matching what `capture` assigns. */
43
+ interface AuditEntry {
44
+ id: number;
45
+ tag: string;
46
+ leaf?: LeafKind;
47
+ w: number;
48
+ h: number;
49
+ }
50
+ /**
51
+ * Walk a live subtree exactly like `capture` does, but return per-node
52
+ * geometry instead of a plate. Rendering the captured plate and auditing both
53
+ * sides gives an id-aligned fidelity diff (used by the M1 bake-off).
54
+ */
55
+ declare function audit(roots: readonly Element[]): AuditEntry[];
56
+ /**
57
+ * Shrink a text run's measured box to what's actually painted. A `Range`'s
58
+ * rect is the text's full geometry and ignores ancestor clipping, so a
59
+ * `-webkit-line-clamp` box (fewer lines than the text fills) or a single-line
60
+ * `text-overflow: ellipsis` (`white-space: nowrap; overflow: hidden`) makes the
61
+ * bar over-measure — a 2-line title bone rendered at its unclamped 5-line
62
+ * height. Painted text can't exceed any clipping ancestor's content box, so we
63
+ * intersect with each: per axis the ancestor clips (`overflow !== visible`),
64
+ * clamp to its content box. A non-clipping or larger ancestor leaves it be, so
65
+ * unclamped text is untouched. Walks to the root since the clamp may sit a few
66
+ * inline wrappers up from the text node.
67
+ */
68
+ interface ClippingStyle {
69
+ overflowX: string;
70
+ overflowY: string;
71
+ paddingTop: string;
72
+ paddingBottom: string;
73
+ paddingLeft: string;
74
+ paddingRight: string;
75
+ borderTopWidth: string;
76
+ borderBottomWidth: string;
77
+ borderLeftWidth: string;
78
+ borderRightWidth: string;
79
+ }
80
+ interface ClippingWindow {
81
+ getComputedStyle(element: Element): ClippingStyle;
82
+ }
83
+ declare function clampToClipping(width: number, height: number, fromParent: Element | null, win: ClippingWindow): {
84
+ width: number;
85
+ height: number;
86
+ };
87
+ //#endregion
88
+ export { AuditEntry, CaptureOptions, CaptureRegimeOptions, CaptureTooLargeError, audit, capture, captureRegime, clampToClipping };
@@ -0,0 +1,2 @@
1
+ import { a as clampToClipping, i as captureRegime, n as audit, r as capture, t as CaptureTooLargeError } from "./serialize-Bq6yJnNV.js";
2
+ export { CaptureTooLargeError, audit, capture, captureRegime, clampToClipping };
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@hueest/xray",
3
+ "version": "0.1.0",
4
+ "description": "Auto-captured skeleton loading screens for Vite — capture the structure, ship the plate.",
5
+ "keywords": [
6
+ "loading",
7
+ "placeholder",
8
+ "skeleton",
9
+ "skeleton-screen",
10
+ "vite",
11
+ "vite-plugin"
12
+ ],
13
+ "homepage": "https://xray-js.dev",
14
+ "bugs": "https://github.com/hueest/xray/issues",
15
+ "license": "MIT",
16
+ "author": "Stefan Maric",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/hueest/xray.git"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "virtual.d.ts"
24
+ ],
25
+ "type": "module",
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./core": {
34
+ "types": "./dist/core.d.ts",
35
+ "import": "./dist/core.js"
36
+ },
37
+ "./react": {
38
+ "types": "./dist/react.d.ts",
39
+ "import": "./dist/react.js"
40
+ },
41
+ "./react/dev": {
42
+ "types": "./dist/react.dev.d.ts",
43
+ "import": "./dist/react.dev.js"
44
+ },
45
+ "./serialize": {
46
+ "types": "./dist/serialize.d.ts",
47
+ "import": "./dist/serialize.js"
48
+ },
49
+ "./client": {
50
+ "types": "./dist/client.d.ts",
51
+ "import": "./dist/client.js"
52
+ },
53
+ "./hud": {
54
+ "types": "./dist/hud.d.ts",
55
+ "import": "./dist/hud.js"
56
+ },
57
+ "./virtual": {
58
+ "types": "./virtual.d.ts"
59
+ }
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "25.9.3",
66
+ "@types/react": "19.2.17",
67
+ "@types/react-dom": "19.2.3",
68
+ "@typescript/native-preview": "7.0.0-dev.20260619.1",
69
+ "happy-dom": "20.10.6",
70
+ "react": "19.2.7",
71
+ "react-dom": "19.2.7",
72
+ "tsdown": "0.22.3",
73
+ "typescript": "6.0.3",
74
+ "vite": "8.0.16",
75
+ "vitest": "4.1.9"
76
+ },
77
+ "peerDependencies": {
78
+ "react": "^18.2.0 || ^19.0.0",
79
+ "vite": ">=7"
80
+ },
81
+ "peerDependenciesMeta": {
82
+ "react": {
83
+ "optional": true
84
+ },
85
+ "vite": {
86
+ "optional": true
87
+ }
88
+ },
89
+ "scripts": {
90
+ "build": "tsdown",
91
+ "dev": "tsdown --watch",
92
+ "test": "vitest run",
93
+ "test:watch": "vitest"
94
+ }
95
+ }
package/virtual.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Type declarations for xray virtual modules.
3
+ *
4
+ * Reference from the consuming app, e.g. in tsconfig:
5
+ * `"types": ["@hueest/xray/virtual"]`, or per file:
6
+ * `/// <reference types="@hueest/xray/virtual" />`.
7
+ *
8
+ * @module @hueest/xray/virtual
9
+ */
10
+
11
+ /**
12
+ * Import a committed or not-yet-captured Plate by name.
13
+ *
14
+ * The import specifier is the Plate identity: `virtual:xray/plates/my-banner`
15
+ * reads and writes `plates/my-banner.json`, and that name travels with the
16
+ * imported Plate. Pass the imported Plate to `<Skeleton>`; do not repeat the
17
+ * name in props.
18
+ *
19
+ * If the file does not exist yet, the Vite plugin resolves an empty Plate with
20
+ * the same name so `<Skeleton fallback={...}>` can render until the first
21
+ * Capture writes the file. Nested `<Skeleton>` boundaries become Stitches; the
22
+ * generated virtual module wires child Plates into `Plate.refs`, so production
23
+ * rendering follows the static import graph with no global registry.
24
+ *
25
+ * Captured Plate files are source artifacts. Commit files under `plates/` with
26
+ * the component change they describe.
27
+ */
28
+ declare module 'virtual:xray/plates/*' {
29
+ import type { Plate } from '@hueest/xray'
30
+
31
+ /**
32
+ * The captured Plate named by the `virtual:xray/plates/<name>` import specifier.
33
+ *
34
+ * Enable this import in a consuming app with
35
+ * `"types": ["@hueest/xray/virtual"]` in tsconfig, or with a per-file
36
+ * `/// <reference types="@hueest/xray/virtual" />` directive.
37
+ *
38
+ * The specifier maps to a file under `plates/`; for example,
39
+ * `virtual:xray/plates/my-banner` maps to `plates/my-banner.json`. If that
40
+ * file does not exist yet, the plugin returns an empty Plate with the same
41
+ * name so a `<Skeleton fallback={...}>` can render until the first Capture.
42
+ * Pass the imported Plate to `<Skeleton>`; do not repeat the name in props.
43
+ *
44
+ * Child Plates referenced by Stitches are wired into `Plate.refs` by the
45
+ * generated virtual module. Production rendering follows that static import
46
+ * graph with no global Plate registry or runtime name lookup.
47
+ *
48
+ * Captured Plate files are source artifacts. Commit them with the component
49
+ * change they describe.
50
+ */
51
+ const plate: Plate
52
+ export default plate
53
+ }