@typecaast/skin-kit 0.4.1 → 0.6.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
@@ -117,6 +117,39 @@ function renderInline(span, key, cn, st) {
117
117
  return span.value;
118
118
  case "code":
119
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
+ );
120
153
  case "link":
121
154
  return /* @__PURE__ */ jsxRuntime.jsx(
122
155
  "a",
@@ -197,6 +230,39 @@ function MessageContent({
197
230
  return null;
198
231
  }) });
199
232
  }
233
+ function renderComposerMentions(text, style, className) {
234
+ const trailing = /@[A-Za-z0-9_][\w.-]*$/.exec(text);
235
+ const splitIdx = trailing !== null && (trailing.index === 0 || /\s/.test(text[trailing.index - 1] ?? "")) ? trailing.index : text.length;
236
+ const head = text.slice(0, splitIdx);
237
+ const tail = text.slice(splitIdx);
238
+ const out = [];
239
+ const re = /(^|\s)(@[A-Za-z0-9_][\w.-]*)/g;
240
+ let last = 0;
241
+ let key = 0;
242
+ let m;
243
+ while ((m = re.exec(head)) !== null) {
244
+ const lead = m[1] ?? "";
245
+ const name = m[2] ?? "";
246
+ const at = m.index + lead.length;
247
+ if (at > last) out.push(head.slice(last, at));
248
+ out.push(
249
+ /* @__PURE__ */ jsxRuntime.jsx(
250
+ "span",
251
+ {
252
+ "data-tc-mark": "mention",
253
+ className,
254
+ style,
255
+ children: name
256
+ },
257
+ `m${key++}`
258
+ )
259
+ );
260
+ last = at + name.length;
261
+ }
262
+ if (last < head.length) out.push(head.slice(last));
263
+ if (tail) out.push(tail);
264
+ return out;
265
+ }
200
266
  var THREAD_SCROLLBAR_CSS = `
201
267
  [data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}
202
268
  [data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}
@@ -224,7 +290,7 @@ function TypecaastStage({
224
290
  [participants]
225
291
  );
226
292
  const composerAuthor = state.composer.from ? byId.get(state.composer.from) : composer === "always" ? selfParticipant : void 0;
227
- const showComposer = composer !== "never" && composerAuthor !== void 0;
293
+ const showComposer = composer !== "never" && (composer === "always" || state.composer.from !== void 0);
228
294
  const typingPlacement = skin.meta.typingPlacement ?? "thread";
229
295
  const typingNodes = state.typingIndicators.map((typing, i) => {
230
296
  const author = byId.get(typing.from);
@@ -546,6 +612,7 @@ exports.easeOutCubic = easeOutCubic;
546
612
  exports.fadeSlideIn = fadeSlideIn;
547
613
  exports.loadSkinFonts = loadSkinFonts;
548
614
  exports.popIn = popIn;
615
+ exports.renderComposerMentions = renderComposerMentions;
549
616
  exports.slotSkinFromDraft = slotSkinFromDraft;
550
617
  exports.useTheme = useTheme;
551
618
  exports.useTokens = useTokens;
@@ -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 // 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 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\n/**\n * Render composer (reply-box) text with completed `@mentions` shown as tags —\n * mirroring how a chat UI commits a mention once you type past the name. A\n * still-being-typed trailing `@name` (no following space yet) stays plain text;\n * other mrkdwn (`*bold*`, `` `code` ``) is left literal, the way composers show\n * raw markup while you type. Pass the skin's own mention `style`/`className` so\n * the in-composer tag matches that platform's sent-message mentions; a skin with\n * no mention style gets plain (untagged) text, which is correct for it.\n */\nexport function renderComposerMentions(\n text: string,\n style?: CSSProperties,\n className?: string,\n): ReactNode[] {\n // A trailing \"@word\" at the very end (no following space) is still being\n // typed — keep it plain until a space commits it to a tag.\n const trailing = /@[A-Za-z0-9_][\\w.-]*$/.exec(text);\n const splitIdx =\n trailing !== null &&\n (trailing.index === 0 || /\\s/.test(text[trailing.index - 1] ?? \"\"))\n ? trailing.index\n : text.length;\n const head = text.slice(0, splitIdx);\n const tail = text.slice(splitIdx);\n\n const out: ReactNode[] = [];\n const re = /(^|\\s)(@[A-Za-z0-9_][\\w.-]*)/g;\n let last = 0;\n let key = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(head)) !== null) {\n const lead = m[1] ?? \"\";\n const name = m[2] ?? \"\";\n const at = m.index + lead.length; // index of the '@'\n if (at > last) out.push(head.slice(last, at));\n out.push(\n <span\n key={`m${key++}`}\n data-tc-mark=\"mention\"\n className={className}\n style={style}\n >\n {name}\n </span>,\n );\n last = at + name.length;\n }\n if (last < head.length) out.push(head.slice(last));\n if (tail) out.push(tail);\n return out;\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 {
@@ -185,6 +191,16 @@ interface MessageContentProps {
185
191
  * in the browser and in Remotion's Node renderer. Skins style via `classNames`.
186
192
  */
187
193
  declare function MessageContent({ nodes, classNames, styles, imageStyle, }: MessageContentProps): ReactElement;
194
+ /**
195
+ * Render composer (reply-box) text with completed `@mentions` shown as tags —
196
+ * mirroring how a chat UI commits a mention once you type past the name. A
197
+ * still-being-typed trailing `@name` (no following space yet) stays plain text;
198
+ * other mrkdwn (`*bold*`, `` `code` ``) is left literal, the way composers show
199
+ * raw markup while you type. Pass the skin's own mention `style`/`className` so
200
+ * the in-composer tag matches that platform's sent-message mentions; a skin with
201
+ * no mention style gets plain (untagged) text, which is correct for it.
202
+ */
203
+ declare function renderComposerMentions(text: string, style?: CSSProperties, className?: string): ReactNode[];
188
204
 
189
205
  interface TypecaastStageProps {
190
206
  state: SimState;
@@ -248,4 +264,4 @@ interface SlotSkinOptions {
248
264
  }
249
265
  declare function slotSkinFromDraft(draft: SlotSkinDraft, opts: SlotSkinOptions): Skin;
250
266
 
251
- export { type ContentClassNames, type ContentStyles, type FadeSlideOptions, type FontDeclaration, type FontSource, MessageContent, type MessageContentProps, type PopOptions, type Skin, type SkinComponents, type SkinMeta, type SkinTokens, type SlotSkinDraft, type SlotSkinOptions, ThemeProvider, type ThemeProviderProps, TypecaastStage, type TypecaastStageProps, TypingDots, type TypingDotsProps, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, slotSkinFromDraft, useTheme, useTokens };
267
+ export { type ContentClassNames, type ContentStyles, type FadeSlideOptions, type FontDeclaration, type FontSource, MessageContent, type MessageContentProps, type PopOptions, type Skin, type SkinComponents, type SkinMeta, type SkinTokens, type SlotSkinDraft, type SlotSkinOptions, ThemeProvider, type ThemeProviderProps, TypecaastStage, type TypecaastStageProps, TypingDots, type TypingDotsProps, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, renderComposerMentions, slotSkinFromDraft, useTheme, useTokens };
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 {
@@ -185,6 +191,16 @@ interface MessageContentProps {
185
191
  * in the browser and in Remotion's Node renderer. Skins style via `classNames`.
186
192
  */
187
193
  declare function MessageContent({ nodes, classNames, styles, imageStyle, }: MessageContentProps): ReactElement;
194
+ /**
195
+ * Render composer (reply-box) text with completed `@mentions` shown as tags —
196
+ * mirroring how a chat UI commits a mention once you type past the name. A
197
+ * still-being-typed trailing `@name` (no following space yet) stays plain text;
198
+ * other mrkdwn (`*bold*`, `` `code` ``) is left literal, the way composers show
199
+ * raw markup while you type. Pass the skin's own mention `style`/`className` so
200
+ * the in-composer tag matches that platform's sent-message mentions; a skin with
201
+ * no mention style gets plain (untagged) text, which is correct for it.
202
+ */
203
+ declare function renderComposerMentions(text: string, style?: CSSProperties, className?: string): ReactNode[];
188
204
 
189
205
  interface TypecaastStageProps {
190
206
  state: SimState;
@@ -248,4 +264,4 @@ interface SlotSkinOptions {
248
264
  }
249
265
  declare function slotSkinFromDraft(draft: SlotSkinDraft, opts: SlotSkinOptions): Skin;
250
266
 
251
- export { type ContentClassNames, type ContentStyles, type FadeSlideOptions, type FontDeclaration, type FontSource, MessageContent, type MessageContentProps, type PopOptions, type Skin, type SkinComponents, type SkinMeta, type SkinTokens, type SlotSkinDraft, type SlotSkinOptions, ThemeProvider, type ThemeProviderProps, TypecaastStage, type TypecaastStageProps, TypingDots, type TypingDotsProps, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, slotSkinFromDraft, useTheme, useTokens };
267
+ export { type ContentClassNames, type ContentStyles, type FadeSlideOptions, type FontDeclaration, type FontSource, MessageContent, type MessageContentProps, type PopOptions, type Skin, type SkinComponents, type SkinMeta, type SkinTokens, type SlotSkinDraft, type SlotSkinOptions, ThemeProvider, type ThemeProviderProps, TypecaastStage, type TypecaastStageProps, TypingDots, type TypingDotsProps, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, renderComposerMentions, slotSkinFromDraft, useTheme, useTokens };
package/dist/index.js CHANGED
@@ -115,6 +115,39 @@ function renderInline(span, key, cn, st) {
115
115
  return span.value;
116
116
  case "code":
117
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
+ );
118
151
  case "link":
119
152
  return /* @__PURE__ */ jsx(
120
153
  "a",
@@ -195,6 +228,39 @@ function MessageContent({
195
228
  return null;
196
229
  }) });
197
230
  }
231
+ function renderComposerMentions(text, style, className) {
232
+ const trailing = /@[A-Za-z0-9_][\w.-]*$/.exec(text);
233
+ const splitIdx = trailing !== null && (trailing.index === 0 || /\s/.test(text[trailing.index - 1] ?? "")) ? trailing.index : text.length;
234
+ const head = text.slice(0, splitIdx);
235
+ const tail = text.slice(splitIdx);
236
+ const out = [];
237
+ const re = /(^|\s)(@[A-Za-z0-9_][\w.-]*)/g;
238
+ let last = 0;
239
+ let key = 0;
240
+ let m;
241
+ while ((m = re.exec(head)) !== null) {
242
+ const lead = m[1] ?? "";
243
+ const name = m[2] ?? "";
244
+ const at = m.index + lead.length;
245
+ if (at > last) out.push(head.slice(last, at));
246
+ out.push(
247
+ /* @__PURE__ */ jsx(
248
+ "span",
249
+ {
250
+ "data-tc-mark": "mention",
251
+ className,
252
+ style,
253
+ children: name
254
+ },
255
+ `m${key++}`
256
+ )
257
+ );
258
+ last = at + name.length;
259
+ }
260
+ if (last < head.length) out.push(head.slice(last));
261
+ if (tail) out.push(tail);
262
+ return out;
263
+ }
198
264
  var THREAD_SCROLLBAR_CSS = `
