@lukas_holdings/castdom 1.0.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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +707 -0
  3. package/bin/castdom.js +2 -0
  4. package/dist/astro.cjs +86 -0
  5. package/dist/astro.cjs.map +1 -0
  6. package/dist/astro.d.cts +88 -0
  7. package/dist/astro.d.ts +88 -0
  8. package/dist/astro.js +80 -0
  9. package/dist/astro.js.map +1 -0
  10. package/dist/chunk-COLESJ66.js +57 -0
  11. package/dist/chunk-COLESJ66.js.map +1 -0
  12. package/dist/chunk-EJRNKHL5.js +31 -0
  13. package/dist/chunk-EJRNKHL5.js.map +1 -0
  14. package/dist/chunk-JRQ6EVQP.cjs +35 -0
  15. package/dist/chunk-JRQ6EVQP.cjs.map +1 -0
  16. package/dist/chunk-KGLTVTHU.js +73 -0
  17. package/dist/chunk-KGLTVTHU.js.map +1 -0
  18. package/dist/chunk-O4OOMGGM.cjs +198 -0
  19. package/dist/chunk-O4OOMGGM.cjs.map +1 -0
  20. package/dist/chunk-ONS533CQ.js +104 -0
  21. package/dist/chunk-ONS533CQ.js.map +1 -0
  22. package/dist/chunk-ORY4OMZ5.cjs +110 -0
  23. package/dist/chunk-ORY4OMZ5.cjs.map +1 -0
  24. package/dist/chunk-QLEBTZIB.cjs +64 -0
  25. package/dist/chunk-QLEBTZIB.cjs.map +1 -0
  26. package/dist/chunk-XS5HAU5E.cjs +109 -0
  27. package/dist/chunk-XS5HAU5E.cjs.map +1 -0
  28. package/dist/chunk-YDT4TPB7.cjs +84 -0
  29. package/dist/chunk-YDT4TPB7.cjs.map +1 -0
  30. package/dist/chunk-ZBJB7WVV.js +193 -0
  31. package/dist/chunk-ZBJB7WVV.js.map +1 -0
  32. package/dist/chunk-ZWZ5ZLJE.js +103 -0
  33. package/dist/chunk-ZWZ5ZLJE.js.map +1 -0
  34. package/dist/cli.js +135 -0
  35. package/dist/index.cjs +540 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +176 -0
  38. package/dist/index.d.ts +176 -0
  39. package/dist/index.js +440 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/next.cjs +65 -0
  42. package/dist/next.cjs.map +1 -0
  43. package/dist/next.d.cts +72 -0
  44. package/dist/next.d.ts +72 -0
  45. package/dist/next.js +48 -0
  46. package/dist/next.js.map +1 -0
  47. package/dist/react.cjs +30 -0
  48. package/dist/react.cjs.map +1 -0
  49. package/dist/react.d.cts +70 -0
  50. package/dist/react.d.ts +70 -0
  51. package/dist/react.js +7 -0
  52. package/dist/react.js.map +1 -0
  53. package/dist/renderer-B1R7u2wm.d.ts +30 -0
  54. package/dist/renderer-Bfzjr6l9.d.cts +30 -0
  55. package/dist/ssr.cjs +46 -0
  56. package/dist/ssr.cjs.map +1 -0
  57. package/dist/ssr.d.cts +83 -0
  58. package/dist/ssr.d.ts +83 -0
  59. package/dist/ssr.js +5 -0
  60. package/dist/ssr.js.map +1 -0
  61. package/dist/types-ChD5jENU.d.cts +105 -0
  62. package/dist/types-ChD5jENU.d.ts +105 -0
  63. package/dist/vite.cjs +83 -0
  64. package/dist/vite.cjs.map +1 -0
  65. package/dist/vite.d.cts +81 -0
  66. package/dist/vite.d.ts +81 -0
  67. package/dist/vite.js +77 -0
  68. package/dist/vite.js.map +1 -0
  69. package/package.json +130 -0
