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