199
265
  [data-typecaast-thread]{scrollbar-width:thin;scrollbar-color:rgba(128,128,128,.4) transparent}
200
266
  [data-typecaast-thread]::-webkit-scrollbar{width:8px;height:8px}
@@ -222,7 +288,7 @@ function TypecaastStage({
222
288
  [participants]
223
289
  );
224
290
  const composerAuthor = state.composer.from ? byId.get(state.composer.from) : composer === "always" ? selfParticipant : void 0;
225
- const showComposer = composer !== "never" && composerAuthor !== void 0;
291
+ const showComposer = composer !== "never" && (composer === "always" || state.composer.from !== void 0);
226
292
  const typingPlacement = skin.meta.typingPlacement ?? "thread";
227
293
  const typingNodes = state.typingIndicators.map((typing, i) => {
228
294
  const author = byId.get(typing.from);
@@ -533,6 +599,6 @@ function slotSkinFromDraft(draft, opts) {
533
599
  };
534
600
  }
535
601
 
536
- export { MessageContent, ThemeProvider, TypecaastStage, TypingDots, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, slotSkinFromDraft, useTheme, useTokens };
602
+ export { MessageContent, ThemeProvider, TypecaastStage, TypingDots, backEaseOut, clamp01, defineSkin, easeOutCubic, fadeSlideIn, loadSkinFonts, popIn, renderComposerMentions, slotSkinFromDraft, useTheme, useTokens };
537
603
  //# sourceMappingURL=index.js.map
538
604
  //# sourceMappingURL=index.js.map
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 // 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 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\n/**\n * Render composer (reply-box) text with completed `@mentions` shown as tags —\n * mirroring how a chat UI commits a mention once you type past the name. A\n * still-being-typed trailing `@name` (no following space yet) stays plain text;\n * other mrkdwn (`*bold*`, `` `code` ``) is left literal, the way composers show\n * raw markup while you type. Pass the skin's own mention `style`/`className` so\n * the in-composer tag matches that platform's sent-message mentions; a skin with\n * no mention style gets plain (untagged) text, which is correct for it.\n */\nexport function renderComposerMentions(\n text: string,\n style?: CSSProperties,\n className?: string,\n): ReactNode[] {\n // A trailing \"@word\" at the very end (no following space) is still being\n // typed — keep it plain until a space commits it to a tag.\n const trailing = /@[A-Za-z0-9_][\\w.-]*$/.exec(text);\n const splitIdx =\n trailing !== null &&\n (trailing.index === 0 || /\\s/.test(text[trailing.index - 1] ?? \"\"))\n ? trailing.index\n : text.length;\n const head = text.slice(0, splitIdx);\n const tail = text.slice(splitIdx);\n\n const out: ReactNode[] = [];\n const re = /(^|\\s)(@[A-Za-z0-9_][\\w.-]*)/g;\n let last = 0;\n let key = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(head)) !== null) {\n const lead = m[1] ?? \"\";\n const name = m[2] ?? \"\";\n const at = m.index + lead.length; // index of the '@'\n if (at > last) out.push(head.slice(last, at));\n out.push(\n <span\n key={`m${key++}`}\n data-tc-mark=\"mention\"\n className={className}\n style={style}\n >\n {name}\n </span>,\n );\n last = at + name.length;\n }\n if (last < head.length) out.push(head.slice(last));\n if (tail) out.push(tail);\n return out;\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.1",
3
+ "version": "0.6.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.6.0",
29
+ "@typecaast/schema": "0.4.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=18",