@typecaast/skin-kit 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -65,6 +65,7 @@ function fadeSlideIn(progress, options = {}) {
65
65
  const eased = (options.easing ?? easeOutCubic)(clamp01(progress));
66
66
  const distance = options.distance ?? 8;
67
67
  const offset = (1 - eased) * distance;
68
+ if (offset === 0) return { opacity: eased, transform: "none" };
68
69
  const translate = options.axis === "x" ? `translateX(${offset}px)` : `translateY(${offset}px)`;
69
70
  return { opacity: eased, transform: translate };
70
71
  }
@@ -116,6 +117,39 @@ function renderInline(span, key, cn, st) {
116
117
  return span.value;
117
118
  case "code":
118
119
  return /* @__PURE__ */ jsxRuntime.jsx("code", { "data-tc-mark": "code", className: cn.code, style: st.code, children: span.value }, key);
120
+ case "bold":
121
+ return /* @__PURE__ */ jsxRuntime.jsx(
122
+ "strong",
123
+ {
124
+ "data-tc-mark": "bold",
125
+ className: cn.bold,
126
+ style: st.bold,
127
+ children: span.value
128
+ },
129
+ key
130
+ );
131
+ case "italic":
132
+ return /* @__PURE__ */ jsxRuntime.jsx(
133
+ "em",
134
+ {
135
+ "data-tc-mark": "italic",
136
+ className: cn.italic,
137
+ style: st.italic,
138
+ children: span.value
139
+ },
140
+ key
141
+ );
142
+ case "strike":
143
+ return /* @__PURE__ */ jsxRuntime.jsx(
144
+ "s",
145
+ {
146
+ "data-tc-mark": "strike",
147
+ className: cn.strike,
148
+ style: st.strike,
149
+ children: span.value
150
+ },
151
+ key
152
+ );
119
153
  case "link":
120
154
  return /* @__PURE__ */ jsxRuntime.jsx(
121
155
  "a",
@@ -223,7 +257,7 @@ function TypecaastStage({
223
257
  [participants]
224
258
  );
225
259
  const composerAuthor = state.composer.from ? byId.get(state.composer.from) : composer === "always" ? selfParticipant : void 0;
226
- const showComposer = composer !== "never" && composerAuthor !== void 0;
260
+ const showComposer = composer !== "never" && (composer === "always" || state.composer.from !== void 0);
227
261
  const typingPlacement = skin.meta.typingPlacement ?? "thread";
228
262
  const typingNodes = state.typingIndicators.map((typing, i) => {
229
263
  const author = byId.get(typing.from);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define-skin.ts","../src/theme.tsx","../src/fonts.ts","../src/animation.tsx","../src/content.tsx","../src/stage.tsx","../src/slot-skin.tsx"],"names":["createContext","jsx","useContext","Fragment","useMemo","jsxs","useRef","useState","useLayoutEffect","createPortal"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Skin } from \"./types.js\";\n\n/**\n * Identity helper for type-safety and a single registration point when\n * authoring a skin. Keeping it a function (rather than a bare object) lets us\n * add validation/registration later without changing skin call sites.\n */\nexport function defineSkin(skin: Skin): Skin {\n return skin;\n}\n","import {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\nimport type { SkinTokens } from \"./types.js\";\n\ninterface ThemeContextValue {\n theme: ResolvedTheme;\n tokens?: SkinTokens;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: \"light\" });\n\nexport interface ThemeProviderProps {\n theme: ResolvedTheme;\n /** Resolved per-theme tokens for the active skin. */\n tokens?: SkinTokens;\n children?: ReactNode;\n}\n\n/** Provides the resolved theme + tokens to every skin component below it. */\nexport function ThemeProvider({\n theme,\n tokens,\n children,\n}: ThemeProviderProps): ReactElement {\n return (\n <ThemeContext.Provider value={{ theme, tokens }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/** The resolved theme (`\"light\" | \"dark\"`) for the current subtree. */\nexport function useTheme(): ResolvedTheme {\n return useContext(ThemeContext).theme;\n}\n\n/** The resolved design tokens for the current subtree, if provided. */\nexport function useTokens(): SkinTokens | undefined {\n return useContext(ThemeContext).tokens;\n}\n","import type { FontDeclaration } from \"./types.js\";\n\n/** Build a CSS `src:` value from a font's sources. */\nfunction srcValue(decl: FontDeclaration): string {\n return decl.sources\n .map((s) => {\n const format = s.format ? ` format(\"${s.format}\")` : \"\";\n return `url(\"${s.url}\")${format}`;\n })\n .join(\", \");\n}\n\n/**\n * Load a skin's declared web fonts so live preview matches the platform (not\n * just video export). SSR-safe: a no-op when `document`/`FontFace` are absent\n * (server render, Remotion Node). Idempotent per family+weight+style.\n *\n * Returns once every face has loaded (or immediately, off the main document).\n */\nexport async function loadSkinFonts(\n fonts: FontDeclaration[] | undefined,\n): Promise<void> {\n if (!fonts || fonts.length === 0) return;\n if (\n typeof document === \"undefined\" ||\n typeof FontFace === \"undefined\" ||\n !document.fonts\n ) {\n return;\n }\n\n const pending: Promise<unknown>[] = [];\n for (const decl of fonts) {\n for (const source of decl.sources) {\n const descriptors: FontFaceDescriptors = {};\n if (source.weight !== undefined)\n descriptors.weight = String(source.weight);\n if (source.style !== undefined) descriptors.style = source.style;\n\n const single: FontDeclaration = { ...decl, sources: [source] };\n const face = new FontFace(decl.family, srcValue(single), descriptors);\n\n // Skip if an identical face is already registered.\n const already = [...document.fonts].some(\n (f) =>\n f.family === decl.family &&\n f.weight === (descriptors.weight ?? \"normal\") &&\n f.style === (descriptors.style ?? \"normal\"),\n );\n if (already) continue;\n\n document.fonts.add(face);\n pending.push(face.load());\n }\n }\n await Promise.all(pending);\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactElement {\n const phase = clamp01(progress) * cycles * Math.PI * 2;\n const dots: ReactNode[] = [];\n for (let i = 0; i < count; i++) {\n const wave = Math.sin(phase - i * 0.9);\n const lift = Math.max(0, wave) * 4;\n const opacity = 0.4 + Math.max(0, wave) * 0.6;\n dots.push(\n <span\n key={i}\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n background: color,\n transform: `translateY(${-lift}px)`,\n opacity,\n }}\n />,\n );\n }\n return (\n <span style={{ display: \"inline-flex\", alignItems: \"flex-end\", gap }}>\n {dots}\n </span>\n );\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactElement {\n return (\n <>\n {nodes.map((node, i) => {\n if (node.type === \"text\") {\n const text = node as TextNode;\n return (\n <span\n key={i}\n data-tc-node=\"text\"\n className={classNames.text}\n style={styles.text}\n >\n {text.spans.map((span, j) =>\n renderInline(span, j, classNames, styles),\n )}\n </span>\n );\n }\n if (node.type === \"image\") {\n return renderImage(node as ImageNode, i, classNames, imageStyle);\n }\n return null; // unknown future node type — skipped\n })}\n </>\n );\n}\n","import { useMemo, type ReactElement } from \"react\";\nimport type { ComposerMode, Participant } from \"@typecaast/schema\";\nimport type { SimState } from \"@typecaast/core\";\nimport { ThemeProvider } from \"./theme.js\";\nimport type { Skin } from \"./types.js\";\n\nexport type { ComposerMode };\n\n// A subtle, native-feeling scrollbar for the scrollable thread, scoped to the\n// thread viewport and injected inline so the embed needs no external stylesheet.\n// A neutral translucent grey reads on both light and dark skins and deepens on\n// hover; WebKit/Blink get the thin overlay-style thumb, Firefox uses the\n// standard `scrollbar-*` props. The selector is global on purpose — identical\n// rules across multiple embeds on a page are harmless and keep them consistent.\nconst THREAD_SCROLLBAR_CSS = `\n[data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}\n[data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}\n[data-typecaast-thread]::-webkit-scrollbar-track{background:transparent}\n[data-typecaast-thread]::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.4);border-radius:8px;border:2px solid transparent;background-clip:padding-box}\n[data-typecaast-thread]:hover::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.6)}\n`;\n\nexport interface TypecaastStageProps {\n state: SimState;\n skin: Skin;\n participants: Participant[];\n /** Skin-specific options from `meta.skin.options`. */\n options?: Record<string, unknown>;\n /**\n * Composer visibility: `auto` (default) shows it only while someone is\n * typing/sending; `always` keeps the reply box visible (idle = placeholder);\n * `never` hides it.\n */\n composer?: ComposerMode;\n}\n\n/**\n * Maps a `SimState` onto a skin's components: a `Frame` wrapping the thread\n * items (Message / SystemMessage), the typing indicators, and the composer.\n * Reactions render inside the skin's `Message` (it reads `message.reactions`).\n *\n * Lives in `skin-kit` (the contract layer) so both `@typecaast/react` and the\n * skins' own stories can render a frame without depending on the React player.\n */\nexport function TypecaastStage({\n state,\n skin,\n participants,\n options,\n composer = \"auto\",\n}: TypecaastStageProps): ReactElement {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n // \"X is typing…\" indicators, minus the viewer's own (you never see yourself\n // typing — that's what the composer shows). Placement is skin-driven: inline\n // in the thread by default, or below the composer (Slack).\n const typingPlacement = skin.meta.typingPlacement ?? \"thread\";\n const typingNodes = state.typingIndicators\n .map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author || author.isSelf) return null;\n return (\n <TypingIndicator\n key={`typing-${typing.from}-${i}`}\n theme={theme}\n typing={typing}\n author={author}\n />\n );\n })\n .filter(Boolean);\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <style>{THREAD_SCROLLBAR_CSS}</style>\n <Frame theme={theme} options={options} composer={composer}>\n {/* Thread viewport. `column-reverse` pins the conversation to the\n bottom (newest message + composer sit at the bottom, older items\n \"shift up\", PLAN §7) and, once the thread outgrows the height, makes\n it scroll from the bottom with the top reachable — entirely in CSS,\n so it renders identically in a live embed, an SSR page, and a video\n frame (no scroll-to-bottom effect, which wouldn't run before a\n Remotion screenshot). Because `column-reverse` lays the first child\n out at the bottom, children render newest-first: the typing\n indicator (most recent activity) first, then messages reversed. The\n engine's scroll.targetOffset is honored here once real layout\n measurement lands. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column-reverse\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflowY: \"auto\",\n // Scrollbar styling lives in THREAD_SCROLLBAR_CSS (the `<style>`\n // above) so it can reach the WebKit pseudo-elements.\n // Breathing room beneath the last message — keeps it off the\n // composer (when shown) and the Frame's bottom edge (when hidden).\n paddingBottom: 16,\n }}\n >\n {typingPlacement === \"thread\" ? typingNodes : null}\n {state.messages\n .map((message, i) => {\n const author = byId.get(message.from);\n if (!author) return null;\n // Index-disambiguated so a config with duplicate message ids can't\n // collide React keys (the builder can produce them transiently).\n const key = `${message.id}-${i}`;\n if (message.variant === \"system\") {\n return (\n <SystemMessage\n key={key}\n theme={theme}\n message={message}\n author={author}\n />\n );\n }\n // Grouping looks at the chronological predecessor — computed\n // before the reverse below, so author-collapsing is unaffected.\n const prev = state.messages[i - 1];\n const previousAuthor = prev ? byId.get(prev.from) : undefined;\n return (\n <Message\n key={key}\n theme={theme}\n message={message}\n author={author}\n previousAuthor={previousAuthor}\n />\n );\n })\n .reverse()}\n </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n options={options}\n />\n ) : null}\n {typingPlacement === \"below-composer\" ? typingNodes : null}\n </Frame>\n </ThemeProvider>\n );\n}\n","/**\n * `slotSkinFromDraft` — build a `Skin` from a slotted HTML draft (the shape\n * the capture pipeline emits). The frame/message/composer HTML strings are\n * injected into a shadow root with the draft's CSS, and `{{slot}}` markers\n * are substituted with per-message text at render time.\n *\n * Lives in `@typecaast/skin-kit` so both `@typecaast/capture` (the runtime\n * untrusted-template path) and `@typecaast/skins` (built-in captured skins\n * like PostHog) can share one implementation without forming a build cycle\n * with `@typecaast/react`. The capture-runtime caller layers `sanitizeHtml`\n * around it; built-ins skip sanitize because their draft.json is\n * version-controlled.\n *\n * Responsive behaviour: the captured DOM is typically taken on a desktop\n * viewport (1000-1500px wide) but the skin renders into a small canvas\n * (often 480×640). We normalise that by:\n * - wrapping the frame slot in a container that is `width:100%; height:100%`\n * with `overflow:hidden`, so the captured outer chrome can't extend\n * past the canvas;\n * - resetting the captured root element's `margin`/`max-width` so a\n * `margin: 0px 234px` baked in at desktop width becomes `margin: 0 auto`;\n * - exposing the captured viewport width as `--captured-viewport-width`\n * for authored CSS to ratio-scale against if it wants.\n */\nimport {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentNode, Participant } from \"@typecaast/schema\";\nimport type {\n Capabilities,\n ComposerProps,\n FrameProps,\n MessageProps,\n SystemProps,\n TypingProps,\n} from \"@typecaast/core\";\nimport type { Skin, SkinComponents, SkinTokens } from \"./types.js\";\n\nconst SLOT_ATTR = \"data-tc-slot\";\n\ninterface TokenSet {\n colors?: Record<string, string>;\n}\n\nexport interface SlotSkinDraft {\n meta: {\n name: string;\n theme?: \"light\" | \"dark\";\n canvas?: { width: number; height: number };\n capturedAt?: { viewportWidth?: number; pixelRatio?: number };\n };\n slots: {\n frame?: string;\n message?: string;\n composer?: string;\n typing?: string;\n system?: string;\n };\n css?: string;\n tokens: TokenSet;\n darkTokens?: TokenSet;\n}\n\nexport interface SlotSkinOptions {\n id: string;\n capabilities: Capabilities;\n /**\n * When true, every slot mount in the shadow root gets a faint dashed\n * outline + a corner badge naming the slot. Used by the `/create-skin`\n * editor to show authors where their `{{body}}` lands.\n */\n slotMarkers?: boolean;\n}\n\nfunction contentToText(content: ContentNode[]): string {\n const out: string[] = [];\n for (const node of content) {\n if (\n node.type === \"text\" &&\n Array.isArray((node as { spans?: unknown }).spans)\n ) {\n for (const span of (\n node as { spans: { value?: string; label?: string }[] }\n ).spans) {\n out.push(span.value ?? span.label ?? \"\");\n }\n } else if (node.type === \"image\") {\n out.push((node as { alt?: string }).alt ?? \"🖼\");\n }\n }\n return out.join(\"\");\n}\n\nfunction initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n}\n\nfunction fmtTime(atMs: number): string {\n const total = Math.floor(atMs / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/**\n * Build the `<style>` text for the shadow root. Order matters:\n * 1. `:host` declarations (tokens as CSS vars, captured viewport width).\n * 2. A reset that normalises the captured root element back to fluid\n * layout — desktop-only `margin: 0 200px`/`max-width: 1200px` would\n * otherwise squeeze content in a small canvas.\n * 3. Author / captured CSS, which can override anything above.\n * 4. Slot-marker overlay if requested.\n */\nfunction styleText(\n tokens: TokenSet,\n css: string,\n capturedAt: SlotSkinDraft[\"meta\"][\"capturedAt\"],\n slotMarkers: boolean,\n): string {\n const vars = Object.entries(tokens.colors ?? {})\n .map(([k, v]) => `--${k}: ${v};`)\n .join(\" \");\n const cv = capturedAt?.viewportWidth\n ? `--captured-viewport-width: ${capturedAt.viewportWidth}px;`\n : \"\";\n const reset = `\n :host { all: initial; display: block; width: 100%; height: 100%; ${vars} ${cv} }\n * { box-sizing: border-box; }\n /* Normalise the captured root: drop desktop-only margins / max-widths\n so the layout re-centers cleanly in any canvas. */\n .tc-slot-root > :first-child {\n margin-left: auto !important;\n margin-right: auto !important;\n max-width: 100% !important;\n }`;\n const markers = slotMarkers\n ? `\n [data-tc-slot] {\n outline: 1px dashed rgba(99, 102, 241, 0.6);\n outline-offset: -1px;\n position: relative;\n }\n [data-tc-slot]::before {\n content: attr(data-tc-slot);\n position: absolute;\n top: 0;\n left: 0;\n transform: translateY(-100%);\n font: 600 9px/1 -apple-system, system-ui, sans-serif;\n padding: 1px 4px;\n background: rgba(99, 102, 241, 0.95);\n color: white;\n border-radius: 2px;\n pointer-events: none;\n z-index: 10;\n }`\n : \"\";\n return `${reset}\\n${css}\\n${markers}`;\n}\n\nfunction fillInto(\n host: HTMLElement,\n templateHtml: string,\n values: Record<string, string>,\n): void {\n host.innerHTML = templateHtml;\n for (const node of host.querySelectorAll(`[${SLOT_ATTR}]`)) {\n const slot = node.getAttribute(SLOT_ATTR);\n if (slot && slot in values) node.textContent = values[slot] ?? \"\";\n }\n}\n\nfunction revealStyle(progress: number): CSSProperties {\n const p = Math.max(0, Math.min(1, progress));\n return {\n opacity: p,\n transform: `translateY(${(1 - p) * 6}px)`,\n willChange: \"opacity, transform\",\n };\n}\n\nexport function slotSkinFromDraft(\n draft: SlotSkinDraft,\n opts: SlotSkinOptions,\n): Skin {\n const lightTokens: SkinTokens = { colors: draft.tokens.colors ?? {} };\n const darkTokens: SkinTokens = draft.darkTokens\n ? { colors: draft.darkTokens.colors ?? {} }\n : lightTokens;\n const slotMarkers = opts.slotMarkers ?? false;\n const cssByTheme = {\n light: styleText(\n lightTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n dark: styleText(\n darkTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n };\n const supportsThemes: (\"light\" | \"dark\")[] = draft.darkTokens\n ? [\"light\", \"dark\"]\n : [draft.meta.theme ?? \"light\"];\n\n const frameHtml = draft.slots.frame;\n const messageHtml = draft.slots.message;\n const composerHtml = draft.slots.composer;\n const systemHtml = draft.slots.system;\n const typingHtml = draft.slots.typing;\n\n const Frame: FC<FrameProps & { children?: ReactNode }> = ({\n theme,\n children,\n }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const [mount, setMount] = useState<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = \"\";\n const style = host.ownerDocument.createElement(\"style\");\n style.textContent = theme === \"dark\" ? cssByTheme.dark : cssByTheme.light;\n shadow.appendChild(style);\n // Wrap the captured frame in a fluid 100%×100% container so any\n // desktop-fixed widths/margins on the captured root get neutralised\n // by the `.tc-slot-root > :first-child` reset.\n const wrapper = host.ownerDocument.createElement(\"div\");\n wrapper.className = \"tc-slot-root\";\n wrapper.style.width = \"100%\";\n wrapper.style.height = \"100%\";\n wrapper.style.display = \"flex\";\n wrapper.style.flexDirection = \"column\";\n wrapper.style.overflow = \"hidden\";\n wrapper.innerHTML = frameHtml ?? `<div ${SLOT_ATTR}=\"messages\"></div>`;\n shadow.appendChild(wrapper);\n const slot =\n wrapper.querySelector<HTMLElement>(`[${SLOT_ATTR}=\"messages\"]`) ??\n wrapper;\n slot.textContent = \"\";\n setMount(slot);\n }, [theme]);\n return (\n <div ref={hostRef} style={{ width: \"100%\", height: \"100%\" }}>\n {mount ? createPortal(children, mount) : null}\n </div>\n );\n };\n\n const Message: FC<MessageProps> = ({ message, author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !messageHtml) return;\n fillInto(el, messageHtml, {\n author: author.name,\n avatar: initials(author.name),\n body: contentToText(message.content),\n time: fmtTime(message.atMs),\n });\n }, [message, author]);\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n };\n\n const SystemMessage: FC<SystemProps> = ({ message }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !systemHtml) return;\n fillInto(el, systemHtml, { body: contentToText(message.content) });\n }, [message]);\n if (systemHtml) {\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n }\n return (\n <div\n style={{\n ...revealStyle(message.revealProgress),\n textAlign: \"center\",\n }}\n >\n {contentToText(message.content)}\n </div>\n );\n };\n\n const TypingIndicator: FC<TypingProps> = ({ author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !typingHtml) return;\n fillInto(el, typingHtml, { author: author.name });\n }, [author]);\n if (typingHtml) {\n return <div ref={ref} />;\n }\n return (\n <div style={{ opacity: 0.7, fontStyle: \"italic\" }}>\n {author.name} is typing…\n </div>\n );\n };\n\n const Composer: FC<ComposerProps> = ({ composer }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (composerHtml) {\n fillInto(el, composerHtml, { composer: composer.text });\n } else {\n el.textContent = composer.text;\n }\n }, [composer]);\n return <div ref={ref} />;\n };\n\n const Avatar: FC<{ participant: Participant; size?: number }> = ({\n participant,\n size = 36,\n }) => (\n <div\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n display: \"grid\",\n placeItems: \"center\",\n background: \"var(--color-1, #ccc)\",\n fontSize: size * 0.4,\n }}\n >\n {initials(participant.name)}\n </div>\n );\n\n const components: SkinComponents = {\n Frame,\n Message,\n SystemMessage,\n TypingIndicator,\n Reaction: () => null,\n Composer,\n Avatar,\n };\n\n return {\n id: opts.id,\n meta: {\n name: draft.meta.name,\n defaultCanvas: draft.meta.canvas ?? { width: 420, height: 720 },\n supportsThemes,\n capabilities: opts.capabilities,\n },\n components,\n tokens: { light: lightTokens, dark: darkTokens },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/define-skin.ts","../src/theme.tsx","../src/fonts.ts","../src/animation.tsx","../src/content.tsx","../src/stage.tsx","../src/slot-skin.tsx"],"names":["createContext","jsx","useContext","Fragment","useMemo","jsxs","useRef","useState","useLayoutEffect","createPortal"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Skin } from \"./types.js\";\n\n/**\n * Identity helper for type-safety and a single registration point when\n * authoring a skin. Keeping it a function (rather than a bare object) lets us\n * add validation/registration later without changing skin call sites.\n */\nexport function defineSkin(skin: Skin): Skin {\n return skin;\n}\n","import {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\nimport type { SkinTokens } from \"./types.js\";\n\ninterface ThemeContextValue {\n theme: ResolvedTheme;\n tokens?: SkinTokens;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: \"light\" });\n\nexport interface ThemeProviderProps {\n theme: ResolvedTheme;\n /** Resolved per-theme tokens for the active skin. */\n tokens?: SkinTokens;\n children?: ReactNode;\n}\n\n/** Provides the resolved theme + tokens to every skin component below it. */\nexport function ThemeProvider({\n theme,\n tokens,\n children,\n}: ThemeProviderProps): ReactElement {\n return (\n <ThemeContext.Provider value={{ theme, tokens }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/** The resolved theme (`\"light\" | \"dark\"`) for the current subtree. */\nexport function useTheme(): ResolvedTheme {\n return useContext(ThemeContext).theme;\n}\n\n/** The resolved design tokens for the current subtree, if provided. */\nexport function useTokens(): SkinTokens | undefined {\n return useContext(ThemeContext).tokens;\n}\n","import type { FontDeclaration } from \"./types.js\";\n\n/** Build a CSS `src:` value from a font's sources. */\nfunction srcValue(decl: FontDeclaration): string {\n return decl.sources\n .map((s) => {\n const format = s.format ? ` format(\"${s.format}\")` : \"\";\n return `url(\"${s.url}\")${format}`;\n })\n .join(\", \");\n}\n\n/**\n * Load a skin's declared web fonts so live preview matches the platform (not\n * just video export). SSR-safe: a no-op when `document`/`FontFace` are absent\n * (server render, Remotion Node). Idempotent per family+weight+style.\n *\n * Returns once every face has loaded (or immediately, off the main document).\n */\nexport async function loadSkinFonts(\n fonts: FontDeclaration[] | undefined,\n): Promise<void> {\n if (!fonts || fonts.length === 0) return;\n if (\n typeof document === \"undefined\" ||\n typeof FontFace === \"undefined\" ||\n !document.fonts\n ) {\n return;\n }\n\n const pending: Promise<unknown>[] = [];\n for (const decl of fonts) {\n for (const source of decl.sources) {\n const descriptors: FontFaceDescriptors = {};\n if (source.weight !== undefined)\n descriptors.weight = String(source.weight);\n if (source.style !== undefined) descriptors.style = source.style;\n\n const single: FontDeclaration = { ...decl, sources: [source] };\n const face = new FontFace(decl.family, srcValue(single), descriptors);\n\n // Skip if an identical face is already registered.\n const already = [...document.fonts].some(\n (f) =>\n f.family === decl.family &&\n f.weight === (descriptors.weight ?? \"normal\") &&\n f.style === (descriptors.style ?? \"normal\"),\n );\n if (already) continue;\n\n document.fonts.add(face);\n pending.push(face.load());\n }\n }\n await Promise.all(pending);\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n // Once settled, drop the transform entirely. Any non-`none` transform — even\n // `translateY(0px)` — establishes a stacking context, which would trap a\n // descendant overlay (e.g. a reaction's hover tooltip) *below* later sibling\n // messages, so neighbouring text paints over the opaque tooltip and it reads\n // as transparent. `none` at rest lets overlays layer above adjacent content.\n if (offset === 0) return { opacity: eased, transform: \"none\" };\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactElement {\n const phase = clamp01(progress) * cycles * Math.PI * 2;\n const dots: ReactNode[] = [];\n for (let i = 0; i < count; i++) {\n const wave = Math.sin(phase - i * 0.9);\n const lift = Math.max(0, wave) * 4;\n const opacity = 0.4 + Math.max(0, wave) * 0.6;\n dots.push(\n <span\n key={i}\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n background: color,\n transform: `translateY(${-lift}px)`,\n opacity,\n }}\n />,\n );\n }\n return (\n <span style={{ display: \"inline-flex\", alignItems: \"flex-end\", gap }}>\n {dots}\n </span>\n );\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n bold?: string;\n italic?: string;\n strike?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n bold?: CSSProperties;\n italic?: CSSProperties;\n strike?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"bold\":\n return (\n <strong\n key={key}\n data-tc-mark=\"bold\"\n className={cn.bold}\n style={st.bold}\n >\n {span.value}\n </strong>\n );\n case \"italic\":\n return (\n <em\n key={key}\n data-tc-mark=\"italic\"\n className={cn.italic}\n style={st.italic}\n >\n {span.value}\n </em>\n );\n case \"strike\":\n return (\n <s\n key={key}\n data-tc-mark=\"strike\"\n className={cn.strike}\n style={st.strike}\n >\n {span.value}\n </s>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactElement {\n return (\n <>\n {nodes.map((node, i) => {\n if (node.type === \"text\") {\n const text = node as TextNode;\n return (\n <span\n key={i}\n data-tc-node=\"text\"\n className={classNames.text}\n style={styles.text}\n >\n {text.spans.map((span, j) =>\n renderInline(span, j, classNames, styles),\n )}\n </span>\n );\n }\n if (node.type === \"image\") {\n return renderImage(node as ImageNode, i, classNames, imageStyle);\n }\n return null; // unknown future node type — skipped\n })}\n </>\n );\n}\n","import { useMemo, type ReactElement } from \"react\";\nimport type { ComposerMode, Participant } from \"@typecaast/schema\";\nimport type { SimState } from \"@typecaast/core\";\nimport { ThemeProvider } from \"./theme.js\";\nimport type { Skin } from \"./types.js\";\n\nexport type { ComposerMode };\n\n// A subtle, native-feeling scrollbar for the scrollable thread, scoped to the\n// thread viewport and injected inline so the embed needs no external stylesheet.\n// A neutral translucent grey reads on both light and dark skins and deepens on\n// hover; WebKit/Blink get the thin overlay-style thumb, Firefox uses the\n// standard `scrollbar-*` props. The selector is global on purpose — identical\n// rules across multiple embeds on a page are harmless and keep them consistent.\nconst THREAD_SCROLLBAR_CSS = `\n[data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}\n[data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}\n[data-typecaast-thread]::-webkit-scrollbar-track{background:transparent}\n[data-typecaast-thread]::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.4);border-radius:8px;border:2px solid transparent;background-clip:padding-box}\n[data-typecaast-thread]:hover::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.6)}\n`;\n\nexport interface TypecaastStageProps {\n state: SimState;\n skin: Skin;\n participants: Participant[];\n /** Skin-specific options from `meta.skin.options`. */\n options?: Record<string, unknown>;\n /**\n * Composer visibility: `auto` (default) shows it only while someone is\n * typing/sending; `always` keeps the reply box visible (idle = placeholder);\n * `never` hides it.\n */\n composer?: ComposerMode;\n}\n\n/**\n * Maps a `SimState` onto a skin's components: a `Frame` wrapping the thread\n * items (Message / SystemMessage), the typing indicators, and the composer.\n * Reactions render inside the skin's `Message` (it reads `message.reactions`).\n *\n * Lives in `skin-kit` (the contract layer) so both `@typecaast/react` and the\n * skins' own stories can render a frame without depending on the React player.\n */\nexport function TypecaastStage({\n state,\n skin,\n participants,\n options,\n composer = \"auto\",\n}: TypecaastStageProps): ReactElement {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (the self participant,\n // if any, is just whose placeholder it is — `author` is optional, so the box\n // shows even with no self); `auto` only shows it while someone is composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer =\n composer !== \"never\" &&\n (composer === \"always\" || state.composer.from !== undefined);\n\n // \"X is typing…\" indicators, minus the viewer's own (you never see yourself\n // typing — that's what the composer shows). Placement is skin-driven: inline\n // in the thread by default, or below the composer (Slack).\n const typingPlacement = skin.meta.typingPlacement ?? \"thread\";\n const typingNodes = state.typingIndicators\n .map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author || author.isSelf) return null;\n return (\n <TypingIndicator\n key={`typing-${typing.from}-${i}`}\n theme={theme}\n typing={typing}\n author={author}\n />\n );\n })\n .filter(Boolean);\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <style>{THREAD_SCROLLBAR_CSS}</style>\n <Frame theme={theme} options={options} composer={composer}>\n {/* Thread viewport. `column-reverse` pins the conversation to the\n bottom (newest message + composer sit at the bottom, older items\n \"shift up\", PLAN §7) and, once the thread outgrows the height, makes\n it scroll from the bottom with the top reachable — entirely in CSS,\n so it renders identically in a live embed, an SSR page, and a video\n frame (no scroll-to-bottom effect, which wouldn't run before a\n Remotion screenshot). Because `column-reverse` lays the first child\n out at the bottom, children render newest-first: the typing\n indicator (most recent activity) first, then messages reversed. The\n engine's scroll.targetOffset is honored here once real layout\n measurement lands. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column-reverse\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflowY: \"auto\",\n // Scrollbar styling lives in THREAD_SCROLLBAR_CSS (the `<style>`\n // above) so it can reach the WebKit pseudo-elements.\n // Breathing room beneath the last message — keeps it off the\n // composer (when shown) and the Frame's bottom edge (when hidden).\n paddingBottom: 16,\n }}\n >\n {typingPlacement === \"thread\" ? typingNodes : null}\n {state.messages\n .map((message, i) => {\n const author = byId.get(message.from);\n if (!author) return null;\n // Index-disambiguated so a config with duplicate message ids can't\n // collide React keys (the builder can produce them transiently).\n const key = `${message.id}-${i}`;\n if (message.variant === \"system\") {\n return (\n <SystemMessage\n key={key}\n theme={theme}\n message={message}\n author={author}\n />\n );\n }\n // Grouping looks at the chronological predecessor — computed\n // before the reverse below, so author-collapsing is unaffected.\n const prev = state.messages[i - 1];\n const previousAuthor = prev ? byId.get(prev.from) : undefined;\n return (\n <Message\n key={key}\n theme={theme}\n message={message}\n author={author}\n previousAuthor={previousAuthor}\n />\n );\n })\n .reverse()}\n </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n options={options}\n />\n ) : null}\n {typingPlacement === \"below-composer\" ? typingNodes : null}\n </Frame>\n </ThemeProvider>\n );\n}\n","/**\n * `slotSkinFromDraft` — build a `Skin` from a slotted HTML draft (the shape\n * the capture pipeline emits). The frame/message/composer HTML strings are\n * injected into a shadow root with the draft's CSS, and `{{slot}}` markers\n * are substituted with per-message text at render time.\n *\n * Lives in `@typecaast/skin-kit` so both `@typecaast/capture` (the runtime\n * untrusted-template path) and `@typecaast/skins` (built-in captured skins\n * like PostHog) can share one implementation without forming a build cycle\n * with `@typecaast/react`. The capture-runtime caller layers `sanitizeHtml`\n * around it; built-ins skip sanitize because their draft.json is\n * version-controlled.\n *\n * Responsive behaviour: the captured DOM is typically taken on a desktop\n * viewport (1000-1500px wide) but the skin renders into a small canvas\n * (often 480×640). We normalise that by:\n * - wrapping the frame slot in a container that is `width:100%; height:100%`\n * with `overflow:hidden`, so the captured outer chrome can't extend\n * past the canvas;\n * - resetting the captured root element's `margin`/`max-width` so a\n * `margin: 0px 234px` baked in at desktop width becomes `margin: 0 auto`;\n * - exposing the captured viewport width as `--captured-viewport-width`\n * for authored CSS to ratio-scale against if it wants.\n */\nimport {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentNode, Participant } from \"@typecaast/schema\";\nimport type {\n Capabilities,\n ComposerProps,\n FrameProps,\n MessageProps,\n SystemProps,\n TypingProps,\n} from \"@typecaast/core\";\nimport type { Skin, SkinComponents, SkinTokens } from \"./types.js\";\n\nconst SLOT_ATTR = \"data-tc-slot\";\n\ninterface TokenSet {\n colors?: Record<string, string>;\n}\n\nexport interface SlotSkinDraft {\n meta: {\n name: string;\n theme?: \"light\" | \"dark\";\n canvas?: { width: number; height: number };\n capturedAt?: { viewportWidth?: number; pixelRatio?: number };\n };\n slots: {\n frame?: string;\n message?: string;\n composer?: string;\n typing?: string;\n system?: string;\n };\n css?: string;\n tokens: TokenSet;\n darkTokens?: TokenSet;\n}\n\nexport interface SlotSkinOptions {\n id: string;\n capabilities: Capabilities;\n /**\n * When true, every slot mount in the shadow root gets a faint dashed\n * outline + a corner badge naming the slot. Used by the `/create-skin`\n * editor to show authors where their `{{body}}` lands.\n */\n slotMarkers?: boolean;\n}\n\nfunction contentToText(content: ContentNode[]): string {\n const out: string[] = [];\n for (const node of content) {\n if (\n node.type === \"text\" &&\n Array.isArray((node as { spans?: unknown }).spans)\n ) {\n for (const span of (\n node as { spans: { value?: string; label?: string }[] }\n ).spans) {\n out.push(span.value ?? span.label ?? \"\");\n }\n } else if (node.type === \"image\") {\n out.push((node as { alt?: string }).alt ?? \"🖼\");\n }\n }\n return out.join(\"\");\n}\n\nfunction initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n}\n\nfunction fmtTime(atMs: number): string {\n const total = Math.floor(atMs / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/**\n * Build the `<style>` text for the shadow root. Order matters:\n * 1. `:host` declarations (tokens as CSS vars, captured viewport width).\n * 2. A reset that normalises the captured root element back to fluid\n * layout — desktop-only `margin: 0 200px`/`max-width: 1200px` would\n * otherwise squeeze content in a small canvas.\n * 3. Author / captured CSS, which can override anything above.\n * 4. Slot-marker overlay if requested.\n */\nfunction styleText(\n tokens: TokenSet,\n css: string,\n capturedAt: SlotSkinDraft[\"meta\"][\"capturedAt\"],\n slotMarkers: boolean,\n): string {\n const vars = Object.entries(tokens.colors ?? {})\n .map(([k, v]) => `--${k}: ${v};`)\n .join(\" \");\n const cv = capturedAt?.viewportWidth\n ? `--captured-viewport-width: ${capturedAt.viewportWidth}px;`\n : \"\";\n const reset = `\n :host { all: initial; display: block; width: 100%; height: 100%; ${vars} ${cv} }\n * { box-sizing: border-box; }\n /* Normalise the captured root: drop desktop-only margins / max-widths\n so the layout re-centers cleanly in any canvas. */\n .tc-slot-root > :first-child {\n margin-left: auto !important;\n margin-right: auto !important;\n max-width: 100% !important;\n }`;\n const markers = slotMarkers\n ? `\n [data-tc-slot] {\n outline: 1px dashed rgba(99, 102, 241, 0.6);\n outline-offset: -1px;\n position: relative;\n }\n [data-tc-slot]::before {\n content: attr(data-tc-slot);\n position: absolute;\n top: 0;\n left: 0;\n transform: translateY(-100%);\n font: 600 9px/1 -apple-system, system-ui, sans-serif;\n padding: 1px 4px;\n background: rgba(99, 102, 241, 0.95);\n color: white;\n border-radius: 2px;\n pointer-events: none;\n z-index: 10;\n }`\n : \"\";\n return `${reset}\\n${css}\\n${markers}`;\n}\n\nfunction fillInto(\n host: HTMLElement,\n templateHtml: string,\n values: Record<string, string>,\n): void {\n host.innerHTML = templateHtml;\n for (const node of host.querySelectorAll(`[${SLOT_ATTR}]`)) {\n const slot = node.getAttribute(SLOT_ATTR);\n if (slot && slot in values) node.textContent = values[slot] ?? \"\";\n }\n}\n\nfunction revealStyle(progress: number): CSSProperties {\n const p = Math.max(0, Math.min(1, progress));\n return {\n opacity: p,\n transform: `translateY(${(1 - p) * 6}px)`,\n willChange: \"opacity, transform\",\n };\n}\n\nexport function slotSkinFromDraft(\n draft: SlotSkinDraft,\n opts: SlotSkinOptions,\n): Skin {\n const lightTokens: SkinTokens = { colors: draft.tokens.colors ?? {} };\n const darkTokens: SkinTokens = draft.darkTokens\n ? { colors: draft.darkTokens.colors ?? {} }\n : lightTokens;\n const slotMarkers = opts.slotMarkers ?? false;\n const cssByTheme = {\n light: styleText(\n lightTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n dark: styleText(\n darkTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n };\n const supportsThemes: (\"light\" | \"dark\")[] = draft.darkTokens\n ? [\"light\", \"dark\"]\n : [draft.meta.theme ?? \"light\"];\n\n const frameHtml = draft.slots.frame;\n const messageHtml = draft.slots.message;\n const composerHtml = draft.slots.composer;\n const systemHtml = draft.slots.system;\n const typingHtml = draft.slots.typing;\n\n const Frame: FC<FrameProps & { children?: ReactNode }> = ({\n theme,\n children,\n }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const [mount, setMount] = useState<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = \"\";\n const style = host.ownerDocument.createElement(\"style\");\n style.textContent = theme === \"dark\" ? cssByTheme.dark : cssByTheme.light;\n shadow.appendChild(style);\n // Wrap the captured frame in a fluid 100%×100% container so any\n // desktop-fixed widths/margins on the captured root get neutralised\n // by the `.tc-slot-root > :first-child` reset.\n const wrapper = host.ownerDocument.createElement(\"div\");\n wrapper.className = \"tc-slot-root\";\n wrapper.style.width = \"100%\";\n wrapper.style.height = \"100%\";\n wrapper.style.display = \"flex\";\n wrapper.style.flexDirection = \"column\";\n wrapper.style.overflow = \"hidden\";\n wrapper.innerHTML = frameHtml ?? `<div ${SLOT_ATTR}=\"messages\"></div>`;\n shadow.appendChild(wrapper);\n const slot =\n wrapper.querySelector<HTMLElement>(`[${SLOT_ATTR}=\"messages\"]`) ??\n wrapper;\n slot.textContent = \"\";\n setMount(slot);\n }, [theme]);\n return (\n <div ref={hostRef} style={{ width: \"100%\", height: \"100%\" }}>\n {mount ? createPortal(children, mount) : null}\n </div>\n );\n };\n\n const Message: FC<MessageProps> = ({ message, author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !messageHtml) return;\n fillInto(el, messageHtml, {\n author: author.name,\n avatar: initials(author.name),\n body: contentToText(message.content),\n time: fmtTime(message.atMs),\n });\n }, [message, author]);\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n };\n\n const SystemMessage: FC<SystemProps> = ({ message }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !systemHtml) return;\n fillInto(el, systemHtml, { body: contentToText(message.content) });\n }, [message]);\n if (systemHtml) {\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n }\n return (\n <div\n style={{\n ...revealStyle(message.revealProgress),\n textAlign: \"center\",\n }}\n >\n {contentToText(message.content)}\n </div>\n );\n };\n\n const TypingIndicator: FC<TypingProps> = ({ author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !typingHtml) return;\n fillInto(el, typingHtml, { author: author.name });\n }, [author]);\n if (typingHtml) {\n return <div ref={ref} />;\n }\n return (\n <div style={{ opacity: 0.7, fontStyle: \"italic\" }}>\n {author.name} is typing…\n </div>\n );\n };\n\n const Composer: FC<ComposerProps> = ({ composer }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (composerHtml) {\n fillInto(el, composerHtml, { composer: composer.text });\n } else {\n el.textContent = composer.text;\n }\n }, [composer]);\n return <div ref={ref} />;\n };\n\n const Avatar: FC<{ participant: Participant; size?: number }> = ({\n participant,\n size = 36,\n }) => (\n <div\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n display: \"grid\",\n placeItems: \"center\",\n background: \"var(--color-1, #ccc)\",\n fontSize: size * 0.4,\n }}\n >\n {initials(participant.name)}\n </div>\n );\n\n const components: SkinComponents = {\n Frame,\n Message,\n SystemMessage,\n TypingIndicator,\n Reaction: () => null,\n Composer,\n Avatar,\n };\n\n return {\n id: opts.id,\n meta: {\n name: draft.meta.name,\n defaultCanvas: draft.meta.canvas ?? { width: 420, height: 720 },\n supportsThemes,\n capabilities: opts.capabilities,\n },\n components,\n tokens: { light: lightTokens, dark: darkTokens },\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -158,6 +158,9 @@ interface ContentClassNames {
158
158
  link?: string;
159
159
  mention?: string;
160
160
  code?: string;
161
+ bold?: string;
162
+ italic?: string;
163
+ strike?: string;
161
164
  emoji?: string;
162
165
  image?: string;
163
166
  }
@@ -167,6 +170,9 @@ interface ContentStyles {
167
170
  link?: CSSProperties;
168
171
  mention?: CSSProperties;
169
172
  code?: CSSProperties;
173
+ bold?: CSSProperties;
174
+ italic?: CSSProperties;
175
+ strike?: CSSProperties;
170
176
  emoji?: CSSProperties;
171
177
  }
172
178
  interface MessageContentProps {
package/dist/index.d.ts CHANGED
@@ -158,6 +158,9 @@ interface ContentClassNames {
158
158
  link?: string;
159
159
  mention?: string;
160
160
  code?: string;
161
+ bold?: string;
162
+ italic?: string;
163
+ strike?: string;
161
164
  emoji?: string;
162
165
  image?: string;
163
166
  }
@@ -167,6 +170,9 @@ interface ContentStyles {
167
170
  link?: CSSProperties;
168
171
  mention?: CSSProperties;
169
172
  code?: CSSProperties;
173
+ bold?: CSSProperties;
174
+ italic?: CSSProperties;
175
+ strike?: CSSProperties;
170
176
  emoji?: CSSProperties;
171
177
  }
172
178
  interface MessageContentProps {
package/dist/index.js CHANGED
@@ -63,6 +63,7 @@ function fadeSlideIn(progress, options = {}) {
63
63
  const eased = (options.easing ?? easeOutCubic)(clamp01(progress));
64
64
  const distance = options.distance ?? 8;
65
65
  const offset = (1 - eased) * distance;
66
+ if (offset === 0) return { opacity: eased, transform: "none" };
66
67
  const translate = options.axis === "x" ? `translateX(${offset}px)` : `translateY(${offset}px)`;
67
68
  return { opacity: eased, transform: translate };
68
69
  }
@@ -114,6 +115,39 @@ function renderInline(span, key, cn, st) {
114
115
  return span.value;
115
116
  case "code":
116
117
  return /* @__PURE__ */ jsx("code", { "data-tc-mark": "code", className: cn.code, style: st.code, children: span.value }, key);
118
+ case "bold":
119
+ return /* @__PURE__ */ jsx(
120
+ "strong",
121
+ {
122
+ "data-tc-mark": "bold",
123
+ className: cn.bold,
124
+ style: st.bold,
125
+ children: span.value
126
+ },
127
+ key
128
+ );
129
+ case "italic":
130
+ return /* @__PURE__ */ jsx(
131
+ "em",
132
+ {
133
+ "data-tc-mark": "italic",
134
+ className: cn.italic,
135
+ style: st.italic,
136
+ children: span.value
137
+ },
138
+ key
139
+ );
140
+ case "strike":
141
+ return /* @__PURE__ */ jsx(
142
+ "s",
143
+ {
144
+ "data-tc-mark": "strike",
145
+ className: cn.strike,
146
+ style: st.strike,
147
+ children: span.value
148
+ },
149
+ key
150
+ );
117
151
  case "link":
118
152
  return /* @__PURE__ */ jsx(
119
153
  "a",
@@ -221,7 +255,7 @@ function TypecaastStage({
221
255
  [participants]
222
256
  );
223
257
  const composerAuthor = state.composer.from ? byId.get(state.composer.from) : composer === "always" ? selfParticipant : void 0;
224
- const showComposer = composer !== "never" && composerAuthor !== void 0;
258
+ const showComposer = composer !== "never" && (composer === "always" || state.composer.from !== void 0);
225
259
  const typingPlacement = skin.meta.typingPlacement ?? "thread";
226
260
  const typingNodes = state.typingIndicators.map((typing, i) => {
227
261
  const author = byId.get(typing.from);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define-skin.ts","../src/theme.tsx","../src/fonts.ts","../src/animation.tsx","../src/content.tsx","../src/stage.tsx","../src/slot-skin.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import type { Skin } from \"./types.js\";\n\n/**\n * Identity helper for type-safety and a single registration point when\n * authoring a skin. Keeping it a function (rather than a bare object) lets us\n * add validation/registration later without changing skin call sites.\n */\nexport function defineSkin(skin: Skin): Skin {\n return skin;\n}\n","import {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\nimport type { SkinTokens } from \"./types.js\";\n\ninterface ThemeContextValue {\n theme: ResolvedTheme;\n tokens?: SkinTokens;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: \"light\" });\n\nexport interface ThemeProviderProps {\n theme: ResolvedTheme;\n /** Resolved per-theme tokens for the active skin. */\n tokens?: SkinTokens;\n children?: ReactNode;\n}\n\n/** Provides the resolved theme + tokens to every skin component below it. */\nexport function ThemeProvider({\n theme,\n tokens,\n children,\n}: ThemeProviderProps): ReactElement {\n return (\n <ThemeContext.Provider value={{ theme, tokens }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/** The resolved theme (`\"light\" | \"dark\"`) for the current subtree. */\nexport function useTheme(): ResolvedTheme {\n return useContext(ThemeContext).theme;\n}\n\n/** The resolved design tokens for the current subtree, if provided. */\nexport function useTokens(): SkinTokens | undefined {\n return useContext(ThemeContext).tokens;\n}\n","import type { FontDeclaration } from \"./types.js\";\n\n/** Build a CSS `src:` value from a font's sources. */\nfunction srcValue(decl: FontDeclaration): string {\n return decl.sources\n .map((s) => {\n const format = s.format ? ` format(\"${s.format}\")` : \"\";\n return `url(\"${s.url}\")${format}`;\n })\n .join(\", \");\n}\n\n/**\n * Load a skin's declared web fonts so live preview matches the platform (not\n * just video export). SSR-safe: a no-op when `document`/`FontFace` are absent\n * (server render, Remotion Node). Idempotent per family+weight+style.\n *\n * Returns once every face has loaded (or immediately, off the main document).\n */\nexport async function loadSkinFonts(\n fonts: FontDeclaration[] | undefined,\n): Promise<void> {\n if (!fonts || fonts.length === 0) return;\n if (\n typeof document === \"undefined\" ||\n typeof FontFace === \"undefined\" ||\n !document.fonts\n ) {\n return;\n }\n\n const pending: Promise<unknown>[] = [];\n for (const decl of fonts) {\n for (const source of decl.sources) {\n const descriptors: FontFaceDescriptors = {};\n if (source.weight !== undefined)\n descriptors.weight = String(source.weight);\n if (source.style !== undefined) descriptors.style = source.style;\n\n const single: FontDeclaration = { ...decl, sources: [source] };\n const face = new FontFace(decl.family, srcValue(single), descriptors);\n\n // Skip if an identical face is already registered.\n const already = [...document.fonts].some(\n (f) =>\n f.family === decl.family &&\n f.weight === (descriptors.weight ?? \"normal\") &&\n f.style === (descriptors.style ?? \"normal\"),\n );\n if (already) continue;\n\n document.fonts.add(face);\n pending.push(face.load());\n }\n }\n await Promise.all(pending);\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactElement {\n const phase = clamp01(progress) * cycles * Math.PI * 2;\n const dots: ReactNode[] = [];\n for (let i = 0; i < count; i++) {\n const wave = Math.sin(phase - i * 0.9);\n const lift = Math.max(0, wave) * 4;\n const opacity = 0.4 + Math.max(0, wave) * 0.6;\n dots.push(\n <span\n key={i}\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n background: color,\n transform: `translateY(${-lift}px)`,\n opacity,\n }}\n />,\n );\n }\n return (\n <span style={{ display: \"inline-flex\", alignItems: \"flex-end\", gap }}>\n {dots}\n </span>\n );\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactElement {\n return (\n <>\n {nodes.map((node, i) => {\n if (node.type === \"text\") {\n const text = node as TextNode;\n return (\n <span\n key={i}\n data-tc-node=\"text\"\n className={classNames.text}\n style={styles.text}\n >\n {text.spans.map((span, j) =>\n renderInline(span, j, classNames, styles),\n )}\n </span>\n );\n }\n if (node.type === \"image\") {\n return renderImage(node as ImageNode, i, classNames, imageStyle);\n }\n return null; // unknown future node type — skipped\n })}\n </>\n );\n}\n","import { useMemo, type ReactElement } from \"react\";\nimport type { ComposerMode, Participant } from \"@typecaast/schema\";\nimport type { SimState } from \"@typecaast/core\";\nimport { ThemeProvider } from \"./theme.js\";\nimport type { Skin } from \"./types.js\";\n\nexport type { ComposerMode };\n\n// A subtle, native-feeling scrollbar for the scrollable thread, scoped to the\n// thread viewport and injected inline so the embed needs no external stylesheet.\n// A neutral translucent grey reads on both light and dark skins and deepens on\n// hover; WebKit/Blink get the thin overlay-style thumb, Firefox uses the\n// standard `scrollbar-*` props. The selector is global on purpose — identical\n// rules across multiple embeds on a page are harmless and keep them consistent.\nconst THREAD_SCROLLBAR_CSS = `\n[data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}\n[data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}\n[data-typecaast-thread]::-webkit-scrollbar-track{background:transparent}\n[data-typecaast-thread]::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.4);border-radius:8px;border:2px solid transparent;background-clip:padding-box}\n[data-typecaast-thread]:hover::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.6)}\n`;\n\nexport interface TypecaastStageProps {\n state: SimState;\n skin: Skin;\n participants: Participant[];\n /** Skin-specific options from `meta.skin.options`. */\n options?: Record<string, unknown>;\n /**\n * Composer visibility: `auto` (default) shows it only while someone is\n * typing/sending; `always` keeps the reply box visible (idle = placeholder);\n * `never` hides it.\n */\n composer?: ComposerMode;\n}\n\n/**\n * Maps a `SimState` onto a skin's components: a `Frame` wrapping the thread\n * items (Message / SystemMessage), the typing indicators, and the composer.\n * Reactions render inside the skin's `Message` (it reads `message.reactions`).\n *\n * Lives in `skin-kit` (the contract layer) so both `@typecaast/react` and the\n * skins' own stories can render a frame without depending on the React player.\n */\nexport function TypecaastStage({\n state,\n skin,\n participants,\n options,\n composer = \"auto\",\n}: TypecaastStageProps): ReactElement {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n // \"X is typing…\" indicators, minus the viewer's own (you never see yourself\n // typing — that's what the composer shows). Placement is skin-driven: inline\n // in the thread by default, or below the composer (Slack).\n const typingPlacement = skin.meta.typingPlacement ?? \"thread\";\n const typingNodes = state.typingIndicators\n .map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author || author.isSelf) return null;\n return (\n <TypingIndicator\n key={`typing-${typing.from}-${i}`}\n theme={theme}\n typing={typing}\n author={author}\n />\n );\n })\n .filter(Boolean);\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <style>{THREAD_SCROLLBAR_CSS}</style>\n <Frame theme={theme} options={options} composer={composer}>\n {/* Thread viewport. `column-reverse` pins the conversation to the\n bottom (newest message + composer sit at the bottom, older items\n \"shift up\", PLAN §7) and, once the thread outgrows the height, makes\n it scroll from the bottom with the top reachable — entirely in CSS,\n so it renders identically in a live embed, an SSR page, and a video\n frame (no scroll-to-bottom effect, which wouldn't run before a\n Remotion screenshot). Because `column-reverse` lays the first child\n out at the bottom, children render newest-first: the typing\n indicator (most recent activity) first, then messages reversed. The\n engine's scroll.targetOffset is honored here once real layout\n measurement lands. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column-reverse\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflowY: \"auto\",\n // Scrollbar styling lives in THREAD_SCROLLBAR_CSS (the `<style>`\n // above) so it can reach the WebKit pseudo-elements.\n // Breathing room beneath the last message — keeps it off the\n // composer (when shown) and the Frame's bottom edge (when hidden).\n paddingBottom: 16,\n }}\n >\n {typingPlacement === \"thread\" ? typingNodes : null}\n {state.messages\n .map((message, i) => {\n const author = byId.get(message.from);\n if (!author) return null;\n // Index-disambiguated so a config with duplicate message ids can't\n // collide React keys (the builder can produce them transiently).\n const key = `${message.id}-${i}`;\n if (message.variant === \"system\") {\n return (\n <SystemMessage\n key={key}\n theme={theme}\n message={message}\n author={author}\n />\n );\n }\n // Grouping looks at the chronological predecessor — computed\n // before the reverse below, so author-collapsing is unaffected.\n const prev = state.messages[i - 1];\n const previousAuthor = prev ? byId.get(prev.from) : undefined;\n return (\n <Message\n key={key}\n theme={theme}\n message={message}\n author={author}\n previousAuthor={previousAuthor}\n />\n );\n })\n .reverse()}\n </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n options={options}\n />\n ) : null}\n {typingPlacement === \"below-composer\" ? typingNodes : null}\n </Frame>\n </ThemeProvider>\n );\n}\n","/**\n * `slotSkinFromDraft` — build a `Skin` from a slotted HTML draft (the shape\n * the capture pipeline emits). The frame/message/composer HTML strings are\n * injected into a shadow root with the draft's CSS, and `{{slot}}` markers\n * are substituted with per-message text at render time.\n *\n * Lives in `@typecaast/skin-kit` so both `@typecaast/capture` (the runtime\n * untrusted-template path) and `@typecaast/skins` (built-in captured skins\n * like PostHog) can share one implementation without forming a build cycle\n * with `@typecaast/react`. The capture-runtime caller layers `sanitizeHtml`\n * around it; built-ins skip sanitize because their draft.json is\n * version-controlled.\n *\n * Responsive behaviour: the captured DOM is typically taken on a desktop\n * viewport (1000-1500px wide) but the skin renders into a small canvas\n * (often 480×640). We normalise that by:\n * - wrapping the frame slot in a container that is `width:100%; height:100%`\n * with `overflow:hidden`, so the captured outer chrome can't extend\n * past the canvas;\n * - resetting the captured root element's `margin`/`max-width` so a\n * `margin: 0px 234px` baked in at desktop width becomes `margin: 0 auto`;\n * - exposing the captured viewport width as `--captured-viewport-width`\n * for authored CSS to ratio-scale against if it wants.\n */\nimport {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentNode, Participant } from \"@typecaast/schema\";\nimport type {\n Capabilities,\n ComposerProps,\n FrameProps,\n MessageProps,\n SystemProps,\n TypingProps,\n} from \"@typecaast/core\";\nimport type { Skin, SkinComponents, SkinTokens } from \"./types.js\";\n\nconst SLOT_ATTR = \"data-tc-slot\";\n\ninterface TokenSet {\n colors?: Record<string, string>;\n}\n\nexport interface SlotSkinDraft {\n meta: {\n name: string;\n theme?: \"light\" | \"dark\";\n canvas?: { width: number; height: number };\n capturedAt?: { viewportWidth?: number; pixelRatio?: number };\n };\n slots: {\n frame?: string;\n message?: string;\n composer?: string;\n typing?: string;\n system?: string;\n };\n css?: string;\n tokens: TokenSet;\n darkTokens?: TokenSet;\n}\n\nexport interface SlotSkinOptions {\n id: string;\n capabilities: Capabilities;\n /**\n * When true, every slot mount in the shadow root gets a faint dashed\n * outline + a corner badge naming the slot. Used by the `/create-skin`\n * editor to show authors where their `{{body}}` lands.\n */\n slotMarkers?: boolean;\n}\n\nfunction contentToText(content: ContentNode[]): string {\n const out: string[] = [];\n for (const node of content) {\n if (\n node.type === \"text\" &&\n Array.isArray((node as { spans?: unknown }).spans)\n ) {\n for (const span of (\n node as { spans: { value?: string; label?: string }[] }\n ).spans) {\n out.push(span.value ?? span.label ?? \"\");\n }\n } else if (node.type === \"image\") {\n out.push((node as { alt?: string }).alt ?? \"🖼\");\n }\n }\n return out.join(\"\");\n}\n\nfunction initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n}\n\nfunction fmtTime(atMs: number): string {\n const total = Math.floor(atMs / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/**\n * Build the `<style>` text for the shadow root. Order matters:\n * 1. `:host` declarations (tokens as CSS vars, captured viewport width).\n * 2. A reset that normalises the captured root element back to fluid\n * layout — desktop-only `margin: 0 200px`/`max-width: 1200px` would\n * otherwise squeeze content in a small canvas.\n * 3. Author / captured CSS, which can override anything above.\n * 4. Slot-marker overlay if requested.\n */\nfunction styleText(\n tokens: TokenSet,\n css: string,\n capturedAt: SlotSkinDraft[\"meta\"][\"capturedAt\"],\n slotMarkers: boolean,\n): string {\n const vars = Object.entries(tokens.colors ?? {})\n .map(([k, v]) => `--${k}: ${v};`)\n .join(\" \");\n const cv = capturedAt?.viewportWidth\n ? `--captured-viewport-width: ${capturedAt.viewportWidth}px;`\n : \"\";\n const reset = `\n :host { all: initial; display: block; width: 100%; height: 100%; ${vars} ${cv} }\n * { box-sizing: border-box; }\n /* Normalise the captured root: drop desktop-only margins / max-widths\n so the layout re-centers cleanly in any canvas. */\n .tc-slot-root > :first-child {\n margin-left: auto !important;\n margin-right: auto !important;\n max-width: 100% !important;\n }`;\n const markers = slotMarkers\n ? `\n [data-tc-slot] {\n outline: 1px dashed rgba(99, 102, 241, 0.6);\n outline-offset: -1px;\n position: relative;\n }\n [data-tc-slot]::before {\n content: attr(data-tc-slot);\n position: absolute;\n top: 0;\n left: 0;\n transform: translateY(-100%);\n font: 600 9px/1 -apple-system, system-ui, sans-serif;\n padding: 1px 4px;\n background: rgba(99, 102, 241, 0.95);\n color: white;\n border-radius: 2px;\n pointer-events: none;\n z-index: 10;\n }`\n : \"\";\n return `${reset}\\n${css}\\n${markers}`;\n}\n\nfunction fillInto(\n host: HTMLElement,\n templateHtml: string,\n values: Record<string, string>,\n): void {\n host.innerHTML = templateHtml;\n for (const node of host.querySelectorAll(`[${SLOT_ATTR}]`)) {\n const slot = node.getAttribute(SLOT_ATTR);\n if (slot && slot in values) node.textContent = values[slot] ?? \"\";\n }\n}\n\nfunction revealStyle(progress: number): CSSProperties {\n const p = Math.max(0, Math.min(1, progress));\n return {\n opacity: p,\n transform: `translateY(${(1 - p) * 6}px)`,\n willChange: \"opacity, transform\",\n };\n}\n\nexport function slotSkinFromDraft(\n draft: SlotSkinDraft,\n opts: SlotSkinOptions,\n): Skin {\n const lightTokens: SkinTokens = { colors: draft.tokens.colors ?? {} };\n const darkTokens: SkinTokens = draft.darkTokens\n ? { colors: draft.darkTokens.colors ?? {} }\n : lightTokens;\n const slotMarkers = opts.slotMarkers ?? false;\n const cssByTheme = {\n light: styleText(\n lightTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n dark: styleText(\n darkTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n };\n const supportsThemes: (\"light\" | \"dark\")[] = draft.darkTokens\n ? [\"light\", \"dark\"]\n : [draft.meta.theme ?? \"light\"];\n\n const frameHtml = draft.slots.frame;\n const messageHtml = draft.slots.message;\n const composerHtml = draft.slots.composer;\n const systemHtml = draft.slots.system;\n const typingHtml = draft.slots.typing;\n\n const Frame: FC<FrameProps & { children?: ReactNode }> = ({\n theme,\n children,\n }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const [mount, setMount] = useState<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = \"\";\n const style = host.ownerDocument.createElement(\"style\");\n style.textContent = theme === \"dark\" ? cssByTheme.dark : cssByTheme.light;\n shadow.appendChild(style);\n // Wrap the captured frame in a fluid 100%×100% container so any\n // desktop-fixed widths/margins on the captured root get neutralised\n // by the `.tc-slot-root > :first-child` reset.\n const wrapper = host.ownerDocument.createElement(\"div\");\n wrapper.className = \"tc-slot-root\";\n wrapper.style.width = \"100%\";\n wrapper.style.height = \"100%\";\n wrapper.style.display = \"flex\";\n wrapper.style.flexDirection = \"column\";\n wrapper.style.overflow = \"hidden\";\n wrapper.innerHTML = frameHtml ?? `<div ${SLOT_ATTR}=\"messages\"></div>`;\n shadow.appendChild(wrapper);\n const slot =\n wrapper.querySelector<HTMLElement>(`[${SLOT_ATTR}=\"messages\"]`) ??\n wrapper;\n slot.textContent = \"\";\n setMount(slot);\n }, [theme]);\n return (\n <div ref={hostRef} style={{ width: \"100%\", height: \"100%\" }}>\n {mount ? createPortal(children, mount) : null}\n </div>\n );\n };\n\n const Message: FC<MessageProps> = ({ message, author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !messageHtml) return;\n fillInto(el, messageHtml, {\n author: author.name,\n avatar: initials(author.name),\n body: contentToText(message.content),\n time: fmtTime(message.atMs),\n });\n }, [message, author]);\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n };\n\n const SystemMessage: FC<SystemProps> = ({ message }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !systemHtml) return;\n fillInto(el, systemHtml, { body: contentToText(message.content) });\n }, [message]);\n if (systemHtml) {\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n }\n return (\n <div\n style={{\n ...revealStyle(message.revealProgress),\n textAlign: \"center\",\n }}\n >\n {contentToText(message.content)}\n </div>\n );\n };\n\n const TypingIndicator: FC<TypingProps> = ({ author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !typingHtml) return;\n fillInto(el, typingHtml, { author: author.name });\n }, [author]);\n if (typingHtml) {\n return <div ref={ref} />;\n }\n return (\n <div style={{ opacity: 0.7, fontStyle: \"italic\" }}>\n {author.name} is typing…\n </div>\n );\n };\n\n const Composer: FC<ComposerProps> = ({ composer }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (composerHtml) {\n fillInto(el, composerHtml, { composer: composer.text });\n } else {\n el.textContent = composer.text;\n }\n }, [composer]);\n return <div ref={ref} />;\n };\n\n const Avatar: FC<{ participant: Participant; size?: number }> = ({\n participant,\n size = 36,\n }) => (\n <div\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n display: \"grid\",\n placeItems: \"center\",\n background: \"var(--color-1, #ccc)\",\n fontSize: size * 0.4,\n }}\n >\n {initials(participant.name)}\n </div>\n );\n\n const components: SkinComponents = {\n Frame,\n Message,\n SystemMessage,\n TypingIndicator,\n Reaction: () => null,\n Composer,\n Avatar,\n };\n\n return {\n id: opts.id,\n meta: {\n name: draft.meta.name,\n defaultCanvas: draft.meta.canvas ?? { width: 420, height: 720 },\n supportsThemes,\n capabilities: opts.capabilities,\n },\n components,\n tokens: { light: lightTokens, dark: darkTokens },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/define-skin.ts","../src/theme.tsx","../src/fonts.ts","../src/animation.tsx","../src/content.tsx","../src/stage.tsx","../src/slot-skin.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import type { Skin } from \"./types.js\";\n\n/**\n * Identity helper for type-safety and a single registration point when\n * authoring a skin. Keeping it a function (rather than a bare object) lets us\n * add validation/registration later without changing skin call sites.\n */\nexport function defineSkin(skin: Skin): Skin {\n return skin;\n}\n","import {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport type { ResolvedTheme } from \"@typecaast/core\";\nimport type { SkinTokens } from \"./types.js\";\n\ninterface ThemeContextValue {\n theme: ResolvedTheme;\n tokens?: SkinTokens;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: \"light\" });\n\nexport interface ThemeProviderProps {\n theme: ResolvedTheme;\n /** Resolved per-theme tokens for the active skin. */\n tokens?: SkinTokens;\n children?: ReactNode;\n}\n\n/** Provides the resolved theme + tokens to every skin component below it. */\nexport function ThemeProvider({\n theme,\n tokens,\n children,\n}: ThemeProviderProps): ReactElement {\n return (\n <ThemeContext.Provider value={{ theme, tokens }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/** The resolved theme (`\"light\" | \"dark\"`) for the current subtree. */\nexport function useTheme(): ResolvedTheme {\n return useContext(ThemeContext).theme;\n}\n\n/** The resolved design tokens for the current subtree, if provided. */\nexport function useTokens(): SkinTokens | undefined {\n return useContext(ThemeContext).tokens;\n}\n","import type { FontDeclaration } from \"./types.js\";\n\n/** Build a CSS `src:` value from a font's sources. */\nfunction srcValue(decl: FontDeclaration): string {\n return decl.sources\n .map((s) => {\n const format = s.format ? ` format(\"${s.format}\")` : \"\";\n return `url(\"${s.url}\")${format}`;\n })\n .join(\", \");\n}\n\n/**\n * Load a skin's declared web fonts so live preview matches the platform (not\n * just video export). SSR-safe: a no-op when `document`/`FontFace` are absent\n * (server render, Remotion Node). Idempotent per family+weight+style.\n *\n * Returns once every face has loaded (or immediately, off the main document).\n */\nexport async function loadSkinFonts(\n fonts: FontDeclaration[] | undefined,\n): Promise<void> {\n if (!fonts || fonts.length === 0) return;\n if (\n typeof document === \"undefined\" ||\n typeof FontFace === \"undefined\" ||\n !document.fonts\n ) {\n return;\n }\n\n const pending: Promise<unknown>[] = [];\n for (const decl of fonts) {\n for (const source of decl.sources) {\n const descriptors: FontFaceDescriptors = {};\n if (source.weight !== undefined)\n descriptors.weight = String(source.weight);\n if (source.style !== undefined) descriptors.style = source.style;\n\n const single: FontDeclaration = { ...decl, sources: [source] };\n const face = new FontFace(decl.family, srcValue(single), descriptors);\n\n // Skip if an identical face is already registered.\n const already = [...document.fonts].some(\n (f) =>\n f.family === decl.family &&\n f.weight === (descriptors.weight ?? \"normal\") &&\n f.style === (descriptors.style ?? \"normal\"),\n );\n if (already) continue;\n\n document.fonts.add(face);\n pending.push(face.load());\n }\n }\n await Promise.all(pending);\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n // Once settled, drop the transform entirely. Any non-`none` transform — even\n // `translateY(0px)` — establishes a stacking context, which would trap a\n // descendant overlay (e.g. a reaction's hover tooltip) *below* later sibling\n // messages, so neighbouring text paints over the opaque tooltip and it reads\n // as transparent. `none` at rest lets overlays layer above adjacent content.\n if (offset === 0) return { opacity: eased, transform: \"none\" };\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactElement {\n const phase = clamp01(progress) * cycles * Math.PI * 2;\n const dots: ReactNode[] = [];\n for (let i = 0; i < count; i++) {\n const wave = Math.sin(phase - i * 0.9);\n const lift = Math.max(0, wave) * 4;\n const opacity = 0.4 + Math.max(0, wave) * 0.6;\n dots.push(\n <span\n key={i}\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n background: color,\n transform: `translateY(${-lift}px)`,\n opacity,\n }}\n />,\n );\n }\n return (\n <span style={{ display: \"inline-flex\", alignItems: \"flex-end\", gap }}>\n {dots}\n </span>\n );\n}\n","import type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n bold?: string;\n italic?: string;\n strike?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n bold?: CSSProperties;\n italic?: CSSProperties;\n strike?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"bold\":\n return (\n <strong\n key={key}\n data-tc-mark=\"bold\"\n className={cn.bold}\n style={st.bold}\n >\n {span.value}\n </strong>\n );\n case \"italic\":\n return (\n <em\n key={key}\n data-tc-mark=\"italic\"\n className={cn.italic}\n style={st.italic}\n >\n {span.value}\n </em>\n );\n case \"strike\":\n return (\n <s\n key={key}\n data-tc-mark=\"strike\"\n className={cn.strike}\n style={st.strike}\n >\n {span.value}\n </s>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactElement {\n return (\n <>\n {nodes.map((node, i) => {\n if (node.type === \"text\") {\n const text = node as TextNode;\n return (\n <span\n key={i}\n data-tc-node=\"text\"\n className={classNames.text}\n style={styles.text}\n >\n {text.spans.map((span, j) =>\n renderInline(span, j, classNames, styles),\n )}\n </span>\n );\n }\n if (node.type === \"image\") {\n return renderImage(node as ImageNode, i, classNames, imageStyle);\n }\n return null; // unknown future node type — skipped\n })}\n </>\n );\n}\n","import { useMemo, type ReactElement } from \"react\";\nimport type { ComposerMode, Participant } from \"@typecaast/schema\";\nimport type { SimState } from \"@typecaast/core\";\nimport { ThemeProvider } from \"./theme.js\";\nimport type { Skin } from \"./types.js\";\n\nexport type { ComposerMode };\n\n// A subtle, native-feeling scrollbar for the scrollable thread, scoped to the\n// thread viewport and injected inline so the embed needs no external stylesheet.\n// A neutral translucent grey reads on both light and dark skins and deepens on\n// hover; WebKit/Blink get the thin overlay-style thumb, Firefox uses the\n// standard `scrollbar-*` props. The selector is global on purpose — identical\n// rules across multiple embeds on a page are harmless and keep them consistent.\nconst THREAD_SCROLLBAR_CSS = `\n[data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}\n[data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}\n[data-typecaast-thread]::-webkit-scrollbar-track{background:transparent}\n[data-typecaast-thread]::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.4);border-radius:8px;border:2px solid transparent;background-clip:padding-box}\n[data-typecaast-thread]:hover::-webkit-scrollbar-thumb{background-color:rgba(128,128,128,.6)}\n`;\n\nexport interface TypecaastStageProps {\n state: SimState;\n skin: Skin;\n participants: Participant[];\n /** Skin-specific options from `meta.skin.options`. */\n options?: Record<string, unknown>;\n /**\n * Composer visibility: `auto` (default) shows it only while someone is\n * typing/sending; `always` keeps the reply box visible (idle = placeholder);\n * `never` hides it.\n */\n composer?: ComposerMode;\n}\n\n/**\n * Maps a `SimState` onto a skin's components: a `Frame` wrapping the thread\n * items (Message / SystemMessage), the typing indicators, and the composer.\n * Reactions render inside the skin's `Message` (it reads `message.reactions`).\n *\n * Lives in `skin-kit` (the contract layer) so both `@typecaast/react` and the\n * skins' own stories can render a frame without depending on the React player.\n */\nexport function TypecaastStage({\n state,\n skin,\n participants,\n options,\n composer = \"auto\",\n}: TypecaastStageProps): ReactElement {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (the self participant,\n // if any, is just whose placeholder it is — `author` is optional, so the box\n // shows even with no self); `auto` only shows it while someone is composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer =\n composer !== \"never\" &&\n (composer === \"always\" || state.composer.from !== undefined);\n\n // \"X is typing…\" indicators, minus the viewer's own (you never see yourself\n // typing — that's what the composer shows). Placement is skin-driven: inline\n // in the thread by default, or below the composer (Slack).\n const typingPlacement = skin.meta.typingPlacement ?? \"thread\";\n const typingNodes = state.typingIndicators\n .map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author || author.isSelf) return null;\n return (\n <TypingIndicator\n key={`typing-${typing.from}-${i}`}\n theme={theme}\n typing={typing}\n author={author}\n />\n );\n })\n .filter(Boolean);\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <style>{THREAD_SCROLLBAR_CSS}</style>\n <Frame theme={theme} options={options} composer={composer}>\n {/* Thread viewport. `column-reverse` pins the conversation to the\n bottom (newest message + composer sit at the bottom, older items\n \"shift up\", PLAN §7) and, once the thread outgrows the height, makes\n it scroll from the bottom with the top reachable — entirely in CSS,\n so it renders identically in a live embed, an SSR page, and a video\n frame (no scroll-to-bottom effect, which wouldn't run before a\n Remotion screenshot). Because `column-reverse` lays the first child\n out at the bottom, children render newest-first: the typing\n indicator (most recent activity) first, then messages reversed. The\n engine's scroll.targetOffset is honored here once real layout\n measurement lands. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column-reverse\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflowY: \"auto\",\n // Scrollbar styling lives in THREAD_SCROLLBAR_CSS (the `<style>`\n // above) so it can reach the WebKit pseudo-elements.\n // Breathing room beneath the last message — keeps it off the\n // composer (when shown) and the Frame's bottom edge (when hidden).\n paddingBottom: 16,\n }}\n >\n {typingPlacement === \"thread\" ? typingNodes : null}\n {state.messages\n .map((message, i) => {\n const author = byId.get(message.from);\n if (!author) return null;\n // Index-disambiguated so a config with duplicate message ids can't\n // collide React keys (the builder can produce them transiently).\n const key = `${message.id}-${i}`;\n if (message.variant === \"system\") {\n return (\n <SystemMessage\n key={key}\n theme={theme}\n message={message}\n author={author}\n />\n );\n }\n // Grouping looks at the chronological predecessor — computed\n // before the reverse below, so author-collapsing is unaffected.\n const prev = state.messages[i - 1];\n const previousAuthor = prev ? byId.get(prev.from) : undefined;\n return (\n <Message\n key={key}\n theme={theme}\n message={message}\n author={author}\n previousAuthor={previousAuthor}\n />\n );\n })\n .reverse()}\n </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n options={options}\n />\n ) : null}\n {typingPlacement === \"below-composer\" ? typingNodes : null}\n </Frame>\n </ThemeProvider>\n );\n}\n","/**\n * `slotSkinFromDraft` — build a `Skin` from a slotted HTML draft (the shape\n * the capture pipeline emits). The frame/message/composer HTML strings are\n * injected into a shadow root with the draft's CSS, and `{{slot}}` markers\n * are substituted with per-message text at render time.\n *\n * Lives in `@typecaast/skin-kit` so both `@typecaast/capture` (the runtime\n * untrusted-template path) and `@typecaast/skins` (built-in captured skins\n * like PostHog) can share one implementation without forming a build cycle\n * with `@typecaast/react`. The capture-runtime caller layers `sanitizeHtml`\n * around it; built-ins skip sanitize because their draft.json is\n * version-controlled.\n *\n * Responsive behaviour: the captured DOM is typically taken on a desktop\n * viewport (1000-1500px wide) but the skin renders into a small canvas\n * (often 480×640). We normalise that by:\n * - wrapping the frame slot in a container that is `width:100%; height:100%`\n * with `overflow:hidden`, so the captured outer chrome can't extend\n * past the canvas;\n * - resetting the captured root element's `margin`/`max-width` so a\n * `margin: 0px 234px` baked in at desktop width becomes `margin: 0 auto`;\n * - exposing the captured viewport width as `--captured-viewport-width`\n * for authored CSS to ratio-scale against if it wants.\n */\nimport {\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentNode, Participant } from \"@typecaast/schema\";\nimport type {\n Capabilities,\n ComposerProps,\n FrameProps,\n MessageProps,\n SystemProps,\n TypingProps,\n} from \"@typecaast/core\";\nimport type { Skin, SkinComponents, SkinTokens } from \"./types.js\";\n\nconst SLOT_ATTR = \"data-tc-slot\";\n\ninterface TokenSet {\n colors?: Record<string, string>;\n}\n\nexport interface SlotSkinDraft {\n meta: {\n name: string;\n theme?: \"light\" | \"dark\";\n canvas?: { width: number; height: number };\n capturedAt?: { viewportWidth?: number; pixelRatio?: number };\n };\n slots: {\n frame?: string;\n message?: string;\n composer?: string;\n typing?: string;\n system?: string;\n };\n css?: string;\n tokens: TokenSet;\n darkTokens?: TokenSet;\n}\n\nexport interface SlotSkinOptions {\n id: string;\n capabilities: Capabilities;\n /**\n * When true, every slot mount in the shadow root gets a faint dashed\n * outline + a corner badge naming the slot. Used by the `/create-skin`\n * editor to show authors where their `{{body}}` lands.\n */\n slotMarkers?: boolean;\n}\n\nfunction contentToText(content: ContentNode[]): string {\n const out: string[] = [];\n for (const node of content) {\n if (\n node.type === \"text\" &&\n Array.isArray((node as { spans?: unknown }).spans)\n ) {\n for (const span of (\n node as { spans: { value?: string; label?: string }[] }\n ).spans) {\n out.push(span.value ?? span.label ?? \"\");\n }\n } else if (node.type === \"image\") {\n out.push((node as { alt?: string }).alt ?? \"🖼\");\n }\n }\n return out.join(\"\");\n}\n\nfunction initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n}\n\nfunction fmtTime(atMs: number): string {\n const total = Math.floor(atMs / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/**\n * Build the `<style>` text for the shadow root. Order matters:\n * 1. `:host` declarations (tokens as CSS vars, captured viewport width).\n * 2. A reset that normalises the captured root element back to fluid\n * layout — desktop-only `margin: 0 200px`/`max-width: 1200px` would\n * otherwise squeeze content in a small canvas.\n * 3. Author / captured CSS, which can override anything above.\n * 4. Slot-marker overlay if requested.\n */\nfunction styleText(\n tokens: TokenSet,\n css: string,\n capturedAt: SlotSkinDraft[\"meta\"][\"capturedAt\"],\n slotMarkers: boolean,\n): string {\n const vars = Object.entries(tokens.colors ?? {})\n .map(([k, v]) => `--${k}: ${v};`)\n .join(\" \");\n const cv = capturedAt?.viewportWidth\n ? `--captured-viewport-width: ${capturedAt.viewportWidth}px;`\n : \"\";\n const reset = `\n :host { all: initial; display: block; width: 100%; height: 100%; ${vars} ${cv} }\n * { box-sizing: border-box; }\n /* Normalise the captured root: drop desktop-only margins / max-widths\n so the layout re-centers cleanly in any canvas. */\n .tc-slot-root > :first-child {\n margin-left: auto !important;\n margin-right: auto !important;\n max-width: 100% !important;\n }`;\n const markers = slotMarkers\n ? `\n [data-tc-slot] {\n outline: 1px dashed rgba(99, 102, 241, 0.6);\n outline-offset: -1px;\n position: relative;\n }\n [data-tc-slot]::before {\n content: attr(data-tc-slot);\n position: absolute;\n top: 0;\n left: 0;\n transform: translateY(-100%);\n font: 600 9px/1 -apple-system, system-ui, sans-serif;\n padding: 1px 4px;\n background: rgba(99, 102, 241, 0.95);\n color: white;\n border-radius: 2px;\n pointer-events: none;\n z-index: 10;\n }`\n : \"\";\n return `${reset}\\n${css}\\n${markers}`;\n}\n\nfunction fillInto(\n host: HTMLElement,\n templateHtml: string,\n values: Record<string, string>,\n): void {\n host.innerHTML = templateHtml;\n for (const node of host.querySelectorAll(`[${SLOT_ATTR}]`)) {\n const slot = node.getAttribute(SLOT_ATTR);\n if (slot && slot in values) node.textContent = values[slot] ?? \"\";\n }\n}\n\nfunction revealStyle(progress: number): CSSProperties {\n const p = Math.max(0, Math.min(1, progress));\n return {\n opacity: p,\n transform: `translateY(${(1 - p) * 6}px)`,\n willChange: \"opacity, transform\",\n };\n}\n\nexport function slotSkinFromDraft(\n draft: SlotSkinDraft,\n opts: SlotSkinOptions,\n): Skin {\n const lightTokens: SkinTokens = { colors: draft.tokens.colors ?? {} };\n const darkTokens: SkinTokens = draft.darkTokens\n ? { colors: draft.darkTokens.colors ?? {} }\n : lightTokens;\n const slotMarkers = opts.slotMarkers ?? false;\n const cssByTheme = {\n light: styleText(\n lightTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n dark: styleText(\n darkTokens,\n draft.css ?? \"\",\n draft.meta.capturedAt,\n slotMarkers,\n ),\n };\n const supportsThemes: (\"light\" | \"dark\")[] = draft.darkTokens\n ? [\"light\", \"dark\"]\n : [draft.meta.theme ?? \"light\"];\n\n const frameHtml = draft.slots.frame;\n const messageHtml = draft.slots.message;\n const composerHtml = draft.slots.composer;\n const systemHtml = draft.slots.system;\n const typingHtml = draft.slots.typing;\n\n const Frame: FC<FrameProps & { children?: ReactNode }> = ({\n theme,\n children,\n }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const [mount, setMount] = useState<HTMLElement | null>(null);\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = \"\";\n const style = host.ownerDocument.createElement(\"style\");\n style.textContent = theme === \"dark\" ? cssByTheme.dark : cssByTheme.light;\n shadow.appendChild(style);\n // Wrap the captured frame in a fluid 100%×100% container so any\n // desktop-fixed widths/margins on the captured root get neutralised\n // by the `.tc-slot-root > :first-child` reset.\n const wrapper = host.ownerDocument.createElement(\"div\");\n wrapper.className = \"tc-slot-root\";\n wrapper.style.width = \"100%\";\n wrapper.style.height = \"100%\";\n wrapper.style.display = \"flex\";\n wrapper.style.flexDirection = \"column\";\n wrapper.style.overflow = \"hidden\";\n wrapper.innerHTML = frameHtml ?? `<div ${SLOT_ATTR}=\"messages\"></div>`;\n shadow.appendChild(wrapper);\n const slot =\n wrapper.querySelector<HTMLElement>(`[${SLOT_ATTR}=\"messages\"]`) ??\n wrapper;\n slot.textContent = \"\";\n setMount(slot);\n }, [theme]);\n return (\n <div ref={hostRef} style={{ width: \"100%\", height: \"100%\" }}>\n {mount ? createPortal(children, mount) : null}\n </div>\n );\n };\n\n const Message: FC<MessageProps> = ({ message, author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !messageHtml) return;\n fillInto(el, messageHtml, {\n author: author.name,\n avatar: initials(author.name),\n body: contentToText(message.content),\n time: fmtTime(message.atMs),\n });\n }, [message, author]);\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n };\n\n const SystemMessage: FC<SystemProps> = ({ message }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !systemHtml) return;\n fillInto(el, systemHtml, { body: contentToText(message.content) });\n }, [message]);\n if (systemHtml) {\n return <div ref={ref} style={revealStyle(message.revealProgress)} />;\n }\n return (\n <div\n style={{\n ...revealStyle(message.revealProgress),\n textAlign: \"center\",\n }}\n >\n {contentToText(message.content)}\n </div>\n );\n };\n\n const TypingIndicator: FC<TypingProps> = ({ author }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el || !typingHtml) return;\n fillInto(el, typingHtml, { author: author.name });\n }, [author]);\n if (typingHtml) {\n return <div ref={ref} />;\n }\n return (\n <div style={{ opacity: 0.7, fontStyle: \"italic\" }}>\n {author.name} is typing…\n </div>\n );\n };\n\n const Composer: FC<ComposerProps> = ({ composer }) => {\n const ref = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (composerHtml) {\n fillInto(el, composerHtml, { composer: composer.text });\n } else {\n el.textContent = composer.text;\n }\n }, [composer]);\n return <div ref={ref} />;\n };\n\n const Avatar: FC<{ participant: Participant; size?: number }> = ({\n participant,\n size = 36,\n }) => (\n <div\n style={{\n width: size,\n height: size,\n borderRadius: \"50%\",\n display: \"grid\",\n placeItems: \"center\",\n background: \"var(--color-1, #ccc)\",\n fontSize: size * 0.4,\n }}\n >\n {initials(participant.name)}\n </div>\n );\n\n const components: SkinComponents = {\n Frame,\n Message,\n SystemMessage,\n TypingIndicator,\n Reaction: () => null,\n Composer,\n Avatar,\n };\n\n return {\n id: opts.id,\n meta: {\n name: draft.meta.name,\n defaultCanvas: draft.meta.canvas ?? { width: 420, height: 720 },\n supportsThemes,\n capabilities: opts.capabilities,\n },\n components,\n tokens: { light: lightTokens, dark: darkTokens },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typecaast/skin-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Skin authoring kit: defineSkin, Skin/Capabilities/SkinTokens types, theme context, font loader.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "zod": "^4.0.0",
28
- "@typecaast/core": "0.4.0",
29
- "@typecaast/schema": "0.2.1"
28
+ "@typecaast/core": "0.5.0",
29
+ "@typecaast/schema": "0.3.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=18",