@typecaast/react 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +49 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +50 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -55,20 +55,24 @@ function useTypecaast(config, options = {}) {
|
|
|
55
55
|
} = options;
|
|
56
56
|
const resolved = useResolvedTheme(theme ?? config.meta.theme);
|
|
57
57
|
const player = react.useMemo(() => {
|
|
58
|
-
const engine = configToEngine(config,
|
|
58
|
+
const engine = configToEngine(config, "light", capabilities);
|
|
59
59
|
return core.createPlayer(engine.getStateAt, {
|
|
60
60
|
durationMs: engine.durationMs,
|
|
61
61
|
steps: engine.steps,
|
|
62
62
|
loop,
|
|
63
63
|
rate
|
|
64
64
|
});
|
|
65
|
-
}, [config,
|
|
66
|
-
const [
|
|
65
|
+
}, [config, capabilities, loop, rate]);
|
|
66
|
+
const [rawState, setRawState] = react.useState(() => player.state);
|
|
67
67
|
const [currentMs, setCurrentMs] = react.useState(() => player.currentMs);
|
|
68
68
|
const [playing, setPlaying] = react.useState(() => player.playing);
|
|
69
|
+
const state = react.useMemo(
|
|
70
|
+
() => rawState.theme === resolved ? rawState : { ...rawState, theme: resolved },
|
|
71
|
+
[rawState, resolved]
|
|
72
|
+
);
|
|
69
73
|
react.useEffect(() => {
|
|
70
74
|
const sync = () => {
|
|
71
|
-
|
|
75
|
+
setRawState(player.state);
|
|
72
76
|
setCurrentMs(player.currentMs);
|
|
73
77
|
};
|
|
74
78
|
sync();
|
|
@@ -147,6 +151,38 @@ function useSkinFonts(skin) {
|
|
|
147
151
|
}, [fonts]);
|
|
148
152
|
return state;
|
|
149
153
|
}
|
|
154
|
+
function collectImageUrls(config) {
|
|
155
|
+
const urls = /* @__PURE__ */ new Set();
|
|
156
|
+
for (const p of config.participants) if (p.avatar) urls.add(p.avatar);
|
|
157
|
+
for (const step of config.timeline) {
|
|
158
|
+
const s = step;
|
|
159
|
+
if (Array.isArray(s.images)) {
|
|
160
|
+
for (const im of s.images)
|
|
161
|
+
if (typeof im?.src === "string") urls.add(im.src);
|
|
162
|
+
}
|
|
163
|
+
if (Array.isArray(s.content)) {
|
|
164
|
+
for (const n of s.content)
|
|
165
|
+
if (n?.type === "image" && typeof n.src === "string") urls.add(n.src);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return [...urls];
|
|
169
|
+
}
|
|
170
|
+
var preloaded = /* @__PURE__ */ new Map();
|
|
171
|
+
function preload(url) {
|
|
172
|
+
if (preloaded.has(url)) return;
|
|
173
|
+
const img = new Image();
|
|
174
|
+
img.decoding = "async";
|
|
175
|
+
img.src = url;
|
|
176
|
+
preloaded.set(url, img);
|
|
177
|
+
void img.decode?.().catch(() => {
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function usePreloadImages(config) {
|
|
181
|
+
react.useEffect(() => {
|
|
182
|
+
if (typeof Image === "undefined") return;
|
|
183
|
+
for (const url of collectImageUrls(config)) preload(url);
|
|
184
|
+
}, [config]);
|
|
185
|
+
}
|
|
150
186
|
var QUERY2 = "(prefers-reduced-motion: reduce)";
|
|
151
187
|
function getMql2() {
|
|
152
188
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
@@ -333,10 +369,17 @@ function rootStyle(canvas) {
|
|
|
333
369
|
}
|
|
334
370
|
var Typecaast = react.forwardRef(
|
|
335
371
|
function Typecaast2(props, ref) {
|
|
336
|
-
const
|
|
337
|
-
() =>
|
|
372
|
+
const configKey = react.useMemo(
|
|
373
|
+
() => JSON.stringify(props.config),
|
|
338
374
|
[props.config]
|
|
339
375
|
);
|
|
376
|
+
const rawConfigRef = react.useRef(props.config);
|
|
377
|
+
rawConfigRef.current = props.config;
|
|
378
|
+
const config = react.useMemo(
|
|
379
|
+
() => schema.configSchema.parse(rawConfigRef.current),
|
|
380
|
+
[configKey]
|
|
381
|
+
);
|
|
382
|
+
usePreloadImages(config);
|
|
340
383
|
if (props.skin)
|
|
341
384
|
return /* @__PURE__ */ jsxRuntime.jsx(Player, { ...props, config, skin: props.skin, ref });
|
|
342
385
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine-adapter.ts","../src/use-resolved-theme.ts","../src/use-typecaast.ts","../src/shadow-frame.tsx","../src/use-skin-fonts.ts","../src/use-reduced-motion.ts","../src/transcript.ts","../src/fit-box.tsx","../src/builtin-skins.ts","../src/typecaast.tsx","../src/resolve-theme.ts"],"names":["createEngine","useSyncExternalStore","useMemo","createPlayer","useState","useEffect","useRef","useLayoutEffect","createPortal","jsxs","Fragment","jsx","loadSkinFonts","QUERY","getMql","subscribe","getSnapshot","getServerSnapshot","forwardRef","Typecaast","configSchema","Suspense","ResolvedPlayer","Player","useImperativeHandle","TypecaastStage"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Config } from \"@typecaast/schema\";\nimport {\n createEngine,\n type Capabilities,\n type EngineHandle,\n type ResolvedTheme,\n} from \"@typecaast/core\";\n\nexport type Engine = EngineHandle;\n\n/**\n * The single seam between a config and a playable engine. M1-UI ran this over a\n * hand-mocked timeline; M1-engine swaps in the real `compile` + `getStateAt`\n * here — and nothing else in the renderer changed (same `Engine` shape).\n *\n * Optional `capabilities` (from the active skin) drop unsupported events/content\n * from the sampled state while leaving the config intact.\n */\nexport function configToEngine(\n config: Config,\n theme: ResolvedTheme,\n capabilities?: Capabilities,\n): Engine {\n return createEngine(config, theme, capabilities);\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nfunction getSnapshot(): boolean {\n return getMql()?.matches ?? false;\n}\n\n/** No `matchMedia` on the server → default to light (consistent with export). */\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/** Reactively tracks the host's `prefers-color-scheme: dark`. */\nexport function usePrefersDark(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * Resolve a theme mode to a concrete theme. `light`/`dark` are forced; `auto`\n * tracks the host `prefers-color-scheme` reactively and falls back to `light`\n * when no preference signal is available.\n */\nexport function useResolvedTheme(mode: ThemeMode): ResolvedTheme {\n const prefersDark = usePrefersDark();\n if (mode === \"light\") return \"light\";\n if (mode === \"dark\") return \"dark\";\n return prefersDark ? \"dark\" : \"light\";\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport type { Config, ThemeMode } from \"@typecaast/schema\";\nimport {\n createPlayer,\n type Capabilities,\n type Player,\n type SimState,\n} from \"@typecaast/core\";\nimport { configToEngine } from \"./engine-adapter.js\";\nimport { useResolvedTheme } from \"./use-resolved-theme.js\";\n\nexport interface UseTypecaastOptions {\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** The active skin's capabilities, to drop what it can't render. */\n capabilities?: Capabilities;\n}\n\n/** Imperative controls + live state returned by {@link useTypecaast}. */\nexport interface TypecaastControls {\n state: SimState;\n /** Current playback time in ms (reactive). */\n currentMs: number;\n play(): void;\n pause(): void;\n seek(timeMs: number): void;\n scrubTo(timeMs: number): void;\n setRate(rate: number): void;\n stepNext(): void;\n stepPrev(): void;\n duration: number;\n rate: number;\n playing: boolean;\n /** Escape hatch to the underlying player. */\n player: Player;\n}\n\n/**\n * Mount a player for a config and expose live state + controls. The player\n * owns the clock (rAF in the browser); this hook bridges its ticks into React\n * state. The builder uses these controls for preview-as-you-go editing.\n *\n * In M1-UI the player runs over the mocked engine (see engine-adapter); the\n * hook's surface is the final one and does not change when the real engine\n * lands.\n */\nexport function useTypecaast(\n config: Config,\n options: UseTypecaastOptions = {},\n): TypecaastControls {\n const {\n theme,\n autoplay = false,\n // `loop` falls back to `config.meta.loop` so a config authored with looping\n // behaves the same in the builder preview and in zero-prop embeds.\n loop = config.meta.loop ?? false,\n rate = 1,\n capabilities,\n } = options;\n const resolved = useResolvedTheme(theme ?? config.meta.theme);\n\n const player = useMemo<Player>(() => {\n const engine = configToEngine(config, resolved, capabilities);\n return createPlayer(engine.getStateAt, {\n durationMs: engine.durationMs,\n steps: engine.steps,\n loop,\n rate,\n });\n }, [config, resolved, capabilities, loop, rate]);\n\n const [state, setState] = useState<SimState>(() => player.state);\n const [currentMs, setCurrentMs] = useState<number>(() => player.currentMs);\n const [playing, setPlaying] = useState<boolean>(() => player.playing);\n\n useEffect(() => {\n const sync = () => {\n setState(player.state);\n setCurrentMs(player.currentMs);\n };\n sync();\n setPlaying(player.playing);\n const offs = [\n player.on(\"tick\", sync),\n player.on(\"seek\", sync),\n player.on(\"play\", () => setPlaying(true)),\n player.on(\"pause\", () => setPlaying(false)),\n ];\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player]);\n\n // Apply `autoplay` once per player (at creation), not reactively. If this read\n // were a dependency of the subscribe/destroy effect above, a consumer toggling\n // a controlled `paused` — which flips the gated `autoplay` value upstream —\n // would tear down and recreate the live player. Post-mount play/pause is the\n // consumer's job (e.g. `<Typecaast>`'s `paused` reconcile).\n // (deps intentionally exclude `autoplay` — see the comment above.)\n useEffect(() => {\n if (autoplay) player.play();\n }, [player]);\n\n return {\n state,\n currentMs,\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t) => player.seek(t),\n scrubTo: (t) => player.scrubTo(t),\n setRate: (r) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n duration: player.durationMs,\n rate: player.rate,\n playing,\n player,\n };\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Base reset injected inside the shadow root. The host page's stylesheet\n * selectors (`*`, `code {}`, `.prose p {}`, …) cannot reach into a shadow tree,\n * so the only thing that leaks in is **inherited** properties from the host\n * element. `all: initial` on `:host` neutralises those (line-height, font-weight,\n * letter-spacing, font-variant, font-family, color) to clean defaults; the\n * skin's `Frame` then sets its own font/colour as usual. The universal\n * `box-sizing: border-box` matches the baseline the skins are authored against.\n */\nconst RESET = `\n:host { all: initial; display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n`;\n\n/**\n * Renders its children inside an **open shadow root**, so an embedded\n * `<Typecaast>` is immune to the host page's global CSS (resets, Tailwind\n * `.prose`, tag rules, inherited `line-height`/font). Used by `<Typecaast>` when\n * `isolate` is set.\n *\n * Client-only by nature — `attachShadow` needs the DOM, so on the server (and\n * the first client render) nothing is portaled; the light-DOM host div still\n * reserves the widget's size, and the visuals hydrate in. The web fonts the\n * skins register via the `FontFace` API live in the document and apply across\n * the shadow boundary, so typography is unaffected.\n */\nexport function ShadowFrame({\n children,\n style,\n}: {\n children: ReactNode;\n style?: CSSProperties;\n}): ReactElement {\n const hostRef = useRef<HTMLDivElement>(null);\n const [root, setRoot] = useState<ShadowRoot | null>(null);\n\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n // `?? shadowRoot` makes this safe under StrictMode's double-invoke (a second\n // attachShadow would throw).\n setRoot(host.shadowRoot ?? host.attachShadow({ mode: \"open\" }));\n }, []);\n\n return (\n <div ref={hostRef} aria-hidden=\"true\" style={style}>\n {root\n ? createPortal(\n <>\n <style>{RESET}</style>\n {children}\n </>,\n root,\n )\n : null}\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { loadSkinFonts, type Skin } from \"@typecaast/skin-kit\";\n\nexport type FontLoadState = \"loading\" | \"loaded\";\n\n/**\n * Load a skin's declared web fonts on mount so the live preview renders in the\n * correct typeface (PLAN §19) — never relying on a host OS font. SSR-safe and\n * a no-op off the DOM (resolves \"loaded\"). Re-runs if the skin's fonts change.\n */\nexport function useSkinFonts(skin: Skin): FontLoadState {\n const fonts = skin.meta.fonts;\n const [state, setState] = useState<FontLoadState>(() =>\n fonts && fonts.length > 0 ? \"loading\" : \"loaded\",\n );\n\n useEffect(() => {\n if (!fonts || fonts.length === 0) {\n setState(\"loaded\");\n return;\n }\n let cancelled = false;\n setState(\"loading\");\n loadSkinFonts(fonts).finally(() => {\n if (!cancelled) setState(\"loaded\");\n });\n return () => {\n cancelled = true;\n };\n }, [fonts]);\n\n return state;\n}\n","import { useSyncExternalStore } from \"react\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nconst getSnapshot = (): boolean => getMql()?.matches ?? false;\nconst getServerSnapshot = (): boolean => false;\n\n/**\n * Tracks `prefers-reduced-motion: reduce`. When true, the player snaps to the\n * final state instead of animating (PLAN §20).\n */\nexport function useReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import type { Config } from \"@typecaast/schema\";\n\nexport interface TranscriptLine {\n name: string;\n text: string;\n}\n\n/**\n * Build a plain-text transcript from the authored config — the accessible\n * representation of the conversation for screen readers (PLAN §20). The config\n * is already structured text, so this is a faithful, non-animated version.\n */\nexport function buildTranscript(config: Config): TranscriptLine[] {\n const name = new Map(config.participants.map((p) => [p.id, p.name]));\n const lines: TranscriptLine[] = [];\n for (const step of config.timeline) {\n let from: string | undefined;\n let text: string | undefined;\n if (step.type === \"message\" || step.type === \"system\") {\n from = step.from;\n text = step.text;\n } else if (step.type === \"composerType\") {\n from = step.from;\n text = step.text;\n }\n if (text) {\n lines.push({ name: from ? (name.get(from) ?? from) : \"System\", text });\n }\n }\n return lines;\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { FitMode, Size } from \"@typecaast/schema\";\n\nexport interface FitBoxProps {\n fit: FitMode;\n canvas: Size;\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * Applies the `fit` strategy between the authoring canvas and the host\n * container (PLAN §7):\n * - `reflow`: **fills both axes** (width + height); content re-wraps to the\n * container width and the bottom-anchored thread clips older messages\n * when they overflow vertically. The widget is container-driven — it\n * never grows past its host as more steps play.\n * - `scale`: renders at exact canvas size, CSS-scaled to fit (layout\n * preserved). Use when you want the skin to look like its native canvas\n * regardless of container size.\n * - `fixed`: exact canvas size, clipped — the only mode where the widget\n * is *not* container-driven.\n */\nexport function FitBox({\n fit,\n canvas,\n children,\n className,\n style,\n}: FitBoxProps): ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<{ w: number; h: number } | null>(\n null,\n );\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (rect) setContainer({ w: rect.width, h: rect.height });\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n if (fit === \"reflow\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"reflow\"\n // `height: 100%` closes the height chain: combined with the parent\n // `<Typecaast>` outer's `aspect-ratio` fallback, the widget fills\n // its host container instead of growing taller as more messages\n // play (the skin's bottom-anchored thread + `overflow: hidden`\n // clip older steps off the top).\n style={{ width: \"100%\", height: \"100%\", ...style }}\n >\n {children}\n </div>\n );\n }\n\n if (fit === \"fixed\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"fixed\"\n style={{\n width: canvas.width,\n height: canvas.height,\n overflow: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n }\n\n // scale: fit the exact-size canvas into the measured container.\n const scale = container\n ? Math.min(container.w / canvas.width, container.h / canvas.height)\n : 1;\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"scale\"\n style={{ width: \"100%\", height: \"100%\", overflow: \"hidden\", ...style }}\n >\n <div\n data-fit-canvas=\"\"\n style={{\n width: canvas.width,\n height: canvas.height,\n transform: `scale(${scale})`,\n transformOrigin: \"top left\",\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n","import type { Skin } from \"@typecaast/skin-kit\";\n\ntype SkinModule = { default: Skin };\n\n/**\n * Lazy loaders for the built-in skins, keyed by `meta.skin.id`. Each value is a\n * **static** `import()` of a per-skin subpath, so bundlers emit one chunk per\n * skin and only the skin a config actually references is fetched. Custom skins\n * bypass this entirely (pass the `skin` prop to `<Typecaast>`).\n *\n * Adding a built-in skin = add one line here + the subpath export in\n * `@typecaast/skins`.\n */\nexport const BUILTIN_SKIN_LOADERS: Record<string, () => Promise<SkinModule>> = {\n slack: () => import(\"@typecaast/skins/slack\"),\n telegram: () => import(\"@typecaast/skins/telegram\"),\n \"claude-code\": () => import(\"@typecaast/skins/claude-code\"),\n imessage: () => import(\"@typecaast/skins/imessage\"),\n whatsapp: () => import(\"@typecaast/skins/whatsapp\"),\n cursor: () => import(\"@typecaast/skins/cursor\"),\n \"messages-macos\": () => import(\"@typecaast/skins/messages-macos\"),\n discord: () => import(\"@typecaast/skins/discord\"),\n};\n\n/** Ids of the built-in skins resolvable by `<Typecaast>` without a `skin` prop. */\nexport const builtinSkinIds = Object.keys(BUILTIN_SKIN_LOADERS);\n\n/**\n * A status-tracked load, cached per id. Tracking the settled state lets us\n * Suspense-read it on **React 18 and 19** alike (via the throw-the-promise\n * primitive) instead of React 19's `use()`, which doesn't exist on 18.\n */\ninterface SkinResource {\n promise: Promise<Skin>;\n status: \"pending\" | \"fulfilled\" | \"rejected\";\n value?: Skin;\n error?: unknown;\n}\n\n// Stable resource per id so the same promise is seen across renders.\nconst cache = new Map<string, SkinResource>();\n\n/**\n * Get (or create) the cached resource for a skin id. Throws synchronously for an\n * unknown id (a render error with a clear message), rather than suspending forever.\n */\nfunction getResource(id: string): SkinResource {\n let resource = cache.get(id);\n if (resource) return resource;\n\n const loader = BUILTIN_SKIN_LOADERS[id];\n if (!loader) {\n throw new Error(\n `Typecaast: unknown skin \"${id}\". Built-in skins: ${builtinSkinIds.join(\n \", \",\n )}. For a custom skin, pass the \\`skin\\` prop.`,\n );\n }\n\n resource = {\n status: \"pending\",\n promise: loader().then((m) => m.default),\n };\n // Record the settled state for the synchronous Suspense read. This handler\n // also keeps `promise`'s rejection from going unhandled.\n void resource.promise.then(\n (skin) => {\n resource!.status = \"fulfilled\";\n resource!.value = skin;\n },\n (error: unknown) => {\n resource!.status = \"rejected\";\n resource!.error = error;\n },\n );\n cache.set(id, resource);\n return resource;\n}\n\n/**\n * Resolve a built-in skin id to a cached promise of its `Skin`. Stable per id.\n */\nexport function loadBuiltinSkin(id: string): Promise<Skin> {\n return getResource(id).promise;\n}\n\n/**\n * Suspense-read a built-in skin by id: returns the `Skin` once loaded, throws the\n * pending promise to suspend, or re-throws a load error. This is the universal\n * Suspense primitive — it works on React 18 and 19, unlike `use()` (19-only).\n */\nexport function readBuiltinSkin(id: string): Skin {\n const resource = getResource(id);\n if (resource.status === \"fulfilled\") return resource.value as Skin;\n if (resource.status === \"rejected\") throw resource.error;\n throw resource.promise;\n}\n","import {\n forwardRef,\n Suspense,\n useEffect,\n useImperativeHandle,\n useMemo,\n type CSSProperties,\n type ForwardedRef,\n type ReactElement,\n} from \"react\";\nimport {\n configSchema,\n type Config,\n type ConfigInput,\n type FitMode,\n type ThemeMode,\n} from \"@typecaast/schema\";\nimport {\n TypecaastStage,\n type ComposerMode,\n type Skin,\n} from \"@typecaast/skin-kit\";\nimport { useTypecaast } from \"./use-typecaast.js\";\nimport { ShadowFrame } from \"./shadow-frame.js\";\nimport { useSkinFonts } from \"./use-skin-fonts.js\";\nimport { useReducedMotion } from \"./use-reduced-motion.js\";\nimport { buildTranscript } from \"./transcript.js\";\nimport { FitBox } from \"./fit-box.js\";\nimport { readBuiltinSkin } from \"./builtin-skins.js\";\n\n/**\n * A loosely-typed config shape that a raw `import`ed `typecaast.json` satisfies —\n * TypeScript widens JSON literals (e.g. `version: number`, `type: string`), so it\n * matches neither `Config` nor `ConfigInput`. It's validated and normalized at\n * runtime, so this stays a convenience surface, not a bypass.\n */\nexport interface RawConfig {\n version: number;\n meta: {\n canvas: { width: number; height: number };\n skin: { id: string; options?: Record<string, unknown> };\n [key: string]: unknown;\n };\n participants: Array<{ id: string; name: string; [key: string]: unknown }>;\n timeline: Array<{ type: string; [key: string]: unknown }>;\n pacing?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * What `<Typecaast config>` accepts: a precise `ConfigInput`/`Config` (full\n * intellisense when hand-authoring) or a raw config object such as an imported\n * `typecaast.json`. All forms are normalized through the schema at runtime.\n */\nexport type TypecaastConfig = ConfigInput | Config | RawConfig;\n\nexport interface TypecaastProps {\n /**\n * The conversation config. Accepts your exported `typecaast.json` directly (or\n * a hand-authored `ConfigInput`); it's validated and defaulted at runtime, so\n * you never need to pre-parse it.\n */\n config: TypecaastConfig;\n /**\n * The skin to render with. **Optional** — by default the built-in skin named\n * by `config.meta.skin.id` is resolved and lazy-loaded (only that skin's chunk\n * is fetched), so the config is the single source of truth and the embed stays\n * fully serializable (works in a React Server Component, no `\"use client\"`).\n * Pass a `Skin` object only to use a custom skin not in `@typecaast/skins`.\n */\n skin?: Skin;\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** Container fit mode; defaults to `config.meta.fit`. */\n fit?: FitMode;\n /** Composer (reply box) visibility: `auto` (default) / `always` / `never`. */\n composer?: ComposerMode;\n /** Accessible label for the simulation. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n /**\n * Controlled pause. When provided it takes over playback: `true` pauses in\n * place, `false` plays/resumes **from the current position** (never restarts).\n * Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it\n * (e.g. `paused={activeTab !== \"slack\"}`) pauses/resumes without unmounting.\n * No-op under `prefers-reduced-motion` (which holds the completed thread).\n */\n paused?: boolean;\n /** Called when playback starts or resumes. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called once when playback reaches the end (non-looping). */\n onEnded?: () => void;\n /**\n * Render the widget inside an **open shadow root** so the host page's global\n * CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)\n * can't leak in and distort it. Recommended when embedding into a page with\n * its own styles. Note: this is **client-only** (a shadow root can't be\n * attached during SSR), so an isolated widget renders a correctly-sized box\n * on the server and hydrates its visuals in — it no longer works inside a\n * pure React Server Component. Default `false`.\n */\n isolate?: boolean;\n}\n\n/**\n * Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases\n * the declarative props don't cover (jump to a time, change rate, step):\n *\n * ```tsx\n * const ref = useRef<TypecaastHandle>(null);\n * <Typecaast ref={ref} config={config} autoplay />;\n * // …later: ref.current?.seek(5000);\n * ```\n *\n * `ref.current` is `null` until the skin has loaded and the player mounts.\n */\nexport interface TypecaastHandle {\n play(): void;\n pause(): void;\n /** Jump to an absolute time in ms (clamped to `[0, duration]`). */\n seek(timeMs: number): void;\n /** Like `seek`, used for scrubbing. */\n scrubTo(timeMs: number): void;\n /** Playback rate multiplier (1 = realtime). */\n setRate(rate: number): void;\n /** Jump to the next / previous step boundary. */\n stepNext(): void;\n stepPrev(): void;\n /** Live playback time in ms. */\n readonly currentMs: number;\n /** Total duration in ms. */\n readonly duration: number;\n /** Whether the clock is currently running. */\n readonly playing: boolean;\n}\n\nconst SR_ONLY: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clipPath: \"inset(50%)\",\n whiteSpace: \"nowrap\",\n border: 0,\n};\n\n/**\n * Default sizing for the outer `<Typecaast>` wrapper. The widget is\n * **container-driven**: it fills its parent in both axes. When the host\n * gives the wrapper a definite height (responsive grid + fixed-height\n * card, hero box with `aspectRatio`, …) `height: 100%` resolves to that\n * height and the skin reflows / scales to fit. When the host gives only a\n * width, `height: 100%` resolves to `auto` and the canvas's own\n * `aspect-ratio` takes over, deriving a sensible height from the\n * authored canvas dimensions instead of letting message content drive\n * the widget taller as more steps play.\n *\n * The user's `style` prop is spread *after* these defaults so any\n * explicit width/height/aspectRatio overrides win — same opt-out story\n * as the `style.position` pass-through.\n */\nfunction rootStyle(canvas: { width: number; height: number }): CSSProperties {\n return {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n aspectRatio: `${canvas.width} / ${canvas.height}`,\n };\n}\n\n/**\n * Renders a `<Typecaast>` from a config. The skin defaults to the built-in named\n * by `config.meta.skin.id` (lazy-loaded by id — see `builtin-skins.ts`); pass an\n * explicit `skin` to use a custom one. `<Typecaast>` is a client component, but\n * since the default path takes only the serializable `config`, the embed drops\n * straight into a React Server Component.\n */\nexport const Typecaast = forwardRef<TypecaastHandle, TypecaastProps>(\n function Typecaast(props, ref): ReactElement {\n // Normalize once: validate and apply schema defaults (pacing, fit, theme, …)\n // so a raw exported `typecaast.json` works without the caller pre-parsing it.\n const config = useMemo<Config>(\n () => configSchema.parse(props.config),\n [props.config],\n );\n\n // Explicit skin object → render synchronously, no lazy load.\n if (props.skin)\n return <Player {...props} config={config} skin={props.skin} ref={ref} />;\n // Otherwise resolve (and lazy-load) the built-in named in the config.\n return (\n <Suspense\n fallback={\n <SkinFallback\n config={config}\n fit={props.fit}\n label={props.label}\n className={props.className}\n style={props.style}\n />\n }\n >\n <ResolvedPlayer {...props} config={config} ref={ref} />\n </Suspense>\n );\n },\n);\n\nconst ResolvedPlayer = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config }\n>(function ResolvedPlayer(props, ref): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} ref={ref} />;\n});\n\n/**\n * The actual player. The animated visuals are `aria-hidden`; an accessible\n * transcript carries the conversation for screen readers, and\n * `prefers-reduced-motion` snaps to the final state instead of animating\n * (PLAN §20).\n */\nconst Player = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config; skin: Skin }\n>(function Player(\n {\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n paused,\n onPlay,\n onPause,\n onEnded,\n isolate,\n },\n ref: ForwardedRef<TypecaastHandle>,\n): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n // Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never\n // flashes a frame of playback. `!paused` is `true` when uncontrolled.\n autoplay: autoplay && !reduced && !paused,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\n const player = tc.player;\n const fonts = useSkinFonts(skin);\n\n // Reduced motion: hold the completed conversation, no animation.\n useEffect(() => {\n if (reduced) tc.seek(tc.duration);\n }, [reduced, tc]);\n\n // Controlled `paused`: reconcile only when the consumer drives it (so an\n // uncontrolled instance keeps its autoplay behavior). Runs after the hook's\n // mount-autoplay effect, and re-applies on player recreation (theme change)\n // via the `tc` dep. No-op under reduced motion.\n useEffect(() => {\n if (paused === undefined || reduced) return;\n if (paused) tc.pause();\n else tc.play();\n }, [paused, reduced, tc]);\n\n // Lifecycle callbacks → player events.\n useEffect(() => {\n const offs: Array<() => void> = [];\n if (onPlay) offs.push(player.on(\"play\", onPlay));\n if (onPause) offs.push(player.on(\"pause\", onPause));\n if (onEnded) offs.push(player.on(\"end\", onEnded));\n return () => offs.forEach((off) => off());\n }, [player, onPlay, onPause, onEnded]);\n\n // Imperative controls (jump to a time, change rate, step). Getters read the\n // live player so values are exact; the handle is stable per player.\n useImperativeHandle(\n ref,\n () => ({\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t: number) => player.seek(t),\n scrubTo: (t: number) => player.scrubTo(t),\n setRate: (r: number) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n get currentMs() {\n return player.currentMs;\n },\n get duration() {\n return player.durationMs;\n },\n get playing() {\n return player.playing;\n },\n }),\n [player],\n );\n\n const transcript = useMemo(() => buildTranscript(config), [config]);\n\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-fonts={fonts}\n role=\"figure\"\n aria-label={label ?? `Chat simulation (${skin.meta.name})`}\n >\n <ol style={SR_ONLY}>\n {transcript.map((line, i) => (\n <li key={i}>\n {line.name}: {line.text}\n </li>\n ))}\n </ol>\n {(() => {\n const visuals = (\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <TypecaastStage\n state={tc.state}\n skin={skin}\n participants={config.participants}\n options={config.meta.skin.options}\n composer={composer ?? config.meta.composer}\n />\n </FitBox>\n );\n const fill = { width: \"100%\", height: \"100%\" } as const;\n // `isolate` renders the visuals inside a shadow root so the host page's\n // CSS can't leak in (client-only). Either way the content is aria-hidden;\n // the accessible transcript above carries the conversation.\n return isolate ? (\n <ShadowFrame style={fill}>{visuals}</ShadowFrame>\n ) : (\n <div aria-hidden=\"true\" style={fill}>\n {visuals}\n </div>\n );\n })()}\n </div>\n );\n});\n\n/**\n * A same-size placeholder shown while a built-in skin's chunk loads, so there's\n * no layout shift between fallback and the rendered skin. (On static/prerendered\n * pages the skin resolves before HTML is emitted, so this never paints.)\n */\nfunction SkinFallback({\n config,\n fit,\n label,\n className,\n style,\n}: Pick<TypecaastProps, \"fit\" | \"label\" | \"className\" | \"style\"> & {\n config: Config;\n}) {\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-typecaast-loading=\"\"\n role=\"figure\"\n aria-label={label ?? \"Chat simulation\"}\n aria-busy=\"true\"\n >\n <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n background: \"var(--tc-skin-loading-bg, transparent)\",\n }}\n />\n </FitBox>\n </div>\n </div>\n );\n}\n","import type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\n/**\n * Resolve a theme mode to a concrete theme. `auto` falls back to `light`\n * here; M1U.4 makes `auto` reactive against the host `prefers-color-scheme`\n * via a hook layered on top of this.\n */\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n return mode === \"dark\" ? \"dark\" : mode === \"light\" ? \"light\" : \"light\";\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine-adapter.ts","../src/use-resolved-theme.ts","../src/use-typecaast.ts","../src/shadow-frame.tsx","../src/use-skin-fonts.ts","../src/use-preload-images.ts","../src/use-reduced-motion.ts","../src/transcript.ts","../src/fit-box.tsx","../src/builtin-skins.ts","../src/typecaast.tsx","../src/resolve-theme.ts"],"names":["createEngine","useSyncExternalStore","useMemo","createPlayer","useState","useEffect","useRef","useLayoutEffect","createPortal","jsxs","Fragment","jsx","loadSkinFonts","QUERY","getMql","subscribe","getSnapshot","getServerSnapshot","forwardRef","Typecaast","configSchema","Suspense","ResolvedPlayer","Player","useImperativeHandle","TypecaastStage"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Config } from \"@typecaast/schema\";\nimport {\n createEngine,\n type Capabilities,\n type EngineHandle,\n type ResolvedTheme,\n} from \"@typecaast/core\";\n\nexport type Engine = EngineHandle;\n\n/**\n * The single seam between a config and a playable engine. M1-UI ran this over a\n * hand-mocked timeline; M1-engine swaps in the real `compile` + `getStateAt`\n * here — and nothing else in the renderer changed (same `Engine` shape).\n *\n * Optional `capabilities` (from the active skin) drop unsupported events/content\n * from the sampled state while leaving the config intact.\n */\nexport function configToEngine(\n config: Config,\n theme: ResolvedTheme,\n capabilities?: Capabilities,\n): Engine {\n return createEngine(config, theme, capabilities);\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nfunction getSnapshot(): boolean {\n return getMql()?.matches ?? false;\n}\n\n/** No `matchMedia` on the server → default to light (consistent with export). */\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/** Reactively tracks the host's `prefers-color-scheme: dark`. */\nexport function usePrefersDark(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * Resolve a theme mode to a concrete theme. `light`/`dark` are forced; `auto`\n * tracks the host `prefers-color-scheme` reactively and falls back to `light`\n * when no preference signal is available.\n */\nexport function useResolvedTheme(mode: ThemeMode): ResolvedTheme {\n const prefersDark = usePrefersDark();\n if (mode === \"light\") return \"light\";\n if (mode === \"dark\") return \"dark\";\n return prefersDark ? \"dark\" : \"light\";\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport type { Config, ThemeMode } from \"@typecaast/schema\";\nimport {\n createPlayer,\n type Capabilities,\n type Player,\n type SimState,\n} from \"@typecaast/core\";\nimport { configToEngine } from \"./engine-adapter.js\";\nimport { useResolvedTheme } from \"./use-resolved-theme.js\";\n\nexport interface UseTypecaastOptions {\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** The active skin's capabilities, to drop what it can't render. */\n capabilities?: Capabilities;\n}\n\n/** Imperative controls + live state returned by {@link useTypecaast}. */\nexport interface TypecaastControls {\n state: SimState;\n /** Current playback time in ms (reactive). */\n currentMs: number;\n play(): void;\n pause(): void;\n seek(timeMs: number): void;\n scrubTo(timeMs: number): void;\n setRate(rate: number): void;\n stepNext(): void;\n stepPrev(): void;\n duration: number;\n rate: number;\n playing: boolean;\n /** Escape hatch to the underlying player. */\n player: Player;\n}\n\n/**\n * Mount a player for a config and expose live state + controls. The player\n * owns the clock (rAF in the browser); this hook bridges its ticks into React\n * state. The builder uses these controls for preview-as-you-go editing.\n *\n * In M1-UI the player runs over the mocked engine (see engine-adapter); the\n * hook's surface is the final one and does not change when the real engine\n * lands.\n */\nexport function useTypecaast(\n config: Config,\n options: UseTypecaastOptions = {},\n): TypecaastControls {\n const {\n theme,\n autoplay = false,\n // `loop` falls back to `config.meta.loop` so a config authored with looping\n // behaves the same in the builder preview and in zero-prop embeds.\n loop = config.meta.loop ?? false,\n rate = 1,\n capabilities,\n } = options;\n const resolved = useResolvedTheme(theme ?? config.meta.theme);\n\n // The player is built **theme-agnostic**. Theme has no effect on the timeline —\n // `sampleState` only stamps it onto `state.theme` (it never changes messages,\n // timings, or the composer) — so it's a pure *render* concern. Keeping it out of\n // the player's deps means a light/dark toggle re-paints the current frame instead\n // of destroying the player and restarting the conversation from t=0. The baked\n // \"light\" is irrelevant; the live theme is overlaid on the sampled state below.\n const player = useMemo<Player>(() => {\n const engine = configToEngine(config, \"light\", capabilities);\n return createPlayer(engine.getStateAt, {\n durationMs: engine.durationMs,\n steps: engine.steps,\n loop,\n rate,\n });\n }, [config, capabilities, loop, rate]);\n\n const [rawState, setRawState] = useState<SimState>(() => player.state);\n const [currentMs, setCurrentMs] = useState<number>(() => player.currentMs);\n const [playing, setPlaying] = useState<boolean>(() => player.playing);\n\n // Overlay the live theme onto the sampled state at the render seam (not in the\n // engine), so flipping the theme yields a new state object against the *same*\n // player — the clock is untouched. Cheap spread; identity-stable when unchanged.\n const state = useMemo<SimState>(\n () =>\n rawState.theme === resolved ? rawState : { ...rawState, theme: resolved },\n [rawState, resolved],\n );\n\n useEffect(() => {\n const sync = () => {\n setRawState(player.state);\n setCurrentMs(player.currentMs);\n };\n sync();\n setPlaying(player.playing);\n const offs = [\n player.on(\"tick\", sync),\n player.on(\"seek\", sync),\n player.on(\"play\", () => setPlaying(true)),\n player.on(\"pause\", () => setPlaying(false)),\n ];\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player]);\n\n // Apply `autoplay` once per player (at creation), not reactively. If this read\n // were a dependency of the subscribe/destroy effect above, a consumer toggling\n // a controlled `paused` — which flips the gated `autoplay` value upstream —\n // would tear down and recreate the live player. Post-mount play/pause is the\n // consumer's job (e.g. `<Typecaast>`'s `paused` reconcile).\n // (deps intentionally exclude `autoplay` — see the comment above.)\n useEffect(() => {\n if (autoplay) player.play();\n }, [player]);\n\n return {\n state,\n currentMs,\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t) => player.seek(t),\n scrubTo: (t) => player.scrubTo(t),\n setRate: (r) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n duration: player.durationMs,\n rate: player.rate,\n playing,\n player,\n };\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Base reset injected inside the shadow root. The host page's stylesheet\n * selectors (`*`, `code {}`, `.prose p {}`, …) cannot reach into a shadow tree,\n * so the only thing that leaks in is **inherited** properties from the host\n * element. `all: initial` on `:host` neutralises those (line-height, font-weight,\n * letter-spacing, font-variant, font-family, color) to clean defaults; the\n * skin's `Frame` then sets its own font/colour as usual. The universal\n * `box-sizing: border-box` matches the baseline the skins are authored against.\n */\nconst RESET = `\n:host { all: initial; display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n`;\n\n/**\n * Renders its children inside an **open shadow root**, so an embedded\n * `<Typecaast>` is immune to the host page's global CSS (resets, Tailwind\n * `.prose`, tag rules, inherited `line-height`/font). Used by `<Typecaast>` when\n * `isolate` is set.\n *\n * Client-only by nature — `attachShadow` needs the DOM, so on the server (and\n * the first client render) nothing is portaled; the light-DOM host div still\n * reserves the widget's size, and the visuals hydrate in. The web fonts the\n * skins register via the `FontFace` API live in the document and apply across\n * the shadow boundary, so typography is unaffected.\n */\nexport function ShadowFrame({\n children,\n style,\n}: {\n children: ReactNode;\n style?: CSSProperties;\n}): ReactElement {\n const hostRef = useRef<HTMLDivElement>(null);\n const [root, setRoot] = useState<ShadowRoot | null>(null);\n\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n // `?? shadowRoot` makes this safe under StrictMode's double-invoke (a second\n // attachShadow would throw).\n setRoot(host.shadowRoot ?? host.attachShadow({ mode: \"open\" }));\n }, []);\n\n return (\n <div ref={hostRef} aria-hidden=\"true\" style={style}>\n {root\n ? createPortal(\n <>\n <style>{RESET}</style>\n {children}\n </>,\n root,\n )\n : null}\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { loadSkinFonts, type Skin } from \"@typecaast/skin-kit\";\n\nexport type FontLoadState = \"loading\" | \"loaded\";\n\n/**\n * Load a skin's declared web fonts on mount so the live preview renders in the\n * correct typeface (PLAN §19) — never relying on a host OS font. SSR-safe and\n * a no-op off the DOM (resolves \"loaded\"). Re-runs if the skin's fonts change.\n */\nexport function useSkinFonts(skin: Skin): FontLoadState {\n const fonts = skin.meta.fonts;\n const [state, setState] = useState<FontLoadState>(() =>\n fonts && fonts.length > 0 ? \"loading\" : \"loaded\",\n );\n\n useEffect(() => {\n if (!fonts || fonts.length === 0) {\n setState(\"loaded\");\n return;\n }\n let cancelled = false;\n setState(\"loading\");\n loadSkinFonts(fonts).finally(() => {\n if (!cancelled) setState(\"loaded\");\n });\n return () => {\n cancelled = true;\n };\n }, [fonts]);\n\n return state;\n}\n","import { useEffect } from \"react\";\nimport type { Config } from \"@typecaast/schema\";\n\n/**\n * Every image URL a config will render: participant avatars plus in-message\n * images (the `images` sugar and explicit `image` content nodes). Deterministic\n * and side-effect free so it's safe to call on the server.\n */\nfunction collectImageUrls(config: Config): string[] {\n const urls = new Set<string>();\n for (const p of config.participants) if (p.avatar) urls.add(p.avatar);\n for (const step of config.timeline) {\n const s = step as {\n images?: Array<{ src?: unknown }>;\n content?: Array<{ type?: unknown; src?: unknown }>;\n };\n if (Array.isArray(s.images))\n for (const im of s.images)\n if (typeof im?.src === \"string\") urls.add(im.src);\n if (Array.isArray(s.content))\n for (const n of s.content)\n if (n?.type === \"image\" && typeof n.src === \"string\") urls.add(n.src);\n }\n return [...urls];\n}\n\n// Keep a live reference to every preloaded image, module-wide. This (a) dedupes\n// across instances/renders so a URL is only fetched once, and (b) retains the\n// `Image` so an in-flight fetch isn't aborted when GC'd mid-flight — the browser\n// HTTP cache then serves the skin's later `<img>` instantly (even inside a\n// shadow root). One element per unique URL; negligible.\nconst preloaded = new Map<string, HTMLImageElement>();\n\nfunction preload(url: string): void {\n if (preloaded.has(url)) return;\n const img = new Image();\n img.decoding = \"async\";\n img.src = url;\n preloaded.set(url, img);\n // Warm the decoded bitmap too so display has no decode hitch; ignore failures\n // (a bad URL just falls back to the skin's normal `<img>`/initials).\n void img.decode?.().catch(() => {});\n}\n\n/**\n * Preload a config's images on mount so an avatar (or in-message image) is\n * cached before its message appears and never \"pops in\" late mid-playback.\n * Client-only and SSR-safe — a no-op where `Image` is undefined.\n */\nexport function usePreloadImages(config: Config): void {\n // `config` is identity-stable across re-renders (`<Typecaast>` de-dupes it by\n // content), so this runs once per distinct config rather than every render.\n useEffect(() => {\n if (typeof Image === \"undefined\") return;\n for (const url of collectImageUrls(config)) preload(url);\n }, [config]);\n}\n","import { useSyncExternalStore } from \"react\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nconst getSnapshot = (): boolean => getMql()?.matches ?? false;\nconst getServerSnapshot = (): boolean => false;\n\n/**\n * Tracks `prefers-reduced-motion: reduce`. When true, the player snaps to the\n * final state instead of animating (PLAN §20).\n */\nexport function useReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import type { Config } from \"@typecaast/schema\";\n\nexport interface TranscriptLine {\n name: string;\n text: string;\n}\n\n/**\n * Build a plain-text transcript from the authored config — the accessible\n * representation of the conversation for screen readers (PLAN §20). The config\n * is already structured text, so this is a faithful, non-animated version.\n */\nexport function buildTranscript(config: Config): TranscriptLine[] {\n const name = new Map(config.participants.map((p) => [p.id, p.name]));\n const lines: TranscriptLine[] = [];\n for (const step of config.timeline) {\n let from: string | undefined;\n let text: string | undefined;\n if (step.type === \"message\" || step.type === \"system\") {\n from = step.from;\n text = step.text;\n } else if (step.type === \"composerType\") {\n from = step.from;\n text = step.text;\n }\n if (text) {\n lines.push({ name: from ? (name.get(from) ?? from) : \"System\", text });\n }\n }\n return lines;\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { FitMode, Size } from \"@typecaast/schema\";\n\nexport interface FitBoxProps {\n fit: FitMode;\n canvas: Size;\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * Applies the `fit` strategy between the authoring canvas and the host\n * container (PLAN §7):\n * - `reflow`: **fills both axes** (width + height); content re-wraps to the\n * container width and the bottom-anchored thread clips older messages\n * when they overflow vertically. The widget is container-driven — it\n * never grows past its host as more steps play.\n * - `scale`: renders at exact canvas size, CSS-scaled to fit (layout\n * preserved). Use when you want the skin to look like its native canvas\n * regardless of container size.\n * - `fixed`: exact canvas size, clipped — the only mode where the widget\n * is *not* container-driven.\n */\nexport function FitBox({\n fit,\n canvas,\n children,\n className,\n style,\n}: FitBoxProps): ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<{ w: number; h: number } | null>(\n null,\n );\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (rect) setContainer({ w: rect.width, h: rect.height });\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n if (fit === \"reflow\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"reflow\"\n // `height: 100%` closes the height chain: combined with the parent\n // `<Typecaast>` outer's `aspect-ratio` fallback, the widget fills\n // its host container instead of growing taller as more messages\n // play (the skin's bottom-anchored thread + `overflow: hidden`\n // clip older steps off the top).\n style={{ width: \"100%\", height: \"100%\", ...style }}\n >\n {children}\n </div>\n );\n }\n\n if (fit === \"fixed\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"fixed\"\n style={{\n width: canvas.width,\n height: canvas.height,\n overflow: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n }\n\n // scale: fit the exact-size canvas into the measured container.\n const scale = container\n ? Math.min(container.w / canvas.width, container.h / canvas.height)\n : 1;\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"scale\"\n style={{ width: \"100%\", height: \"100%\", overflow: \"hidden\", ...style }}\n >\n <div\n data-fit-canvas=\"\"\n style={{\n width: canvas.width,\n height: canvas.height,\n transform: `scale(${scale})`,\n transformOrigin: \"top left\",\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n","import type { Skin } from \"@typecaast/skin-kit\";\n\ntype SkinModule = { default: Skin };\n\n/**\n * Lazy loaders for the built-in skins, keyed by `meta.skin.id`. Each value is a\n * **static** `import()` of a per-skin subpath, so bundlers emit one chunk per\n * skin and only the skin a config actually references is fetched. Custom skins\n * bypass this entirely (pass the `skin` prop to `<Typecaast>`).\n *\n * Adding a built-in skin = add one line here + the subpath export in\n * `@typecaast/skins`.\n */\nexport const BUILTIN_SKIN_LOADERS: Record<string, () => Promise<SkinModule>> = {\n slack: () => import(\"@typecaast/skins/slack\"),\n telegram: () => import(\"@typecaast/skins/telegram\"),\n \"claude-code\": () => import(\"@typecaast/skins/claude-code\"),\n imessage: () => import(\"@typecaast/skins/imessage\"),\n whatsapp: () => import(\"@typecaast/skins/whatsapp\"),\n cursor: () => import(\"@typecaast/skins/cursor\"),\n \"messages-macos\": () => import(\"@typecaast/skins/messages-macos\"),\n discord: () => import(\"@typecaast/skins/discord\"),\n};\n\n/** Ids of the built-in skins resolvable by `<Typecaast>` without a `skin` prop. */\nexport const builtinSkinIds = Object.keys(BUILTIN_SKIN_LOADERS);\n\n/**\n * A status-tracked load, cached per id. Tracking the settled state lets us\n * Suspense-read it on **React 18 and 19** alike (via the throw-the-promise\n * primitive) instead of React 19's `use()`, which doesn't exist on 18.\n */\ninterface SkinResource {\n promise: Promise<Skin>;\n status: \"pending\" | \"fulfilled\" | \"rejected\";\n value?: Skin;\n error?: unknown;\n}\n\n// Stable resource per id so the same promise is seen across renders.\nconst cache = new Map<string, SkinResource>();\n\n/**\n * Get (or create) the cached resource for a skin id. Throws synchronously for an\n * unknown id (a render error with a clear message), rather than suspending forever.\n */\nfunction getResource(id: string): SkinResource {\n let resource = cache.get(id);\n if (resource) return resource;\n\n const loader = BUILTIN_SKIN_LOADERS[id];\n if (!loader) {\n throw new Error(\n `Typecaast: unknown skin \"${id}\". Built-in skins: ${builtinSkinIds.join(\n \", \",\n )}. For a custom skin, pass the \\`skin\\` prop.`,\n );\n }\n\n resource = {\n status: \"pending\",\n promise: loader().then((m) => m.default),\n };\n // Record the settled state for the synchronous Suspense read. This handler\n // also keeps `promise`'s rejection from going unhandled.\n void resource.promise.then(\n (skin) => {\n resource!.status = \"fulfilled\";\n resource!.value = skin;\n },\n (error: unknown) => {\n resource!.status = \"rejected\";\n resource!.error = error;\n },\n );\n cache.set(id, resource);\n return resource;\n}\n\n/**\n * Resolve a built-in skin id to a cached promise of its `Skin`. Stable per id.\n */\nexport function loadBuiltinSkin(id: string): Promise<Skin> {\n return getResource(id).promise;\n}\n\n/**\n * Suspense-read a built-in skin by id: returns the `Skin` once loaded, throws the\n * pending promise to suspend, or re-throws a load error. This is the universal\n * Suspense primitive — it works on React 18 and 19, unlike `use()` (19-only).\n */\nexport function readBuiltinSkin(id: string): Skin {\n const resource = getResource(id);\n if (resource.status === \"fulfilled\") return resource.value as Skin;\n if (resource.status === \"rejected\") throw resource.error;\n throw resource.promise;\n}\n","import {\n forwardRef,\n Suspense,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type ForwardedRef,\n type ReactElement,\n} from \"react\";\nimport {\n configSchema,\n type Config,\n type ConfigInput,\n type FitMode,\n type ThemeMode,\n} from \"@typecaast/schema\";\nimport {\n TypecaastStage,\n type ComposerMode,\n type Skin,\n} from \"@typecaast/skin-kit\";\nimport { useTypecaast } from \"./use-typecaast.js\";\nimport { ShadowFrame } from \"./shadow-frame.js\";\nimport { useSkinFonts } from \"./use-skin-fonts.js\";\nimport { usePreloadImages } from \"./use-preload-images.js\";\nimport { useReducedMotion } from \"./use-reduced-motion.js\";\nimport { buildTranscript } from \"./transcript.js\";\nimport { FitBox } from \"./fit-box.js\";\nimport { readBuiltinSkin } from \"./builtin-skins.js\";\n\n/**\n * A loosely-typed config shape that a raw `import`ed `typecaast.json` satisfies —\n * TypeScript widens JSON literals (e.g. `version: number`, `type: string`), so it\n * matches neither `Config` nor `ConfigInput`. It's validated and normalized at\n * runtime, so this stays a convenience surface, not a bypass.\n */\nexport interface RawConfig {\n version: number;\n meta: {\n canvas: { width: number; height: number };\n skin: { id: string; options?: Record<string, unknown> };\n [key: string]: unknown;\n };\n participants: Array<{ id: string; name: string; [key: string]: unknown }>;\n timeline: Array<{ type: string; [key: string]: unknown }>;\n pacing?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * What `<Typecaast config>` accepts: a precise `ConfigInput`/`Config` (full\n * intellisense when hand-authoring) or a raw config object such as an imported\n * `typecaast.json`. All forms are normalized through the schema at runtime.\n */\nexport type TypecaastConfig = ConfigInput | Config | RawConfig;\n\nexport interface TypecaastProps {\n /**\n * The conversation config. Accepts your exported `typecaast.json` directly (or\n * a hand-authored `ConfigInput`); it's validated and defaulted at runtime, so\n * you never need to pre-parse it.\n */\n config: TypecaastConfig;\n /**\n * The skin to render with. **Optional** — by default the built-in skin named\n * by `config.meta.skin.id` is resolved and lazy-loaded (only that skin's chunk\n * is fetched), so the config is the single source of truth and the embed stays\n * fully serializable (works in a React Server Component, no `\"use client\"`).\n * Pass a `Skin` object only to use a custom skin not in `@typecaast/skins`.\n */\n skin?: Skin;\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** Container fit mode; defaults to `config.meta.fit`. */\n fit?: FitMode;\n /** Composer (reply box) visibility: `auto` (default) / `always` / `never`. */\n composer?: ComposerMode;\n /** Accessible label for the simulation. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n /**\n * Controlled pause. When provided it takes over playback: `true` pauses in\n * place, `false` plays/resumes **from the current position** (never restarts).\n * Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it\n * (e.g. `paused={activeTab !== \"slack\"}`) pauses/resumes without unmounting.\n * No-op under `prefers-reduced-motion` (which holds the completed thread).\n */\n paused?: boolean;\n /** Called when playback starts or resumes. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called once when playback reaches the end (non-looping). */\n onEnded?: () => void;\n /**\n * Render the widget inside an **open shadow root** so the host page's global\n * CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)\n * can't leak in and distort it. Recommended when embedding into a page with\n * its own styles. Note: this is **client-only** (a shadow root can't be\n * attached during SSR), so an isolated widget renders a correctly-sized box\n * on the server and hydrates its visuals in — it no longer works inside a\n * pure React Server Component. Default `false`.\n */\n isolate?: boolean;\n}\n\n/**\n * Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases\n * the declarative props don't cover (jump to a time, change rate, step):\n *\n * ```tsx\n * const ref = useRef<TypecaastHandle>(null);\n * <Typecaast ref={ref} config={config} autoplay />;\n * // …later: ref.current?.seek(5000);\n * ```\n *\n * `ref.current` is `null` until the skin has loaded and the player mounts.\n */\nexport interface TypecaastHandle {\n play(): void;\n pause(): void;\n /** Jump to an absolute time in ms (clamped to `[0, duration]`). */\n seek(timeMs: number): void;\n /** Like `seek`, used for scrubbing. */\n scrubTo(timeMs: number): void;\n /** Playback rate multiplier (1 = realtime). */\n setRate(rate: number): void;\n /** Jump to the next / previous step boundary. */\n stepNext(): void;\n stepPrev(): void;\n /** Live playback time in ms. */\n readonly currentMs: number;\n /** Total duration in ms. */\n readonly duration: number;\n /** Whether the clock is currently running. */\n readonly playing: boolean;\n}\n\nconst SR_ONLY: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clipPath: \"inset(50%)\",\n whiteSpace: \"nowrap\",\n border: 0,\n};\n\n/**\n * Default sizing for the outer `<Typecaast>` wrapper. The widget is\n * **container-driven**: it fills its parent in both axes. When the host\n * gives the wrapper a definite height (responsive grid + fixed-height\n * card, hero box with `aspectRatio`, …) `height: 100%` resolves to that\n * height and the skin reflows / scales to fit. When the host gives only a\n * width, `height: 100%` resolves to `auto` and the canvas's own\n * `aspect-ratio` takes over, deriving a sensible height from the\n * authored canvas dimensions instead of letting message content drive\n * the widget taller as more steps play.\n *\n * The user's `style` prop is spread *after* these defaults so any\n * explicit width/height/aspectRatio overrides win — same opt-out story\n * as the `style.position` pass-through.\n */\nfunction rootStyle(canvas: { width: number; height: number }): CSSProperties {\n return {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n aspectRatio: `${canvas.width} / ${canvas.height}`,\n };\n}\n\n/**\n * Renders a `<Typecaast>` from a config. The skin defaults to the built-in named\n * by `config.meta.skin.id` (lazy-loaded by id — see `builtin-skins.ts`); pass an\n * explicit `skin` to use a custom one. `<Typecaast>` is a client component, but\n * since the default path takes only the serializable `config`, the embed drops\n * straight into a React Server Component.\n */\nexport const Typecaast = forwardRef<TypecaastHandle, TypecaastProps>(\n function Typecaast(props, ref): ReactElement {\n // Normalize once: validate and apply schema defaults (pacing, fit, theme, …)\n // so a raw exported `typecaast.json` works without the caller pre-parsing it.\n //\n // Key the parse on the config's *content*, not its object identity, so a host\n // that rebuilds an equal config inline on each render (common on customer\n // sites, where unrelated state changes re-render the tree) doesn't churn a new\n // `Config` → new compiled timeline → new player → restart. A stable digest ⇒\n // stable `Config` identity ⇒ `compile`'s WeakMap hits ⇒ the player holds and\n // playback continues. `JSON.stringify` runs only when `props.config` identity\n // changes; configs are small, plain-JSON scripts (no cycles).\n const configKey = useMemo(\n () => JSON.stringify(props.config),\n [props.config],\n );\n const rawConfigRef = useRef(props.config);\n rawConfigRef.current = props.config;\n const config = useMemo<Config>(\n () => configSchema.parse(rawConfigRef.current),\n [configKey],\n );\n\n // Warm the cache for every avatar / in-message image up front (even while the\n // skin chunk is still loading) so they don't pop in late once their message\n // appears mid-playback.\n usePreloadImages(config);\n\n // Explicit skin object → render synchronously, no lazy load.\n if (props.skin)\n return <Player {...props} config={config} skin={props.skin} ref={ref} />;\n // Otherwise resolve (and lazy-load) the built-in named in the config.\n return (\n <Suspense\n fallback={\n <SkinFallback\n config={config}\n fit={props.fit}\n label={props.label}\n className={props.className}\n style={props.style}\n />\n }\n >\n <ResolvedPlayer {...props} config={config} ref={ref} />\n </Suspense>\n );\n },\n);\n\nconst ResolvedPlayer = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config }\n>(function ResolvedPlayer(props, ref): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} ref={ref} />;\n});\n\n/**\n * The actual player. The animated visuals are `aria-hidden`; an accessible\n * transcript carries the conversation for screen readers, and\n * `prefers-reduced-motion` snaps to the final state instead of animating\n * (PLAN §20).\n */\nconst Player = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config; skin: Skin }\n>(function Player(\n {\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n paused,\n onPlay,\n onPause,\n onEnded,\n isolate,\n },\n ref: ForwardedRef<TypecaastHandle>,\n): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n // Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never\n // flashes a frame of playback. `!paused` is `true` when uncontrolled.\n autoplay: autoplay && !reduced && !paused,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\n const player = tc.player;\n const fonts = useSkinFonts(skin);\n\n // Reduced motion: hold the completed conversation, no animation.\n useEffect(() => {\n if (reduced) tc.seek(tc.duration);\n }, [reduced, tc]);\n\n // Controlled `paused`: reconcile only when the consumer drives it (so an\n // uncontrolled instance keeps its autoplay behavior). Runs after the hook's\n // mount-autoplay effect; re-applies whenever the controls object changes\n // (play/pause are idempotent). No-op under reduced motion.\n useEffect(() => {\n if (paused === undefined || reduced) return;\n if (paused) tc.pause();\n else tc.play();\n }, [paused, reduced, tc]);\n\n // Lifecycle callbacks → player events.\n useEffect(() => {\n const offs: Array<() => void> = [];\n if (onPlay) offs.push(player.on(\"play\", onPlay));\n if (onPause) offs.push(player.on(\"pause\", onPause));\n if (onEnded) offs.push(player.on(\"end\", onEnded));\n return () => offs.forEach((off) => off());\n }, [player, onPlay, onPause, onEnded]);\n\n // Imperative controls (jump to a time, change rate, step). Getters read the\n // live player so values are exact; the handle is stable per player.\n useImperativeHandle(\n ref,\n () => ({\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t: number) => player.seek(t),\n scrubTo: (t: number) => player.scrubTo(t),\n setRate: (r: number) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n get currentMs() {\n return player.currentMs;\n },\n get duration() {\n return player.durationMs;\n },\n get playing() {\n return player.playing;\n },\n }),\n [player],\n );\n\n const transcript = useMemo(() => buildTranscript(config), [config]);\n\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-fonts={fonts}\n role=\"figure\"\n aria-label={label ?? `Chat simulation (${skin.meta.name})`}\n >\n <ol style={SR_ONLY}>\n {transcript.map((line, i) => (\n <li key={i}>\n {line.name}: {line.text}\n </li>\n ))}\n </ol>\n {(() => {\n const visuals = (\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <TypecaastStage\n state={tc.state}\n skin={skin}\n participants={config.participants}\n options={config.meta.skin.options}\n composer={composer ?? config.meta.composer}\n />\n </FitBox>\n );\n const fill = { width: \"100%\", height: \"100%\" } as const;\n // `isolate` renders the visuals inside a shadow root so the host page's\n // CSS can't leak in (client-only). Either way the content is aria-hidden;\n // the accessible transcript above carries the conversation.\n return isolate ? (\n <ShadowFrame style={fill}>{visuals}</ShadowFrame>\n ) : (\n <div aria-hidden=\"true\" style={fill}>\n {visuals}\n </div>\n );\n })()}\n </div>\n );\n});\n\n/**\n * A same-size placeholder shown while a built-in skin's chunk loads, so there's\n * no layout shift between fallback and the rendered skin. (On static/prerendered\n * pages the skin resolves before HTML is emitted, so this never paints.)\n */\nfunction SkinFallback({\n config,\n fit,\n label,\n className,\n style,\n}: Pick<TypecaastProps, \"fit\" | \"label\" | \"className\" | \"style\"> & {\n config: Config;\n}) {\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-typecaast-loading=\"\"\n role=\"figure\"\n aria-label={label ?? \"Chat simulation\"}\n aria-busy=\"true\"\n >\n <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n background: \"var(--tc-skin-loading-bg, transparent)\",\n }}\n />\n </FitBox>\n </div>\n </div>\n );\n}\n","import type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\n/**\n * Resolve a theme mode to a concrete theme. `auto` falls back to `light`\n * here; M1U.4 makes `auto` reactive against the host `prefers-color-scheme`\n * via a hook layered on top of this.\n */\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n return mode === \"dark\" ? \"dark\" : mode === \"light\" ? \"light\" : \"light\";\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { forwardRef, useMemo, Suspense, useEffect, useImperativeHandle, useSyncExternalStore, useState,
|
|
2
|
+
import { forwardRef, useMemo, useRef, Suspense, useEffect, useImperativeHandle, useSyncExternalStore, useState, useLayoutEffect } from 'react';
|
|
3
3
|
import { configSchema } from '@typecaast/schema';
|
|
4
4
|
import { TypecaastStage, loadSkinFonts } from '@typecaast/skin-kit';
|
|
5
5
|
export { TypecaastStage } from '@typecaast/skin-kit';
|
|
@@ -54,20 +54,24 @@ function useTypecaast(config, options = {}) {
|
|
|
54
54
|
} = options;
|
|
55
55
|
const resolved = useResolvedTheme(theme ?? config.meta.theme);
|
|
56
56
|
const player = useMemo(() => {
|
|
57
|
-
const engine = configToEngine(config,
|
|
57
|
+
const engine = configToEngine(config, "light", capabilities);
|
|
58
58
|
return createPlayer(engine.getStateAt, {
|
|
59
59
|
durationMs: engine.durationMs,
|
|
60
60
|
steps: engine.steps,
|
|
61
61
|
loop,
|
|
62
62
|
rate
|
|
63
63
|
});
|
|
64
|
-
}, [config,
|
|
65
|
-
const [
|
|
64
|
+
}, [config, capabilities, loop, rate]);
|
|
65
|
+
const [rawState, setRawState] = useState(() => player.state);
|
|
66
66
|
const [currentMs, setCurrentMs] = useState(() => player.currentMs);
|
|
67
67
|
const [playing, setPlaying] = useState(() => player.playing);
|
|
68
|
+
const state = useMemo(
|
|
69
|
+
() => rawState.theme === resolved ? rawState : { ...rawState, theme: resolved },
|
|
70
|
+
[rawState, resolved]
|
|
71
|
+
);
|
|
68
72
|
useEffect(() => {
|
|
69
73
|
const sync = () => {
|
|
70
|
-
|
|
74
|
+
setRawState(player.state);
|
|
71
75
|
setCurrentMs(player.currentMs);
|
|
72
76
|
};
|
|
73
77
|
sync();
|
|
@@ -146,6 +150,38 @@ function useSkinFonts(skin) {
|
|
|
146
150
|
}, [fonts]);
|
|
147
151
|
return state;
|
|
148
152
|
}
|
|
153
|
+
function collectImageUrls(config) {
|
|
154
|
+
const urls = /* @__PURE__ */ new Set();
|
|
155
|
+
for (const p of config.participants) if (p.avatar) urls.add(p.avatar);
|
|
156
|
+
for (const step of config.timeline) {
|
|
157
|
+
const s = step;
|
|
158
|
+
if (Array.isArray(s.images)) {
|
|
159
|
+
for (const im of s.images)
|
|
160
|
+
if (typeof im?.src === "string") urls.add(im.src);
|
|
161
|
+
}
|
|
162
|
+
if (Array.isArray(s.content)) {
|
|
163
|
+
for (const n of s.content)
|
|
164
|
+
if (n?.type === "image" && typeof n.src === "string") urls.add(n.src);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return [...urls];
|
|
168
|
+
}
|
|
169
|
+
var preloaded = /* @__PURE__ */ new Map();
|
|
170
|
+
function preload(url) {
|
|
171
|
+
if (preloaded.has(url)) return;
|
|
172
|
+
const img = new Image();
|
|
173
|
+
img.decoding = "async";
|
|
174
|
+
img.src = url;
|
|
175
|
+
preloaded.set(url, img);
|
|
176
|
+
void img.decode?.().catch(() => {
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function usePreloadImages(config) {
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (typeof Image === "undefined") return;
|
|
182
|
+
for (const url of collectImageUrls(config)) preload(url);
|
|
183
|
+
}, [config]);
|
|
184
|
+
}
|
|
149
185
|
var QUERY2 = "(prefers-reduced-motion: reduce)";
|
|
150
186
|
function getMql2() {
|
|
151
187
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
@@ -332,10 +368,17 @@ function rootStyle(canvas) {
|
|
|
332
368
|
}
|
|
333
369
|
var Typecaast = forwardRef(
|
|
334
370
|
function Typecaast2(props, ref) {
|
|
335
|
-
const
|
|
336
|
-
() =>
|
|
371
|
+
const configKey = useMemo(
|
|
372
|
+
() => JSON.stringify(props.config),
|
|
337
373
|
[props.config]
|
|
338
374
|
);
|
|
375
|
+
const rawConfigRef = useRef(props.config);
|
|
376
|
+
rawConfigRef.current = props.config;
|
|
377
|
+
const config = useMemo(
|
|
378
|
+
() => configSchema.parse(rawConfigRef.current),
|
|
379
|
+
[configKey]
|
|
380
|
+
);
|
|
381
|
+
usePreloadImages(config);
|
|
339
382
|
if (props.skin)
|
|
340
383
|
return /* @__PURE__ */ jsx(Player, { ...props, config, skin: props.skin, ref });
|
|
341
384
|
return /* @__PURE__ */ jsx(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine-adapter.ts","../src/use-resolved-theme.ts","../src/use-typecaast.ts","../src/shadow-frame.tsx","../src/use-skin-fonts.ts","../src/use-reduced-motion.ts","../src/transcript.ts","../src/fit-box.tsx","../src/builtin-skins.ts","../src/typecaast.tsx","../src/resolve-theme.ts"],"names":["useState","useEffect","QUERY","getMql","subscribe","getSnapshot","getServerSnapshot","useSyncExternalStore","useRef","useLayoutEffect","jsx","Typecaast","useMemo","ResolvedPlayer","Player","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import type { Config } from \"@typecaast/schema\";\nimport {\n createEngine,\n type Capabilities,\n type EngineHandle,\n type ResolvedTheme,\n} from \"@typecaast/core\";\n\nexport type Engine = EngineHandle;\n\n/**\n * The single seam between a config and a playable engine. M1-UI ran this over a\n * hand-mocked timeline; M1-engine swaps in the real `compile` + `getStateAt`\n * here — and nothing else in the renderer changed (same `Engine` shape).\n *\n * Optional `capabilities` (from the active skin) drop unsupported events/content\n * from the sampled state while leaving the config intact.\n */\nexport function configToEngine(\n config: Config,\n theme: ResolvedTheme,\n capabilities?: Capabilities,\n): Engine {\n return createEngine(config, theme, capabilities);\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nfunction getSnapshot(): boolean {\n return getMql()?.matches ?? false;\n}\n\n/** No `matchMedia` on the server → default to light (consistent with export). */\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/** Reactively tracks the host's `prefers-color-scheme: dark`. */\nexport function usePrefersDark(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * Resolve a theme mode to a concrete theme. `light`/`dark` are forced; `auto`\n * tracks the host `prefers-color-scheme` reactively and falls back to `light`\n * when no preference signal is available.\n */\nexport function useResolvedTheme(mode: ThemeMode): ResolvedTheme {\n const prefersDark = usePrefersDark();\n if (mode === \"light\") return \"light\";\n if (mode === \"dark\") return \"dark\";\n return prefersDark ? \"dark\" : \"light\";\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport type { Config, ThemeMode } from \"@typecaast/schema\";\nimport {\n createPlayer,\n type Capabilities,\n type Player,\n type SimState,\n} from \"@typecaast/core\";\nimport { configToEngine } from \"./engine-adapter.js\";\nimport { useResolvedTheme } from \"./use-resolved-theme.js\";\n\nexport interface UseTypecaastOptions {\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** The active skin's capabilities, to drop what it can't render. */\n capabilities?: Capabilities;\n}\n\n/** Imperative controls + live state returned by {@link useTypecaast}. */\nexport interface TypecaastControls {\n state: SimState;\n /** Current playback time in ms (reactive). */\n currentMs: number;\n play(): void;\n pause(): void;\n seek(timeMs: number): void;\n scrubTo(timeMs: number): void;\n setRate(rate: number): void;\n stepNext(): void;\n stepPrev(): void;\n duration: number;\n rate: number;\n playing: boolean;\n /** Escape hatch to the underlying player. */\n player: Player;\n}\n\n/**\n * Mount a player for a config and expose live state + controls. The player\n * owns the clock (rAF in the browser); this hook bridges its ticks into React\n * state. The builder uses these controls for preview-as-you-go editing.\n *\n * In M1-UI the player runs over the mocked engine (see engine-adapter); the\n * hook's surface is the final one and does not change when the real engine\n * lands.\n */\nexport function useTypecaast(\n config: Config,\n options: UseTypecaastOptions = {},\n): TypecaastControls {\n const {\n theme,\n autoplay = false,\n // `loop` falls back to `config.meta.loop` so a config authored with looping\n // behaves the same in the builder preview and in zero-prop embeds.\n loop = config.meta.loop ?? false,\n rate = 1,\n capabilities,\n } = options;\n const resolved = useResolvedTheme(theme ?? config.meta.theme);\n\n const player = useMemo<Player>(() => {\n const engine = configToEngine(config, resolved, capabilities);\n return createPlayer(engine.getStateAt, {\n durationMs: engine.durationMs,\n steps: engine.steps,\n loop,\n rate,\n });\n }, [config, resolved, capabilities, loop, rate]);\n\n const [state, setState] = useState<SimState>(() => player.state);\n const [currentMs, setCurrentMs] = useState<number>(() => player.currentMs);\n const [playing, setPlaying] = useState<boolean>(() => player.playing);\n\n useEffect(() => {\n const sync = () => {\n setState(player.state);\n setCurrentMs(player.currentMs);\n };\n sync();\n setPlaying(player.playing);\n const offs = [\n player.on(\"tick\", sync),\n player.on(\"seek\", sync),\n player.on(\"play\", () => setPlaying(true)),\n player.on(\"pause\", () => setPlaying(false)),\n ];\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player]);\n\n // Apply `autoplay` once per player (at creation), not reactively. If this read\n // were a dependency of the subscribe/destroy effect above, a consumer toggling\n // a controlled `paused` — which flips the gated `autoplay` value upstream —\n // would tear down and recreate the live player. Post-mount play/pause is the\n // consumer's job (e.g. `<Typecaast>`'s `paused` reconcile).\n // (deps intentionally exclude `autoplay` — see the comment above.)\n useEffect(() => {\n if (autoplay) player.play();\n }, [player]);\n\n return {\n state,\n currentMs,\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t) => player.seek(t),\n scrubTo: (t) => player.scrubTo(t),\n setRate: (r) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n duration: player.durationMs,\n rate: player.rate,\n playing,\n player,\n };\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Base reset injected inside the shadow root. The host page's stylesheet\n * selectors (`*`, `code {}`, `.prose p {}`, …) cannot reach into a shadow tree,\n * so the only thing that leaks in is **inherited** properties from the host\n * element. `all: initial` on `:host` neutralises those (line-height, font-weight,\n * letter-spacing, font-variant, font-family, color) to clean defaults; the\n * skin's `Frame` then sets its own font/colour as usual. The universal\n * `box-sizing: border-box` matches the baseline the skins are authored against.\n */\nconst RESET = `\n:host { all: initial; display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n`;\n\n/**\n * Renders its children inside an **open shadow root**, so an embedded\n * `<Typecaast>` is immune to the host page's global CSS (resets, Tailwind\n * `.prose`, tag rules, inherited `line-height`/font). Used by `<Typecaast>` when\n * `isolate` is set.\n *\n * Client-only by nature — `attachShadow` needs the DOM, so on the server (and\n * the first client render) nothing is portaled; the light-DOM host div still\n * reserves the widget's size, and the visuals hydrate in. The web fonts the\n * skins register via the `FontFace` API live in the document and apply across\n * the shadow boundary, so typography is unaffected.\n */\nexport function ShadowFrame({\n children,\n style,\n}: {\n children: ReactNode;\n style?: CSSProperties;\n}): ReactElement {\n const hostRef = useRef<HTMLDivElement>(null);\n const [root, setRoot] = useState<ShadowRoot | null>(null);\n\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n // `?? shadowRoot` makes this safe under StrictMode's double-invoke (a second\n // attachShadow would throw).\n setRoot(host.shadowRoot ?? host.attachShadow({ mode: \"open\" }));\n }, []);\n\n return (\n <div ref={hostRef} aria-hidden=\"true\" style={style}>\n {root\n ? createPortal(\n <>\n <style>{RESET}</style>\n {children}\n </>,\n root,\n )\n : null}\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { loadSkinFonts, type Skin } from \"@typecaast/skin-kit\";\n\nexport type FontLoadState = \"loading\" | \"loaded\";\n\n/**\n * Load a skin's declared web fonts on mount so the live preview renders in the\n * correct typeface (PLAN §19) — never relying on a host OS font. SSR-safe and\n * a no-op off the DOM (resolves \"loaded\"). Re-runs if the skin's fonts change.\n */\nexport function useSkinFonts(skin: Skin): FontLoadState {\n const fonts = skin.meta.fonts;\n const [state, setState] = useState<FontLoadState>(() =>\n fonts && fonts.length > 0 ? \"loading\" : \"loaded\",\n );\n\n useEffect(() => {\n if (!fonts || fonts.length === 0) {\n setState(\"loaded\");\n return;\n }\n let cancelled = false;\n setState(\"loading\");\n loadSkinFonts(fonts).finally(() => {\n if (!cancelled) setState(\"loaded\");\n });\n return () => {\n cancelled = true;\n };\n }, [fonts]);\n\n return state;\n}\n","import { useSyncExternalStore } from \"react\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nconst getSnapshot = (): boolean => getMql()?.matches ?? false;\nconst getServerSnapshot = (): boolean => false;\n\n/**\n * Tracks `prefers-reduced-motion: reduce`. When true, the player snaps to the\n * final state instead of animating (PLAN §20).\n */\nexport function useReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import type { Config } from \"@typecaast/schema\";\n\nexport interface TranscriptLine {\n name: string;\n text: string;\n}\n\n/**\n * Build a plain-text transcript from the authored config — the accessible\n * representation of the conversation for screen readers (PLAN §20). The config\n * is already structured text, so this is a faithful, non-animated version.\n */\nexport function buildTranscript(config: Config): TranscriptLine[] {\n const name = new Map(config.participants.map((p) => [p.id, p.name]));\n const lines: TranscriptLine[] = [];\n for (const step of config.timeline) {\n let from: string | undefined;\n let text: string | undefined;\n if (step.type === \"message\" || step.type === \"system\") {\n from = step.from;\n text = step.text;\n } else if (step.type === \"composerType\") {\n from = step.from;\n text = step.text;\n }\n if (text) {\n lines.push({ name: from ? (name.get(from) ?? from) : \"System\", text });\n }\n }\n return lines;\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { FitMode, Size } from \"@typecaast/schema\";\n\nexport interface FitBoxProps {\n fit: FitMode;\n canvas: Size;\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * Applies the `fit` strategy between the authoring canvas and the host\n * container (PLAN §7):\n * - `reflow`: **fills both axes** (width + height); content re-wraps to the\n * container width and the bottom-anchored thread clips older messages\n * when they overflow vertically. The widget is container-driven — it\n * never grows past its host as more steps play.\n * - `scale`: renders at exact canvas size, CSS-scaled to fit (layout\n * preserved). Use when you want the skin to look like its native canvas\n * regardless of container size.\n * - `fixed`: exact canvas size, clipped — the only mode where the widget\n * is *not* container-driven.\n */\nexport function FitBox({\n fit,\n canvas,\n children,\n className,\n style,\n}: FitBoxProps): ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<{ w: number; h: number } | null>(\n null,\n );\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (rect) setContainer({ w: rect.width, h: rect.height });\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n if (fit === \"reflow\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"reflow\"\n // `height: 100%` closes the height chain: combined with the parent\n // `<Typecaast>` outer's `aspect-ratio` fallback, the widget fills\n // its host container instead of growing taller as more messages\n // play (the skin's bottom-anchored thread + `overflow: hidden`\n // clip older steps off the top).\n style={{ width: \"100%\", height: \"100%\", ...style }}\n >\n {children}\n </div>\n );\n }\n\n if (fit === \"fixed\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"fixed\"\n style={{\n width: canvas.width,\n height: canvas.height,\n overflow: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n }\n\n // scale: fit the exact-size canvas into the measured container.\n const scale = container\n ? Math.min(container.w / canvas.width, container.h / canvas.height)\n : 1;\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"scale\"\n style={{ width: \"100%\", height: \"100%\", overflow: \"hidden\", ...style }}\n >\n <div\n data-fit-canvas=\"\"\n style={{\n width: canvas.width,\n height: canvas.height,\n transform: `scale(${scale})`,\n transformOrigin: \"top left\",\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n","import type { Skin } from \"@typecaast/skin-kit\";\n\ntype SkinModule = { default: Skin };\n\n/**\n * Lazy loaders for the built-in skins, keyed by `meta.skin.id`. Each value is a\n * **static** `import()` of a per-skin subpath, so bundlers emit one chunk per\n * skin and only the skin a config actually references is fetched. Custom skins\n * bypass this entirely (pass the `skin` prop to `<Typecaast>`).\n *\n * Adding a built-in skin = add one line here + the subpath export in\n * `@typecaast/skins`.\n */\nexport const BUILTIN_SKIN_LOADERS: Record<string, () => Promise<SkinModule>> = {\n slack: () => import(\"@typecaast/skins/slack\"),\n telegram: () => import(\"@typecaast/skins/telegram\"),\n \"claude-code\": () => import(\"@typecaast/skins/claude-code\"),\n imessage: () => import(\"@typecaast/skins/imessage\"),\n whatsapp: () => import(\"@typecaast/skins/whatsapp\"),\n cursor: () => import(\"@typecaast/skins/cursor\"),\n \"messages-macos\": () => import(\"@typecaast/skins/messages-macos\"),\n discord: () => import(\"@typecaast/skins/discord\"),\n};\n\n/** Ids of the built-in skins resolvable by `<Typecaast>` without a `skin` prop. */\nexport const builtinSkinIds = Object.keys(BUILTIN_SKIN_LOADERS);\n\n/**\n * A status-tracked load, cached per id. Tracking the settled state lets us\n * Suspense-read it on **React 18 and 19** alike (via the throw-the-promise\n * primitive) instead of React 19's `use()`, which doesn't exist on 18.\n */\ninterface SkinResource {\n promise: Promise<Skin>;\n status: \"pending\" | \"fulfilled\" | \"rejected\";\n value?: Skin;\n error?: unknown;\n}\n\n// Stable resource per id so the same promise is seen across renders.\nconst cache = new Map<string, SkinResource>();\n\n/**\n * Get (or create) the cached resource for a skin id. Throws synchronously for an\n * unknown id (a render error with a clear message), rather than suspending forever.\n */\nfunction getResource(id: string): SkinResource {\n let resource = cache.get(id);\n if (resource) return resource;\n\n const loader = BUILTIN_SKIN_LOADERS[id];\n if (!loader) {\n throw new Error(\n `Typecaast: unknown skin \"${id}\". Built-in skins: ${builtinSkinIds.join(\n \", \",\n )}. For a custom skin, pass the \\`skin\\` prop.`,\n );\n }\n\n resource = {\n status: \"pending\",\n promise: loader().then((m) => m.default),\n };\n // Record the settled state for the synchronous Suspense read. This handler\n // also keeps `promise`'s rejection from going unhandled.\n void resource.promise.then(\n (skin) => {\n resource!.status = \"fulfilled\";\n resource!.value = skin;\n },\n (error: unknown) => {\n resource!.status = \"rejected\";\n resource!.error = error;\n },\n );\n cache.set(id, resource);\n return resource;\n}\n\n/**\n * Resolve a built-in skin id to a cached promise of its `Skin`. Stable per id.\n */\nexport function loadBuiltinSkin(id: string): Promise<Skin> {\n return getResource(id).promise;\n}\n\n/**\n * Suspense-read a built-in skin by id: returns the `Skin` once loaded, throws the\n * pending promise to suspend, or re-throws a load error. This is the universal\n * Suspense primitive — it works on React 18 and 19, unlike `use()` (19-only).\n */\nexport function readBuiltinSkin(id: string): Skin {\n const resource = getResource(id);\n if (resource.status === \"fulfilled\") return resource.value as Skin;\n if (resource.status === \"rejected\") throw resource.error;\n throw resource.promise;\n}\n","import {\n forwardRef,\n Suspense,\n useEffect,\n useImperativeHandle,\n useMemo,\n type CSSProperties,\n type ForwardedRef,\n type ReactElement,\n} from \"react\";\nimport {\n configSchema,\n type Config,\n type ConfigInput,\n type FitMode,\n type ThemeMode,\n} from \"@typecaast/schema\";\nimport {\n TypecaastStage,\n type ComposerMode,\n type Skin,\n} from \"@typecaast/skin-kit\";\nimport { useTypecaast } from \"./use-typecaast.js\";\nimport { ShadowFrame } from \"./shadow-frame.js\";\nimport { useSkinFonts } from \"./use-skin-fonts.js\";\nimport { useReducedMotion } from \"./use-reduced-motion.js\";\nimport { buildTranscript } from \"./transcript.js\";\nimport { FitBox } from \"./fit-box.js\";\nimport { readBuiltinSkin } from \"./builtin-skins.js\";\n\n/**\n * A loosely-typed config shape that a raw `import`ed `typecaast.json` satisfies —\n * TypeScript widens JSON literals (e.g. `version: number`, `type: string`), so it\n * matches neither `Config` nor `ConfigInput`. It's validated and normalized at\n * runtime, so this stays a convenience surface, not a bypass.\n */\nexport interface RawConfig {\n version: number;\n meta: {\n canvas: { width: number; height: number };\n skin: { id: string; options?: Record<string, unknown> };\n [key: string]: unknown;\n };\n participants: Array<{ id: string; name: string; [key: string]: unknown }>;\n timeline: Array<{ type: string; [key: string]: unknown }>;\n pacing?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * What `<Typecaast config>` accepts: a precise `ConfigInput`/`Config` (full\n * intellisense when hand-authoring) or a raw config object such as an imported\n * `typecaast.json`. All forms are normalized through the schema at runtime.\n */\nexport type TypecaastConfig = ConfigInput | Config | RawConfig;\n\nexport interface TypecaastProps {\n /**\n * The conversation config. Accepts your exported `typecaast.json` directly (or\n * a hand-authored `ConfigInput`); it's validated and defaulted at runtime, so\n * you never need to pre-parse it.\n */\n config: TypecaastConfig;\n /**\n * The skin to render with. **Optional** — by default the built-in skin named\n * by `config.meta.skin.id` is resolved and lazy-loaded (only that skin's chunk\n * is fetched), so the config is the single source of truth and the embed stays\n * fully serializable (works in a React Server Component, no `\"use client\"`).\n * Pass a `Skin` object only to use a custom skin not in `@typecaast/skins`.\n */\n skin?: Skin;\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** Container fit mode; defaults to `config.meta.fit`. */\n fit?: FitMode;\n /** Composer (reply box) visibility: `auto` (default) / `always` / `never`. */\n composer?: ComposerMode;\n /** Accessible label for the simulation. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n /**\n * Controlled pause. When provided it takes over playback: `true` pauses in\n * place, `false` plays/resumes **from the current position** (never restarts).\n * Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it\n * (e.g. `paused={activeTab !== \"slack\"}`) pauses/resumes without unmounting.\n * No-op under `prefers-reduced-motion` (which holds the completed thread).\n */\n paused?: boolean;\n /** Called when playback starts or resumes. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called once when playback reaches the end (non-looping). */\n onEnded?: () => void;\n /**\n * Render the widget inside an **open shadow root** so the host page's global\n * CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)\n * can't leak in and distort it. Recommended when embedding into a page with\n * its own styles. Note: this is **client-only** (a shadow root can't be\n * attached during SSR), so an isolated widget renders a correctly-sized box\n * on the server and hydrates its visuals in — it no longer works inside a\n * pure React Server Component. Default `false`.\n */\n isolate?: boolean;\n}\n\n/**\n * Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases\n * the declarative props don't cover (jump to a time, change rate, step):\n *\n * ```tsx\n * const ref = useRef<TypecaastHandle>(null);\n * <Typecaast ref={ref} config={config} autoplay />;\n * // …later: ref.current?.seek(5000);\n * ```\n *\n * `ref.current` is `null` until the skin has loaded and the player mounts.\n */\nexport interface TypecaastHandle {\n play(): void;\n pause(): void;\n /** Jump to an absolute time in ms (clamped to `[0, duration]`). */\n seek(timeMs: number): void;\n /** Like `seek`, used for scrubbing. */\n scrubTo(timeMs: number): void;\n /** Playback rate multiplier (1 = realtime). */\n setRate(rate: number): void;\n /** Jump to the next / previous step boundary. */\n stepNext(): void;\n stepPrev(): void;\n /** Live playback time in ms. */\n readonly currentMs: number;\n /** Total duration in ms. */\n readonly duration: number;\n /** Whether the clock is currently running. */\n readonly playing: boolean;\n}\n\nconst SR_ONLY: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clipPath: \"inset(50%)\",\n whiteSpace: \"nowrap\",\n border: 0,\n};\n\n/**\n * Default sizing for the outer `<Typecaast>` wrapper. The widget is\n * **container-driven**: it fills its parent in both axes. When the host\n * gives the wrapper a definite height (responsive grid + fixed-height\n * card, hero box with `aspectRatio`, …) `height: 100%` resolves to that\n * height and the skin reflows / scales to fit. When the host gives only a\n * width, `height: 100%` resolves to `auto` and the canvas's own\n * `aspect-ratio` takes over, deriving a sensible height from the\n * authored canvas dimensions instead of letting message content drive\n * the widget taller as more steps play.\n *\n * The user's `style` prop is spread *after* these defaults so any\n * explicit width/height/aspectRatio overrides win — same opt-out story\n * as the `style.position` pass-through.\n */\nfunction rootStyle(canvas: { width: number; height: number }): CSSProperties {\n return {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n aspectRatio: `${canvas.width} / ${canvas.height}`,\n };\n}\n\n/**\n * Renders a `<Typecaast>` from a config. The skin defaults to the built-in named\n * by `config.meta.skin.id` (lazy-loaded by id — see `builtin-skins.ts`); pass an\n * explicit `skin` to use a custom one. `<Typecaast>` is a client component, but\n * since the default path takes only the serializable `config`, the embed drops\n * straight into a React Server Component.\n */\nexport const Typecaast = forwardRef<TypecaastHandle, TypecaastProps>(\n function Typecaast(props, ref): ReactElement {\n // Normalize once: validate and apply schema defaults (pacing, fit, theme, …)\n // so a raw exported `typecaast.json` works without the caller pre-parsing it.\n const config = useMemo<Config>(\n () => configSchema.parse(props.config),\n [props.config],\n );\n\n // Explicit skin object → render synchronously, no lazy load.\n if (props.skin)\n return <Player {...props} config={config} skin={props.skin} ref={ref} />;\n // Otherwise resolve (and lazy-load) the built-in named in the config.\n return (\n <Suspense\n fallback={\n <SkinFallback\n config={config}\n fit={props.fit}\n label={props.label}\n className={props.className}\n style={props.style}\n />\n }\n >\n <ResolvedPlayer {...props} config={config} ref={ref} />\n </Suspense>\n );\n },\n);\n\nconst ResolvedPlayer = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config }\n>(function ResolvedPlayer(props, ref): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} ref={ref} />;\n});\n\n/**\n * The actual player. The animated visuals are `aria-hidden`; an accessible\n * transcript carries the conversation for screen readers, and\n * `prefers-reduced-motion` snaps to the final state instead of animating\n * (PLAN §20).\n */\nconst Player = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config; skin: Skin }\n>(function Player(\n {\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n paused,\n onPlay,\n onPause,\n onEnded,\n isolate,\n },\n ref: ForwardedRef<TypecaastHandle>,\n): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n // Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never\n // flashes a frame of playback. `!paused` is `true` when uncontrolled.\n autoplay: autoplay && !reduced && !paused,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\n const player = tc.player;\n const fonts = useSkinFonts(skin);\n\n // Reduced motion: hold the completed conversation, no animation.\n useEffect(() => {\n if (reduced) tc.seek(tc.duration);\n }, [reduced, tc]);\n\n // Controlled `paused`: reconcile only when the consumer drives it (so an\n // uncontrolled instance keeps its autoplay behavior). Runs after the hook's\n // mount-autoplay effect, and re-applies on player recreation (theme change)\n // via the `tc` dep. No-op under reduced motion.\n useEffect(() => {\n if (paused === undefined || reduced) return;\n if (paused) tc.pause();\n else tc.play();\n }, [paused, reduced, tc]);\n\n // Lifecycle callbacks → player events.\n useEffect(() => {\n const offs: Array<() => void> = [];\n if (onPlay) offs.push(player.on(\"play\", onPlay));\n if (onPause) offs.push(player.on(\"pause\", onPause));\n if (onEnded) offs.push(player.on(\"end\", onEnded));\n return () => offs.forEach((off) => off());\n }, [player, onPlay, onPause, onEnded]);\n\n // Imperative controls (jump to a time, change rate, step). Getters read the\n // live player so values are exact; the handle is stable per player.\n useImperativeHandle(\n ref,\n () => ({\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t: number) => player.seek(t),\n scrubTo: (t: number) => player.scrubTo(t),\n setRate: (r: number) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n get currentMs() {\n return player.currentMs;\n },\n get duration() {\n return player.durationMs;\n },\n get playing() {\n return player.playing;\n },\n }),\n [player],\n );\n\n const transcript = useMemo(() => buildTranscript(config), [config]);\n\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-fonts={fonts}\n role=\"figure\"\n aria-label={label ?? `Chat simulation (${skin.meta.name})`}\n >\n <ol style={SR_ONLY}>\n {transcript.map((line, i) => (\n <li key={i}>\n {line.name}: {line.text}\n </li>\n ))}\n </ol>\n {(() => {\n const visuals = (\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <TypecaastStage\n state={tc.state}\n skin={skin}\n participants={config.participants}\n options={config.meta.skin.options}\n composer={composer ?? config.meta.composer}\n />\n </FitBox>\n );\n const fill = { width: \"100%\", height: \"100%\" } as const;\n // `isolate` renders the visuals inside a shadow root so the host page's\n // CSS can't leak in (client-only). Either way the content is aria-hidden;\n // the accessible transcript above carries the conversation.\n return isolate ? (\n <ShadowFrame style={fill}>{visuals}</ShadowFrame>\n ) : (\n <div aria-hidden=\"true\" style={fill}>\n {visuals}\n </div>\n );\n })()}\n </div>\n );\n});\n\n/**\n * A same-size placeholder shown while a built-in skin's chunk loads, so there's\n * no layout shift between fallback and the rendered skin. (On static/prerendered\n * pages the skin resolves before HTML is emitted, so this never paints.)\n */\nfunction SkinFallback({\n config,\n fit,\n label,\n className,\n style,\n}: Pick<TypecaastProps, \"fit\" | \"label\" | \"className\" | \"style\"> & {\n config: Config;\n}) {\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-typecaast-loading=\"\"\n role=\"figure\"\n aria-label={label ?? \"Chat simulation\"}\n aria-busy=\"true\"\n >\n <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n background: \"var(--tc-skin-loading-bg, transparent)\",\n }}\n />\n </FitBox>\n </div>\n </div>\n );\n}\n","import type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\n/**\n * Resolve a theme mode to a concrete theme. `auto` falls back to `light`\n * here; M1U.4 makes `auto` reactive against the host `prefers-color-scheme`\n * via a hook layered on top of this.\n */\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n return mode === \"dark\" ? \"dark\" : mode === \"light\" ? \"light\" : \"light\";\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine-adapter.ts","../src/use-resolved-theme.ts","../src/use-typecaast.ts","../src/shadow-frame.tsx","../src/use-skin-fonts.ts","../src/use-preload-images.ts","../src/use-reduced-motion.ts","../src/transcript.ts","../src/fit-box.tsx","../src/builtin-skins.ts","../src/typecaast.tsx","../src/resolve-theme.ts"],"names":["useState","useEffect","QUERY","getMql","subscribe","getSnapshot","getServerSnapshot","useSyncExternalStore","useRef","useLayoutEffect","jsx","Typecaast","useMemo","ResolvedPlayer","Player","jsxs"],"mappings":";;;;;;;;;AAkBO,SAAS,cAAA,CACd,MAAA,EACA,KAAA,EACA,YAAA,EACQ;AACR,EAAA,OAAO,YAAA,CAAa,MAAA,EAAQ,KAAA,EAAO,YAAY,CAAA;AACjD;ACpBA,IAAM,KAAA,GAAQ,8BAAA;AAEd,SAAS,MAAA,GAAgC;AACvC,EAAA,IACE,OAAO,MAAA,KAAW,WAAA,IAClB,OAAO,MAAA,CAAO,eAAe,UAAA,EAC7B;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA,CAAO,WAAW,KAAK,CAAA;AAChC;AAEA,SAAS,UAAU,QAAA,EAAkC;AACnD,EAAA,MAAM,MAAM,MAAA,EAAO;AACnB,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,MAAM;AAAA,EAAC,CAAA;AACxB,EAAA,GAAA,CAAI,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACvC,EAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AACzD;AAEA,SAAS,WAAA,GAAuB;AAC9B,EAAA,OAAO,MAAA,IAAU,OAAA,IAAW,KAAA;AAC9B;AAGA,SAAS,iBAAA,GAA6B;AACpC,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,cAAA,GAA0B;AACxC,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,iBAAiB,CAAA;AACvE;AAOO,SAAS,iBAAiB,IAAA,EAAgC;AAC/D,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,IAAI,IAAA,KAAS,SAAS,OAAO,OAAA;AAC7B,EAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,MAAA;AAC5B,EAAA,OAAO,cAAc,MAAA,GAAS,OAAA;AAChC;;;ACEO,SAAS,YAAA,CACd,MAAA,EACA,OAAA,GAA+B,EAAC,EACb;AACnB,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,QAAA,GAAW,KAAA;AAAA;AAAA;AAAA,IAGX,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,IAAA,IAAQ,KAAA;AAAA,IAC3B,IAAA,GAAO,CAAA;AAAA,IACP;AAAA,GACF,GAAI,OAAA;AACJ,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,KAAA,IAAS,MAAA,CAAO,KAAK,KAAK,CAAA;AAQ5D,EAAA,MAAM,MAAA,GAAS,QAAgB,MAAM;AACnC,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,MAAA,EAAQ,OAAA,EAAS,YAAY,CAAA;AAC3D,IAAA,OAAO,YAAA,CAAa,OAAO,UAAA,EAAY;AAAA,MACrC,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,MAAA,EAAQ,YAAA,EAAc,IAAA,EAAM,IAAI,CAAC,CAAA;AAErC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,IAAI,QAAA,CAAmB,MAAM,OAAO,KAAK,CAAA;AACrE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,IAAI,QAAA,CAAiB,MAAM,OAAO,SAAS,CAAA;AACzE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,IAAI,QAAA,CAAkB,MAAM,OAAO,OAAO,CAAA;AAKpE,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,MACE,SAAS,KAAA,KAAU,QAAA,GAAW,WAAW,EAAE,GAAG,QAAA,EAAU,KAAA,EAAO,QAAA,EAAS;AAAA,IAC1E,CAAC,UAAU,QAAQ;AAAA,GACrB;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,WAAA,CAAY,OAAO,KAAK,CAAA;AACxB,MAAA,YAAA,CAAa,OAAO,SAAS,CAAA;AAAA,IAC/B,CAAA;AACA,IAAA,IAAA,EAAK;AACL,IAAA,UAAA,CAAW,OAAO,OAAO,CAAA;AACzB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,IAAI,CAAA;AAAA,MACtB,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,IAAI,CAAA;AAAA,MACtB,OAAO,EAAA,CAAG,MAAA,EAAQ,MAAM,UAAA,CAAW,IAAI,CAAC,CAAA;AAAA,MACxC,OAAO,EAAA,CAAG,OAAA,EAAS,MAAM,UAAA,CAAW,KAAK,CAAC;AAAA,KAC5C;AACA,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,KAAQ,GAAA,EAAK,CAAA;AAC3B,MAAA,MAAA,CAAO,OAAA,EAAQ;AAAA,IACjB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAQX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,QAAA,SAAiB,IAAA,EAAK;AAAA,EAC5B,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,SAAA;AAAA,IACA,IAAA,EAAM,MAAM,MAAA,CAAO,IAAA,EAAK;AAAA,IACxB,KAAA,EAAO,MAAM,MAAA,CAAO,KAAA,EAAM;AAAA,IAC1B,IAAA,EAAM,CAAC,CAAA,KAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAC1B,OAAA,EAAS,CAAC,CAAA,KAAM,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,IAChC,OAAA,EAAS,CAAC,CAAA,KAAM,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,IAChC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,IAChC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,IAChC,UAAU,MAAA,CAAO,UAAA;AAAA,IACjB,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,OAAA;AAAA,IACA;AAAA,GACF;AACF;ACtHA,IAAM,KAAA,GAAQ;AAAA;AAAA;AAAA,CAAA;AAiBP,SAAS,WAAA,CAAY;AAAA,EAC1B,QAAA;AAAA,EACA;AACF,CAAA,EAGiB;AACf,EAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,SAA4B,IAAI,CAAA;AAExD,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AAGX,IAAA,OAAA,CAAQ,IAAA,CAAK,cAAc,IAAA,CAAK,YAAA,CAAa,EAAE,IAAA,EAAM,MAAA,EAAQ,CAAC,CAAA;AAAA,EAChE,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,2BACG,KAAA,EAAA,EAAI,GAAA,EAAK,SAAS,aAAA,EAAY,MAAA,EAAO,OACnC,QAAA,EAAA,IAAA,GACG,YAAA;AAAA,oBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,WAAO,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,MACb;AAAA,KAAA,EACH,CAAA;AAAA,IACA;AAAA,MAEF,IAAA,EACN,CAAA;AAEJ;ACzDO,SAAS,aAAa,IAAA,EAA2B;AACtD,EAAA,MAAM,KAAA,GAAQ,KAAK,IAAA,CAAK,KAAA;AACxB,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,QAAAA;AAAA,IAAwB,MAChD,KAAA,IAAS,KAAA,CAAM,MAAA,GAAS,IAAI,SAAA,GAAY;AAAA,GAC1C;AAEA,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAChC,MAAA,QAAA,CAAS,QAAQ,CAAA;AACjB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,QAAA,CAAS,SAAS,CAAA;AAClB,IAAA,aAAA,CAAc,KAAK,CAAA,CAAE,OAAA,CAAQ,MAAM;AACjC,MAAA,IAAI,CAAC,SAAA,EAAW,QAAA,CAAS,QAAQ,CAAA;AAAA,IACnC,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,KAAA;AACT;ACxBA,SAAS,iBAAiB,MAAA,EAA0B;AAClD,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,CAAA,IAAK,OAAO,YAAA,EAAc,IAAI,EAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,MAAM,CAAA;AACpE,EAAA,KAAA,MAAW,IAAA,IAAQ,OAAO,QAAA,EAAU;AAClC,IAAA,MAAM,CAAA,GAAI,IAAA;AAIV,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,MAAM,CAAA,EAAA;AACxB,MAAA,KAAA,MAAW,MAAM,CAAA,CAAE,MAAA;AACjB,QAAA,IAAI,OAAO,EAAA,EAAI,GAAA,KAAQ,UAAU,IAAA,CAAK,GAAA,CAAI,GAAG,GAAG,CAAA;AAAA,IAAA;AACpD,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,OAAO,CAAA,EAAA;AACzB,MAAA,KAAA,MAAW,KAAK,CAAA,CAAE,OAAA;AAChB,QAAA,IAAI,CAAA,EAAG,IAAA,KAAS,OAAA,IAAW,OAAO,CAAA,CAAE,QAAQ,QAAA,EAAU,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA;AAAA,IAAA;AAAA,EAC1E;AACA,EAAA,OAAO,CAAC,GAAG,IAAI,CAAA;AACjB;AAOA,IAAM,SAAA,uBAAgB,GAAA,EAA8B;AAEpD,SAAS,QAAQ,GAAA,EAAmB;AAClC,EAAA,IAAI,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA,EAAG;AACxB,EAAA,MAAM,GAAA,GAAM,IAAI,KAAA,EAAM;AACtB,EAAA,GAAA,CAAI,QAAA,GAAW,OAAA;AACf,EAAA,GAAA,CAAI,GAAA,GAAM,GAAA;AACV,EAAA,SAAA,CAAU,GAAA,CAAI,KAAK,GAAG,CAAA;AAGtB,EAAA,KAAK,GAAA,CAAI,MAAA,IAAS,CAAE,KAAA,CAAM,MAAM;AAAA,EAAC,CAAC,CAAA;AACpC;AAOO,SAAS,iBAAiB,MAAA,EAAsB;AAGrD,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,OAAO,UAAU,WAAA,EAAa;AAClC,IAAA,KAAA,MAAW,GAAA,IAAO,gBAAA,CAAiB,MAAM,CAAA,UAAW,GAAG,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AACb;ACtDA,IAAMC,MAAAA,GAAQ,kCAAA;AAEd,SAASC,OAAAA,GAAgC;AACvC,EAAA,IACE,OAAO,MAAA,KAAW,WAAA,IAClB,OAAO,MAAA,CAAO,eAAe,UAAA,EAC7B;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA,CAAO,WAAWD,MAAK,CAAA;AAChC;AAEA,SAASE,WAAU,QAAA,EAAkC;AACnD,EAAA,MAAM,MAAMD,OAAAA,EAAO;AACnB,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,MAAM;AAAA,EAAC,CAAA;AACxB,EAAA,GAAA,CAAI,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACvC,EAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AACzD;AAEA,IAAME,YAAAA,GAAc,MAAeF,OAAAA,EAAO,EAAG,OAAA,IAAW,KAAA;AACxD,IAAMG,qBAAoB,MAAe,KAAA;AAMlC,SAAS,gBAAA,GAA4B;AAC1C,EAAA,OAAOC,oBAAAA,CAAqBH,UAAAA,EAAWC,YAAAA,EAAaC,kBAAiB,CAAA;AACvE;;;AClBO,SAAS,gBAAgB,MAAA,EAAkC;AAChE,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,MAAA,CAAO,aAAa,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAA,CAAE,IAAI,CAAC,CAAC,CAAA;AACnE,EAAA,MAAM,QAA0B,EAAC;AACjC,EAAA,KAAA,MAAW,IAAA,IAAQ,OAAO,QAAA,EAAU;AAClC,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,SAAA,IAAa,IAAA,CAAK,SAAS,QAAA,EAAU;AACrD,MAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AACZ,MAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,IACd,CAAA,MAAA,IAAW,IAAA,CAAK,IAAA,KAAS,cAAA,EAAgB;AACvC,MAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AACZ,MAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,IACd;AACA,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,IAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,IAAK,IAAA,GAAQ,QAAA,EAAU,IAAA,EAAM,CAAA;AAAA,IACvE;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;ACCO,SAAS,MAAA,CAAO;AAAA,EACrB,GAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAA8B;AAC5B,EAAA,MAAM,GAAA,GAAME,OAAuB,IAAI,CAAA;AACvC,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIR,QAAAA;AAAA,IAChC;AAAA,GACF;AAEA,EAAAS,gBAAgB,MAAM;AACpB,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,IAAM,OAAO,cAAA,KAAmB,WAAA,EAAa;AAClD,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,CAAC,OAAA,KAAY;AACzC,MAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,CAAC,CAAA,EAAG,WAAA;AACzB,MAAA,IAAI,IAAA,eAAmB,EAAE,CAAA,EAAG,KAAK,KAAA,EAAO,CAAA,EAAG,IAAA,CAAK,MAAA,EAAQ,CAAA;AAAA,IAC1D,CAAC,CAAA;AACD,IAAA,EAAA,CAAG,QAAQ,EAAE,CAAA;AACb,IAAA,OAAO,MAAM,GAAG,UAAA,EAAW;AAAA,EAC7B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,uBACEC,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA;AAAA,QACA,UAAA,EAAS,QAAA;AAAA,QAMT,OAAO,EAAE,KAAA,EAAO,QAAQ,MAAA,EAAQ,MAAA,EAAQ,GAAG,KAAA,EAAM;AAAA,QAEhD;AAAA;AAAA,KACH;AAAA,EAEJ;AAEA,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,uBACEA,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA;AAAA,QACA,UAAA,EAAS,OAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL,OAAO,MAAA,CAAO,KAAA;AAAA,UACd,QAAQ,MAAA,CAAO,MAAA;AAAA,UACf,QAAA,EAAU,QAAA;AAAA,UACV,GAAG;AAAA,SACL;AAAA,QAEC;AAAA;AAAA,KACH;AAAA,EAEJ;AAGA,EAAA,MAAM,KAAA,GAAQ,SAAA,GACV,IAAA,CAAK,GAAA,CAAI,SAAA,CAAU,CAAA,GAAI,MAAA,CAAO,KAAA,EAAO,SAAA,CAAU,CAAA,GAAI,MAAA,CAAO,MAAM,CAAA,GAChE,CAAA;AACJ,EAAA,uBACEA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA,EAAS,OAAA;AAAA,MACT,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAU,GAAG,KAAA,EAAM;AAAA,MAErE,QAAA,kBAAAA,GAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,iBAAA,EAAgB,EAAA;AAAA,UAChB,KAAA,EAAO;AAAA,YACL,OAAO,MAAA,CAAO,KAAA;AAAA,YACd,QAAQ,MAAA,CAAO,MAAA;AAAA,YACf,SAAA,EAAW,SAAS,KAAK,CAAA,CAAA,CAAA;AAAA,YACzB,eAAA,EAAiB;AAAA,WACnB;AAAA,UAEC;AAAA;AAAA;AACH;AAAA,GACF;AAEJ;;;ACrGO,IAAM,oBAAA,GAAkE;AAAA,EAC7E,KAAA,EAAO,MAAM,OAAO,wBAAwB,CAAA;AAAA,EAC5C,QAAA,EAAU,MAAM,OAAO,2BAA2B,CAAA;AAAA,EAClD,aAAA,EAAe,MAAM,OAAO,8BAA8B,CAAA;AAAA,EAC1D,QAAA,EAAU,MAAM,OAAO,2BAA2B,CAAA;AAAA,EAClD,QAAA,EAAU,MAAM,OAAO,2BAA2B,CAAA;AAAA,EAClD,MAAA,EAAQ,MAAM,OAAO,yBAAyB,CAAA;AAAA,EAC9C,gBAAA,EAAkB,MAAM,OAAO,iCAAiC,CAAA;AAAA,EAChE,OAAA,EAAS,MAAM,OAAO,0BAA0B;AAClD;AAGO,IAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,oBAAoB;AAe9D,IAAM,KAAA,uBAAY,GAAA,EAA0B;AAM5C,SAAS,YAAY,EAAA,EAA0B;AAC7C,EAAA,IAAI,QAAA,GAAW,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAC3B,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,MAAA,GAAS,qBAAqB,EAAE,CAAA;AACtC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,yBAAA,EAA4B,EAAE,CAAA,mBAAA,EAAsB,cAAA,CAAe,IAAA;AAAA,QACjE;AAAA,OACD,CAAA,4CAAA;AAAA,KACH;AAAA,EACF;AAEA,EAAA,QAAA,GAAW;AAAA,IACT,MAAA,EAAQ,SAAA;AAAA,IACR,SAAS,MAAA,EAAO,CAAE,KAAK,CAAC,CAAA,KAAM,EAAE,OAAO;AAAA,GACzC;AAGA,EAAA,KAAK,SAAS,OAAA,CAAQ,IAAA;AAAA,IACpB,CAAC,IAAA,KAAS;AACR,MAAA,QAAA,CAAU,MAAA,GAAS,WAAA;AACnB,MAAA,QAAA,CAAU,KAAA,GAAQ,IAAA;AAAA,IACpB,CAAA;AAAA,IACA,CAAC,KAAA,KAAmB;AAClB,MAAA,QAAA,CAAU,MAAA,GAAS,UAAA;AACnB,MAAA,QAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,IACpB;AAAA,GACF;AACA,EAAA,KAAA,CAAM,GAAA,CAAI,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,QAAA;AACT;AAKO,SAAS,gBAAgB,EAAA,EAA2B;AACzD,EAAA,OAAO,WAAA,CAAY,EAAE,CAAA,CAAE,OAAA;AACzB;AAOO,SAAS,gBAAgB,EAAA,EAAkB;AAChD,EAAA,MAAM,QAAA,GAAW,YAAY,EAAE,CAAA;AAC/B,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA,CAAS,KAAA;AACrD,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,UAAA,EAAY,MAAM,QAAA,CAAS,KAAA;AACnD,EAAA,MAAM,QAAA,CAAS,OAAA;AACjB;ACgDA,IAAM,OAAA,GAAyB;AAAA,EAC7B,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,MAAA,EAAQ,CAAA;AAAA,EACR,OAAA,EAAS,CAAA;AAAA,EACT,MAAA,EAAQ,EAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,QAAA,EAAU,YAAA;AAAA,EACV,UAAA,EAAY,QAAA;AAAA,EACZ,MAAA,EAAQ;AACV,CAAA;AAiBA,SAAS,UAAU,MAAA,EAA0D;AAC3E,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,aAAa,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,GAAA,EAAM,OAAO,MAAM,CAAA;AAAA,GACjD;AACF;AASO,IAAM,SAAA,GAAY,UAAA;AAAA,EACvB,SAASC,UAAAA,CAAU,KAAA,EAAO,GAAA,EAAmB;AAW3C,IAAA,MAAM,SAAA,GAAYC,OAAAA;AAAA,MAChB,MAAM,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,MAAM,CAAA;AAAA,MACjC,CAAC,MAAM,MAAM;AAAA,KACf;AACA,IAAA,MAAM,YAAA,GAAeJ,MAAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AACxC,IAAA,YAAA,CAAa,UAAU,KAAA,CAAM,MAAA;AAC7B,IAAA,MAAM,MAAA,GAASI,OAAAA;AAAA,MACb,MAAM,YAAA,CAAa,KAAA,CAAM,YAAA,CAAa,OAAO,CAAA;AAAA,MAC7C,CAAC,SAAS;AAAA,KACZ;AAKA,IAAA,gBAAA,CAAiB,MAAM,CAAA;AAGvB,IAAA,IAAI,KAAA,CAAM,IAAA;AACR,MAAA,uBAAOF,IAAC,MAAA,EAAA,EAAQ,GAAG,OAAO,MAAA,EAAgB,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,GAAA,EAAU,CAAA;AAExE,IAAA,uBACEA,GAAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,0BACEA,GAAAA;AAAA,UAAC,YAAA;AAAA,UAAA;AAAA,YACC,MAAA;AAAA,YACA,KAAK,KAAA,CAAM,GAAA;AAAA,YACX,OAAO,KAAA,CAAM,KAAA;AAAA,YACb,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,OAAO,KAAA,CAAM;AAAA;AAAA,SACf;AAAA,QAGF,0BAAAA,GAAAA,CAAC,cAAA,EAAA,EAAgB,GAAG,KAAA,EAAO,QAAgB,GAAA,EAAU;AAAA;AAAA,KACvD;AAAA,EAEJ;AACF;AAEA,IAAM,cAAA,GAAiB,UAAA,CAGrB,SAASG,eAAAA,CAAe,OAAO,GAAA,EAAmB;AAClD,EAAA,MAAM,OAAO,eAAA,CAAgB,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,KAAK,EAAE,CAAA;AACtD,EAAA,uBAAOH,GAAAA,CAAC,MAAA,EAAA,EAAQ,GAAG,KAAA,EAAO,MAAY,GAAA,EAAU,CAAA;AAClD,CAAC,CAAA;AAQD,IAAM,MAAA,GAAS,UAAA,CAGb,SAASI,OAAAA,CACT;AAAA,EACE,MAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EACA,GAAA,EACc;AACd,EAAA,MAAM,UAAU,gBAAA,EAAiB;AACjC,EAAA,MAAM,EAAA,GAAK,aAAa,MAAA,EAAQ;AAAA,IAC9B,KAAA;AAAA;AAAA;AAAA,IAGA,QAAA,EAAU,QAAA,IAAY,CAAC,OAAA,IAAW,CAAC,MAAA;AAAA,IACnC,IAAA,EAAM,QAAQ,CAAC,OAAA;AAAA,IACf,IAAA;AAAA,IACA,YAAA,EAAc,KAAK,IAAA,CAAK;AAAA,GACzB,CAAA;AACD,EAAA,MAAM,SAAS,EAAA,CAAG,MAAA;AAClB,EAAA,MAAM,KAAA,GAAQ,aAAa,IAAI,CAAA;AAG/B,EAAAb,UAAU,MAAM;AACd,IAAA,IAAI,OAAA,EAAS,EAAA,CAAG,IAAA,CAAK,EAAA,CAAG,QAAQ,CAAA;AAAA,EAClC,CAAA,EAAG,CAAC,OAAA,EAAS,EAAE,CAAC,CAAA;AAMhB,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,MAAA,KAAW,UAAa,OAAA,EAAS;AACrC,IAAA,IAAI,MAAA,KAAW,KAAA,EAAM;AAAA,YACb,IAAA,EAAK;AAAA,EACf,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,EAAE,CAAC,CAAA;AAGxB,EAAAA,UAAU,MAAM;AACd,IAAA,MAAM,OAA0B,EAAC;AACjC,IAAA,IAAI,QAAQ,IAAA,CAAK,IAAA,CAAK,OAAO,EAAA,CAAG,MAAA,EAAQ,MAAM,CAAC,CAAA;AAC/C,IAAA,IAAI,SAAS,IAAA,CAAK,IAAA,CAAK,OAAO,EAAA,CAAG,OAAA,EAAS,OAAO,CAAC,CAAA;AAClD,IAAA,IAAI,SAAS,IAAA,CAAK,IAAA,CAAK,OAAO,EAAA,CAAG,KAAA,EAAO,OAAO,CAAC,CAAA;AAChD,IAAA,OAAO,MAAM,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,KAAQ,KAAK,CAAA;AAAA,EAC1C,GAAG,CAAC,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,OAAO,CAAC,CAAA;AAIrC,EAAA,mBAAA;AAAA,IACE,GAAA;AAAA,IACA,OAAO;AAAA,MACL,IAAA,EAAM,MAAM,MAAA,CAAO,IAAA,EAAK;AAAA,MACxB,KAAA,EAAO,MAAM,MAAA,CAAO,KAAA,EAAM;AAAA,MAC1B,IAAA,EAAM,CAAC,CAAA,KAAc,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MAClC,OAAA,EAAS,CAAC,CAAA,KAAc,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,MACxC,OAAA,EAAS,CAAC,CAAA,KAAc,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,MACxC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,MAChC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,MAChC,IAAI,SAAA,GAAY;AACd,QAAA,OAAO,MAAA,CAAO,SAAA;AAAA,MAChB,CAAA;AAAA,MACA,IAAI,QAAA,GAAW;AACb,QAAA,OAAO,MAAA,CAAO,UAAA;AAAA,MAChB,CAAA;AAAA,MACA,IAAI,OAAA,GAAU;AACZ,QAAA,OAAO,MAAA,CAAO,OAAA;AAAA,MAChB;AAAA,KACF,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,UAAA,GAAaW,QAAQ,MAAM,eAAA,CAAgB,MAAM,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAElE,EAAA,uBACEG,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,KAAA,EAAO,EAAE,GAAG,SAAA,CAAU,OAAO,IAAA,CAAK,MAAM,CAAA,EAAG,GAAG,KAAA,EAAM;AAAA,MACpD,gBAAA,EAAe,EAAA;AAAA,MACf,YAAA,EAAY,KAAA;AAAA,MACZ,IAAA,EAAK,QAAA;AAAA,MACL,YAAA,EAAY,KAAA,IAAS,CAAA,iBAAA,EAAoB,IAAA,CAAK,KAAK,IAAI,CAAA,CAAA,CAAA;AAAA,MAEvD,QAAA,EAAA;AAAA,wBAAAL,GAAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAO,OAAA,EACR,QAAA,EAAA,UAAA,CAAW,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,qBACrBK,IAAAA,CAAC,IAAA,EAAA,EACE,QAAA,EAAA;AAAA,UAAA,IAAA,CAAK,IAAA;AAAA,UAAK,IAAA;AAAA,UAAG,IAAA,CAAK;AAAA,SAAA,EAAA,EADZ,CAET,CACD,CAAA,EACH,CAAA;AAAA,QAAA,CACE,MAAM;AACN,UAAA,MAAM,OAAA,mBACJL,GAAAA,CAAC,MAAA,EAAA,EAAO,GAAA,EAAK,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,GAAA,EAAK,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,QACvD,QAAA,kBAAAA,GAAAA;AAAA,YAAC,cAAA;AAAA,YAAA;AAAA,cACC,OAAO,EAAA,CAAG,KAAA;AAAA,cACV,IAAA;AAAA,cACA,cAAc,MAAA,CAAO,YAAA;AAAA,cACrB,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAA;AAAA,cAC1B,QAAA,EAAU,QAAA,IAAY,MAAA,CAAO,IAAA,CAAK;AAAA;AAAA,WACpC,EACF,CAAA;AAEF,UAAA,MAAM,IAAA,GAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAO;AAI7C,UAAA,OAAO,OAAA,mBACLA,GAAAA,CAAC,WAAA,EAAA,EAAY,OAAO,IAAA,EAAO,QAAA,EAAA,OAAA,EAAQ,CAAA,mBAEnCA,IAAC,KAAA,EAAA,EAAI,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,MAC5B,QAAA,EAAA,OAAA,EACH,CAAA;AAAA,QAEJ,CAAA;AAAG;AAAA;AAAA,GACL;AAEJ,CAAC,CAAA;AAOD,SAAS,YAAA,CAAa;AAAA,EACpB,MAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAEG;AACD,EAAA,uBACEA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,KAAA,EAAO,EAAE,GAAG,SAAA,CAAU,OAAO,IAAA,CAAK,MAAM,CAAA,EAAG,GAAG,KAAA,EAAM;AAAA,MACpD,gBAAA,EAAe,EAAA;AAAA,MACf,wBAAA,EAAuB,EAAA;AAAA,MACvB,IAAA,EAAK,QAAA;AAAA,MACL,cAAY,KAAA,IAAS,iBAAA;AAAA,MACrB,WAAA,EAAU,MAAA;AAAA,MAEV,QAAA,kBAAAA,GAAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAY,MAAA,EAAO,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO,EAC7D,0BAAAA,GAAAA,CAAC,MAAA,EAAA,EAAO,GAAA,EAAK,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,MAAA,EACvD,QAAA,kBAAAA,GAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,KAAA,EAAO,MAAA;AAAA,YACP,MAAA,EAAQ,MAAA;AAAA,YACR,UAAA,EAAY;AAAA;AACd;AAAA,SAEJ,CAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;AC5ZO,SAAS,aAAa,IAAA,EAAgC;AAC3D,EAAA,OAAO,IAAA,KAAS,MAAA,GAAS,MAAA,GAAS,IAAA,KAAS,UAAU,OAAA,GAAU,OAAA;AACjE","file":"index.js","sourcesContent":["import type { Config } from \"@typecaast/schema\";\nimport {\n createEngine,\n type Capabilities,\n type EngineHandle,\n type ResolvedTheme,\n} from \"@typecaast/core\";\n\nexport type Engine = EngineHandle;\n\n/**\n * The single seam between a config and a playable engine. M1-UI ran this over a\n * hand-mocked timeline; M1-engine swaps in the real `compile` + `getStateAt`\n * here — and nothing else in the renderer changed (same `Engine` shape).\n *\n * Optional `capabilities` (from the active skin) drop unsupported events/content\n * from the sampled state while leaving the config intact.\n */\nexport function configToEngine(\n config: Config,\n theme: ResolvedTheme,\n capabilities?: Capabilities,\n): Engine {\n return createEngine(config, theme, capabilities);\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nfunction getSnapshot(): boolean {\n return getMql()?.matches ?? false;\n}\n\n/** No `matchMedia` on the server → default to light (consistent with export). */\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/** Reactively tracks the host's `prefers-color-scheme: dark`. */\nexport function usePrefersDark(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * Resolve a theme mode to a concrete theme. `light`/`dark` are forced; `auto`\n * tracks the host `prefers-color-scheme` reactively and falls back to `light`\n * when no preference signal is available.\n */\nexport function useResolvedTheme(mode: ThemeMode): ResolvedTheme {\n const prefersDark = usePrefersDark();\n if (mode === \"light\") return \"light\";\n if (mode === \"dark\") return \"dark\";\n return prefersDark ? \"dark\" : \"light\";\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport type { Config, ThemeMode } from \"@typecaast/schema\";\nimport {\n createPlayer,\n type Capabilities,\n type Player,\n type SimState,\n} from \"@typecaast/core\";\nimport { configToEngine } from \"./engine-adapter.js\";\nimport { useResolvedTheme } from \"./use-resolved-theme.js\";\n\nexport interface UseTypecaastOptions {\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** The active skin's capabilities, to drop what it can't render. */\n capabilities?: Capabilities;\n}\n\n/** Imperative controls + live state returned by {@link useTypecaast}. */\nexport interface TypecaastControls {\n state: SimState;\n /** Current playback time in ms (reactive). */\n currentMs: number;\n play(): void;\n pause(): void;\n seek(timeMs: number): void;\n scrubTo(timeMs: number): void;\n setRate(rate: number): void;\n stepNext(): void;\n stepPrev(): void;\n duration: number;\n rate: number;\n playing: boolean;\n /** Escape hatch to the underlying player. */\n player: Player;\n}\n\n/**\n * Mount a player for a config and expose live state + controls. The player\n * owns the clock (rAF in the browser); this hook bridges its ticks into React\n * state. The builder uses these controls for preview-as-you-go editing.\n *\n * In M1-UI the player runs over the mocked engine (see engine-adapter); the\n * hook's surface is the final one and does not change when the real engine\n * lands.\n */\nexport function useTypecaast(\n config: Config,\n options: UseTypecaastOptions = {},\n): TypecaastControls {\n const {\n theme,\n autoplay = false,\n // `loop` falls back to `config.meta.loop` so a config authored with looping\n // behaves the same in the builder preview and in zero-prop embeds.\n loop = config.meta.loop ?? false,\n rate = 1,\n capabilities,\n } = options;\n const resolved = useResolvedTheme(theme ?? config.meta.theme);\n\n // The player is built **theme-agnostic**. Theme has no effect on the timeline —\n // `sampleState` only stamps it onto `state.theme` (it never changes messages,\n // timings, or the composer) — so it's a pure *render* concern. Keeping it out of\n // the player's deps means a light/dark toggle re-paints the current frame instead\n // of destroying the player and restarting the conversation from t=0. The baked\n // \"light\" is irrelevant; the live theme is overlaid on the sampled state below.\n const player = useMemo<Player>(() => {\n const engine = configToEngine(config, \"light\", capabilities);\n return createPlayer(engine.getStateAt, {\n durationMs: engine.durationMs,\n steps: engine.steps,\n loop,\n rate,\n });\n }, [config, capabilities, loop, rate]);\n\n const [rawState, setRawState] = useState<SimState>(() => player.state);\n const [currentMs, setCurrentMs] = useState<number>(() => player.currentMs);\n const [playing, setPlaying] = useState<boolean>(() => player.playing);\n\n // Overlay the live theme onto the sampled state at the render seam (not in the\n // engine), so flipping the theme yields a new state object against the *same*\n // player — the clock is untouched. Cheap spread; identity-stable when unchanged.\n const state = useMemo<SimState>(\n () =>\n rawState.theme === resolved ? rawState : { ...rawState, theme: resolved },\n [rawState, resolved],\n );\n\n useEffect(() => {\n const sync = () => {\n setRawState(player.state);\n setCurrentMs(player.currentMs);\n };\n sync();\n setPlaying(player.playing);\n const offs = [\n player.on(\"tick\", sync),\n player.on(\"seek\", sync),\n player.on(\"play\", () => setPlaying(true)),\n player.on(\"pause\", () => setPlaying(false)),\n ];\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player]);\n\n // Apply `autoplay` once per player (at creation), not reactively. If this read\n // were a dependency of the subscribe/destroy effect above, a consumer toggling\n // a controlled `paused` — which flips the gated `autoplay` value upstream —\n // would tear down and recreate the live player. Post-mount play/pause is the\n // consumer's job (e.g. `<Typecaast>`'s `paused` reconcile).\n // (deps intentionally exclude `autoplay` — see the comment above.)\n useEffect(() => {\n if (autoplay) player.play();\n }, [player]);\n\n return {\n state,\n currentMs,\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t) => player.seek(t),\n scrubTo: (t) => player.scrubTo(t),\n setRate: (r) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n duration: player.durationMs,\n rate: player.rate,\n playing,\n player,\n };\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Base reset injected inside the shadow root. The host page's stylesheet\n * selectors (`*`, `code {}`, `.prose p {}`, …) cannot reach into a shadow tree,\n * so the only thing that leaks in is **inherited** properties from the host\n * element. `all: initial` on `:host` neutralises those (line-height, font-weight,\n * letter-spacing, font-variant, font-family, color) to clean defaults; the\n * skin's `Frame` then sets its own font/colour as usual. The universal\n * `box-sizing: border-box` matches the baseline the skins are authored against.\n */\nconst RESET = `\n:host { all: initial; display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n`;\n\n/**\n * Renders its children inside an **open shadow root**, so an embedded\n * `<Typecaast>` is immune to the host page's global CSS (resets, Tailwind\n * `.prose`, tag rules, inherited `line-height`/font). Used by `<Typecaast>` when\n * `isolate` is set.\n *\n * Client-only by nature — `attachShadow` needs the DOM, so on the server (and\n * the first client render) nothing is portaled; the light-DOM host div still\n * reserves the widget's size, and the visuals hydrate in. The web fonts the\n * skins register via the `FontFace` API live in the document and apply across\n * the shadow boundary, so typography is unaffected.\n */\nexport function ShadowFrame({\n children,\n style,\n}: {\n children: ReactNode;\n style?: CSSProperties;\n}): ReactElement {\n const hostRef = useRef<HTMLDivElement>(null);\n const [root, setRoot] = useState<ShadowRoot | null>(null);\n\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n // `?? shadowRoot` makes this safe under StrictMode's double-invoke (a second\n // attachShadow would throw).\n setRoot(host.shadowRoot ?? host.attachShadow({ mode: \"open\" }));\n }, []);\n\n return (\n <div ref={hostRef} aria-hidden=\"true\" style={style}>\n {root\n ? createPortal(\n <>\n <style>{RESET}</style>\n {children}\n </>,\n root,\n )\n : null}\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { loadSkinFonts, type Skin } from \"@typecaast/skin-kit\";\n\nexport type FontLoadState = \"loading\" | \"loaded\";\n\n/**\n * Load a skin's declared web fonts on mount so the live preview renders in the\n * correct typeface (PLAN §19) — never relying on a host OS font. SSR-safe and\n * a no-op off the DOM (resolves \"loaded\"). Re-runs if the skin's fonts change.\n */\nexport function useSkinFonts(skin: Skin): FontLoadState {\n const fonts = skin.meta.fonts;\n const [state, setState] = useState<FontLoadState>(() =>\n fonts && fonts.length > 0 ? \"loading\" : \"loaded\",\n );\n\n useEffect(() => {\n if (!fonts || fonts.length === 0) {\n setState(\"loaded\");\n return;\n }\n let cancelled = false;\n setState(\"loading\");\n loadSkinFonts(fonts).finally(() => {\n if (!cancelled) setState(\"loaded\");\n });\n return () => {\n cancelled = true;\n };\n }, [fonts]);\n\n return state;\n}\n","import { useEffect } from \"react\";\nimport type { Config } from \"@typecaast/schema\";\n\n/**\n * Every image URL a config will render: participant avatars plus in-message\n * images (the `images` sugar and explicit `image` content nodes). Deterministic\n * and side-effect free so it's safe to call on the server.\n */\nfunction collectImageUrls(config: Config): string[] {\n const urls = new Set<string>();\n for (const p of config.participants) if (p.avatar) urls.add(p.avatar);\n for (const step of config.timeline) {\n const s = step as {\n images?: Array<{ src?: unknown }>;\n content?: Array<{ type?: unknown; src?: unknown }>;\n };\n if (Array.isArray(s.images))\n for (const im of s.images)\n if (typeof im?.src === \"string\") urls.add(im.src);\n if (Array.isArray(s.content))\n for (const n of s.content)\n if (n?.type === \"image\" && typeof n.src === \"string\") urls.add(n.src);\n }\n return [...urls];\n}\n\n// Keep a live reference to every preloaded image, module-wide. This (a) dedupes\n// across instances/renders so a URL is only fetched once, and (b) retains the\n// `Image` so an in-flight fetch isn't aborted when GC'd mid-flight — the browser\n// HTTP cache then serves the skin's later `<img>` instantly (even inside a\n// shadow root). One element per unique URL; negligible.\nconst preloaded = new Map<string, HTMLImageElement>();\n\nfunction preload(url: string): void {\n if (preloaded.has(url)) return;\n const img = new Image();\n img.decoding = \"async\";\n img.src = url;\n preloaded.set(url, img);\n // Warm the decoded bitmap too so display has no decode hitch; ignore failures\n // (a bad URL just falls back to the skin's normal `<img>`/initials).\n void img.decode?.().catch(() => {});\n}\n\n/**\n * Preload a config's images on mount so an avatar (or in-message image) is\n * cached before its message appears and never \"pops in\" late mid-playback.\n * Client-only and SSR-safe — a no-op where `Image` is undefined.\n */\nexport function usePreloadImages(config: Config): void {\n // `config` is identity-stable across re-renders (`<Typecaast>` de-dupes it by\n // content), so this runs once per distinct config rather than every render.\n useEffect(() => {\n if (typeof Image === \"undefined\") return;\n for (const url of collectImageUrls(config)) preload(url);\n }, [config]);\n}\n","import { useSyncExternalStore } from \"react\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\nfunction getMql(): MediaQueryList | null {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return null;\n }\n return window.matchMedia(QUERY);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const mql = getMql();\n if (!mql) return () => {};\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\nconst getSnapshot = (): boolean => getMql()?.matches ?? false;\nconst getServerSnapshot = (): boolean => false;\n\n/**\n * Tracks `prefers-reduced-motion: reduce`. When true, the player snaps to the\n * final state instead of animating (PLAN §20).\n */\nexport function useReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import type { Config } from \"@typecaast/schema\";\n\nexport interface TranscriptLine {\n name: string;\n text: string;\n}\n\n/**\n * Build a plain-text transcript from the authored config — the accessible\n * representation of the conversation for screen readers (PLAN §20). The config\n * is already structured text, so this is a faithful, non-animated version.\n */\nexport function buildTranscript(config: Config): TranscriptLine[] {\n const name = new Map(config.participants.map((p) => [p.id, p.name]));\n const lines: TranscriptLine[] = [];\n for (const step of config.timeline) {\n let from: string | undefined;\n let text: string | undefined;\n if (step.type === \"message\" || step.type === \"system\") {\n from = step.from;\n text = step.text;\n } else if (step.type === \"composerType\") {\n from = step.from;\n text = step.text;\n }\n if (text) {\n lines.push({ name: from ? (name.get(from) ?? from) : \"System\", text });\n }\n }\n return lines;\n}\n","import {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { FitMode, Size } from \"@typecaast/schema\";\n\nexport interface FitBoxProps {\n fit: FitMode;\n canvas: Size;\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * Applies the `fit` strategy between the authoring canvas and the host\n * container (PLAN §7):\n * - `reflow`: **fills both axes** (width + height); content re-wraps to the\n * container width and the bottom-anchored thread clips older messages\n * when they overflow vertically. The widget is container-driven — it\n * never grows past its host as more steps play.\n * - `scale`: renders at exact canvas size, CSS-scaled to fit (layout\n * preserved). Use when you want the skin to look like its native canvas\n * regardless of container size.\n * - `fixed`: exact canvas size, clipped — the only mode where the widget\n * is *not* container-driven.\n */\nexport function FitBox({\n fit,\n canvas,\n children,\n className,\n style,\n}: FitBoxProps): ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<{ w: number; h: number } | null>(\n null,\n );\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (rect) setContainer({ w: rect.width, h: rect.height });\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n if (fit === \"reflow\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"reflow\"\n // `height: 100%` closes the height chain: combined with the parent\n // `<Typecaast>` outer's `aspect-ratio` fallback, the widget fills\n // its host container instead of growing taller as more messages\n // play (the skin's bottom-anchored thread + `overflow: hidden`\n // clip older steps off the top).\n style={{ width: \"100%\", height: \"100%\", ...style }}\n >\n {children}\n </div>\n );\n }\n\n if (fit === \"fixed\") {\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"fixed\"\n style={{\n width: canvas.width,\n height: canvas.height,\n overflow: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n }\n\n // scale: fit the exact-size canvas into the measured container.\n const scale = container\n ? Math.min(container.w / canvas.width, container.h / canvas.height)\n : 1;\n return (\n <div\n ref={ref}\n className={className}\n data-fit=\"scale\"\n style={{ width: \"100%\", height: \"100%\", overflow: \"hidden\", ...style }}\n >\n <div\n data-fit-canvas=\"\"\n style={{\n width: canvas.width,\n height: canvas.height,\n transform: `scale(${scale})`,\n transformOrigin: \"top left\",\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n","import type { Skin } from \"@typecaast/skin-kit\";\n\ntype SkinModule = { default: Skin };\n\n/**\n * Lazy loaders for the built-in skins, keyed by `meta.skin.id`. Each value is a\n * **static** `import()` of a per-skin subpath, so bundlers emit one chunk per\n * skin and only the skin a config actually references is fetched. Custom skins\n * bypass this entirely (pass the `skin` prop to `<Typecaast>`).\n *\n * Adding a built-in skin = add one line here + the subpath export in\n * `@typecaast/skins`.\n */\nexport const BUILTIN_SKIN_LOADERS: Record<string, () => Promise<SkinModule>> = {\n slack: () => import(\"@typecaast/skins/slack\"),\n telegram: () => import(\"@typecaast/skins/telegram\"),\n \"claude-code\": () => import(\"@typecaast/skins/claude-code\"),\n imessage: () => import(\"@typecaast/skins/imessage\"),\n whatsapp: () => import(\"@typecaast/skins/whatsapp\"),\n cursor: () => import(\"@typecaast/skins/cursor\"),\n \"messages-macos\": () => import(\"@typecaast/skins/messages-macos\"),\n discord: () => import(\"@typecaast/skins/discord\"),\n};\n\n/** Ids of the built-in skins resolvable by `<Typecaast>` without a `skin` prop. */\nexport const builtinSkinIds = Object.keys(BUILTIN_SKIN_LOADERS);\n\n/**\n * A status-tracked load, cached per id. Tracking the settled state lets us\n * Suspense-read it on **React 18 and 19** alike (via the throw-the-promise\n * primitive) instead of React 19's `use()`, which doesn't exist on 18.\n */\ninterface SkinResource {\n promise: Promise<Skin>;\n status: \"pending\" | \"fulfilled\" | \"rejected\";\n value?: Skin;\n error?: unknown;\n}\n\n// Stable resource per id so the same promise is seen across renders.\nconst cache = new Map<string, SkinResource>();\n\n/**\n * Get (or create) the cached resource for a skin id. Throws synchronously for an\n * unknown id (a render error with a clear message), rather than suspending forever.\n */\nfunction getResource(id: string): SkinResource {\n let resource = cache.get(id);\n if (resource) return resource;\n\n const loader = BUILTIN_SKIN_LOADERS[id];\n if (!loader) {\n throw new Error(\n `Typecaast: unknown skin \"${id}\". Built-in skins: ${builtinSkinIds.join(\n \", \",\n )}. For a custom skin, pass the \\`skin\\` prop.`,\n );\n }\n\n resource = {\n status: \"pending\",\n promise: loader().then((m) => m.default),\n };\n // Record the settled state for the synchronous Suspense read. This handler\n // also keeps `promise`'s rejection from going unhandled.\n void resource.promise.then(\n (skin) => {\n resource!.status = \"fulfilled\";\n resource!.value = skin;\n },\n (error: unknown) => {\n resource!.status = \"rejected\";\n resource!.error = error;\n },\n );\n cache.set(id, resource);\n return resource;\n}\n\n/**\n * Resolve a built-in skin id to a cached promise of its `Skin`. Stable per id.\n */\nexport function loadBuiltinSkin(id: string): Promise<Skin> {\n return getResource(id).promise;\n}\n\n/**\n * Suspense-read a built-in skin by id: returns the `Skin` once loaded, throws the\n * pending promise to suspend, or re-throws a load error. This is the universal\n * Suspense primitive — it works on React 18 and 19, unlike `use()` (19-only).\n */\nexport function readBuiltinSkin(id: string): Skin {\n const resource = getResource(id);\n if (resource.status === \"fulfilled\") return resource.value as Skin;\n if (resource.status === \"rejected\") throw resource.error;\n throw resource.promise;\n}\n","import {\n forwardRef,\n Suspense,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type ForwardedRef,\n type ReactElement,\n} from \"react\";\nimport {\n configSchema,\n type Config,\n type ConfigInput,\n type FitMode,\n type ThemeMode,\n} from \"@typecaast/schema\";\nimport {\n TypecaastStage,\n type ComposerMode,\n type Skin,\n} from \"@typecaast/skin-kit\";\nimport { useTypecaast } from \"./use-typecaast.js\";\nimport { ShadowFrame } from \"./shadow-frame.js\";\nimport { useSkinFonts } from \"./use-skin-fonts.js\";\nimport { usePreloadImages } from \"./use-preload-images.js\";\nimport { useReducedMotion } from \"./use-reduced-motion.js\";\nimport { buildTranscript } from \"./transcript.js\";\nimport { FitBox } from \"./fit-box.js\";\nimport { readBuiltinSkin } from \"./builtin-skins.js\";\n\n/**\n * A loosely-typed config shape that a raw `import`ed `typecaast.json` satisfies —\n * TypeScript widens JSON literals (e.g. `version: number`, `type: string`), so it\n * matches neither `Config` nor `ConfigInput`. It's validated and normalized at\n * runtime, so this stays a convenience surface, not a bypass.\n */\nexport interface RawConfig {\n version: number;\n meta: {\n canvas: { width: number; height: number };\n skin: { id: string; options?: Record<string, unknown> };\n [key: string]: unknown;\n };\n participants: Array<{ id: string; name: string; [key: string]: unknown }>;\n timeline: Array<{ type: string; [key: string]: unknown }>;\n pacing?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * What `<Typecaast config>` accepts: a precise `ConfigInput`/`Config` (full\n * intellisense when hand-authoring) or a raw config object such as an imported\n * `typecaast.json`. All forms are normalized through the schema at runtime.\n */\nexport type TypecaastConfig = ConfigInput | Config | RawConfig;\n\nexport interface TypecaastProps {\n /**\n * The conversation config. Accepts your exported `typecaast.json` directly (or\n * a hand-authored `ConfigInput`); it's validated and defaulted at runtime, so\n * you never need to pre-parse it.\n */\n config: TypecaastConfig;\n /**\n * The skin to render with. **Optional** — by default the built-in skin named\n * by `config.meta.skin.id` is resolved and lazy-loaded (only that skin's chunk\n * is fetched), so the config is the single source of truth and the embed stays\n * fully serializable (works in a React Server Component, no `\"use client\"`).\n * Pass a `Skin` object only to use a custom skin not in `@typecaast/skins`.\n */\n skin?: Skin;\n /** Force a theme; otherwise resolved from `config.meta.theme`. */\n theme?: ThemeMode;\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n /** Container fit mode; defaults to `config.meta.fit`. */\n fit?: FitMode;\n /** Composer (reply box) visibility: `auto` (default) / `always` / `never`. */\n composer?: ComposerMode;\n /** Accessible label for the simulation. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n /**\n * Controlled pause. When provided it takes over playback: `true` pauses in\n * place, `false` plays/resumes **from the current position** (never restarts).\n * Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it\n * (e.g. `paused={activeTab !== \"slack\"}`) pauses/resumes without unmounting.\n * No-op under `prefers-reduced-motion` (which holds the completed thread).\n */\n paused?: boolean;\n /** Called when playback starts or resumes. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called once when playback reaches the end (non-looping). */\n onEnded?: () => void;\n /**\n * Render the widget inside an **open shadow root** so the host page's global\n * CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)\n * can't leak in and distort it. Recommended when embedding into a page with\n * its own styles. Note: this is **client-only** (a shadow root can't be\n * attached during SSR), so an isolated widget renders a correctly-sized box\n * on the server and hydrates its visuals in — it no longer works inside a\n * pure React Server Component. Default `false`.\n */\n isolate?: boolean;\n}\n\n/**\n * Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases\n * the declarative props don't cover (jump to a time, change rate, step):\n *\n * ```tsx\n * const ref = useRef<TypecaastHandle>(null);\n * <Typecaast ref={ref} config={config} autoplay />;\n * // …later: ref.current?.seek(5000);\n * ```\n *\n * `ref.current` is `null` until the skin has loaded and the player mounts.\n */\nexport interface TypecaastHandle {\n play(): void;\n pause(): void;\n /** Jump to an absolute time in ms (clamped to `[0, duration]`). */\n seek(timeMs: number): void;\n /** Like `seek`, used for scrubbing. */\n scrubTo(timeMs: number): void;\n /** Playback rate multiplier (1 = realtime). */\n setRate(rate: number): void;\n /** Jump to the next / previous step boundary. */\n stepNext(): void;\n stepPrev(): void;\n /** Live playback time in ms. */\n readonly currentMs: number;\n /** Total duration in ms. */\n readonly duration: number;\n /** Whether the clock is currently running. */\n readonly playing: boolean;\n}\n\nconst SR_ONLY: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clipPath: \"inset(50%)\",\n whiteSpace: \"nowrap\",\n border: 0,\n};\n\n/**\n * Default sizing for the outer `<Typecaast>` wrapper. The widget is\n * **container-driven**: it fills its parent in both axes. When the host\n * gives the wrapper a definite height (responsive grid + fixed-height\n * card, hero box with `aspectRatio`, …) `height: 100%` resolves to that\n * height and the skin reflows / scales to fit. When the host gives only a\n * width, `height: 100%` resolves to `auto` and the canvas's own\n * `aspect-ratio` takes over, deriving a sensible height from the\n * authored canvas dimensions instead of letting message content drive\n * the widget taller as more steps play.\n *\n * The user's `style` prop is spread *after* these defaults so any\n * explicit width/height/aspectRatio overrides win — same opt-out story\n * as the `style.position` pass-through.\n */\nfunction rootStyle(canvas: { width: number; height: number }): CSSProperties {\n return {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n aspectRatio: `${canvas.width} / ${canvas.height}`,\n };\n}\n\n/**\n * Renders a `<Typecaast>` from a config. The skin defaults to the built-in named\n * by `config.meta.skin.id` (lazy-loaded by id — see `builtin-skins.ts`); pass an\n * explicit `skin` to use a custom one. `<Typecaast>` is a client component, but\n * since the default path takes only the serializable `config`, the embed drops\n * straight into a React Server Component.\n */\nexport const Typecaast = forwardRef<TypecaastHandle, TypecaastProps>(\n function Typecaast(props, ref): ReactElement {\n // Normalize once: validate and apply schema defaults (pacing, fit, theme, …)\n // so a raw exported `typecaast.json` works without the caller pre-parsing it.\n //\n // Key the parse on the config's *content*, not its object identity, so a host\n // that rebuilds an equal config inline on each render (common on customer\n // sites, where unrelated state changes re-render the tree) doesn't churn a new\n // `Config` → new compiled timeline → new player → restart. A stable digest ⇒\n // stable `Config` identity ⇒ `compile`'s WeakMap hits ⇒ the player holds and\n // playback continues. `JSON.stringify` runs only when `props.config` identity\n // changes; configs are small, plain-JSON scripts (no cycles).\n const configKey = useMemo(\n () => JSON.stringify(props.config),\n [props.config],\n );\n const rawConfigRef = useRef(props.config);\n rawConfigRef.current = props.config;\n const config = useMemo<Config>(\n () => configSchema.parse(rawConfigRef.current),\n [configKey],\n );\n\n // Warm the cache for every avatar / in-message image up front (even while the\n // skin chunk is still loading) so they don't pop in late once their message\n // appears mid-playback.\n usePreloadImages(config);\n\n // Explicit skin object → render synchronously, no lazy load.\n if (props.skin)\n return <Player {...props} config={config} skin={props.skin} ref={ref} />;\n // Otherwise resolve (and lazy-load) the built-in named in the config.\n return (\n <Suspense\n fallback={\n <SkinFallback\n config={config}\n fit={props.fit}\n label={props.label}\n className={props.className}\n style={props.style}\n />\n }\n >\n <ResolvedPlayer {...props} config={config} ref={ref} />\n </Suspense>\n );\n },\n);\n\nconst ResolvedPlayer = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config }\n>(function ResolvedPlayer(props, ref): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} ref={ref} />;\n});\n\n/**\n * The actual player. The animated visuals are `aria-hidden`; an accessible\n * transcript carries the conversation for screen readers, and\n * `prefers-reduced-motion` snaps to the final state instead of animating\n * (PLAN §20).\n */\nconst Player = forwardRef<\n TypecaastHandle,\n Omit<TypecaastProps, \"config\"> & { config: Config; skin: Skin }\n>(function Player(\n {\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n paused,\n onPlay,\n onPause,\n onEnded,\n isolate,\n },\n ref: ForwardedRef<TypecaastHandle>,\n): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n // Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never\n // flashes a frame of playback. `!paused` is `true` when uncontrolled.\n autoplay: autoplay && !reduced && !paused,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\n const player = tc.player;\n const fonts = useSkinFonts(skin);\n\n // Reduced motion: hold the completed conversation, no animation.\n useEffect(() => {\n if (reduced) tc.seek(tc.duration);\n }, [reduced, tc]);\n\n // Controlled `paused`: reconcile only when the consumer drives it (so an\n // uncontrolled instance keeps its autoplay behavior). Runs after the hook's\n // mount-autoplay effect; re-applies whenever the controls object changes\n // (play/pause are idempotent). No-op under reduced motion.\n useEffect(() => {\n if (paused === undefined || reduced) return;\n if (paused) tc.pause();\n else tc.play();\n }, [paused, reduced, tc]);\n\n // Lifecycle callbacks → player events.\n useEffect(() => {\n const offs: Array<() => void> = [];\n if (onPlay) offs.push(player.on(\"play\", onPlay));\n if (onPause) offs.push(player.on(\"pause\", onPause));\n if (onEnded) offs.push(player.on(\"end\", onEnded));\n return () => offs.forEach((off) => off());\n }, [player, onPlay, onPause, onEnded]);\n\n // Imperative controls (jump to a time, change rate, step). Getters read the\n // live player so values are exact; the handle is stable per player.\n useImperativeHandle(\n ref,\n () => ({\n play: () => player.play(),\n pause: () => player.pause(),\n seek: (t: number) => player.seek(t),\n scrubTo: (t: number) => player.scrubTo(t),\n setRate: (r: number) => player.setRate(r),\n stepNext: () => player.stepNext(),\n stepPrev: () => player.stepPrev(),\n get currentMs() {\n return player.currentMs;\n },\n get duration() {\n return player.durationMs;\n },\n get playing() {\n return player.playing;\n },\n }),\n [player],\n );\n\n const transcript = useMemo(() => buildTranscript(config), [config]);\n\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-fonts={fonts}\n role=\"figure\"\n aria-label={label ?? `Chat simulation (${skin.meta.name})`}\n >\n <ol style={SR_ONLY}>\n {transcript.map((line, i) => (\n <li key={i}>\n {line.name}: {line.text}\n </li>\n ))}\n </ol>\n {(() => {\n const visuals = (\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <TypecaastStage\n state={tc.state}\n skin={skin}\n participants={config.participants}\n options={config.meta.skin.options}\n composer={composer ?? config.meta.composer}\n />\n </FitBox>\n );\n const fill = { width: \"100%\", height: \"100%\" } as const;\n // `isolate` renders the visuals inside a shadow root so the host page's\n // CSS can't leak in (client-only). Either way the content is aria-hidden;\n // the accessible transcript above carries the conversation.\n return isolate ? (\n <ShadowFrame style={fill}>{visuals}</ShadowFrame>\n ) : (\n <div aria-hidden=\"true\" style={fill}>\n {visuals}\n </div>\n );\n })()}\n </div>\n );\n});\n\n/**\n * A same-size placeholder shown while a built-in skin's chunk loads, so there's\n * no layout shift between fallback and the rendered skin. (On static/prerendered\n * pages the skin resolves before HTML is emitted, so this never paints.)\n */\nfunction SkinFallback({\n config,\n fit,\n label,\n className,\n style,\n}: Pick<TypecaastProps, \"fit\" | \"label\" | \"className\" | \"style\"> & {\n config: Config;\n}) {\n return (\n <div\n className={className}\n style={{ ...rootStyle(config.meta.canvas), ...style }}\n data-typecaast=\"\"\n data-typecaast-loading=\"\"\n role=\"figure\"\n aria-label={label ?? \"Chat simulation\"}\n aria-busy=\"true\"\n >\n <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\n <FitBox fit={fit ?? config.meta.fit} canvas={config.meta.canvas}>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n background: \"var(--tc-skin-loading-bg, transparent)\",\n }}\n />\n </FitBox>\n </div>\n </div>\n );\n}\n","import type { ThemeMode } from \"@typecaast/schema\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\n\n/**\n * Resolve a theme mode to a concrete theme. `auto` falls back to `light`\n * here; M1U.4 makes `auto` reactive against the host `prefers-color-scheme`\n * via a hook layered on top of this.\n */\nexport function resolveTheme(mode: ThemeMode): ResolvedTheme {\n return mode === \"dark\" ? \"dark\" : mode === \"light\" ? \"light\" : \"light\";\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecaast/react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "React renderer: <Typecaast> real-time player + useTypecaast hook.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@typecaast/core": "0.4.0",
|
|
28
28
|
"@typecaast/schema": "0.2.1",
|
|
29
|
-
"@typecaast/skin-kit": "0.4.
|
|
30
|
-
"@typecaast/skins": "0.3.
|
|
29
|
+
"@typecaast/skin-kit": "0.4.1",
|
|
30
|
+
"@typecaast/skins": "0.3.3"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=18"
|