@typecaast/react 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +40 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +41 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var react = require('react');
|
|
|
5
5
|
var schema = require('@typecaast/schema');
|
|
6
6
|
var skinKit = require('@typecaast/skin-kit');
|
|
7
7
|
var core = require('@typecaast/core');
|
|
8
|
+
var reactDom = require('react-dom');
|
|
8
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
10
|
|
|
10
11
|
// src/typecaast.tsx
|
|
@@ -102,6 +103,29 @@ function useTypecaast(config, options = {}) {
|
|
|
102
103
|
player
|
|
103
104
|
};
|
|
104
105
|
}
|
|
106
|
+
var RESET = `
|
|
107
|
+
:host { all: initial; display: block; width: 100%; height: 100%; }
|
|
108
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
109
|
+
`;
|
|
110
|
+
function ShadowFrame({
|
|
111
|
+
children,
|
|
112
|
+
style
|
|
113
|
+
}) {
|
|
114
|
+
const hostRef = react.useRef(null);
|
|
115
|
+
const [root, setRoot] = react.useState(null);
|
|
116
|
+
react.useLayoutEffect(() => {
|
|
117
|
+
const host = hostRef.current;
|
|
118
|
+
if (!host) return;
|
|
119
|
+
setRoot(host.shadowRoot ?? host.attachShadow({ mode: "open" }));
|
|
120
|
+
}, []);
|
|
121
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: hostRef, "aria-hidden": "true", style, children: root ? reactDom.createPortal(
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: RESET }),
|
|
124
|
+
children
|
|
125
|
+
] }),
|
|
126
|
+
root
|
|
127
|
+
) : null });
|
|
128
|
+
}
|
|
105
129
|
function useSkinFonts(skin) {
|
|
106
130
|
const fonts = skin.meta.fonts;
|
|
107
131
|
const [state, setState] = react.useState(
|
|
@@ -352,7 +376,8 @@ var Player = react.forwardRef(function Player2({
|
|
|
352
376
|
paused,
|
|
353
377
|
onPlay,
|
|
354
378
|
onPause,
|
|
355
|
-
onEnded
|
|
379
|
+
onEnded,
|
|
380
|
+
isolate
|
|
356
381
|
}, ref) {
|
|
357
382
|
const reduced = useReducedMotion();
|
|
358
383
|
const tc = useTypecaast(config, {
|
|
@@ -419,16 +444,20 @@ var Player = react.forwardRef(function Player2({
|
|
|
419
444
|
": ",
|
|
420
445
|
line.text
|
|
421
446
|
] }, i)) }),
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
447
|
+
(() => {
|
|
448
|
+
const visuals = /* @__PURE__ */ jsxRuntime.jsx(FitBox, { fit: fit ?? config.meta.fit, canvas: config.meta.canvas, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
449
|
+
skinKit.TypecaastStage,
|
|
450
|
+
{
|
|
451
|
+
state: tc.state,
|
|
452
|
+
skin,
|
|
453
|
+
participants: config.participants,
|
|
454
|
+
options: config.meta.skin.options,
|
|
455
|
+
composer: composer ?? config.meta.composer
|
|
456
|
+
}
|
|
457
|
+
) });
|
|
458
|
+
const fill = { width: "100%", height: "100%" };
|
|
459
|
+
return isolate ? /* @__PURE__ */ jsxRuntime.jsx(ShadowFrame, { style: fill, children: visuals }) : /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: fill, children: visuals });
|
|
460
|
+
})()
|
|
432
461
|
]
|
|
433
462
|
}
|
|
434
463
|
);
|
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/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","loadSkinFonts","QUERY","getMql","subscribe","getSnapshot","getServerSnapshot","useRef","useLayoutEffect","jsx","forwardRef","Typecaast","configSchema","Suspense","ResolvedPlayer","Player","useImperativeHandle","jsxs","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 { 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 { 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\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 },\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 <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\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 </div>\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-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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -84,6 +84,16 @@ interface TypecaastProps {
|
|
|
84
84
|
onPause?: () => void;
|
|
85
85
|
/** Called once when playback reaches the end (non-looping). */
|
|
86
86
|
onEnded?: () => void;
|
|
87
|
+
/**
|
|
88
|
+
* Render the widget inside an **open shadow root** so the host page's global
|
|
89
|
+
* CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)
|
|
90
|
+
* can't leak in and distort it. Recommended when embedding into a page with
|
|
91
|
+
* its own styles. Note: this is **client-only** (a shadow root can't be
|
|
92
|
+
* attached during SSR), so an isolated widget renders a correctly-sized box
|
|
93
|
+
* on the server and hydrates its visuals in — it no longer works inside a
|
|
94
|
+
* pure React Server Component. Default `false`.
|
|
95
|
+
*/
|
|
96
|
+
isolate?: boolean;
|
|
87
97
|
}
|
|
88
98
|
/**
|
|
89
99
|
* Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases
|
package/dist/index.d.ts
CHANGED
|
@@ -84,6 +84,16 @@ interface TypecaastProps {
|
|
|
84
84
|
onPause?: () => void;
|
|
85
85
|
/** Called once when playback reaches the end (non-looping). */
|
|
86
86
|
onEnded?: () => void;
|
|
87
|
+
/**
|
|
88
|
+
* Render the widget inside an **open shadow root** so the host page's global
|
|
89
|
+
* CSS (resets, Tailwind `.prose`, tag rules, inherited `line-height`/font)
|
|
90
|
+
* can't leak in and distort it. Recommended when embedding into a page with
|
|
91
|
+
* its own styles. Note: this is **client-only** (a shadow root can't be
|
|
92
|
+
* attached during SSR), so an isolated widget renders a correctly-sized box
|
|
93
|
+
* on the server and hydrates its visuals in — it no longer works inside a
|
|
94
|
+
* pure React Server Component. Default `false`.
|
|
95
|
+
*/
|
|
96
|
+
isolate?: boolean;
|
|
87
97
|
}
|
|
88
98
|
/**
|
|
89
99
|
* Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,8 @@ import { configSchema } from '@typecaast/schema';
|
|
|
4
4
|
import { TypecaastStage, loadSkinFonts } from '@typecaast/skin-kit';
|
|
5
5
|
export { TypecaastStage } from '@typecaast/skin-kit';
|
|
6
6
|
import { createPlayer, createEngine } from '@typecaast/core';
|
|
7
|
-
import {
|
|
7
|
+
import { createPortal } from 'react-dom';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
9
|
|
|
9
10
|
// src/typecaast.tsx
|
|
10
11
|
function configToEngine(config, theme, capabilities) {
|
|
@@ -101,6 +102,29 @@ function useTypecaast(config, options = {}) {
|
|
|
101
102
|
player
|
|
102
103
|
};
|
|
103
104
|
}
|
|
105
|
+
var RESET = `
|
|
106
|
+
:host { all: initial; display: block; width: 100%; height: 100%; }
|
|
107
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
108
|
+
`;
|
|
109
|
+
function ShadowFrame({
|
|
110
|
+
children,
|
|
111
|
+
style
|
|
112
|
+
}) {
|
|
113
|
+
const hostRef = useRef(null);
|
|
114
|
+
const [root, setRoot] = useState(null);
|
|
115
|
+
useLayoutEffect(() => {
|
|
116
|
+
const host = hostRef.current;
|
|
117
|
+
if (!host) return;
|
|
118
|
+
setRoot(host.shadowRoot ?? host.attachShadow({ mode: "open" }));
|
|
119
|
+
}, []);
|
|
120
|
+
return /* @__PURE__ */ jsx("div", { ref: hostRef, "aria-hidden": "true", style, children: root ? createPortal(
|
|
121
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
/* @__PURE__ */ jsx("style", { children: RESET }),
|
|
123
|
+
children
|
|
124
|
+
] }),
|
|
125
|
+
root
|
|
126
|
+
) : null });
|
|
127
|
+
}
|
|
104
128
|
function useSkinFonts(skin) {
|
|
105
129
|
const fonts = skin.meta.fonts;
|
|
106
130
|
const [state, setState] = useState(
|
|
@@ -351,7 +375,8 @@ var Player = forwardRef(function Player2({
|
|
|
351
375
|
paused,
|
|
352
376
|
onPlay,
|
|
353
377
|
onPause,
|
|
354
|
-
onEnded
|
|
378
|
+
onEnded,
|
|
379
|
+
isolate
|
|
355
380
|
}, ref) {
|
|
356
381
|
const reduced = useReducedMotion();
|
|
357
382
|
const tc = useTypecaast(config, {
|
|
@@ -418,16 +443,20 @@ var Player = forwardRef(function Player2({
|
|
|
418
443
|
": ",
|
|
419
444
|
line.text
|
|
420
445
|
] }, i)) }),
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
446
|
+
(() => {
|
|
447
|
+
const visuals = /* @__PURE__ */ jsx(FitBox, { fit: fit ?? config.meta.fit, canvas: config.meta.canvas, children: /* @__PURE__ */ jsx(
|
|
448
|
+
TypecaastStage,
|
|
449
|
+
{
|
|
450
|
+
state: tc.state,
|
|
451
|
+
skin,
|
|
452
|
+
participants: config.participants,
|
|
453
|
+
options: config.meta.skin.options,
|
|
454
|
+
composer: composer ?? config.meta.composer
|
|
455
|
+
}
|
|
456
|
+
) });
|
|
457
|
+
const fill = { width: "100%", height: "100%" };
|
|
458
|
+
return isolate ? /* @__PURE__ */ jsx(ShadowFrame, { style: fill, children: visuals }) : /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: fill, children: visuals });
|
|
459
|
+
})()
|
|
431
460
|
]
|
|
432
461
|
}
|
|
433
462
|
);
|
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/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","Typecaast","useMemo","jsx","ResolvedPlayer","Player"],"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 { 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 { 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\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 },\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 <div aria-hidden=\"true\" style={{ width: \"100%\", height: \"100%\" }}>\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 </div>\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-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;AAE5D,EAAA,MAAM,MAAA,GAAS,QAAgB,MAAM;AACnC,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,MAAA,EAAQ,QAAA,EAAU,YAAY,CAAA;AAC5D,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,UAAU,YAAA,EAAc,IAAA,EAAM,IAAI,CAAC,CAAA;AAE/C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAI,QAAA,CAAmB,MAAM,OAAO,KAAK,CAAA;AAC/D,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;AAEpE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,QAAA,CAAS,OAAO,KAAK,CAAA;AACrB,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;ACvGA,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;AC9BA,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;AC8CA,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;AAG3C,IAAA,MAAM,MAAA,GAASC,OAAAA;AAAA,MACb,MAAM,YAAA,CAAa,KAAA,CAAM,KAAA,CAAM,MAAM,CAAA;AAAA,MACrC,CAAC,MAAM,MAAM;AAAA,KACf;AAGA,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;;;ACvYO,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 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"]}
|