@typecaast/react 0.3.0 → 0.4.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 +79 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -3
- package/dist/index.d.ts +49 -3
- package/dist/index.js +81 -36
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -78,12 +78,14 @@ function useTypecaast(config, options = {}) {
|
|
|
78
78
|
player.on("play", () => setPlaying(true)),
|
|
79
79
|
player.on("pause", () => setPlaying(false))
|
|
80
80
|
];
|
|
81
|
-
if (autoplay) player.play();
|
|
82
81
|
return () => {
|
|
83
82
|
offs.forEach((off) => off());
|
|
84
83
|
player.destroy();
|
|
85
84
|
};
|
|
86
|
-
}, [player
|
|
85
|
+
}, [player]);
|
|
86
|
+
react.useEffect(() => {
|
|
87
|
+
if (autoplay) player.play();
|
|
88
|
+
}, [player]);
|
|
87
89
|
return {
|
|
88
90
|
state,
|
|
89
91
|
currentMs,
|
|
@@ -305,35 +307,37 @@ function rootStyle(canvas) {
|
|
|
305
307
|
aspectRatio: `${canvas.width} / ${canvas.height}`
|
|
306
308
|
};
|
|
307
309
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
310
|
+
var Typecaast = react.forwardRef(
|
|
311
|
+
function Typecaast2(props, ref) {
|
|
312
|
+
const config = react.useMemo(
|
|
313
|
+
() => schema.configSchema.parse(props.config),
|
|
314
|
+
[props.config]
|
|
315
|
+
);
|
|
316
|
+
if (props.skin)
|
|
317
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Player, { ...props, config, skin: props.skin, ref });
|
|
318
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
319
|
+
react.Suspense,
|
|
320
|
+
{
|
|
321
|
+
fallback: /* @__PURE__ */ jsxRuntime.jsx(
|
|
322
|
+
SkinFallback,
|
|
323
|
+
{
|
|
324
|
+
config,
|
|
325
|
+
fit: props.fit,
|
|
326
|
+
label: props.label,
|
|
327
|
+
className: props.className,
|
|
328
|
+
style: props.style
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ResolvedPlayer, { ...props, config, ref })
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
var ResolvedPlayer = react.forwardRef(function ResolvedPlayer2(props, ref) {
|
|
333
337
|
const skin = readBuiltinSkin(props.config.meta.skin.id);
|
|
334
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Player, { ...props, skin });
|
|
335
|
-
}
|
|
336
|
-
|
|
338
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Player, { ...props, skin, ref });
|
|
339
|
+
});
|
|
340
|
+
var Player = react.forwardRef(function Player2({
|
|
337
341
|
config,
|
|
338
342
|
skin,
|
|
339
343
|
theme,
|
|
@@ -344,20 +348,61 @@ function Player({
|
|
|
344
348
|
composer,
|
|
345
349
|
label,
|
|
346
350
|
className,
|
|
347
|
-
style
|
|
348
|
-
|
|
351
|
+
style,
|
|
352
|
+
paused,
|
|
353
|
+
onPlay,
|
|
354
|
+
onPause,
|
|
355
|
+
onEnded
|
|
356
|
+
}, ref) {
|
|
349
357
|
const reduced = useReducedMotion();
|
|
350
358
|
const tc = useTypecaast(config, {
|
|
351
359
|
theme,
|
|
352
|
-
|
|
360
|
+
// Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never
|
|
361
|
+
// flashes a frame of playback. `!paused` is `true` when uncontrolled.
|
|
362
|
+
autoplay: autoplay && !reduced && !paused,
|
|
353
363
|
loop: loop && !reduced,
|
|
354
364
|
rate,
|
|
355
365
|
capabilities: skin.meta.capabilities
|
|
356
366
|
});
|
|
367
|
+
const player = tc.player;
|
|
357
368
|
const fonts = useSkinFonts(skin);
|
|
358
369
|
react.useEffect(() => {
|
|
359
370
|
if (reduced) tc.seek(tc.duration);
|
|
360
371
|
}, [reduced, tc]);
|
|
372
|
+
react.useEffect(() => {
|
|
373
|
+
if (paused === void 0 || reduced) return;
|
|
374
|
+
if (paused) tc.pause();
|
|
375
|
+
else tc.play();
|
|
376
|
+
}, [paused, reduced, tc]);
|
|
377
|
+
react.useEffect(() => {
|
|
378
|
+
const offs = [];
|
|
379
|
+
if (onPlay) offs.push(player.on("play", onPlay));
|
|
380
|
+
if (onPause) offs.push(player.on("pause", onPause));
|
|
381
|
+
if (onEnded) offs.push(player.on("end", onEnded));
|
|
382
|
+
return () => offs.forEach((off) => off());
|
|
383
|
+
}, [player, onPlay, onPause, onEnded]);
|
|
384
|
+
react.useImperativeHandle(
|
|
385
|
+
ref,
|
|
386
|
+
() => ({
|
|
387
|
+
play: () => player.play(),
|
|
388
|
+
pause: () => player.pause(),
|
|
389
|
+
seek: (t) => player.seek(t),
|
|
390
|
+
scrubTo: (t) => player.scrubTo(t),
|
|
391
|
+
setRate: (r) => player.setRate(r),
|
|
392
|
+
stepNext: () => player.stepNext(),
|
|
393
|
+
stepPrev: () => player.stepPrev(),
|
|
394
|
+
get currentMs() {
|
|
395
|
+
return player.currentMs;
|
|
396
|
+
},
|
|
397
|
+
get duration() {
|
|
398
|
+
return player.durationMs;
|
|
399
|
+
},
|
|
400
|
+
get playing() {
|
|
401
|
+
return player.playing;
|
|
402
|
+
}
|
|
403
|
+
}),
|
|
404
|
+
[player]
|
|
405
|
+
);
|
|
361
406
|
const transcript = react.useMemo(() => buildTranscript(config), [config]);
|
|
362
407
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
363
408
|
"div",
|
|
@@ -387,7 +432,7 @@ function Player({
|
|
|
387
432
|
]
|
|
388
433
|
}
|
|
389
434
|
);
|
|
390
|
-
}
|
|
435
|
+
});
|
|
391
436
|
function SkinFallback({
|
|
392
437
|
config,
|
|
393
438
|
fit,
|
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","configSchema","Suspense","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 if (autoplay) player.play();\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player, autoplay]);\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 Suspense,\n useEffect,\n useMemo,\n type CSSProperties,\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\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 function Typecaast(props: TypecaastProps): 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} />;\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} />\n </Suspense>\n );\n}\n\nfunction ResolvedPlayer(\n props: Omit<TypecaastProps, \"config\"> & { config: Config },\n): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} />;\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 */\nfunction Player({\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n}: Omit<TypecaastProps, \"config\"> & {\n config: Config;\n skin: Skin;\n}): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n autoplay: autoplay && !reduced,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\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 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/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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties, ReactNode, ReactElement } from 'react';
|
|
2
3
|
import { ConfigInput, Config, ThemeMode, FitMode, Size } from '@typecaast/schema';
|
|
3
4
|
import { Skin, ComposerMode } from '@typecaast/skin-kit';
|
|
4
5
|
export { ComposerMode, TypecaastStage, TypecaastStageProps } from '@typecaast/skin-kit';
|
|
@@ -69,6 +70,51 @@ interface TypecaastProps {
|
|
|
69
70
|
label?: string;
|
|
70
71
|
className?: string;
|
|
71
72
|
style?: CSSProperties;
|
|
73
|
+
/**
|
|
74
|
+
* Controlled pause. When provided it takes over playback: `true` pauses in
|
|
75
|
+
* place, `false` plays/resumes **from the current position** (never restarts).
|
|
76
|
+
* Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it
|
|
77
|
+
* (e.g. `paused={activeTab !== "slack"}`) pauses/resumes without unmounting.
|
|
78
|
+
* No-op under `prefers-reduced-motion` (which holds the completed thread).
|
|
79
|
+
*/
|
|
80
|
+
paused?: boolean;
|
|
81
|
+
/** Called when playback starts or resumes. */
|
|
82
|
+
onPlay?: () => void;
|
|
83
|
+
/** Called when playback pauses. */
|
|
84
|
+
onPause?: () => void;
|
|
85
|
+
/** Called once when playback reaches the end (non-looping). */
|
|
86
|
+
onEnded?: () => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases
|
|
90
|
+
* the declarative props don't cover (jump to a time, change rate, step):
|
|
91
|
+
*
|
|
92
|
+
* ```tsx
|
|
93
|
+
* const ref = useRef<TypecaastHandle>(null);
|
|
94
|
+
* <Typecaast ref={ref} config={config} autoplay />;
|
|
95
|
+
* // …later: ref.current?.seek(5000);
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* `ref.current` is `null` until the skin has loaded and the player mounts.
|
|
99
|
+
*/
|
|
100
|
+
interface TypecaastHandle {
|
|
101
|
+
play(): void;
|
|
102
|
+
pause(): void;
|
|
103
|
+
/** Jump to an absolute time in ms (clamped to `[0, duration]`). */
|
|
104
|
+
seek(timeMs: number): void;
|
|
105
|
+
/** Like `seek`, used for scrubbing. */
|
|
106
|
+
scrubTo(timeMs: number): void;
|
|
107
|
+
/** Playback rate multiplier (1 = realtime). */
|
|
108
|
+
setRate(rate: number): void;
|
|
109
|
+
/** Jump to the next / previous step boundary. */
|
|
110
|
+
stepNext(): void;
|
|
111
|
+
stepPrev(): void;
|
|
112
|
+
/** Live playback time in ms. */
|
|
113
|
+
readonly currentMs: number;
|
|
114
|
+
/** Total duration in ms. */
|
|
115
|
+
readonly duration: number;
|
|
116
|
+
/** Whether the clock is currently running. */
|
|
117
|
+
readonly playing: boolean;
|
|
72
118
|
}
|
|
73
119
|
/**
|
|
74
120
|
* Renders a `<Typecaast>` from a config. The skin defaults to the built-in named
|
|
@@ -77,7 +123,7 @@ interface TypecaastProps {
|
|
|
77
123
|
* since the default path takes only the serializable `config`, the embed drops
|
|
78
124
|
* straight into a React Server Component.
|
|
79
125
|
*/
|
|
80
|
-
declare
|
|
126
|
+
declare const Typecaast: react.ForwardRefExoticComponent<TypecaastProps & react.RefAttributes<TypecaastHandle>>;
|
|
81
127
|
|
|
82
128
|
interface UseTypecaastOptions {
|
|
83
129
|
/** Force a theme; otherwise resolved from `config.meta.theme`. */
|
|
@@ -200,4 +246,4 @@ declare const builtinSkinIds: string[];
|
|
|
200
246
|
*/
|
|
201
247
|
declare function loadBuiltinSkin(id: string): Promise<Skin>;
|
|
202
248
|
|
|
203
|
-
export { BUILTIN_SKIN_LOADERS, FitBox, type FitBoxProps, type FontLoadState, type RawConfig, type TranscriptLine, Typecaast, type TypecaastConfig, type TypecaastControls, type TypecaastProps, type UseTypecaastOptions, buildTranscript, builtinSkinIds, loadBuiltinSkin, resolveTheme, usePrefersDark, useReducedMotion, useResolvedTheme, useSkinFonts, useTypecaast };
|
|
249
|
+
export { BUILTIN_SKIN_LOADERS, FitBox, type FitBoxProps, type FontLoadState, type RawConfig, type TranscriptLine, Typecaast, type TypecaastConfig, type TypecaastControls, type TypecaastHandle, type TypecaastProps, type UseTypecaastOptions, buildTranscript, builtinSkinIds, loadBuiltinSkin, resolveTheme, usePrefersDark, useReducedMotion, useResolvedTheme, useSkinFonts, useTypecaast };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties, ReactNode, ReactElement } from 'react';
|
|
2
3
|
import { ConfigInput, Config, ThemeMode, FitMode, Size } from '@typecaast/schema';
|
|
3
4
|
import { Skin, ComposerMode } from '@typecaast/skin-kit';
|
|
4
5
|
export { ComposerMode, TypecaastStage, TypecaastStageProps } from '@typecaast/skin-kit';
|
|
@@ -69,6 +70,51 @@ interface TypecaastProps {
|
|
|
69
70
|
label?: string;
|
|
70
71
|
className?: string;
|
|
71
72
|
style?: CSSProperties;
|
|
73
|
+
/**
|
|
74
|
+
* Controlled pause. When provided it takes over playback: `true` pauses in
|
|
75
|
+
* place, `false` plays/resumes **from the current position** (never restarts).
|
|
76
|
+
* Omit it to keep the default `autoplay` behavior (uncontrolled). Toggling it
|
|
77
|
+
* (e.g. `paused={activeTab !== "slack"}`) pauses/resumes without unmounting.
|
|
78
|
+
* No-op under `prefers-reduced-motion` (which holds the completed thread).
|
|
79
|
+
*/
|
|
80
|
+
paused?: boolean;
|
|
81
|
+
/** Called when playback starts or resumes. */
|
|
82
|
+
onPlay?: () => void;
|
|
83
|
+
/** Called when playback pauses. */
|
|
84
|
+
onPause?: () => void;
|
|
85
|
+
/** Called once when playback reaches the end (non-looping). */
|
|
86
|
+
onEnded?: () => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Imperative playback controls, exposed via a `ref` on `<Typecaast>` for cases
|
|
90
|
+
* the declarative props don't cover (jump to a time, change rate, step):
|
|
91
|
+
*
|
|
92
|
+
* ```tsx
|
|
93
|
+
* const ref = useRef<TypecaastHandle>(null);
|
|
94
|
+
* <Typecaast ref={ref} config={config} autoplay />;
|
|
95
|
+
* // …later: ref.current?.seek(5000);
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* `ref.current` is `null` until the skin has loaded and the player mounts.
|
|
99
|
+
*/
|
|
100
|
+
interface TypecaastHandle {
|
|
101
|
+
play(): void;
|
|
102
|
+
pause(): void;
|
|
103
|
+
/** Jump to an absolute time in ms (clamped to `[0, duration]`). */
|
|
104
|
+
seek(timeMs: number): void;
|
|
105
|
+
/** Like `seek`, used for scrubbing. */
|
|
106
|
+
scrubTo(timeMs: number): void;
|
|
107
|
+
/** Playback rate multiplier (1 = realtime). */
|
|
108
|
+
setRate(rate: number): void;
|
|
109
|
+
/** Jump to the next / previous step boundary. */
|
|
110
|
+
stepNext(): void;
|
|
111
|
+
stepPrev(): void;
|
|
112
|
+
/** Live playback time in ms. */
|
|
113
|
+
readonly currentMs: number;
|
|
114
|
+
/** Total duration in ms. */
|
|
115
|
+
readonly duration: number;
|
|
116
|
+
/** Whether the clock is currently running. */
|
|
117
|
+
readonly playing: boolean;
|
|
72
118
|
}
|
|
73
119
|
/**
|
|
74
120
|
* Renders a `<Typecaast>` from a config. The skin defaults to the built-in named
|
|
@@ -77,7 +123,7 @@ interface TypecaastProps {
|
|
|
77
123
|
* since the default path takes only the serializable `config`, the embed drops
|
|
78
124
|
* straight into a React Server Component.
|
|
79
125
|
*/
|
|
80
|
-
declare
|
|
126
|
+
declare const Typecaast: react.ForwardRefExoticComponent<TypecaastProps & react.RefAttributes<TypecaastHandle>>;
|
|
81
127
|
|
|
82
128
|
interface UseTypecaastOptions {
|
|
83
129
|
/** Force a theme; otherwise resolved from `config.meta.theme`. */
|
|
@@ -200,4 +246,4 @@ declare const builtinSkinIds: string[];
|
|
|
200
246
|
*/
|
|
201
247
|
declare function loadBuiltinSkin(id: string): Promise<Skin>;
|
|
202
248
|
|
|
203
|
-
export { BUILTIN_SKIN_LOADERS, FitBox, type FitBoxProps, type FontLoadState, type RawConfig, type TranscriptLine, Typecaast, type TypecaastConfig, type TypecaastControls, type TypecaastProps, type UseTypecaastOptions, buildTranscript, builtinSkinIds, loadBuiltinSkin, resolveTheme, usePrefersDark, useReducedMotion, useResolvedTheme, useSkinFonts, useTypecaast };
|
|
249
|
+
export { BUILTIN_SKIN_LOADERS, FitBox, type FitBoxProps, type FontLoadState, type RawConfig, type TranscriptLine, Typecaast, type TypecaastConfig, type TypecaastControls, type TypecaastHandle, type TypecaastProps, type UseTypecaastOptions, buildTranscript, builtinSkinIds, loadBuiltinSkin, resolveTheme, usePrefersDark, useReducedMotion, useResolvedTheme, useSkinFonts, useTypecaast };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { forwardRef, useMemo, Suspense, useEffect, useImperativeHandle, useSyncExternalStore, useState, useRef, useLayoutEffect } from 'react';
|
|
3
3
|
import { configSchema } from '@typecaast/schema';
|
|
4
|
-
import {
|
|
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
7
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -77,12 +77,14 @@ function useTypecaast(config, options = {}) {
|
|
|
77
77
|
player.on("play", () => setPlaying(true)),
|
|
78
78
|
player.on("pause", () => setPlaying(false))
|
|
79
79
|
];
|
|
80
|
-
if (autoplay) player.play();
|
|
81
80
|
return () => {
|
|
82
81
|
offs.forEach((off) => off());
|
|
83
82
|
player.destroy();
|
|
84
83
|
};
|
|
85
|
-
}, [player
|
|
84
|
+
}, [player]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (autoplay) player.play();
|
|
87
|
+
}, [player]);
|
|
86
88
|
return {
|
|
87
89
|
state,
|
|
88
90
|
currentMs,
|
|
@@ -304,35 +306,37 @@ function rootStyle(canvas) {
|
|
|
304
306
|
aspectRatio: `${canvas.width} / ${canvas.height}`
|
|
305
307
|
};
|
|
306
308
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
309
|
+
var Typecaast = forwardRef(
|
|
310
|
+
function Typecaast2(props, ref) {
|
|
311
|
+
const config = useMemo(
|
|
312
|
+
() => configSchema.parse(props.config),
|
|
313
|
+
[props.config]
|
|
314
|
+
);
|
|
315
|
+
if (props.skin)
|
|
316
|
+
return /* @__PURE__ */ jsx(Player, { ...props, config, skin: props.skin, ref });
|
|
317
|
+
return /* @__PURE__ */ jsx(
|
|
318
|
+
Suspense,
|
|
319
|
+
{
|
|
320
|
+
fallback: /* @__PURE__ */ jsx(
|
|
321
|
+
SkinFallback,
|
|
322
|
+
{
|
|
323
|
+
config,
|
|
324
|
+
fit: props.fit,
|
|
325
|
+
label: props.label,
|
|
326
|
+
className: props.className,
|
|
327
|
+
style: props.style
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
children: /* @__PURE__ */ jsx(ResolvedPlayer, { ...props, config, ref })
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
var ResolvedPlayer = forwardRef(function ResolvedPlayer2(props, ref) {
|
|
332
336
|
const skin = readBuiltinSkin(props.config.meta.skin.id);
|
|
333
|
-
return /* @__PURE__ */ jsx(Player, { ...props, skin });
|
|
334
|
-
}
|
|
335
|
-
|
|
337
|
+
return /* @__PURE__ */ jsx(Player, { ...props, skin, ref });
|
|
338
|
+
});
|
|
339
|
+
var Player = forwardRef(function Player2({
|
|
336
340
|
config,
|
|
337
341
|
skin,
|
|
338
342
|
theme,
|
|
@@ -343,20 +347,61 @@ function Player({
|
|
|
343
347
|
composer,
|
|
344
348
|
label,
|
|
345
349
|
className,
|
|
346
|
-
style
|
|
347
|
-
|
|
350
|
+
style,
|
|
351
|
+
paused,
|
|
352
|
+
onPlay,
|
|
353
|
+
onPause,
|
|
354
|
+
onEnded
|
|
355
|
+
}, ref) {
|
|
348
356
|
const reduced = useReducedMotion();
|
|
349
357
|
const tc = useTypecaast(config, {
|
|
350
358
|
theme,
|
|
351
|
-
|
|
359
|
+
// Gate mount-autoplay with `!paused` so a `paused`-at-mount instance never
|
|
360
|
+
// flashes a frame of playback. `!paused` is `true` when uncontrolled.
|
|
361
|
+
autoplay: autoplay && !reduced && !paused,
|
|
352
362
|
loop: loop && !reduced,
|
|
353
363
|
rate,
|
|
354
364
|
capabilities: skin.meta.capabilities
|
|
355
365
|
});
|
|
366
|
+
const player = tc.player;
|
|
356
367
|
const fonts = useSkinFonts(skin);
|
|
357
368
|
useEffect(() => {
|
|
358
369
|
if (reduced) tc.seek(tc.duration);
|
|
359
370
|
}, [reduced, tc]);
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
if (paused === void 0 || reduced) return;
|
|
373
|
+
if (paused) tc.pause();
|
|
374
|
+
else tc.play();
|
|
375
|
+
}, [paused, reduced, tc]);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
const offs = [];
|
|
378
|
+
if (onPlay) offs.push(player.on("play", onPlay));
|
|
379
|
+
if (onPause) offs.push(player.on("pause", onPause));
|
|
380
|
+
if (onEnded) offs.push(player.on("end", onEnded));
|
|
381
|
+
return () => offs.forEach((off) => off());
|
|
382
|
+
}, [player, onPlay, onPause, onEnded]);
|
|
383
|
+
useImperativeHandle(
|
|
384
|
+
ref,
|
|
385
|
+
() => ({
|
|
386
|
+
play: () => player.play(),
|
|
387
|
+
pause: () => player.pause(),
|
|
388
|
+
seek: (t) => player.seek(t),
|
|
389
|
+
scrubTo: (t) => player.scrubTo(t),
|
|
390
|
+
setRate: (r) => player.setRate(r),
|
|
391
|
+
stepNext: () => player.stepNext(),
|
|
392
|
+
stepPrev: () => player.stepPrev(),
|
|
393
|
+
get currentMs() {
|
|
394
|
+
return player.currentMs;
|
|
395
|
+
},
|
|
396
|
+
get duration() {
|
|
397
|
+
return player.durationMs;
|
|
398
|
+
},
|
|
399
|
+
get playing() {
|
|
400
|
+
return player.playing;
|
|
401
|
+
}
|
|
402
|
+
}),
|
|
403
|
+
[player]
|
|
404
|
+
);
|
|
360
405
|
const transcript = useMemo(() => buildTranscript(config), [config]);
|
|
361
406
|
return /* @__PURE__ */ jsxs(
|
|
362
407
|
"div",
|
|
@@ -386,7 +431,7 @@ function Player({
|
|
|
386
431
|
]
|
|
387
432
|
}
|
|
388
433
|
);
|
|
389
|
-
}
|
|
434
|
+
});
|
|
390
435
|
function SkinFallback({
|
|
391
436
|
config,
|
|
392
437
|
fit,
|
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","useMemo","jsx"],"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 if (autoplay) player.play();\n return () => {\n offs.forEach((off) => off());\n player.destroy();\n };\n }, [player, autoplay]);\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 Suspense,\n useEffect,\n useMemo,\n type CSSProperties,\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\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 function Typecaast(props: TypecaastProps): 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} />;\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} />\n </Suspense>\n );\n}\n\nfunction ResolvedPlayer(\n props: Omit<TypecaastProps, \"config\"> & { config: Config },\n): ReactElement {\n const skin = readBuiltinSkin(props.config.meta.skin.id);\n return <Player {...props} skin={skin} />;\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 */\nfunction Player({\n config,\n skin,\n theme,\n autoplay,\n loop,\n rate,\n fit,\n composer,\n label,\n className,\n style,\n}: Omit<TypecaastProps, \"config\"> & {\n config: Config;\n skin: Skin;\n}): ReactElement {\n const reduced = useReducedMotion();\n const tc = useTypecaast(config, {\n theme,\n autoplay: autoplay && !reduced,\n loop: loop && !reduced,\n rate,\n capabilities: skin.meta.capabilities,\n });\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 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/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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecaast/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React renderer: <Typecaast> real-time player + useTypecaast hook.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@typecaast/core": "0.
|
|
27
|
+
"@typecaast/core": "0.4.0",
|
|
28
28
|
"@typecaast/schema": "0.2.1",
|
|
29
|
-
"@typecaast/skin-kit": "0.3.
|
|
30
|
-
"@typecaast/skins": "0.
|
|
29
|
+
"@typecaast/skin-kit": "0.3.2",
|
|
30
|
+
"@typecaast/skins": "0.3.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=18"
|