@@ -0,0 +1,198 @@
1
+ 'use strict';
2
+
3
+ var chunkORY4OMZ5_cjs = require('./chunk-ORY4OMZ5.cjs');
4
+ var chunkYDT4TPB7_cjs = require('./chunk-YDT4TPB7.cjs');
5
+ var react = require('react');
6
+
7
+ function CastDOM({
8
+ name,
9
+ children,
10
+ loading,
11
+ fallback,
12
+ animation = "shimmer",
13
+ color,
14
+ shimmerColor,
15
+ duration,
16
+ className,
17
+ style,
18
+ onSkeletonShow,
19
+ onContentShow,
20
+ ariaLabel
21
+ }) {
22
+ const [isLoading, setIsLoading] = react.useState(loading ?? true);
23
+ const [viewportWidth, setViewportWidth] = react.useState(
24
+ typeof window !== "undefined" ? window.innerWidth : 1280
25
+ );
26
+ const containerRef = react.useRef(null);
27
+ const shownRef = react.useRef(false);
28
+ react.useEffect(() => {
29
+ if (loading !== void 0) {
30
+ setIsLoading(loading);
31
+ }
32
+ }, [loading]);
33
+ react.useEffect(() => {
34
+ if (loading === void 0 && children) {
35
+ setIsLoading(false);
36
+ }
37
+ }, [loading, children]);
38
+ react.useEffect(() => {
39
+ if (typeof window === "undefined") return;
40
+ const onResize = () => setViewportWidth(window.innerWidth);
41
+ window.addEventListener("resize", onResize, { passive: true });
42
+ return () => window.removeEventListener("resize", onResize);
43
+ }, []);
44
+ react.useEffect(() => {
45
+ if (isLoading && !shownRef.current) {
46
+ shownRef.current = true;
47
+ onSkeletonShow?.();
48
+ }
49
+ if (!isLoading && shownRef.current) {
50
+ onContentShow?.();
51
+ }
52
+ }, [isLoading, onSkeletonShow, onContentShow]);
53
+ const entry = chunkYDT4TPB7_cjs.get(name);
54
+ if (!isLoading) {
55
+ return react.createElement(
56
+ "div",
57
+ {
58
+ ref: containerRef,
59
+ className,
60
+ style,
61
+ "data-castdom": name
62
+ },
63
+ children
64
+ );
65
+ }
66
+ if (!entry) {
67
+ return fallback ? react.createElement("div", { className, style }, fallback) : null;
68
+ }
69
+ const bp = chunkORY4OMZ5_cjs.selectBreakpoint(entry.data, viewportWidth);
70
+ if (!bp) return fallback ? react.createElement("div", { className, style }, fallback) : null;
71
+ return react.createElement(SkeletonRenderer, {
72
+ name,
73
+ breakpoint: bp,
74
+ animation,
75
+ color,
76
+ shimmerColor,
77
+ duration,
78
+ className,
79
+ style,
80
+ ariaLabel
81
+ });
82
+ }
83
+ function SkeletonRenderer({
84
+ name,
85
+ breakpoint,
86
+ animation,
87
+ color = "#e0e0e0",
88
+ shimmerColor = "#f0f0f0",
89
+ duration = 1500,
90
+ className,
91
+ style,
92
+ ariaLabel
93
+ }) {
94
+ const bones = breakpoint.bones;
95
+ const containerStyle = {
96
+ position: "relative",
97
+ width: breakpoint.containerWidth,
98
+ height: breakpoint.containerHeight,
99
+ overflow: "hidden",
100
+ ...style
101
+ };
102
+ const getBackground = react.useCallback(() => {
103
+ switch (animation) {
104
+ case "shimmer":
105
+ return `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;
106
+ case "pulse":
107
+ case "wave":
108
+ case "none":
109
+ return color;
110
+ }
111
+ }, [animation, color, shimmerColor]);
112
+ const getAnimationCSS = react.useCallback(() => {
113
+ switch (animation) {
114
+ case "shimmer":
115
+ return `castdom-shimmer ${duration}ms ease-in-out infinite`;
116
+ case "pulse":
117
+ return `castdom-pulse ${duration}ms ease-in-out infinite`;
118
+ case "wave":
119
+ return `castdom-wave ${duration}ms ease-in-out infinite`;
120
+ case "none":
121
+ return "none";
122
+ }
123
+ }, [animation, duration]);
124
+ return react.createElement(
125
+ "div",
126
+ {
127
+ className: `castdom-skeleton castdom-${name}${className ? ` ${className}` : ""}`,
128
+ style: containerStyle,
129
+ role: "status",
130
+ "aria-busy": true,
131
+ "aria-label": ariaLabel ?? `Loading ${name}`
132
+ },
133
+ bones.map(
134
+ (bone, i) => react.createElement("div", {
135
+ key: i,
136
+ className: "castdom-bone",
137
+ "aria-hidden": true,
138
+ style: {
139
+ position: "absolute",
140
+ left: bone.x,
141
+ top: bone.y,
142
+ width: bone.w,
143
+ height: bone.h,
144
+ borderRadius: bone.r >= 9999 ? "50%" : bone.r,
145
+ background: getBackground(),
146
+ backgroundSize: animation === "shimmer" ? "200% 100%" : void 0,
147
+ animation: getAnimationCSS(),
148
+ animationDelay: animation === "wave" ? `${i * 50}ms` : void 0
149
+ }
150
+ })
151
+ )
152
+ );
153
+ }
154
+ function CastDOMStyle({
155
+ skeletons
156
+ }) {
157
+ const css = react.useMemo(() => {
158
+ const parts = [
159
+ "@keyframes castdom-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}",
160
+ "@keyframes castdom-pulse{0%,100%{opacity:1}50%{opacity:0.4}}",
161
+ "@keyframes castdom-wave{0%{opacity:0.4}50%{opacity:1}100%{opacity:0.4}}",
162
+ "@media(prefers-reduced-motion:reduce){.castdom-bone{animation:none!important}}"
163
+ ];
164
+ return parts.join("");
165
+ }, []);
166
+ return react.createElement("style", {
167
+ dangerouslySetInnerHTML: { __html: css },
168
+ "data-castdom": "critical"
169
+ });
170
+ }
171
+ function useCastDOM(name) {
172
+ const entry = chunkYDT4TPB7_cjs.get(name);
173
+ const [vw, setVW] = react.useState(
174
+ typeof window !== "undefined" ? window.innerWidth : 1280
175
+ );
176
+ react.useEffect(() => {
177
+ if (typeof window === "undefined") return;
178
+ const onResize = () => setVW(window.innerWidth);
179
+ window.addEventListener("resize", onResize, { passive: true });
180
+ return () => window.removeEventListener("resize", onResize);
181
+ }, []);
182
+ const breakpoint = entry ? chunkORY4OMZ5_cjs.selectBreakpoint(entry.data, vw) : null;
183
+ return {
184
+ exists: !!entry,
185
+ data: entry?.data ?? null,
186
+ breakpoint,
187
+ css: entry?.css ?? "",
188
+ html: breakpoint ? entry?.html[breakpoint.viewport] ?? "" : ""
189
+ };
190
+ }
191
+ var react_default = CastDOM;
192
+
193
+ exports.CastDOM = CastDOM;
194
+ exports.CastDOMStyle = CastDOMStyle;
195
+ exports.react_default = react_default;
196
+ exports.useCastDOM = useCastDOM;
197
+ //# sourceMappingURL=chunk-O4OOMGGM.cjs.map
198
+ //# sourceMappingURL=chunk-O4OOMGGM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/react.tsx"],"names":["useState","useRef","useEffect","get","createElement","selectBreakpoint","useCallback","useMemo"],"mappings":"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","file":"chunk-O4OOMGGM.cjs","sourcesContent":["\"use client\";\n\nimport {\n type ReactNode,\n type CSSProperties,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n createElement,\n} from \"react\";\nimport type { SkeletonData, BreakpointData, CastDOMConfig } from \"../core/types.js\";\nimport { get as getFromRegistry } from \"../core/registry.js\";\nimport { selectBreakpoint } from \"../core/responsive.js\";\n\nexport interface CastDOMProps {\n /** Name of the registered skeleton */\n name: string;\n /** Children to render once loaded */\n children: ReactNode;\n /** Whether the content is still loading (default: false when children mount) */\n loading?: boolean;\n /** Custom fallback while skeleton data loads from registry */\n fallback?: ReactNode;\n /** Animation type override */\n animation?: \"shimmer\" | \"pulse\" | \"wave\" | \"none\";\n /** Base color override */\n color?: string;\n /** Shimmer highlight color override */\n shimmerColor?: string;\n /** Animation duration override (ms) */\n duration?: number;\n /** Additional CSS class */\n className?: string;\n /** Additional inline styles */\n style?: CSSProperties;\n /** Callback when skeleton is shown */\n onSkeletonShow?: () => void;\n /** Callback when real content replaces skeleton */\n onContentShow?: () => void;\n /** Aria label for the loading state */\n ariaLabel?: string;\n}\n\n/**\n * <CastDOM> — wraps your component and shows an extracted skeleton while loading.\n *\n * Usage:\n * ```tsx\n * <CastDOM name=\"user-card\" loading={isLoading}>\n * <UserCard data={data} />\n * </CastDOM>\n * ```\n *\n * The skeleton auto-resolves from the global registry.\n * Register skeletons once at app entry with `loadManifest()`.\n */\nexport function CastDOM({\n name,\n children,\n loading,\n fallback,\n animation = \"shimmer\",\n color,\n shimmerColor,\n duration,\n className,\n style,\n onSkeletonShow,\n onContentShow,\n ariaLabel,\n}: CastDOMProps): ReactNode {\n const [isLoading, setIsLoading] = useState(loading ?? true);\n const [viewportWidth, setViewportWidth] = useState<number>(\n typeof window !== \"undefined\" ? window.innerWidth : 1280\n );\n const containerRef = useRef<HTMLDivElement>(null);\n const shownRef = useRef(false);\n\n // Track loading state\n useEffect(() => {\n if (loading !== undefined) {\n setIsLoading(loading);\n }\n }, [loading]);\n\n // Auto-detect loading completion when loading prop is not provided\n useEffect(() => {\n if (loading === undefined && children) {\n setIsLoading(false);\n }\n }, [loading, children]);\n\n // Track viewport width for responsive skeletons\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const onResize = () => setViewportWidth(window.innerWidth);\n window.addEventListener(\"resize\", onResize, { passive: true });\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n // Callbacks\n useEffect(() => {\n if (isLoading && !shownRef.current) {\n shownRef.current = true;\n onSkeletonShow?.();\n }\n if (!isLoading && shownRef.current) {\n onContentShow?.();\n }\n }, [isLoading, onSkeletonShow, onContentShow]);\n\n // Get skeleton from registry\n const entry = getFromRegistry(name);\n\n if (!isLoading) {\n return createElement(\n \"div\",\n {\n ref: containerRef,\n className,\n style,\n \"data-castdom\": name,\n },\n children\n );\n }\n\n // No skeleton data — show fallback or nothing\n if (!entry) {\n return fallback\n ? createElement(\"div\", { className, style }, fallback)\n : null;\n }\n\n // Select best breakpoint\n const bp = selectBreakpoint(entry.data, viewportWidth);\n if (!bp) return fallback ? createElement(\"div\", { className, style }, fallback) : null;\n\n // Render skeleton\n return createElement(SkeletonRenderer, {\n name,\n breakpoint: bp,\n animation,\n color,\n shimmerColor,\n duration,\n className,\n style,\n ariaLabel,\n });\n}\n\n/** Internal skeleton renderer — pure CSS, no JS animation runtime */\ninterface SkeletonRendererProps {\n name: string;\n breakpoint: BreakpointData;\n animation: \"shimmer\" | \"pulse\" | \"wave\" | \"none\";\n color?: string;\n shimmerColor?: string;\n duration?: number;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\nfunction SkeletonRenderer({\n name,\n breakpoint,\n animation,\n color = \"#e0e0e0\",\n shimmerColor = \"#f0f0f0\",\n duration = 1500,\n className,\n style,\n ariaLabel,\n}: SkeletonRendererProps): ReactNode {\n const bones = breakpoint.bones;\n\n const containerStyle: CSSProperties = {\n position: \"relative\",\n width: breakpoint.containerWidth,\n height: breakpoint.containerHeight,\n overflow: \"hidden\",\n ...style,\n };\n\n const getBackground = useCallback(() => {\n switch (animation) {\n case \"shimmer\":\n return `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;\n case \"pulse\":\n case \"wave\":\n case \"none\":\n return color;\n }\n }, [animation, color, shimmerColor]);\n\n const getAnimationCSS = useCallback((): string => {\n switch (animation) {\n case \"shimmer\":\n return `castdom-shimmer ${duration}ms ease-in-out infinite`;\n case \"pulse\":\n return `castdom-pulse ${duration}ms ease-in-out infinite`;\n case \"wave\":\n return `castdom-wave ${duration}ms ease-in-out infinite`;\n case \"none\":\n return \"none\";\n }\n }, [animation, duration]);\n\n return createElement(\n \"div\",\n {\n className: `castdom-skeleton castdom-${name}${className ? ` ${className}` : \"\"}`,\n style: containerStyle,\n role: \"status\",\n \"aria-busy\": true,\n \"aria-label\": ariaLabel ?? `Loading ${name}`,\n },\n bones.map((bone, i) =>\n createElement(\"div\", {\n key: i,\n className: \"castdom-bone\",\n \"aria-hidden\": true,\n style: {\n position: \"absolute\" as const,\n left: bone.x,\n top: bone.y,\n width: bone.w,\n height: bone.h,\n borderRadius: bone.r >= 9999 ? \"50%\" : bone.r,\n background: getBackground(),\n backgroundSize: animation === \"shimmer\" ? \"200% 100%\" : undefined,\n animation: getAnimationCSS(),\n animationDelay: animation === \"wave\" ? `${i * 50}ms` : undefined,\n } satisfies CSSProperties,\n })\n )\n );\n}\n\n/**\n * <CastDOMStyle> — renders the critical CSS for registered skeletons.\n * Place this in your <head> or layout for optimal performance.\n *\n * ```tsx\n * <head>\n * <CastDOMStyle skeletons={[\"user-card\", \"feed-item\"]} />\n * </head>\n * ```\n */\nexport function CastDOMStyle({\n skeletons,\n}: {\n skeletons?: string[];\n}): ReactNode {\n const css = useMemo(() => {\n const parts: string[] = [\n \"@keyframes castdom-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}\",\n \"@keyframes castdom-pulse{0%,100%{opacity:1}50%{opacity:0.4}}\",\n \"@keyframes castdom-wave{0%{opacity:0.4}50%{opacity:1}100%{opacity:0.4}}\",\n \"@media(prefers-reduced-motion:reduce){.castdom-bone{animation:none!important}}\",\n ];\n return parts.join(\"\");\n }, []);\n\n return createElement(\"style\", {\n dangerouslySetInnerHTML: { __html: css },\n \"data-castdom\": \"critical\",\n });\n}\n\n/**\n * Hook: useCastDOM — programmatic access to skeleton data.\n */\nexport function useCastDOM(name: string) {\n const entry = getFromRegistry(name);\n const [vw, setVW] = useState(\n typeof window !== \"undefined\" ? window.innerWidth : 1280\n );\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const onResize = () => setVW(window.innerWidth);\n window.addEventListener(\"resize\", onResize, { passive: true });\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n const breakpoint = entry ? selectBreakpoint(entry.data, vw) : null;\n\n return {\n exists: !!entry,\n data: entry?.data ?? null,\n breakpoint,\n css: entry?.css ?? \"\",\n html: breakpoint ? entry?.html[breakpoint.viewport] ?? \"\" : \"\",\n };\n}\n\nexport default CastDOM;\n"]}
@@ -0,0 +1,104 @@
1
+ import { DEFAULTS } from './chunk-EJRNKHL5.js';
2
+
3
+ // src/core/responsive.ts
4
+ function selectBreakpoint(skeleton, viewportWidth) {
5
+ const bps = [...skeleton.breakpoints].sort(
6
+ (a, b) => a.viewport - b.viewport
7
+ );
8
+ if (bps.length === 0) return null;
9
+ if (bps.length === 1) return bps[0];
10
+ let best = bps[0];
11
+ let bestDist = Math.abs(viewportWidth - best.viewport);
12
+ for (let i = 1; i < bps.length; i++) {
13
+ const dist = Math.abs(viewportWidth - bps[i].viewport);
14
+ if (dist < bestDist) {
15
+ best = bps[i];
16
+ bestDist = dist;
17
+ }
18
+ }
19
+ return best;
20
+ }
21
+ function interpolateBones(from, to, targetWidth) {
22
+ const range = to.viewport - from.viewport;
23
+ if (range === 0) return from;
24
+ const t = Math.max(0, Math.min(1, (targetWidth - from.viewport) / range));
25
+ const containerWidth = lerp(from.containerWidth, to.containerWidth, t);
26
+ const containerHeight = lerp(from.containerHeight, to.containerHeight, t);
27
+ containerWidth / from.containerWidth;
28
+ containerHeight / from.containerHeight;
29
+ if (from.bones.length !== to.bones.length) {
30
+ const base = t < 0.5 ? from : to;
31
+ const sx = containerWidth / base.containerWidth;
32
+ const sy = containerHeight / base.containerHeight;
33
+ return {
34
+ viewport: targetWidth,
35
+ containerWidth,
36
+ containerHeight,
37
+ bones: base.bones.map((bone) => ({
38
+ ...bone,
39
+ x: bone.x * sx,
40
+ y: bone.y * sy,
41
+ w: bone.w * sx,
42
+ h: bone.h * sy
43
+ }))
44
+ };
45
+ }
46
+ const bones = from.bones.map((fromBone, i) => {
47
+ const toBone = to.bones[i];
48
+ return {
49
+ x: lerp(fromBone.x, toBone.x, t),
50
+ y: lerp(fromBone.y, toBone.y, t),
51
+ w: lerp(fromBone.w, toBone.w, t),
52
+ h: lerp(fromBone.h, toBone.h, t),
53
+ r: lerp(fromBone.r, toBone.r, t),
54
+ kind: t < 0.5 ? fromBone.kind : toBone.kind
55
+ };
56
+ });
57
+ return {
58
+ viewport: targetWidth,
59
+ containerWidth,
60
+ containerHeight,
61
+ bones
62
+ };
63
+ }
64
+ function lerp(a, b, t) {
65
+ return a + (b - a) * t;
66
+ }
67
+ function diffBreakpoints(base, target, threshold = 2) {
68
+ const changed = [];
69
+ const bones = [];
70
+ const maxLen = Math.max(base.bones.length, target.bones.length);
71
+ for (let i = 0; i < maxLen; i++) {
72
+ const a = base.bones[i];
73
+ const b = target.bones[i];
74
+ if (!a || !b) {
75
+ changed.push(i);
76
+ bones.push(b || a);
77
+ continue;
78
+ }
79
+ const dx = Math.abs(a.x - b.x);
80
+ const dy = Math.abs(a.y - b.y);
81
+ const dw = Math.abs(a.w - b.w);
82
+ const dh = Math.abs(a.h - b.h);
83
+ if (dx > threshold || dy > threshold || dw > threshold || dh > threshold) {
84
+ changed.push(i);
85
+ bones.push(b);
86
+ }
87
+ }
88
+ return { changed, bones };
89
+ }
90
+ function validateBreakpoints(breakpoints) {
91
+ const unique = [...new Set(breakpoints)].sort((a, b) => a - b);
92
+ if (unique.length === 0) {
93
+ return [...DEFAULTS.breakpoints];
94
+ }
95
+ return unique.filter((bp) => bp >= 200 && bp <= 3840);
96
+ }
97
+ function getStaleBreakpoints(skeleton, targetBreakpoints) {
98
+ const existing = new Set(skeleton.breakpoints.map((bp) => bp.viewport));
99
+ return targetBreakpoints.filter((bp) => !existing.has(bp));
100
+ }
101
+
102
+ export { diffBreakpoints, getStaleBreakpoints, interpolateBones, selectBreakpoint, validateBreakpoints };
103
+ //# sourceMappingURL=chunk-ONS533CQ.js.map
104
+ //# sourceMappingURL=chunk-ONS533CQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/responsive.ts"],"names":[],"mappings":"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","file":"chunk-ONS533CQ.js","sourcesContent":["import type { BreakpointData, Bone, SkeletonData } from \"./types.js\";\nimport { DEFAULTS } from \"./types.js\";\n\n/**\n * Responsive breakpoint system for CastDOM.\n *\n * Handles:\n * - Selecting the best breakpoint for a given viewport width\n * - Interpolating between breakpoints for fluid layouts\n * - Diffing breakpoints to only store changes\n */\n\n/** Select the best breakpoint for a given viewport width */\nexport function selectBreakpoint(\n skeleton: SkeletonData,\n viewportWidth: number\n): BreakpointData | null {\n const bps = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n\n if (bps.length === 0) return null;\n if (bps.length === 1) return bps[0];\n\n // Find closest breakpoint\n let best = bps[0];\n let bestDist = Math.abs(viewportWidth - best.viewport);\n\n for (let i = 1; i < bps.length; i++) {\n const dist = Math.abs(viewportWidth - bps[i].viewport);\n if (dist < bestDist) {\n best = bps[i];\n bestDist = dist;\n }\n }\n\n return best;\n}\n\n/**\n * Interpolate bones between two breakpoints for a target viewport width.\n * Provides fluid skeleton sizing between captured breakpoints.\n */\nexport function interpolateBones(\n from: BreakpointData,\n to: BreakpointData,\n targetWidth: number\n): BreakpointData {\n // Calculate interpolation factor\n const range = to.viewport - from.viewport;\n if (range === 0) return from;\n\n const t = Math.max(0, Math.min(1, (targetWidth - from.viewport) / range));\n\n // Interpolate container dimensions\n const containerWidth = lerp(from.containerWidth, to.containerWidth, t);\n const containerHeight = lerp(from.containerHeight, to.containerHeight, t);\n\n // Scale factor for bone positions\n const scaleX = containerWidth / from.containerWidth;\n const scaleY = containerHeight / from.containerHeight;\n\n // If bone counts differ, use the closest breakpoint's bones\n if (from.bones.length !== to.bones.length) {\n const base = t < 0.5 ? from : to;\n const sx = containerWidth / base.containerWidth;\n const sy = containerHeight / base.containerHeight;\n\n return {\n viewport: targetWidth,\n containerWidth,\n containerHeight,\n bones: base.bones.map((bone) => ({\n ...bone,\n x: bone.x * sx,\n y: bone.y * sy,\n w: bone.w * sx,\n h: bone.h * sy,\n })),\n };\n }\n\n // Interpolate matching bones\n const bones: Bone[] = from.bones.map((fromBone, i) => {\n const toBone = to.bones[i];\n return {\n x: lerp(fromBone.x, toBone.x, t),\n y: lerp(fromBone.y, toBone.y, t),\n w: lerp(fromBone.w, toBone.w, t),\n h: lerp(fromBone.h, toBone.h, t),\n r: lerp(fromBone.r, toBone.r, t),\n kind: t < 0.5 ? fromBone.kind : toBone.kind,\n };\n });\n\n return {\n viewport: targetWidth,\n containerWidth,\n containerHeight,\n bones,\n };\n}\n\n/** Linear interpolation */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n/**\n * Compute a diff between two breakpoints.\n * Only stores bones that changed position/size significantly.\n * Used to reduce storage when breakpoints are similar.\n */\nexport function diffBreakpoints(\n base: BreakpointData,\n target: BreakpointData,\n threshold = 2\n): { changed: number[]; bones: Bone[] } {\n const changed: number[] = [];\n const bones: Bone[] = [];\n\n const maxLen = Math.max(base.bones.length, target.bones.length);\n\n for (let i = 0; i < maxLen; i++) {\n const a = base.bones[i];\n const b = target.bones[i];\n\n if (!a || !b) {\n changed.push(i);\n bones.push(b || a);\n continue;\n }\n\n const dx = Math.abs(a.x - b.x);\n const dy = Math.abs(a.y - b.y);\n const dw = Math.abs(a.w - b.w);\n const dh = Math.abs(a.h - b.h);\n\n if (dx > threshold || dy > threshold || dw > threshold || dh > threshold) {\n changed.push(i);\n bones.push(b);\n }\n }\n\n return { changed, bones };\n}\n\n/**\n * Validate breakpoint configuration.\n * Ensures breakpoints are sorted, unique, and reasonable.\n */\nexport function validateBreakpoints(breakpoints: number[]): number[] {\n const unique = [...new Set(breakpoints)].sort((a, b) => a - b);\n\n if (unique.length === 0) {\n return [...DEFAULTS.breakpoints];\n }\n\n // Filter out unreasonable values\n return unique.filter((bp) => bp >= 200 && bp <= 3840);\n}\n\n/**\n * Determine which breakpoints need re-extraction.\n * Compares existing data against current config.\n */\nexport function getStaleBreakpoints(\n skeleton: SkeletonData,\n targetBreakpoints: number[]\n): number[] {\n const existing = new Set(skeleton.breakpoints.map((bp) => bp.viewport));\n return targetBreakpoints.filter((bp) => !existing.has(bp));\n}\n"]}
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ var chunkJRQ6EVQP_cjs = require('./chunk-JRQ6EVQP.cjs');
4
+
5
+ // src/core/responsive.ts
6
+ function selectBreakpoint(skeleton, viewportWidth) {
7
+ const bps = [...skeleton.breakpoints].sort(
8
+ (a, b) => a.viewport - b.viewport
9
+ );
10
+ if (bps.length === 0) return null;
11
+ if (bps.length === 1) return bps[0];
12
+ let best = bps[0];
13
+ let bestDist = Math.abs(viewportWidth - best.viewport);
14
+ for (let i = 1; i < bps.length; i++) {
15
+ const dist = Math.abs(viewportWidth - bps[i].viewport);
16
+ if (dist < bestDist) {
17
+ best = bps[i];
18
+ bestDist = dist;
19
+ }
20
+ }
21
+ return best;
22
+ }
23
+ function interpolateBones(from, to, targetWidth) {
24
+ const range = to.viewport - from.viewport;
25
+ if (range === 0) return from;
26
+ const t = Math.max(0, Math.min(1, (targetWidth - from.viewport) / range));
27
+ const containerWidth = lerp(from.containerWidth, to.containerWidth, t);
28
+ const containerHeight = lerp(from.containerHeight, to.containerHeight, t);
29
+ containerWidth / from.containerWidth;
30
+ containerHeight / from.containerHeight;
31
+ if (from.bones.length !== to.bones.length) {
32
+ const base = t < 0.5 ? from : to;
33
+ const sx = containerWidth / base.containerWidth;
34
+ const sy = containerHeight / base.containerHeight;
35
+ return {
36
+ viewport: targetWidth,
37
+ containerWidth,
38
+ containerHeight,
39
+ bones: base.bones.map((bone) => ({
40
+ ...bone,
41
+ x: bone.x * sx,
42
+ y: bone.y * sy,
43
+ w: bone.w * sx,
44
+ h: bone.h * sy
45
+ }))
46
+ };
47
+ }
48
+ const bones = from.bones.map((fromBone, i) => {
49
+ const toBone = to.bones[i];
50
+ return {
51
+ x: lerp(fromBone.x, toBone.x, t),
52
+ y: lerp(fromBone.y, toBone.y, t),
53
+ w: lerp(fromBone.w, toBone.w, t),
54
+ h: lerp(fromBone.h, toBone.h, t),
55
+ r: lerp(fromBone.r, toBone.r, t),
56
+ kind: t < 0.5 ? fromBone.kind : toBone.kind
57
+ };
58
+ });
59
+ return {
60
+ viewport: targetWidth,
61
+ containerWidth,
62
+ containerHeight,
63
+ bones
64
+ };
65
+ }
66
+ function lerp(a, b, t) {
67
+ return a + (b - a) * t;
68
+ }
69
+ function diffBreakpoints(base, target, threshold = 2) {
70
+ const changed = [];
71
+ const bones = [];
72
+ const maxLen = Math.max(base.bones.length, target.bones.length);
73
+ for (let i = 0; i < maxLen; i++) {
74
+ const a = base.bones[i];
75
+ const b = target.bones[i];
76
+ if (!a || !b) {
77
+ changed.push(i);
78
+ bones.push(b || a);
79
+ continue;
80
+ }
81
+ const dx = Math.abs(a.x - b.x);
82
+ const dy = Math.abs(a.y - b.y);
83
+ const dw = Math.abs(a.w - b.w);
84
+ const dh = Math.abs(a.h - b.h);
85
+ if (dx > threshold || dy > threshold || dw > threshold || dh > threshold) {
86
+ changed.push(i);
87
+ bones.push(b);
88
+ }
89
+ }
90
+ return { changed, bones };
91
+ }
92
+ function validateBreakpoints(breakpoints) {
93
+ const unique = [...new Set(breakpoints)].sort((a, b) => a - b);
94
+ if (unique.length === 0) {
95
+ return [...chunkJRQ6EVQP_cjs.DEFAULTS.breakpoints];
96
+ }
97
+ return unique.filter((bp) => bp >= 200 && bp <= 3840);
98
+ }
99
+ function getStaleBreakpoints(skeleton, targetBreakpoints) {
100
+ const existing = new Set(skeleton.breakpoints.map((bp) => bp.viewport));
101
+ return targetBreakpoints.filter((bp) => !existing.has(bp));
102
+ }
103
+
104
+ exports.diffBreakpoints = diffBreakpoints;
105
+ exports.getStaleBreakpoints = getStaleBreakpoints;
106
+ exports.interpolateBones = interpolateBones;
107
+ exports.selectBreakpoint = selectBreakpoint;
108
+ exports.validateBreakpoints = validateBreakpoints;
109
+ //# sourceMappingURL=chunk-ORY4OMZ5.cjs.map
110
+ //# sourceMappingURL=chunk-ORY4OMZ5.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/responsive.ts"],"names":["DEFAULTS"],"mappings":"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","file":"chunk-ORY4OMZ5.cjs","sourcesContent":["import type { BreakpointData, Bone, SkeletonData } from \"./types.js\";\nimport { DEFAULTS } from \"./types.js\";\n\n/**\n * Responsive breakpoint system for CastDOM.\n *\n * Handles:\n * - Selecting the best breakpoint for a given viewport width\n * - Interpolating between breakpoints for fluid layouts\n * - Diffing breakpoints to only store changes\n */\n\n/** Select the best breakpoint for a given viewport width */\nexport function selectBreakpoint(\n skeleton: SkeletonData,\n viewportWidth: number\n): BreakpointData | null {\n const bps = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n\n if (bps.length === 0) return null;\n if (bps.length === 1) return bps[0];\n\n // Find closest breakpoint\n let best = bps[0];\n let bestDist = Math.abs(viewportWidth - best.viewport);\n\n for (let i = 1; i < bps.length; i++) {\n const dist = Math.abs(viewportWidth - bps[i].viewport);\n if (dist < bestDist) {\n best = bps[i];\n bestDist = dist;\n }\n }\n\n return best;\n}\n\n/**\n * Interpolate bones between two breakpoints for a target viewport width.\n * Provides fluid skeleton sizing between captured breakpoints.\n */\nexport function interpolateBones(\n from: BreakpointData,\n to: BreakpointData,\n targetWidth: number\n): BreakpointData {\n // Calculate interpolation factor\n const range = to.viewport - from.viewport;\n if (range === 0) return from;\n\n const t = Math.max(0, Math.min(1, (targetWidth - from.viewport) / range));\n\n // Interpolate container dimensions\n const containerWidth = lerp(from.containerWidth, to.containerWidth, t);\n const containerHeight = lerp(from.containerHeight, to.containerHeight, t);\n\n // Scale factor for bone positions\n const scaleX = containerWidth / from.containerWidth;\n const scaleY = containerHeight / from.containerHeight;\n\n // If bone counts differ, use the closest breakpoint's bones\n if (from.bones.length !== to.bones.length) {\n const base = t < 0.5 ? from : to;\n const sx = containerWidth / base.containerWidth;\n const sy = containerHeight / base.containerHeight;\n\n return {\n viewport: targetWidth,\n containerWidth,\n containerHeight,\n bones: base.bones.map((bone) => ({\n ...bone,\n x: bone.x * sx,\n y: bone.y * sy,\n w: bone.w * sx,\n h: bone.h * sy,\n })),\n };\n }\n\n // Interpolate matching bones\n const bones: Bone[] = from.bones.map((fromBone, i) => {\n const toBone = to.bones[i];\n return {\n x: lerp(fromBone.x, toBone.x, t),\n y: lerp(fromBone.y, toBone.y, t),\n w: lerp(fromBone.w, toBone.w, t),\n h: lerp(fromBone.h, toBone.h, t),\n r: lerp(fromBone.r, toBone.r, t),\n kind: t < 0.5 ? fromBone.kind : toBone.kind,\n };\n });\n\n return {\n viewport: targetWidth,\n containerWidth,\n containerHeight,\n bones,\n };\n}\n\n/** Linear interpolation */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n/**\n * Compute a diff between two breakpoints.\n * Only stores bones that changed position/size significantly.\n * Used to reduce storage when breakpoints are similar.\n */\nexport function diffBreakpoints(\n base: BreakpointData,\n target: BreakpointData,\n threshold = 2\n): { changed: number[]; bones: Bone[] } {\n const changed: number[] = [];\n const bones: Bone[] = [];\n\n const maxLen = Math.max(base.bones.length, target.bones.length);\n\n for (let i = 0; i < maxLen; i++) {\n const a = base.bones[i];\n const b = target.bones[i];\n\n if (!a || !b) {\n changed.push(i);\n bones.push(b || a);\n continue;\n }\n\n const dx = Math.abs(a.x - b.x);\n const dy = Math.abs(a.y - b.y);\n const dw = Math.abs(a.w - b.w);\n const dh = Math.abs(a.h - b.h);\n\n if (dx > threshold || dy > threshold || dw > threshold || dh > threshold) {\n changed.push(i);\n bones.push(b);\n }\n }\n\n return { changed, bones };\n}\n\n/**\n * Validate breakpoint configuration.\n * Ensures breakpoints are sorted, unique, and reasonable.\n */\nexport function validateBreakpoints(breakpoints: number[]): number[] {\n const unique = [...new Set(breakpoints)].sort((a, b) => a - b);\n\n if (unique.length === 0) {\n return [...DEFAULTS.breakpoints];\n }\n\n // Filter out unreasonable values\n return unique.filter((bp) => bp >= 200 && bp <= 3840);\n}\n\n/**\n * Determine which breakpoints need re-extraction.\n * Compares existing data against current config.\n */\nexport function getStaleBreakpoints(\n skeleton: SkeletonData,\n targetBreakpoints: number[]\n): number[] {\n const existing = new Set(skeleton.breakpoints.map((bp) => bp.viewport));\n return targetBreakpoints.filter((bp) => !existing.has(bp));\n}\n"]}
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ var chunkXS5HAU5E_cjs = require('./chunk-XS5HAU5E.cjs');
4
+
5
+ // src/seo/ssr.ts
6
+ function renderSkeleton(skeleton, options) {
7
+ const inlineCSS = options?.inlineCSS ?? true;
8
+ const hydrationAttrs = options?.hydrationAttrs ?? true;
9
+ let html;
10
+ if (inlineCSS) {
11
+ html = chunkXS5HAU5E_cjs.renderStandalone(skeleton, options?.config);
12
+ } else {
13
+ html = chunkXS5HAU5E_cjs.renderResponsiveHTML(skeleton, options?.config);
14
+ }
15
+ if (hydrationAttrs) {
16
+ html = html.replace(
17
+ /class="castdom-skeleton/,
18
+ `data-castdom-ssr="true" data-castdom-name="${skeleton.name}" data-castdom-hash="${skeleton.hash}" class="castdom-skeleton`
19
+ );
20
+ }
21
+ if (options?.noscript) {
22
+ html = `<noscript>${html}</noscript>`;
23
+ }
24
+ return html;
25
+ }
26
+ function renderSkeletons(skeletons, options) {
27
+ const css = chunkXS5HAU5E_cjs.generateCriticalCSS(skeletons, options?.config);
28
+ const html = {};
29
+ for (const skeleton of skeletons) {
30
+ html[skeleton.name] = renderSkeleton(skeleton, {
31
+ ...options,
32
+ inlineCSS: false
33
+ // CSS is shared
34
+ });
35
+ }
36
+ return { css, html };
37
+ }
38
+ function renderCriticalStyleTag(skeletons, config) {
39
+ const css = chunkXS5HAU5E_cjs.generateCriticalCSS(skeletons, config);
40
+ return `<style data-castdom="critical">${css}</style>`;
41
+ }
42
+ function renderHydrationScript() {
43
+ return `<script data-castdom="hydration">(function(){if(typeof window==='undefined')return;function r(){document.querySelectorAll('[data-castdom-ssr]').forEach(function(el){if(el.nextElementSibling&&!el.nextElementSibling.hasAttribute('data-castdom-ssr')){el.remove()}})}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',r)}else{setTimeout(r,0)}})()</script>`;
44
+ }
45
+ function renderSSRFragment(skeletons, options) {
46
+ const { css, html } = renderSkeletons(skeletons, options);
47
+ const head = [
48
+ `<style data-castdom="critical">${css}</style>`,
49
+ renderHydrationScript()
50
+ ].join("\n");
51
+ return { head, body: html };
52
+ }
53
+ function renderPreloadHints() {
54
+ return "";
55
+ }
56
+
57
+ exports.renderCriticalStyleTag = renderCriticalStyleTag;
58
+ exports.renderHydrationScript = renderHydrationScript;
59
+ exports.renderPreloadHints = renderPreloadHints;
60
+ exports.renderSSRFragment = renderSSRFragment;
61
+ exports.renderSkeleton = renderSkeleton;
62
+ exports.renderSkeletons = renderSkeletons;
63
+ //# sourceMappingURL=chunk-QLEBTZIB.cjs.map
64
+ //# sourceMappingURL=chunk-QLEBTZIB.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/seo/ssr.ts"],"names":["renderStandalone","renderResponsiveHTML","generateCriticalCSS"],"mappings":";;;;;AAyCO,SAAS,cAAA,CACd,UACA,OAAA,EACQ;AACR,EAAA,MAAM,SAAA,GAAY,SAAS,SAAA,IAAa,IAAA;AACxC,EAAA,MAAM,cAAA,GAAiB,SAAS,cAAA,IAAkB,IAAA;AAElD,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,IAAA,GAAOA,kCAAA,CAAiB,QAAA,EAAU,OAAA,EAAS,MAAM,CAAA;AAAA,EACnD,CAAA,MAAO;AACL,IAAA,IAAA,GAAOC,sCAAA,CAAqB,QAAA,EAAU,OAAA,EAAS,MAAM,CAAA;AAAA,EACvD;AAGA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA;AAAA,MACV,yBAAA;AAAA,MACA,CAAA,2CAAA,EAA8C,QAAA,CAAS,IAAI,CAAA,qBAAA,EAAwB,SAAS,IAAI,CAAA,yBAAA;AAAA,KAClG;AAAA,EACF;AAGA,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,IAAA,GAAO,aAAa,IAAI,CAAA,WAAA,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,IAAA;AACT;AAQO,SAAS,eAAA,CACd,WACA,OAAA,EAC+C;AAC/C,EAAA,MAAM,GAAA,GAAMC,qCAAA,CAAoB,SAAA,EAAW,OAAA,EAAS,MAAM,CAAA;AAE1D,EAAA,MAAM,OAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,IAAA,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,GAAI,cAAA,CAAe,QAAA,EAAU;AAAA,MAC7C,GAAG,OAAA;AAAA,MACH,SAAA,EAAW;AAAA;AAAA,KACZ,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,EAAE,KAAK,IAAA,EAAK;AACrB;AAMO,SAAS,sBAAA,CACd,WACA,MAAA,EACQ;AACR,EAAA,MAAM,GAAA,GAAMA,qCAAA,CAAoB,SAAA,EAAW,MAAM,CAAA;AACjD,EAAA,OAAO,kCAAkC,GAAG,CAAA,QAAA,CAAA;AAC9C;AAaO,SAAS,qBAAA,GAAgC;AAC9C,EAAA,OAAO,CAAA,6XAAA,CAAA;AACT;AAQO,SAAS,iBAAA,CACd,WACA,OAAA,EACgD;AAChD,EAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAK,GAAI,eAAA,CAAgB,WAAW,OAAO,CAAA;AAExD,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,kCAAkC,GAAG,CAAA,QAAA,CAAA;AAAA,IACrC,qBAAA;AAAsB,GACxB,CAAE,KAAK,IAAI,CAAA;AAEX,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,IAAA,EAAK;AAC5B;AAMO,SAAS,kBAAA,GAA6B;AAG3C,EAAA,OAAO,EAAA;AACT","file":"chunk-QLEBTZIB.cjs","sourcesContent":["import type { SkeletonData, CastDOMConfig } from \"../core/types.js\";\nimport { renderStandalone, renderResponsiveHTML, generateCriticalCSS } from \"../core/renderer.js\";\nimport { DEFAULTS } from \"../core/types.js\";\n\n/**\n * SSR (Server-Side Rendering) utilities for CastDOM.\n *\n * These functions generate HTML that can be embedded directly in\n * server-rendered pages. The skeletons render as pure CSS —\n * no JavaScript is needed on the client to display them.\n *\n * Benefits for SEO:\n * - Crawlers see structured placeholder content instead of blank areas\n * - Zero CLS (Cumulative Layout Shift) — skeletons match exact dimensions\n * - Instant visual feedback — no FOUC (Flash of Unstyled Content)\n * - Proper ARIA attributes for accessibility\n */\n\nexport interface SSRRenderOptions {\n /** Include <style> tag with critical CSS (default: true) */\n inlineCSS?: boolean;\n /** Wrap in a <noscript> fallback (default: false) */\n noscript?: boolean;\n /** Add data attributes for hydration (default: true) */\n hydrationAttrs?: boolean;\n /** Custom config overrides */\n config?: Partial<CastDOMConfig>;\n}\n\n/**\n * Render a skeleton to an HTML string for SSR.\n * Includes inline critical CSS for immediate rendering.\n *\n * ```ts\n * // In your server handler / getServerSideProps / loader:\n * import { renderSkeleton } from 'castdom/ssr';\n *\n * const skeletonHTML = renderSkeleton(skeletonData);\n * // Embed in your page HTML\n * ```\n */\nexport function renderSkeleton(\n skeleton: SkeletonData,\n options?: SSRRenderOptions\n): string {\n const inlineCSS = options?.inlineCSS ?? true;\n const hydrationAttrs = options?.hydrationAttrs ?? true;\n\n let html: string;\n if (inlineCSS) {\n html = renderStandalone(skeleton, options?.config);\n } else {\n html = renderResponsiveHTML(skeleton, options?.config);\n }\n\n // Add hydration data attributes\n if (hydrationAttrs) {\n html = html.replace(\n /class=\"castdom-skeleton/,\n `data-castdom-ssr=\"true\" data-castdom-name=\"${skeleton.name}\" data-castdom-hash=\"${skeleton.hash}\" class=\"castdom-skeleton`\n );\n }\n\n // Wrap in noscript if requested\n if (options?.noscript) {\n html = `<noscript>${html}</noscript>`;\n }\n\n return html;\n}\n\n/**\n * Render multiple skeletons with shared CSS (deduplicates the critical CSS).\n *\n * More efficient than calling renderSkeleton() multiple times\n * when you have several skeletons on one page.\n */\nexport function renderSkeletons(\n skeletons: SkeletonData[],\n options?: SSRRenderOptions\n): { css: string; html: Record<string, string> } {\n const css = generateCriticalCSS(skeletons, options?.config);\n\n const html: Record<string, string> = {};\n for (const skeleton of skeletons) {\n html[skeleton.name] = renderSkeleton(skeleton, {\n ...options,\n inlineCSS: false, // CSS is shared\n });\n }\n\n return { css, html };\n}\n\n/**\n * Generate a <style> tag with critical CSS for given skeletons.\n * Place in <head> for optimal rendering performance.\n */\nexport function renderCriticalStyleTag(\n skeletons: SkeletonData[],\n config?: Partial<CastDOMConfig>\n): string {\n const css = generateCriticalCSS(skeletons, config);\n return `<style data-castdom=\"critical\">${css}</style>`;\n}\n\n/**\n * Generate a hydration script that removes SSR skeletons\n * once the client-side app takes over.\n *\n * This is a tiny (~200 byte) inline script that:\n * 1. Waits for DOMContentLoaded\n * 2. Finds all [data-castdom-ssr] elements\n * 3. Removes them once the real content renders\n *\n * The script is safe to inline — no external dependencies.\n */\nexport function renderHydrationScript(): string {\n return `<script data-castdom=\"hydration\">(function(){if(typeof window==='undefined')return;function r(){document.querySelectorAll('[data-castdom-ssr]').forEach(function(el){if(el.nextElementSibling&&!el.nextElementSibling.hasAttribute('data-castdom-ssr')){el.remove()}})}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',r)}else{setTimeout(r,0)}})()</script>`;\n}\n\n/**\n * Generate a complete SSR skeleton page fragment.\n * Includes: critical CSS + skeleton HTML + hydration script.\n *\n * Designed to be dropped directly into your HTML template.\n */\nexport function renderSSRFragment(\n skeletons: SkeletonData[],\n options?: SSRRenderOptions\n): { head: string; body: Record<string, string> } {\n const { css, html } = renderSkeletons(skeletons, options);\n\n const head = [\n `<style data-castdom=\"critical\">${css}</style>`,\n renderHydrationScript(),\n ].join(\"\\n\");\n\n return { head, body: html };\n}\n\n/**\n * Preload hint generator for skeleton assets.\n * Generates <link> tags for optimal loading.\n */\nexport function renderPreloadHints(): string {\n // CastDOM is CSS-only at runtime, so no assets to preload.\n // This function exists for API consistency and future-proofing.\n return \"\";\n}\n\n// Re-export core utilities for convenience\nexport { generateCriticalCSS } from \"../core/renderer.js\";\nexport { renderStandalone, renderResponsiveHTML } from \"../core/renderer.js\";\n"]}