@typecaast/skin-kit 0.2.0 → 0.2.1
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 +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -227,7 +227,10 @@ function TypecaastStage({
|
|
|
227
227
|
justifyContent: "flex-end",
|
|
228
228
|
flex: "1 1 auto",
|
|
229
229
|
minHeight: 0,
|
|
230
|
-
overflow: "hidden"
|
|
230
|
+
overflow: "hidden",
|
|
231
|
+
// Breathing room beneath the last message — keeps it off the
|
|
232
|
+
// composer (when shown) and the Frame's bottom edge (when hidden).
|
|
233
|
+
paddingBottom: 16
|
|
231
234
|
},
|
|
232
235
|
children: [
|
|
233
236
|
state.messages.map((message, i) => {
|
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"],"names":["createContext","jsx","useContext","Fragment","useMemo","jsxs"],"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 { createContext, useContext, type ReactNode } 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): ReactNode {\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, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactNode {\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, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactNode {\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 ReactNode } 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\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): ReactNode {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <Frame theme={theme} options={options}>\n {/* Thread viewport: bottom-anchored + clipped, so as the thread grows\n older items shift up out of view (\"content shifts up\", PLAN §7).\n The engine's scroll.targetOffset is honored here once real layout\n measurement lands; for now the flex anchor gives the same feel. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflow: \"hidden\",\n }}\n >\n {state.messages.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 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 {state.typingIndicators.map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author) 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 </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n />\n ) : null}\n </Frame>\n </ThemeProvider>\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"],"names":["createContext","jsx","useContext","Fragment","useMemo","jsxs"],"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 { createContext, useContext, type ReactNode } 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): ReactNode {\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, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactNode {\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, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactNode {\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 ReactNode } 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\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): ReactNode {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <Frame theme={theme} options={options}>\n {/* Thread viewport: bottom-anchored + clipped, so as the thread grows\n older items shift up out of view (\"content shifts up\", PLAN §7).\n The engine's scroll.targetOffset is honored here once real layout\n measurement lands; for now the flex anchor gives the same feel. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflow: \"hidden\",\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 {state.messages.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 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 {state.typingIndicators.map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author) 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 </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n />\n ) : null}\n </Frame>\n </ThemeProvider>\n );\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -225,7 +225,10 @@ function TypecaastStage({
|
|
|
225
225
|
justifyContent: "flex-end",
|
|
226
226
|
flex: "1 1 auto",
|
|
227
227
|
minHeight: 0,
|
|
228
|
-
overflow: "hidden"
|
|
228
|
+
overflow: "hidden",
|
|
229
|
+
// Breathing room beneath the last message — keeps it off the
|
|
230
|
+
// composer (when shown) and the Frame's bottom edge (when hidden).
|
|
231
|
+
paddingBottom: 16
|
|
229
232
|
},
|
|
230
233
|
children: [
|
|
231
234
|
state.messages.map((message, i) => {
|
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"],"names":["jsx"],"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 { createContext, useContext, type ReactNode } 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): ReactNode {\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, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactNode {\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, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactNode {\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 ReactNode } 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\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): ReactNode {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <Frame theme={theme} options={options}>\n {/* Thread viewport: bottom-anchored + clipped, so as the thread grows\n older items shift up out of view (\"content shifts up\", PLAN §7).\n The engine's scroll.targetOffset is honored here once real layout\n measurement lands; for now the flex anchor gives the same feel. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflow: \"hidden\",\n }}\n >\n {state.messages.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 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 {state.typingIndicators.map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author) 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 </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n />\n ) : null}\n </Frame>\n </ThemeProvider>\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"],"names":["jsx"],"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 { createContext, useContext, type ReactNode } 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): ReactNode {\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, ReactNode } from \"react\";\n\n/**\n * Animation primitives are **pure functions of progress** (0..1), not CSS\n * transitions or JS timers — so the React preview and the Remotion render\n * animate identically frame-for-frame (PLAN §7). Skins call these driven by\n * `revealProgress` / typing `progress` from `SimState`.\n */\n\nexport const clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Decelerating ease, good for reveals. */\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/** Back-ease-out with overshoot, good for pops. Lands exactly on 1 at t=1. */\nexport function backEaseOut(t: number, tension = 2.2): number {\n const c3 = tension + 1;\n return 1 + c3 * Math.pow(t - 1, 3) + tension * Math.pow(t - 1, 2);\n}\n\nexport interface FadeSlideOptions {\n /** Slide distance in px at progress 0 (default 8). */\n distance?: number;\n /** Axis to slide along (default \"y\"). */\n axis?: \"x\" | \"y\";\n easing?: (t: number) => number;\n}\n\n/** Fade + slide-in reveal. `progress` 0 → hidden+offset, 1 → shown+settled. */\nexport function fadeSlideIn(\n progress: number,\n options: FadeSlideOptions = {},\n): CSSProperties {\n const eased = (options.easing ?? easeOutCubic)(clamp01(progress));\n const distance = options.distance ?? 8;\n const offset = (1 - eased) * distance;\n const translate =\n options.axis === \"x\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`;\n return { opacity: eased, transform: translate };\n}\n\nexport interface PopOptions {\n /** Overshoot tension (default 2.2 ≈ ~10% overshoot). */\n tension?: number;\n}\n\n/** Scale pop-in (with a little overshoot), good for reactions landing. */\nexport function popIn(\n progress: number,\n options: PopOptions = {},\n): CSSProperties {\n const p = clamp01(progress);\n const scale = backEaseOut(p, options.tension ?? 2.2);\n return {\n opacity: clamp01(p * 3),\n transform: `scale(${scale})`,\n transformOrigin: \"center\",\n };\n}\n\nexport interface TypingDotsProps {\n /** 0..1 progress through the indicator's shown duration. */\n progress?: number;\n count?: number;\n /** Bounce cycles across the full progress (default 4). */\n cycles?: number;\n /** Dot color (default `currentColor`). */\n color?: string;\n /** Dot diameter in px (default 6). */\n size?: number;\n /** Gap between dots in px (default 4). */\n gap?: number;\n}\n\n/**\n * The three-dot bouncing typing indicator, animated purely from `progress`\n * (deterministic per frame). Skins style it via props or wrap it.\n */\nexport function TypingDots({\n progress = 0,\n count = 3,\n cycles = 4,\n color = \"currentColor\",\n size = 6,\n gap = 4,\n}: TypingDotsProps): ReactNode {\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, ReactNode } from \"react\";\nimport type {\n ContentNode,\n ImageNode,\n InlineNode,\n TextNode,\n} from \"@typecaast/schema\";\n\nexport interface ContentClassNames {\n text?: string;\n link?: string;\n mention?: string;\n code?: string;\n emoji?: string;\n image?: string;\n}\n\n/** Per-mark inline styles, so skins can theme marks without a CSS file. */\nexport interface ContentStyles {\n text?: CSSProperties;\n link?: CSSProperties;\n mention?: CSSProperties;\n code?: CSSProperties;\n emoji?: CSSProperties;\n}\n\nexport interface MessageContentProps {\n nodes: ContentNode[];\n /** Per-mark class names so skins style marks with their own CSS. */\n classNames?: ContentClassNames;\n /** Per-mark inline styles (merged with the defaults). */\n styles?: ContentStyles;\n /** Extra style for in-message images (skins set radius, max size, etc.). */\n imageStyle?: CSSProperties;\n}\n\nfunction renderInline(\n span: InlineNode,\n key: number,\n cn: ContentClassNames,\n st: ContentStyles,\n): ReactNode {\n switch (span.type) {\n case \"text\":\n return span.value;\n case \"code\":\n return (\n <code key={key} data-tc-mark=\"code\" className={cn.code} style={st.code}>\n {span.value}\n </code>\n );\n case \"link\":\n return (\n <a\n key={key}\n data-tc-mark=\"link\"\n className={cn.link}\n style={st.link}\n href={span.href}\n rel=\"noreferrer\"\n >\n {span.label ?? span.href}\n </a>\n );\n case \"mention\":\n return (\n <span\n key={key}\n data-tc-mark=\"mention\"\n className={cn.mention}\n style={st.mention}\n >\n {span.label}\n </span>\n );\n case \"emoji\":\n return (\n <span\n key={key}\n data-tc-mark=\"emoji\"\n className={cn.emoji}\n style={st.emoji}\n >\n {span.value}\n </span>\n );\n }\n}\n\nfunction renderImage(\n node: ImageNode,\n key: number,\n cn: ContentClassNames,\n imageStyle?: CSSProperties,\n): ReactNode {\n return (\n <img\n key={key}\n data-tc-node=\"image\"\n className={cn.image}\n src={node.src}\n alt={node.alt ?? \"\"}\n width={node.width}\n height={node.height}\n style={{ maxWidth: \"100%\", display: \"block\", ...imageStyle }}\n />\n );\n}\n\n/**\n * Render a message body (`ContentNode[]`) to React: text nodes with inline\n * marks (code/link/mention/emoji) and in-message images. Unknown node types are\n * skipped (forward-compatible — PLAN §6). SSR-safe, so it renders identically\n * in the browser and in Remotion's Node renderer. Skins style via `classNames`.\n */\nexport function MessageContent({\n nodes,\n classNames = {},\n styles = {},\n imageStyle,\n}: MessageContentProps): ReactNode {\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 ReactNode } 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\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): ReactNode {\n const theme = state.theme;\n const byId = useMemo(() => {\n const map = new Map<string, Participant>();\n for (const p of participants) map.set(p.id, p);\n return map;\n }, [participants]);\n\n const { Frame, Message, SystemMessage, TypingIndicator, Composer } =\n skin.components;\n const tokens = skin.tokens?.[theme];\n // `always` keeps the reply box mounted even when idle (falls back to the self\n // participant so a placeholder shows); `auto` only shows it while composing.\n const selfParticipant = useMemo(\n () => participants.find((p) => p.isSelf),\n [participants],\n );\n const composerAuthor = state.composer.from\n ? byId.get(state.composer.from)\n : composer === \"always\"\n ? selfParticipant\n : undefined;\n const showComposer = composer !== \"never\" && composerAuthor !== undefined;\n\n return (\n <ThemeProvider theme={theme} tokens={tokens}>\n <Frame theme={theme} options={options}>\n {/* Thread viewport: bottom-anchored + clipped, so as the thread grows\n older items shift up out of view (\"content shifts up\", PLAN §7).\n The engine's scroll.targetOffset is honored here once real layout\n measurement lands; for now the flex anchor gives the same feel. */}\n <div\n data-typecaast-thread=\"\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n flex: \"1 1 auto\",\n minHeight: 0,\n overflow: \"hidden\",\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 {state.messages.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 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 {state.typingIndicators.map((typing, i) => {\n const author = byId.get(typing.from);\n if (!author) 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 </div>\n {showComposer ? (\n <Composer\n theme={theme}\n composer={state.composer}\n author={composerAuthor}\n />\n ) : null}\n </Frame>\n </ThemeProvider>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecaast/skin-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Skin authoring kit: defineSkin, Skin/Capabilities/SkinTokens types, theme context, font loader.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@typecaast/core": "0.
|
|
28
|
-
"@typecaast/schema": "0.
|
|
27
|
+
"@typecaast/core": "0.2.0",
|
|
28
|
+
"@typecaast/schema": "0.2.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"react": ">=18",
|