@usetheo/ui 0.1.0-next.0 → 0.1.0-next.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +116 -9
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugin-Atb0VKtr.d.ts +172 -0
  6. package/dist/slide/index.d.ts +212 -0
  7. package/dist/slide/index.js +714 -0
  8. package/dist/slide/index.js.map +1 -0
  9. package/dist/slide/plugins/emoji/index.d.ts +29 -0
  10. package/dist/slide/plugins/emoji/index.js +157 -0
  11. package/dist/slide/plugins/emoji/index.js.map +1 -0
  12. package/dist/slide/plugins/math/index.d.ts +13 -0
  13. package/dist/slide/plugins/math/index.js +145 -0
  14. package/dist/slide/plugins/math/index.js.map +1 -0
  15. package/dist/slide/plugins/mermaid/index.d.ts +55 -0
  16. package/dist/slide/plugins/mermaid/index.js +218 -0
  17. package/dist/slide/plugins/mermaid/index.js.map +1 -0
  18. package/dist/slide/plugins/shiki/index.d.ts +18 -0
  19. package/dist/slide/plugins/shiki/index.js +87 -0
  20. package/dist/slide/plugins/shiki/index.js.map +1 -0
  21. package/dist/slide/themes/default.css +256 -0
  22. package/dist/slide/themes/layouts.css +143 -0
  23. package/dist/slide/themes/violet-forge.css +256 -0
  24. package/dist/slide-deck/index.css +52 -0
  25. package/dist/slide-deck/index.css.map +1 -0
  26. package/dist/slide-deck/index.d.ts +377 -0
  27. package/dist/slide-deck/index.js +1797 -0
  28. package/dist/slide-deck/index.js.map +1 -0
  29. package/dist/whiteboard/index.d.ts +258 -0
  30. package/dist/whiteboard/index.js +738 -0
  31. package/dist/whiteboard/index.js.map +1 -0
  32. package/package.json +141 -9
  33. package/registry/r/agent-composer.json +4 -4
  34. package/registry/r/agent-editor.json +9 -9
  35. package/registry/r/agent-error-card.json +2 -2
  36. package/registry/r/agent-event.json +4 -4
  37. package/registry/r/agent-handoff.json +2 -2
  38. package/registry/r/agent-profile.json +2 -2
  39. package/registry/r/agent-starting-state.json +2 -2
  40. package/registry/r/agent-stream.json +9 -9
  41. package/registry/r/agent-streaming.json +2 -2
  42. package/registry/r/agent-timeline.json +4 -4
  43. package/registry/r/approval-card.json +4 -4
  44. package/registry/r/artifact-preview.json +2 -2
  45. package/registry/r/attachment-chip.json +4 -4
  46. package/registry/r/audit-log-entry.json +3 -3
  47. package/registry/r/auto-compact-notice.json +2 -2
  48. package/registry/r/avatar.json +2 -2
  49. package/registry/r/badge.json +2 -2
  50. package/registry/r/browser-controls.json +2 -2
  51. package/registry/r/build-log-stream.json +2 -2
  52. package/registry/r/button.json +2 -2
  53. package/registry/r/capability-indicator.json +3 -3
  54. package/registry/r/card.json +2 -2
  55. package/registry/r/chat-composer.json +3 -3
  56. package/registry/r/chat-message.json +3 -3
  57. package/registry/r/chat-thread.json +2 -2
  58. package/registry/r/checkbox.json +2 -2
  59. package/registry/r/command-palette.json +4 -4
  60. package/registry/r/context-card.json +3 -3
  61. package/registry/r/context-window-bar.json +2 -2
  62. package/registry/r/cost-meter.json +2 -2
  63. package/registry/r/created-files-card.json +3 -3
  64. package/registry/r/cron-job-card.json +2 -2
  65. package/registry/r/cron-jobs-list.json +3 -3
  66. package/registry/r/deployment-row.json +3 -3
  67. package/registry/r/dialog.json +2 -2
  68. package/registry/r/diff-viewer.json +2 -2
  69. package/registry/r/domain-config.json +6 -6
  70. package/registry/r/empty-state.json +3 -3
  71. package/registry/r/env-var-editor.json +5 -5
  72. package/registry/r/folder-context-card.json +3 -3
  73. package/registry/r/folder-selector.json +2 -2
  74. package/registry/r/form-field.json +2 -2
  75. package/registry/r/hook-config.json +2 -2
  76. package/registry/r/hook-event-log.json +2 -2
  77. package/registry/r/input.json +2 -2
  78. package/registry/r/intent-selector.json +3 -3
  79. package/registry/r/label.json +2 -2
  80. package/registry/r/lane-board.json +2 -2
  81. package/registry/r/login-split.json +2 -2
  82. package/registry/r/mcp-server-card.json +2 -2
  83. package/registry/r/mcp-server-list.json +3 -3
  84. package/registry/r/memory-editor.json +3 -3
  85. package/registry/r/mention-menu.json +3 -3
  86. package/registry/r/metrics-panel.json +2 -2
  87. package/registry/r/model-card.json +3 -3
  88. package/registry/r/model-selector.json +2 -2
  89. package/registry/r/permission-matrix.json +2 -2
  90. package/registry/r/permission-modal.json +4 -4
  91. package/registry/r/preview-env-card.json +5 -5
  92. package/registry/r/preview-panel.json +3 -3
  93. package/registry/r/progress-checklist.json +3 -3
  94. package/registry/r/project-card.json +5 -5
  95. package/registry/r/project-switcher.json +2 -2
  96. package/registry/r/quick-action-chips.json +3 -3
  97. package/registry/r/radio-group.json +2 -2
  98. package/registry/r/recent-folders-list.json +2 -2
  99. package/registry/r/rollback-ui.json +4 -4
  100. package/registry/r/rule-card.json +3 -3
  101. package/registry/r/rule-editor.json +10 -10
  102. package/registry/r/rule-types.json +1 -1
  103. package/registry/r/run-stats.json +2 -2
  104. package/registry/r/running-tasks-panel.json +2 -2
  105. package/registry/r/scroll-area.json +2 -2
  106. package/registry/r/select.json +2 -2
  107. package/registry/r/session-list-item.json +2 -2
  108. package/registry/r/session-timeline.json +2 -2
  109. package/registry/r/sheet.json +2 -2
  110. package/registry/r/sidebar.json +2 -2
  111. package/registry/r/skeleton.json +2 -2
  112. package/registry/r/skill-card.json +4 -4
  113. package/registry/r/skill-editor.json +10 -10
  114. package/registry/r/skills-list.json +3 -3
  115. package/registry/r/social-auth-row.json +3 -3
  116. package/registry/r/steps-rail.json +2 -2
  117. package/registry/r/sub-agent-dispatch.json +2 -2
  118. package/registry/r/switch.json +2 -2
  119. package/registry/r/system-prompt-editor.json +2 -2
  120. package/registry/r/tabs.json +2 -2
  121. package/registry/r/task-header.json +4 -4
  122. package/registry/r/task-plan.json +2 -2
  123. package/registry/r/terminal-panel.json +2 -2
  124. package/registry/r/textarea.json +2 -2
  125. package/registry/r/theme-provider.json +2 -2
  126. package/registry/r/theme-script.json +1 -1
  127. package/registry/r/theo-ui-provider.json +2 -2
  128. package/registry/r/toast.json +2 -2
  129. package/registry/r/token-usage-chart.json +2 -2
  130. package/registry/r/tool-call-card.json +3 -3
  131. package/registry/r/tool-call.json +2 -2
  132. package/registry/r/tool-result.json +2 -2
  133. package/registry/r/tools-list.json +3 -3
  134. package/registry/r/tooltip.json +2 -2
  135. package/registry/r/topnav.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/primitives/slide/alerts.ts","../../src/components/primitives/slide/marpit-bg.ts","../../src/components/primitives/slide/plugin.ts","../../src/components/primitives/slide/sanitize.ts","../../src/components/primitives/slide/schema.ts","../../src/components/primitives/slide/frontmatter.ts","../../src/components/primitives/slide/validate.ts","../../src/components/primitives/slide/parse.ts","../../src/components/primitives/slide/use-slide-fit.ts","../../src/components/primitives/slide/slide.tsx","../../src/components/primitives/slide/themes/index.ts","../../src/components/primitives/slide/json-schema.ts"],"names":["jsx","jsxs","useState","useEffect","z"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * GFM alerts post-processor — converts `> [!NOTE]` blockquotes into\n * `<aside class=\"theo-slide-alert\" data-theo-slide-alert-type=\"...\">` (ADR D3 of\n * the slide-rich-content plan).\n *\n * GitHub Flavored Markdown introduced this convention in 2023 for callouts.\n * `remark-gfm` (already in our dep tree) parses the blockquote but does NOT\n * distinguish alerts from regular blockquotes. We post-process the mdast tree\n * to detect the canonical marker and annotate with `hName` + `hProperties` so\n * `mdast-util-to-hast` emits the desired hast element.\n *\n * Five tag families are supported, mirroring GitHub:\n * - NOTE (info / blue)\n * - TIP (success / green)\n * - IMPORTANT (purple)\n * - WARNING (yellow)\n * - CAUTION (red)\n *\n * Case-insensitive. Marker is stripped from the rendered text. Regular\n * blockquotes (without `[!TYPE]` prefix) are left untouched.\n */\nimport type { Blockquote, Paragraph, Root, Text } from \"mdast\";\n\n/** Canonical alert types — used by CSS via `data-theo-slide-alert-type=\"...\"`. */\nexport const ALERT_TYPES = [\"note\", \"tip\", \"important\", \"warning\", \"caution\"] as const;\nexport type AlertType = (typeof ALERT_TYPES)[number];\n\nconst ALERT_RE = /^\\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\][ \\t]*(?:\\r?\\n|$)/i;\n\n/**\n * Walk a mdast tree and mutate any blockquote that opens with `[!TYPE]` into\n * an `<aside>` annotation. Mutation is in-place to keep the pipeline allocation\n * profile flat; the same tree is returned for chainability.\n */\nexport function detectAlerts(tree: Root): Root {\n for (const node of tree.children) {\n if (node.type !== \"blockquote\") continue;\n transformIfAlert(node);\n }\n return tree;\n}\n\nfunction transformIfAlert(node: Blockquote): void {\n const firstChild = node.children[0];\n if (!firstChild || firstChild.type !== \"paragraph\") return;\n const paragraph = firstChild as Paragraph;\n const firstInline = paragraph.children[0];\n if (!firstInline || firstInline.type !== \"text\") return;\n const text = firstInline as Text;\n const match = ALERT_RE.exec(text.value);\n if (!match) return;\n\n const matched = match[1];\n if (!matched) return;\n const type = matched.toLowerCase() as AlertType;\n\n // Strip the marker from the rendered text.\n text.value = text.value.replace(ALERT_RE, \"\");\n // If the first inline became empty, drop it so the paragraph doesn't render\n // an empty leading line.\n if (text.value === \"\" && paragraph.children.length > 1) {\n paragraph.children.shift();\n }\n // Annotate so mdast-util-to-hast emits <aside> with the data attribute.\n node.data = {\n ...node.data,\n hName: \"aside\",\n hProperties: {\n ...((node.data as { hProperties?: Record<string, unknown> })?.hProperties ?? {}),\n className: [\"theo-slide-alert\"],\n \"data-theo-slide-alert-type\": type,\n },\n };\n}\n","/**\n * Marpit-style `![bg](url)` background syntax detector.\n *\n * Marpit popularised the convention of declaring a slide background via a\n * standalone image whose alt-text starts with `bg`:\n *\n * ![bg](https://example.com/photo.jpg)\n * ![bg cover](url) ![bg fit](url) ![bg left](url) ![bg right](url)\n *\n * LLMs trained on Marp output emit this naturally, so the slide primitive\n * adopts the same surface — but in a STRICTLY safe way:\n *\n * 1. Only triggers when the paragraph contains a SINGLE image (avoids\n * collisions with inline images mixed with text).\n * 2. First-bg-wins (multiple `![bg]` directives in one slide → only the first\n * is honoured; the others are also dropped from the tree to prevent\n * duplicate rendering).\n * 3. The extracted result goes into `ParsedSlide.extractedBackground` (D18 /\n * EC-5) and the Slide component prefers an explicit `frontmatter.backgroundImage`\n * over the Marpit extraction.\n * 4. URL sanitization happens at the caller (`parseSlide`) via `sanitizeBgUrl`.\n */\nimport type { Image, Paragraph, Root } from \"mdast\";\n\nconst BG_ALT_RE = /^bg(?:\\s+(\\w+))?/i;\n\nconst VALID_MODIFIERS = new Set([\"cover\", \"fit\", \"left\", \"right\"]);\n\nexport interface ExtractedMarpitBackground {\n url: string;\n modifier?: \"cover\" | \"fit\" | \"left\" | \"right\";\n}\n\n/**\n * Walk the mdast tree, extract the first `![bg](url)` directive, drop ALL\n * matching paragraphs from the tree. Returns a new tree (children array\n * is filtered; node identities are preserved otherwise).\n */\nexport function extractMarpitBackgrounds(tree: Root): {\n tree: Root;\n background?: ExtractedMarpitBackground;\n} {\n let background: ExtractedMarpitBackground | undefined;\n const filteredChildren = tree.children.filter((node) => {\n if (node.type !== \"paragraph\") return true;\n const p = node as Paragraph;\n // Single-child guard: paragraphs with mixed content (text + image) are\n // kept as-is — only \"image-only\" paragraphs are candidates.\n if (p.children.length !== 1) return true;\n const child = p.children[0];\n if (!child || child.type !== \"image\") return true;\n const img = child as Image;\n const match = BG_ALT_RE.exec(img.alt ?? \"\");\n if (!match) return true;\n // Capture once; drop subsequent bg paragraphs to avoid duplicate output.\n if (!background) {\n const modifierRaw = match[1]?.toLowerCase();\n const modifier =\n modifierRaw && VALID_MODIFIERS.has(modifierRaw)\n ? (modifierRaw as \"cover\" | \"fit\" | \"left\" | \"right\")\n : undefined;\n background = {\n url: img.url,\n modifier,\n };\n }\n return false; // drop this paragraph from the tree\n });\n return {\n tree: { ...tree, children: filteredChildren },\n background,\n };\n}\n","/**\n * Slide plugin contract — extension points for rich-content engines.\n *\n * Plugins are composable transformers applied during `parseSlide`. Each plugin\n * may inspect/mutate the mdast tree (before HTML conversion), the hast tree\n * (after HTML conversion, before sanitize), and override React component\n * renderers (after sanitize). They may also declare sanitize-schema extensions\n * so custom tags (e.g. Shiki spans, KaTeX MathML, Mermaid SVG) survive the\n * security barrier.\n *\n * Design rules (ADRs in `.claude/knowledge-base/plans/slide-rich-content-plan.md`):\n * D1 — explicit `plugins` prop (no auto-detect).\n * D2 — plugin shape: { name, mdastTransform?, hastTransform?, components? }.\n * D13 — execution order: mdast → hast → sanitize (with merged extensions) → components.\n * D16 — plugin error isolation: each plugin call is try/catch wrapped; errors\n * are collected, not thrown.\n * D17 — sanitize-schema merge is OBLIGATORY for plugins emitting non-default\n * tags; without it, the sanitize step silently strips the content.\n *\n * Sanitize is the security barrier. Plugins NEVER bypass `hast-util-sanitize`.\n */\nimport type { Root as HastRoot } from \"hast\";\nimport type { Root as MdastRoot } from \"mdast\";\nimport type { FC } from \"react\";\nimport type { SlideValidationError } from \"./schema.js\";\n\n/** Sanitize schema extension declared by a plugin. Merged with `defaultSchema` in `parseSlide`. */\nexport interface SlideSanitizeExtension {\n /** Additional element tag names allowed past the sanitizer. */\n tagNames?: string[];\n /** Additional attribute allow-list keyed by tag name (or `\"*\"` for all tags). */\n attributes?: Record<string, string[]>;\n}\n\n/** A composable transformer + component-override unit. */\nexport interface SlidePlugin {\n /** Stable identifier used in PLUGIN_ERROR messages and dev logs. */\n name: string;\n /** Mutate the mdast tree before HTML conversion. Return value is forwarded. */\n mdastTransform?: (tree: MdastRoot) => Promise<MdastRoot> | MdastRoot;\n /** Mutate the hast tree after HTML conversion, before sanitize. */\n hastTransform?: (tree: HastRoot) => Promise<HastRoot> | HastRoot;\n /** React component overrides merged into the consumer's `components` map. */\n // biome-ignore lint/suspicious/noExplicitAny: third-party component override map\n components?: Record<string, FC<any>>;\n /** Sanitize-schema extension. Required for plugins emitting non-default tags. */\n sanitizeSchemaExtension?: SlideSanitizeExtension;\n}\n\n/** Merged sanitize-schema extensions from all plugins. */\nexport interface MergedSanitizeExtensions {\n tagNames: string[];\n attributes: Record<string, string[]>;\n}\n\n/** Composer over a plugin array. Hooks orchestrate transformation + error isolation. */\nexport interface PluginComposer {\n /** Run all `mdastTransform` hooks in array order. Collects errors. */\n runMdast(tree: MdastRoot, errors: SlideValidationError[]): Promise<MdastRoot>;\n /** Run all `hastTransform` hooks in array order. Collects errors. */\n runHast(tree: HastRoot, errors: SlideValidationError[]): Promise<HastRoot>;\n /** Merge all plugin component overrides; later-plugin-wins on conflict. */\n // biome-ignore lint/suspicious/noExplicitAny: third-party component override map\n mergedComponents(): Record<string, FC<any>>;\n /** Merge sanitize-schema extensions across all plugins (dedupes tag names). */\n mergedSanitizeExtensions(): MergedSanitizeExtensions;\n}\n\n/**\n * Build a composer over an array of plugins.\n *\n * Order semantics:\n * - mdast transforms run sequentially in array order.\n * - hast transforms run sequentially in array order.\n * - components merge: later plugin wins on conflict (Object.assign semantics).\n * - sanitize-schema extensions: union of tag names; union of attributes by tag.\n *\n * Error semantics (D16):\n * - Each plugin call is wrapped in try/catch.\n * - On throw, an error of code `PLUGIN_ERROR` is pushed to `errors[]` and the\n * pipeline continues with the **non-transformed** input from that plugin\n * (i.e. subsequent plugins see the tree that failed plugin received).\n * - The composer NEVER throws; `parseSlide` keeps its \"never throws on input\"\n * contract (RFC 0002 D9).\n */\nexport function composePlugins(plugins: SlidePlugin[]): PluginComposer {\n return {\n async runMdast(tree, errors) {\n let current = tree;\n for (const p of plugins) {\n if (!p.mdastTransform) continue;\n const previous = current;\n try {\n const result = await p.mdastTransform(current);\n if (!result || result.type !== \"root\") {\n // Defensive: a buggy plugin may return a non-Root node. Reject and\n // keep the previous tree so subsequent plugins see a valid input.\n throw new Error(\"mdastTransform returned non-Root node\");\n }\n current = result;\n } catch (e) {\n current = previous;\n errors.push(makePluginError(p.name, \"mdastTransform\", e));\n }\n }\n return current;\n },\n async runHast(tree, errors) {\n let current = tree;\n for (const p of plugins) {\n if (!p.hastTransform) continue;\n const previous = current;\n try {\n const result = await p.hastTransform(current);\n if (!result || result.type !== \"root\") {\n throw new Error(\"hastTransform returned non-Root node\");\n }\n current = result;\n } catch (e) {\n current = previous;\n errors.push(makePluginError(p.name, \"hastTransform\", e));\n }\n }\n return current;\n },\n mergedComponents() {\n // biome-ignore lint/suspicious/noExplicitAny: third-party component override map\n const out: Record<string, FC<any>> = {};\n for (const p of plugins) {\n if (p.components) Object.assign(out, p.components);\n }\n return out;\n },\n mergedSanitizeExtensions() {\n const tagNames = new Set<string>();\n const attributes: Record<string, Set<string>> = {};\n for (const p of plugins) {\n const ext = p.sanitizeSchemaExtension;\n if (!ext) continue;\n if (ext.tagNames) {\n for (const tag of ext.tagNames) tagNames.add(tag);\n }\n if (ext.attributes) {\n for (const [tag, attrs] of Object.entries(ext.attributes)) {\n if (!attributes[tag]) attributes[tag] = new Set();\n for (const a of attrs) attributes[tag].add(a);\n }\n }\n }\n const mergedAttrs: Record<string, string[]> = {};\n for (const [tag, set] of Object.entries(attributes)) {\n mergedAttrs[tag] = Array.from(set);\n }\n return {\n tagNames: Array.from(tagNames),\n attributes: mergedAttrs,\n };\n },\n };\n}\n\nfunction makePluginError(\n name: string,\n hook: \"mdastTransform\" | \"hastTransform\",\n e: unknown,\n): SlideValidationError {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n code: \"PLUGIN_ERROR\",\n path: [],\n message: `Plugin '${name}' failed in ${hook}: ${msg}`,\n got: name,\n };\n}\n","/**\n * Sanitize schema for slide body hast trees.\n *\n * ADR D8: `defaultSchema` from `hast-util-sanitize` is used WITHOUT extensions\n * in v0.1. The default schema:\n * - Strips `<script>`, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<input>`,\n * `<style>`, `<link>` and other dangerous tags.\n * - Keeps `clobberPrefix: \"user-content-\"` so user-supplied IDs cannot\n * clobber DOM lookups.\n * - Allows the standard CommonMark + GFM safe subset (h1-h6, p, ul, ol, li,\n * blockquote, code, pre, a[href], img[src/alt], table family, em, strong,\n * del, br, hr, etc.).\n *\n * A `looseSlideSanitizeSchema` (with `figure`/`figcaption`) is an explicit\n * opt-in for v0.2 — gated by security review.\n */\nimport type { Element, Root } from \"hast\";\nimport type { MergedSanitizeExtensions } from \"./plugin.js\";\n\nlet cachedBuiltSchema: import(\"hast-util-sanitize\").Schema | undefined;\n\n/**\n * Tier 1 built-in extensions to the default sanitize schema. These cover the\n * tags emitted by built-in Slide features (alerts in `aside`, layout / header\n * / footer / paginate metadata attributes on the outer section wrapper). They\n * are NOT plugin-declared because they ship with the Slide primitive.\n *\n * Note: layout / header / footer / paginate are applied at the React component\n * level (outside the hast tree), so their attributes don't need sanitize rules.\n * Only `aside` (alerts) ends up in the hast.\n */\nconst TIER_1_TAG_NAMES: string[] = [\"aside\"];\nconst TIER_1_ATTRIBUTES: Record<string, string[]> = {\n aside: [\"className\", \"data-theo-slide-alert-type\"],\n \"*\": [\"className\"],\n};\n\n/**\n * Lazy accessor for the sanitize schema.\n *\n * Always merges `defaultSchema` with the Tier 1 baseline (aside + className).\n * When `extensions` is provided, plugin-declared tag names + attributes are\n * unioned on top (D17 / EC-3). Plugins NEVER bypass sanitize — they declare\n * what they need via `sanitizeSchemaExtension`.\n *\n * Implementation notes:\n * - The baseline schema (default + Tier 1) is cached.\n * - Plugin-merged schemas are NOT cached (depends on combination).\n */\nexport async function getSlideSanitizeSchema(\n extensions?: MergedSanitizeExtensions,\n): Promise<import(\"hast-util-sanitize\").Schema> {\n const baseline = await getBaselineSchema();\n if (\n !extensions ||\n (extensions.tagNames.length === 0 && Object.keys(extensions.attributes).length === 0)\n ) {\n return baseline;\n }\n return mergeSchema(baseline, extensions);\n}\n\nasync function getBaselineSchema(): Promise<import(\"hast-util-sanitize\").Schema> {\n if (cachedBuiltSchema) return cachedBuiltSchema;\n const { defaultSchema } = await import(\"hast-util-sanitize\");\n cachedBuiltSchema = mergeSchema(defaultSchema, {\n tagNames: TIER_1_TAG_NAMES,\n attributes: TIER_1_ATTRIBUTES,\n });\n return cachedBuiltSchema;\n}\n\nfunction mergeSchema(\n base: import(\"hast-util-sanitize\").Schema,\n extensions: MergedSanitizeExtensions,\n): import(\"hast-util-sanitize\").Schema {\n // Merge tag names (deduplicated set).\n const baseTagNames = (base.tagNames ?? []) as string[];\n const tagSet = new Set<string>(baseTagNames);\n for (const t of extensions.tagNames) tagSet.add(t);\n // Merge attributes per tag.\n const baseAttrs = (base.attributes ?? {}) as Record<string, unknown[]>;\n const mergedAttrs: Record<string, unknown[]> = { ...baseAttrs };\n for (const [tag, attrs] of Object.entries(extensions.attributes)) {\n const baseline = (mergedAttrs[tag] ?? []) as unknown[];\n const seen = new Set<string>();\n const combined: unknown[] = [];\n for (const a of baseline) {\n const key = typeof a === \"string\" ? a : JSON.stringify(a);\n if (!seen.has(key)) {\n seen.add(key);\n combined.push(a);\n }\n }\n for (const a of attrs) {\n if (!seen.has(a)) {\n seen.add(a);\n combined.push(a);\n }\n }\n mergedAttrs[tag] = combined;\n }\n return {\n ...base,\n tagNames: Array.from(tagSet),\n attributes: mergedAttrs,\n } as import(\"hast-util-sanitize\").Schema;\n}\n\n/** Count elements by tagName in a hast tree. Used for BANNED_TAG detection (ADR D13). */\nexport function collectTagCounts(tree: Root | Element): Map<string, number> {\n const counts = new Map<string, number>();\n const walk = (node: Root | Element | { type: string; children?: unknown[] }): void => {\n if (node.type === \"element\") {\n const tag = (node as Element).tagName;\n counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n const children = (node as { children?: unknown[] }).children;\n if (Array.isArray(children)) {\n for (const child of children) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n walk(child as Root | Element);\n }\n }\n }\n };\n walk(tree);\n return counts;\n}\n","/**\n * Zod schema for the Slide YAML frontmatter (`SlideFrontmatter`) and the\n * composed `SlideInput` (frontmatter + body).\n *\n * Design (see RFC 0002 and the plan in\n * `.claude/knowledge-base/plans/slide-view-primitive-plan.md` §16.3 and ADRs D4 / D14):\n *\n * - **`.strict()`** on frontmatter — unknown keys produce `INVALID_FRONTMATTER`\n * with a precise path, so an LLM can self-correct.\n * - Numeric values are `.finite()` to reject `NaN` / `Infinity`. (Currently no\n * numeric directives ship; keep the helper for future fields.)\n * - String values capped to defensible sizes to reject DoS-shaped inputs.\n * - `body` capped to 50 KB (≈30 slides' worth of text) per the sanity rule.\n * - `theme` is the canonical enum of built-in themes. Custom themes are not\n * registered via frontmatter in v0.1 — caller wraps `<Slide>` with their\n * own CSS overrides.\n */\nimport { z } from \"zod\";\n\n/** Built-in slide themes. Adding a theme means landing a CSS file in `themes/`. */\nexport const slideTheme = z.enum([\"default\", \"violet-forge\"]);\nexport type SlideTheme = z.infer<typeof slideTheme>;\n\n/** Built-in slide layouts (rich-content plan T2.1). */\nexport const slideLayout = z.enum([\n \"default\",\n \"title\",\n \"two-column\",\n \"image-right\",\n \"image-left\",\n \"code-output\",\n \"section\",\n]);\nexport type SlideLayout = z.infer<typeof slideLayout>;\n\nconst cssColor = z.string().max(64);\n// BCP-47 language tag — loose validation, just sanity to reject obvious junk.\nconst langTag = z\n .string()\n .regex(/^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/, \"Expected BCP-47 language tag\")\n .max(35);\n\n// EC-7 / rich-content T3.1 helpers.\nconst SAFE_URL_SCHEMES = [\"http://\", \"https://\"];\n\n/**\n * Sanitize a `backgroundImage` URL.\n *\n * Allowed: `http(s)://...` URLs (validated via `URL` constructor).\n * Rejected: `javascript:`, `vbscript:`, all `data:` URLs (EC-7 — perf + DoS).\n * Accepts a bare URL or a `url(...)` wrapper. Returns the unwrapped URL on\n * success, `null` on rejection.\n */\nexport function sanitizeBgUrl(input: string): string | null {\n try {\n const trimmed = input.trim();\n const url = trimmed.startsWith(\"url(\")\n ? trimmed.replace(/^url\\(\\s*['\"]?/, \"\").replace(/['\"]?\\s*\\)$/, \"\")\n : trimmed;\n const lower = url.toLowerCase();\n if (lower.startsWith(\"javascript:\") || lower.startsWith(\"vbscript:\")) return null;\n // EC-7: reject ALL data: URLs (including data:image/*) — slides inflate the\n // markdown payload and shifts loading cost. Consumer should host images.\n if (lower.startsWith(\"data:\")) return null;\n if (!SAFE_URL_SCHEMES.some((s) => lower.startsWith(s))) return null;\n new URL(url); // throws on malformed\n return url;\n } catch {\n return null;\n }\n}\n\n/** YAML frontmatter accepted by `<Slide>`. */\nexport const slideFrontmatter = z\n .object({\n theme: slideTheme.optional(),\n /** Rich-content T2: built-in layout (CSS grid). */\n layout: slideLayout.optional(),\n /** Rich-content T3: background URL (sanitized: http(s) only). */\n backgroundImage: z\n .string()\n .max(500_000)\n .transform((v) => {\n if (!v) return undefined;\n const sanitized = sanitizeBgUrl(v);\n return sanitized ?? undefined;\n })\n .optional(),\n /** Rich-content T3: CSS gradient string (validated by prefix). */\n backgroundGradient: z\n .string()\n .max(500)\n .regex(\n /^(linear|radial|conic)-gradient\\(/i,\n \"Must start with linear-/radial-/conic-gradient(\",\n )\n .optional(),\n /** Rich-content T5: header overlay text (plain, ≤ 200 chars). */\n header: z.string().max(200).optional(),\n /** Rich-content T5: footer overlay text (plain, ≤ 200 chars). */\n footer: z.string().max(200).optional(),\n /** Rich-content T5: pagination — `true` shows page number, `\"skip\"` hides. */\n paginate: z.union([z.boolean(), z.literal(\"skip\"), z.literal(\"hold\")]).optional(),\n lang: langTag.optional(),\n color: cssColor.optional(),\n backgroundColor: cssColor.optional(),\n })\n .strict();\nexport type SlideFrontmatter = z.infer<typeof slideFrontmatter>;\n\n/** Composed input: validated frontmatter object + raw markdown body string. */\nexport const slideInput = z.object({\n frontmatter: slideFrontmatter,\n body: z.string().max(50_000),\n});\nexport type SlideInput = z.infer<typeof slideInput>;\n\n/** Discrete validation-error codes — kept centralized so consumers can pattern-match safely. */\nexport type SlideValidationErrorCode =\n | \"INVALID_FRONTMATTER\"\n | \"FRONTMATTER_TOO_LARGE\"\n | \"MULTIPLE_SLIDES\"\n | \"CONTENT_TOO_LARGE\"\n | \"BANNED_TAG\"\n | \"BANNED_ATTRIBUTE\"\n | \"INVALID_ASPECT_RATIO\"\n // Rich-content plan (Tier 1 + Tier 2 plugins):\n | \"PLUGIN_ERROR\" // D16: a plugin's mdast/hast transform threw — pipeline continued\n | \"PLUGIN_PEER_DEP_MISSING\" // EC-2: dynamic import of a plugin's peer-dep failed\n | \"MARPIT_BG_UNSAFE_URL\"; // D18/EC-5: Marpit ![bg](url) rejected by URL sanitizer\n\nexport interface SlideValidationError {\n /** Path inside the input (frontmatter key, ['body'], or []). */\n path: (string | number)[];\n /** Human-readable explanation. */\n message: string;\n /** Discriminator for consumers. */\n code: SlideValidationErrorCode;\n /** Offending value when extractable from Zod's `received` or by walking the input. */\n got?: unknown;\n}\n","/**\n * Lightweight, dependency-free extractor that separates YAML frontmatter from\n * the markdown body.\n *\n * Strips a leading BOM (``) before matching the regex — common when\n * markdown is pasted from Word/Notion. See plan ADR D14 and edge-case EC-4.\n *\n * Returns `{ rawFrontmatter, body, tooLarge? }`:\n * - `rawFrontmatter`: the YAML string between the two `---` delimiters, or\n * `null` if no frontmatter is present.\n * - `body`: the markdown after the closing `---\\n` (or the full input when\n * no frontmatter).\n * - `tooLarge`: `true` if `rawFrontmatter` exceeds `MAX_RAW_FRONTMATTER`\n * bytes — the caller turns this into `FRONTMATTER_TOO_LARGE`.\n */\n\n/** Maximum size of the raw frontmatter string before YAML parsing. 10 KB cap. */\nexport const MAX_RAW_FRONTMATTER = 10_240;\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n([\\s\\S]*)$/;\nconst BOM = \"\";\n\nexport interface ExtractFrontmatterResult {\n rawFrontmatter: string | null;\n body: string;\n tooLarge?: boolean;\n}\n\nexport function extractFrontmatter(md: string): ExtractFrontmatterResult {\n const normalized = md.startsWith(BOM) ? md.slice(1) : md;\n const match = FRONTMATTER_RE.exec(normalized);\n if (!match) {\n return { rawFrontmatter: null, body: normalized };\n }\n const raw = match[1] ?? \"\";\n const body = match[2] ?? \"\";\n if (raw.length > MAX_RAW_FRONTMATTER) {\n return { rawFrontmatter: raw, body, tooLarge: true };\n }\n return { rawFrontmatter: raw, body };\n}\n","import { extractFrontmatter } from \"./frontmatter.js\";\n/**\n * Async validator for slide markdown input.\n *\n * Returns `Promise<ValidationResult>` (ADR D11 — sync impossible because\n * `yaml` and `mdast-util-from-markdown` are lazy-imported peer-deps).\n *\n * Pipeline:\n * 1. Strip BOM + extract frontmatter (`frontmatter.ts`).\n * 2. Reject `FRONTMATTER_TOO_LARGE` early if raw > 10 KB (ADR D14).\n * 3. Parse YAML in safe mode → validate against `slideFrontmatter` (Zod).\n * 4. Validate body length → emit `CONTENT_TOO_LARGE` if > 50 KB.\n * 5. Detect multi-slide via mdast `thematicBreak` walk (ADR D12) — not\n * a regex, so `---` inside fenced code blocks does not false-positive.\n * 6. Return `{ ok, input, errors[] }`.\n *\n * Errors are structured so an LLM can self-correct from the callback.\n */\nimport {\n type SlideFrontmatter,\n type SlideInput,\n type SlideValidationError,\n slideFrontmatter,\n} from \"./schema.js\";\n\nexport type ValidationResult =\n | { ok: true; input: SlideInput; errors: SlideValidationError[] }\n | { ok: false; errors: SlideValidationError[] };\n\ninterface ZodLikeIssue {\n path: ReadonlyArray<string | number>;\n message: string;\n code: string;\n received?: unknown;\n}\n\nconst MAX_BODY = 50_000;\n\nfunction valueAtPath(input: unknown, path: ReadonlyArray<string | number>): unknown {\n let cursor: unknown = input;\n for (const segment of path) {\n if (cursor === null || typeof cursor !== \"object\") return undefined;\n cursor = (cursor as Record<string | number, unknown>)[segment];\n }\n return cursor;\n}\n\nfunction formatZodIssue(\n issue: ZodLikeIssue,\n input: unknown,\n pathPrefix: (string | number)[],\n): SlideValidationError {\n const error: SlideValidationError = {\n path: [...pathPrefix, ...issue.path],\n message: issue.message,\n code: \"INVALID_FRONTMATTER\",\n };\n if (\"received\" in issue && issue.received !== undefined) {\n error.got = issue.received;\n } else if (issue.path.length > 0) {\n const value = valueAtPath(input, issue.path);\n if (value !== undefined) error.got = value;\n }\n return error;\n}\n\nasync function detectMultiSlide(body: string): Promise<{\n multi: boolean;\n firstSlideBody: string;\n}> {\n // mdast-based — regex would false-positive on `---` inside fenced code blocks.\n // Cost: one extra parse. Cheap; would be parsed again in `parse.ts` anyway.\n const { fromMarkdown } = await import(\"mdast-util-from-markdown\");\n const tree = fromMarkdown(body);\n const hrIdx = tree.children.findIndex((n) => n.type === \"thematicBreak\");\n if (hrIdx === -1) {\n return { multi: false, firstSlideBody: body };\n }\n const firstHr = tree.children[hrIdx];\n const offset = firstHr?.position?.start.offset;\n if (typeof offset !== \"number\") {\n return { multi: true, firstSlideBody: body };\n }\n return { multi: true, firstSlideBody: body.slice(0, offset) };\n}\n\n/**\n * Validate the markdown input. Returns errors structured for LLM self-correction.\n *\n * Async because frontmatter YAML parsing and mdast multi-slide detection both\n * use lazy-imported peer-deps (yaml + mdast-util-from-markdown).\n */\nexport async function validateSlide(markdown: string): Promise<ValidationResult> {\n const errors: SlideValidationError[] = [];\n const extracted = extractFrontmatter(markdown);\n\n if (extracted.tooLarge) {\n errors.push({\n code: \"FRONTMATTER_TOO_LARGE\",\n path: [],\n message: `Raw frontmatter exceeds ${10_240} bytes.`,\n got: extracted.rawFrontmatter?.length,\n });\n return { ok: false, errors };\n }\n\n let frontmatter: SlideFrontmatter = {};\n if (extracted.rawFrontmatter !== null) {\n let parsed: unknown;\n try {\n const yaml = await import(\"yaml\");\n parsed = yaml.parse(extracted.rawFrontmatter);\n } catch (e) {\n errors.push({\n code: \"INVALID_FRONTMATTER\",\n path: [],\n message: e instanceof Error ? e.message : \"YAML parse failed.\",\n got: extracted.rawFrontmatter,\n });\n return { ok: false, errors };\n }\n if (parsed === null || parsed === undefined) {\n // empty frontmatter — treat as {}\n frontmatter = {};\n } else if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n errors.push({\n code: \"INVALID_FRONTMATTER\",\n path: [],\n message: \"Frontmatter must be a YAML mapping (object).\",\n got: parsed,\n });\n return { ok: false, errors };\n } else {\n const result = slideFrontmatter.safeParse(parsed);\n if (!result.success) {\n for (const issue of result.error.issues) {\n errors.push(formatZodIssue(issue as unknown as ZodLikeIssue, parsed, []));\n }\n return { ok: false, errors };\n }\n frontmatter = result.data;\n }\n }\n\n let body = extracted.body;\n\n if (body.length > MAX_BODY) {\n errors.push({\n code: \"CONTENT_TOO_LARGE\",\n path: [\"body\"],\n message: `Body exceeds ${MAX_BODY} characters.`,\n got: body.length,\n });\n return { ok: false, errors };\n }\n\n const multi = await detectMultiSlide(body);\n if (multi.multi) {\n body = multi.firstSlideBody;\n errors.push({\n code: \"MULTIPLE_SLIDES\",\n path: [\"body\"],\n message:\n \"Input contains a top-level thematic break (---). <Slide> renders single-slide markdown only. Only the first slide was rendered.\",\n });\n }\n\n return { ok: true, input: { frontmatter, body }, errors };\n}\n","import type { Root as HastRoot } from \"hast\";\n/**\n * Markdown → React tree pipeline for the Slide primitive.\n *\n * parseSlide(markdown)\n * ├─ validateSlide(markdown) → { frontmatter, body, errors? }\n * ├─ parseBody(body) → mdast Root (micromark + GFM)\n * ├─ mdastToHast(mdastTree) → hast Root (allowDangerousHtml: false)\n * ├─ sanitizeHast(hastTree) → hast Root + BANNED_TAG diff (ADR D13)\n * └─ hastToReact(hastTree) → React tree (jsx-runtime, no innerHTML)\n *\n * Every transform is composed of lazy-imported peer-deps so the slide bundle\n * stays under budget and never vendors the markdown stack into the barrel\n * (ADR D2 / D3 of the slide plan).\n */\nimport type { Root as MdastRoot } from \"mdast\";\nimport type { ReactElement } from \"react\";\nimport { detectAlerts } from \"./alerts.js\";\nimport { extractMarpitBackgrounds } from \"./marpit-bg.js\";\nimport { type MergedSanitizeExtensions, type SlidePlugin, composePlugins } from \"./plugin.js\";\nimport { collectTagCounts, getSlideSanitizeSchema } from \"./sanitize.js\";\nimport { type SlideFrontmatter, type SlideValidationError, sanitizeBgUrl } from \"./schema.js\";\nimport { validateSlide } from \"./validate.js\";\n\nexport interface ParseSlideOptions {\n /** Override individual element renderers (passed to hast-util-to-jsx-runtime). */\n components?: Record<string, unknown>;\n /** Rich-content plugins (Tier 2). Order matters — D13 / RFC 0004. */\n plugins?: SlidePlugin[];\n}\n\nexport interface ExtractedBackground {\n /** Sanitized URL (http/https only). */\n url: string;\n /** Optional Marpit modifier: `cover` | `fit` | `left` | `right`. */\n modifier?: \"cover\" | \"fit\" | \"left\" | \"right\";\n}\n\nexport interface ParsedSlide {\n frontmatter: SlideFrontmatter;\n /** React element renderable as the body of a slide. Always defined (may be empty Fragment). */\n tree: ReactElement;\n /** Validation + sanitize errors collected during parsing. Empty on success. */\n errors: SlideValidationError[];\n /** True when the input contained a top-level thematic break and only the first slide was rendered. */\n truncated: boolean;\n /**\n * Background image extracted from Marpit `![bg](url)` syntax (D18 / EC-5).\n * Precedence: `frontmatter.backgroundImage` > `extractedBackground.url`.\n */\n extractedBackground?: ExtractedBackground;\n}\n\nexport async function parseBody(body: string): Promise<MdastRoot> {\n const [{ fromMarkdown }, { gfmFromMarkdown }, { gfm }] = await Promise.all([\n import(\"mdast-util-from-markdown\"),\n import(\"mdast-util-gfm\"),\n import(\"micromark-extension-gfm\"),\n ]);\n return fromMarkdown(body, {\n extensions: [gfm()],\n mdastExtensions: [gfmFromMarkdown()],\n });\n}\n\nexport async function mdastToHast(tree: MdastRoot): Promise<HastRoot> {\n const { toHast } = await import(\"mdast-util-to-hast\");\n const hast = toHast(tree, { allowDangerousHtml: false });\n if (!hast || hast.type !== \"root\") {\n return { type: \"root\", children: hast ? [hast] : [] } as HastRoot;\n }\n return hast as HastRoot;\n}\n\nexport async function sanitizeHast(\n tree: HastRoot,\n extensions?: MergedSanitizeExtensions,\n): Promise<{ tree: HastRoot; bannedTags: string[] }> {\n const schema = await getSlideSanitizeSchema(extensions);\n const { sanitize } = await import(\"hast-util-sanitize\");\n const preCount = collectTagCounts(tree);\n const safe = sanitize(tree, schema);\n const safeRoot: HastRoot =\n safe.type === \"root\" ? (safe as HastRoot) : ({ type: \"root\", children: [safe] } as HastRoot);\n const postCount = collectTagCounts(safeRoot);\n const bannedTags: string[] = [];\n for (const [tag, before] of preCount) {\n const after = postCount.get(tag) ?? 0;\n if (after < before) {\n // Push once per stripped tag name; agents can self-correct from this signal.\n bannedTags.push(tag);\n }\n }\n return { tree: safeRoot, bannedTags };\n}\n\nexport async function hastToReact(\n tree: HastRoot,\n components?: Record<string, unknown>,\n): Promise<ReactElement> {\n const { Fragment, jsx, jsxs } = await import(\"react/jsx-runtime\");\n const { toJsxRuntime } = await import(\"hast-util-to-jsx-runtime\");\n return toJsxRuntime(tree, {\n Fragment,\n jsx,\n jsxs,\n components,\n }) as ReactElement;\n}\n\n/**\n * Public entry point. Validates → parses → sanitizes → returns a React tree.\n *\n * Never throws on input. Errors (validation, BANNED_TAG) are collected into\n * `errors[]` so callers can show them via `onValidationError`.\n */\nexport async function parseSlide(\n markdown: string,\n opts: ParseSlideOptions = {},\n): Promise<ParsedSlide> {\n const errors: SlideValidationError[] = [];\n let frontmatter: SlideFrontmatter = {};\n let body = markdown;\n let truncated = false;\n\n const validation = await validateSlide(markdown);\n if (validation.ok) {\n frontmatter = validation.input.frontmatter;\n body = validation.input.body;\n errors.push(...validation.errors);\n truncated = validation.errors.some((e) => e.code === \"MULTIPLE_SLIDES\");\n } else {\n errors.push(...validation.errors);\n // Best-effort recovery: render the body as if frontmatter was absent.\n // This preserves \"never throw, always render something\" contract.\n body = markdown;\n }\n\n const compose = composePlugins(opts.plugins ?? []);\n\n const rawMdast = await parseBody(body);\n // Tier 1 — GFM alerts (D3): in-tree post-process, no plugin needed.\n // Runs BEFORE plugin mdastTransforms so plugins observing the tree see the\n // normalized aside shape (consistent with how `> [!NOTE]` is treated as\n // native GFM by consumers).\n detectAlerts(rawMdast);\n // Tier 1 — Marpit ![bg](url) (D18 / EC-5): extract before plugins run so the\n // tree handed to plugins is free of bg directives.\n const { tree: mdastNoBg, background: marpitBg } = extractMarpitBackgrounds(rawMdast);\n let extractedBackground: ExtractedBackground | undefined;\n if (marpitBg) {\n const safeUrl = sanitizeBgUrl(marpitBg.url);\n if (safeUrl) {\n extractedBackground = { url: safeUrl, modifier: marpitBg.modifier };\n } else {\n errors.push({\n code: \"MARPIT_BG_UNSAFE_URL\",\n path: [],\n message: \"Marpit ![bg](url) rejected: unsafe scheme or malformed URL.\",\n got: marpitBg.url.slice(0, 80),\n });\n }\n }\n const transformedMdast = await compose.runMdast(mdastNoBg, errors);\n const rawHast = await mdastToHast(transformedMdast);\n const transformedHast = await compose.runHast(rawHast, errors);\n\n // D17 / EC-3: merge plugin sanitize-schema extensions with defaultSchema so\n // plugins emitting non-default tags (Shiki spans, KaTeX MathML, Mermaid SVG)\n // survive the security barrier. Sanitize ALWAYS runs.\n const sanitizeExtensions = compose.mergedSanitizeExtensions();\n const { tree: safeTree, bannedTags } = await sanitizeHast(transformedHast, sanitizeExtensions);\n for (const tag of bannedTags) {\n errors.push({\n code: \"BANNED_TAG\",\n path: [\"body\"],\n message: `Tag <${tag}> was stripped by the slide sanitizer.`,\n got: tag,\n });\n }\n\n // D2 / D13: plugin component overrides merge on top of consumer's components;\n // plugin definitions win on conflict (last-write-wins in mergedComponents).\n const mergedComponents = {\n ...(opts.components ?? {}),\n ...compose.mergedComponents(),\n };\n const tree = await hastToReact(safeTree, mergedComponents);\n\n return { frontmatter, tree, errors, truncated, extractedBackground };\n}\n","/**\n * Container-fit hook for the Slide primitive.\n *\n * Observes the host element via `ResizeObserver` and computes a CSS scale\n * factor that fits the fixed logical canvas (default 1280×720) inside it,\n * preserving aspect ratio. Clamped to `[minScale, maxScale]`.\n *\n * Algorithm: `scale = clamp(min(W / canvasW, H / canvasH), min, max)`.\n *\n * Adapted from Reveal.js `transformSlides` (see reference doc §4.5 / §14.2).\n *\n * Cleanup: disconnects the observer on unmount or when deps change.\n */\nimport { type RefObject, useEffect, useState } from \"react\";\n\nexport interface UseSlideFitOptions {\n /** Lower clamp for scale. Default 0.1. */\n minScale?: number;\n /** Upper clamp for scale. Default 4. */\n maxScale?: number;\n}\n\nexport function useSlideFit(\n ref: RefObject<HTMLElement | null>,\n canvasW: number,\n canvasH: number,\n opts: UseSlideFitOptions = {},\n): number {\n const { minScale = 0.1, maxScale = 4 } = opts;\n const [scale, setScale] = useState(1);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (typeof ResizeObserver === \"undefined\") {\n // Test environments without ResizeObserver: do a one-shot measurement.\n const { width, height } = el.getBoundingClientRect();\n if (width > 0 && height > 0) {\n const raw = Math.min(width / canvasW, height / canvasH);\n setScale(Math.max(minScale, Math.min(raw, maxScale)));\n }\n return;\n }\n const update = (): void => {\n const { width, height } = el.getBoundingClientRect();\n if (width <= 0 || height <= 0) return;\n const raw = Math.min(width / canvasW, height / canvasH);\n const clamped = Math.max(minScale, Math.min(raw, maxScale));\n if (Number.isFinite(clamped)) setScale(clamped);\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, [ref, canvasW, canvasH, minScale, maxScale]);\n\n return scale;\n}\n","import {\n type FC,\n type ReactElement,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n/**\n * `<Slide>` — view-only primitive that renders markdown + YAML frontmatter\n * into a themed, fixed-aspect surface. Lives in the isolated subpath\n * `@usetheo/ui/slide`.\n *\n * See RFC 0002 (`docs/rfcs/0002-slide.md`) and the plan in\n * `.claude/knowledge-base/plans/slide-view-primitive-plan.md`.\n *\n * SSR note: initial render returns the section wrapper; the parsed React tree\n * fills in client-side via the useEffect → `parseSlide` chain. Consumers\n * wrapping in Suspense / skeleton can mitigate visible jump.\n *\n * Performance tip: pass a memoized `onValidationError` (via `useCallback`)\n * and a memoized `components` map. Inline arrows recreate on each render\n * and cause re-parses.\n */\nimport { type ParsedSlide, parseSlide } from \"./parse.js\";\nimport type { SlidePlugin } from \"./plugin.js\";\nimport type { SlideValidationError } from \"./schema.js\";\nimport type { SlideTheme } from \"./themes/index.js\";\nimport { useSlideFit } from \"./use-slide-fit.js\";\n\nexport type { SlideTheme } from \"./themes/index.js\";\nexport type {\n SlideValidationError,\n SlideValidationErrorCode,\n} from \"./schema.js\";\nexport type { SlidePlugin } from \"./plugin.js\";\n\nexport interface SlideAspectRatio {\n width: number;\n height: number;\n}\n\nexport interface SlideProps {\n /**\n * Slide markdown. CommonMark + GFM + optional YAML frontmatter delimited\n * by `---`. Top-level horizontal rules (`---` on their own line outside\n * frontmatter) imply a deck split and trigger `MULTIPLE_SLIDES`; only the\n * first slide is rendered.\n *\n * Note: `<figure>`/`<figcaption>` tags are stripped by the default\n * sanitize schema (D8). Use `<img>` directly for captionless images.\n */\n markdown: string;\n /** Theme name. Defaults to `\"default\"`. */\n theme?: SlideTheme;\n /**\n * Aspect ratio of the logical canvas. Default `\"16:9\"` → 1280×720.\n * Custom `{ width, height }` accepted; zero/negative/NaN fallback to 16:9.\n */\n aspectRatio?: \"16:9\" | \"4:3\" | SlideAspectRatio;\n /** Lower clamp for container-fit scale. Default 0.1. */\n minScale?: number;\n /** Upper clamp for container-fit scale. Default 4. */\n maxScale?: number;\n /** Best-effort callback invoked in useEffect when validation/sanitize emits errors. */\n onValidationError?: (errors: SlideValidationError[]) => void;\n /** Override individual element renderers (passed to hast-util-to-jsx-runtime). */\n // biome-ignore lint/suspicious/noExplicitAny: third-party component override map\n components?: Record<string, FC<any>>;\n /**\n * Rich-content plugins (Tier 2). Each plugin transforms the mdast/hast tree\n * or adds React component overrides. Pass MEMOIZED arrays to avoid re-parses\n * on every render (D15).\n *\n * @example\n * const plugins = useMemo(() => [emojiPlugin(), shikiPlugin()], []);\n * <Slide markdown={md} plugins={plugins} />\n */\n plugins?: SlidePlugin[];\n /** Accessible label for the slide. Defaults to `\"Slide\"`. */\n \"aria-label\"?: string;\n /** Optional class applied to the outer host element (sizing/positioning hook). */\n className?: string;\n}\n\nconst ASPECT_PRESETS: Record<\"16:9\" | \"4:3\", SlideAspectRatio> = {\n \"16:9\": { width: 1280, height: 720 },\n \"4:3\": { width: 960, height: 720 },\n};\n\n/**\n * Resolve aspectRatio prop to concrete dimensions. Invalid custom values\n * (zero, negative, non-finite) silently fall back to 16:9 — surfaced via\n * `INVALID_ASPECT_RATIO` error in `onValidationError`. ADR D14.\n */\nfunction resolveCanvas(ar: SlideProps[\"aspectRatio\"]): {\n width: number;\n height: number;\n invalid?: boolean;\n} {\n if (!ar || ar === \"16:9\") return ASPECT_PRESETS[\"16:9\"];\n if (ar === \"4:3\") return ASPECT_PRESETS[\"4:3\"];\n if (\n ar.width <= 0 ||\n ar.height <= 0 ||\n !Number.isFinite(ar.width) ||\n !Number.isFinite(ar.height)\n ) {\n return { ...ASPECT_PRESETS[\"16:9\"], invalid: true };\n }\n return ar;\n}\n\nexport const Slide: FC<SlideProps> = ({\n markdown,\n theme = \"default\",\n aspectRatio = \"16:9\",\n minScale,\n maxScale,\n onValidationError,\n components,\n plugins,\n className,\n \"aria-label\": ariaLabel = \"Slide\",\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const canvas = useMemo(() => resolveCanvas(aspectRatio), [aspectRatio]);\n const scale = useSlideFit(containerRef, canvas.width, canvas.height, {\n minScale,\n maxScale,\n });\n\n const [parsed, setParsed] = useState<ParsedSlide | null>(null);\n\n // Surface INVALID_ASPECT_RATIO immediately when the prop is invalid.\n // Independent of markdown parsing so consumers see the signal even with\n // an empty markdown payload.\n useEffect(() => {\n if (canvas.invalid && onValidationError) {\n onValidationError([\n {\n code: \"INVALID_ASPECT_RATIO\",\n path: [\"aspectRatio\"],\n message:\n \"aspectRatio width/height must be positive finite numbers; falling back to 16:9.\",\n got: aspectRatio,\n },\n ]);\n }\n }, [canvas.invalid, onValidationError, aspectRatio]);\n\n // EC-7 / version counter — prevents older parses from overwriting newer\n // ones on rapid prop changes.\n const versionRef = useRef(0);\n useEffect(() => {\n const myVersion = ++versionRef.current;\n let cancelled = false;\n parseSlide(markdown, { components, plugins }).then(\n (result) => {\n if (cancelled || myVersion !== versionRef.current) return;\n setParsed(result);\n if (result.errors.length > 0 && onValidationError) {\n onValidationError(result.errors);\n }\n },\n (err: unknown) => {\n // Defensive: parseSlide promises to never throw, but if a runtime\n // failure escapes (e.g. peer-dep missing), surface a synthetic error\n // and keep the slide visible with empty body.\n if (cancelled || myVersion !== versionRef.current) return;\n if (onValidationError) {\n onValidationError([\n {\n code: \"INVALID_FRONTMATTER\",\n path: [],\n message: err instanceof Error ? err.message : \"parseSlide rejected.\",\n },\n ]);\n }\n setParsed({\n frontmatter: {},\n tree: emptyFragment(),\n errors: [],\n truncated: false,\n });\n },\n );\n return () => {\n cancelled = true;\n };\n }, [markdown, components, plugins, onValidationError]);\n\n const treeNode: ReactNode = parsed?.tree ?? null;\n const slideThemeAttr: SlideTheme = theme;\n\n // Tier 1 — frontmatter-driven attributes & overlays.\n const fm = parsed?.frontmatter ?? {};\n const layout = fm.layout;\n // Background precedence: explicit frontmatter > Marpit ![bg](url) (D18 / EC-5).\n const bgUrl = fm.backgroundImage ?? parsed?.extractedBackground?.url;\n const bgModifier = parsed?.extractedBackground?.modifier;\n const bgGradient = fm.backgroundGradient;\n const headerText = fm.header;\n const footerText = fm.footer;\n const paginateValue = fm.paginate;\n const showPaginate = paginateValue === true;\n\n // Background style: gradient > image > inherited.\n const backgroundImageStyle: string | undefined = bgGradient\n ? bgGradient\n : bgUrl\n ? `url(\"${bgUrl}\")`\n : undefined;\n\n return (\n <div\n ref={containerRef}\n className={[\"theo-slide-host\", className].filter(Boolean).join(\" \")}\n data-theo-slide-host\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n width: \"100%\",\n height: \"100%\",\n }}\n >\n {/*\n Section is taken out of normal flow with position:absolute so its\n 1280×720 layout box doesn't inflate the host. `translate(-50%, -50%)`\n anchors the section's center to the host's center; the scale applies\n relative to that same origin. Mirrors Reveal.js transformSlides()\n (see `.claude/knowledge-base/reference/slide.md` §4.5 / §14.2).\n */}\n <section\n aria-roledescription=\"slide\"\n aria-label={ariaLabel}\n className=\"theo-slide\"\n data-theo-slide-theme={slideThemeAttr}\n data-theo-slide-layout={layout ?? \"default\"}\n data-theo-slide-bg-modifier={bgModifier}\n style={{\n position: \"absolute\",\n top: \"50%\",\n left: \"50%\",\n width: canvas.width,\n height: canvas.height,\n transform: `translate(-50%, -50%) scale(${scale})`,\n transformOrigin: \"center\",\n padding: \"var(--theo-slide-padding, 64px)\",\n color: \"inherit\",\n // Background fills the canvas. When neither image nor gradient is set,\n // the slide inherits the parent surface (same pattern as Whiteboard).\n background: \"transparent\",\n // Only set backgroundImage + ancillary props when one is provided —\n // otherwise the shorthand `background: transparent` is preserved as-is\n // (important for the inherit-from-parent guarantee).\n ...(backgroundImageStyle\n ? {\n backgroundImage: backgroundImageStyle,\n backgroundSize: bgModifier === \"fit\" ? \"contain\" : \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n }\n : {}),\n fontFamily:\n \"var(--theo-slide-font-family, system-ui, -apple-system, 'Segoe UI', sans-serif)\",\n fontSize: \"var(--theo-slide-font-base, 28px)\",\n lineHeight: 1.5,\n boxSizing: \"border-box\",\n overflow: \"hidden\",\n }}\n >\n {headerText ? (\n <div className=\"theo-slide-header\" aria-hidden=\"true\">\n {headerText}\n </div>\n ) : null}\n {treeNode}\n {footerText ? (\n <div className=\"theo-slide-footer\" aria-hidden=\"true\">\n {footerText}\n </div>\n ) : null}\n {showPaginate ? (\n <div className=\"theo-slide-paginate\" aria-hidden=\"true\">\n 1\n </div>\n ) : null}\n </section>\n </div>\n );\n};\n\n/** Empty React fragment placeholder used when parseSlide fails fatally. */\nfunction emptyFragment(): ReactElement {\n // Cheap fallback that does not require importing Fragment from react/jsx-runtime.\n // Using a real element keeps the type as ReactElement (not ReactNode).\n return { type: \"span\", props: { children: null }, key: null } as unknown as ReactElement;\n}\n","/**\n * Theme registry for the Slide primitive. Built-in themes that ship with the\n * `@usetheo/ui/slide` subpath. Custom themes are NOT registered through this\n * module in v0.1 — consumers override CSS variables on `.theo-slide` directly.\n */\n\nexport const slideThemes = [\"default\", \"violet-forge\"] as const;\nexport type SlideTheme = (typeof slideThemes)[number];\n\n/** Returns true if `value` is a recognized built-in theme. */\nexport function isSlideTheme(value: unknown): value is SlideTheme {\n return typeof value === \"string\" && (slideThemes as readonly string[]).includes(value);\n}\n","/**\n * JSON Schema for the Slide YAML frontmatter — derived from the Zod\n * `slideFrontmatter` schema via Zod 4's native `z.toJSONSchema()`.\n *\n * Purpose: enable LLM-driven generation pipelines (OpenAI structured outputs,\n * Anthropic tool use, function calling, JSON-mode constrained generation) to\n * produce frontmatter that is GUARANTEED to pass `validateSlide` without\n * round-tripping through human-written prompt engineering.\n *\n * Usage (Anthropic tool use):\n *\n * import { slideFrontmatterJsonSchema } from \"@usetheo/ui/slide\";\n *\n * const tool = {\n * name: \"render_slide\",\n * description: \"Render a presentation slide.\",\n * input_schema: {\n * type: \"object\",\n * properties: {\n * frontmatter: slideFrontmatterJsonSchema,\n * body: { type: \"string\", description: \"CommonMark + GFM markdown body\" },\n * },\n * required: [\"body\"],\n * },\n * };\n *\n * The output matches Zod's behaviour exactly — anything that passes the JSON\n * Schema validator passes Zod, and vice-versa.\n *\n * Companion guide: `docs/slide-llm-guide.md` — copy-paste system prompt that\n * documents every Tier 1 + Tier 2 feature with examples.\n */\nimport { z } from \"zod\";\nimport { slideFrontmatter } from \"./schema.js\";\n\ninterface JsonSchemaProperties {\n [key: string]: Record<string, unknown>;\n}\n\ninterface JsonSchemaWithProperties {\n properties?: JsonSchemaProperties;\n [key: string]: unknown;\n}\n\n/**\n * JSON Schema (Draft 2020-12) describing all accepted frontmatter fields:\n * `theme`, `layout`, `lang`, `color`, `backgroundColor`, `backgroundImage`,\n * `backgroundGradient`, `header`, `footer`, `paginate`.\n *\n * Note: `backgroundImage` uses a Zod `.transform()` (URL sanitization) which\n * cannot be represented in JSON Schema natively. We pass `unrepresentable: \"any\"`\n * to fall back to `{}` for transformed fields, then enrich `backgroundImage`\n * with manual metadata so LLMs still get useful constraints.\n *\n * Generated once at module load. Treat as immutable.\n */\nconst rawSchema = z.toJSONSchema(slideFrontmatter, {\n unrepresentable: \"any\",\n}) as JsonSchemaWithProperties;\n\nif (rawSchema.properties) {\n // Backgrounds carry hard constraints (http(s) only, ≤500_000 chars) that\n // Zod erases through `.transform()`. Inline them here so the LLM tooling\n // sees the same contract `sanitizeBgUrl` enforces at runtime.\n rawSchema.properties.backgroundImage = {\n type: \"string\",\n format: \"uri\",\n maxLength: 500_000,\n pattern: \"^https?://\",\n description:\n \"Slide background image URL. Only http(s) schemes are accepted; data: URLs are rejected at runtime (use a hosted image).\",\n };\n}\n\nexport const slideFrontmatterJsonSchema = rawSchema;\n"]}
@@ -0,0 +1,29 @@
1
+ import { a as SlidePlugin } from '../../../plugin-Atb0VKtr.js';
2
+ import 'hast';
3
+ import 'mdast';
4
+ import 'react';
5
+ import 'zod';
6
+
7
+ /**
8
+ * 100 emoji shortcodes commonly used in tech presentations.
9
+ *
10
+ * Selection rationale: covers the top ~80% of GitHub markdown usage in code
11
+ * docs, release notes, slides. Unicode-native (no external font dependency)
12
+ * so the appearance follows the OS emoji font (Apple Color Emoji on macOS,
13
+ * Segoe UI Emoji on Windows, Noto Color Emoji on Linux/Android).
14
+ *
15
+ * Adding entries: keep keys lowercase, kebab/underscore allowed, single Unicode
16
+ * scalar value (sequences like 👨‍💻 are also OK).
17
+ *
18
+ * Twemoji variant: out of scope — `slide/plugins/emoji-twemoji` is a v0.5
19
+ * candidate if demand emerges.
20
+ */
21
+ declare const EMOJI_MAP: Record<string, string>;
22
+
23
+ interface EmojiPluginOptions {
24
+ /** Override or extend the default emoji map. Merged on top of the built-in 100. */
25
+ extra?: Record<string, string>;
26
+ }
27
+ declare function emojiPlugin(opts?: EmojiPluginOptions): SlidePlugin;
28
+
29
+ export { EMOJI_MAP, type EmojiPluginOptions, emojiPlugin };
@@ -0,0 +1,157 @@
1
+ // src/components/primitives/slide/plugins/emoji/map.ts
2
+ var EMOJI_MAP = {
3
+ // Faces & reactions
4
+ smile: "\u{1F600}",
5
+ grin: "\u{1F601}",
6
+ joy: "\u{1F602}",
7
+ rofl: "\u{1F923}",
8
+ wink: "\u{1F609}",
9
+ sweat_smile: "\u{1F605}",
10
+ blush: "\u{1F60A}",
11
+ thinking: "\u{1F914}",
12
+ neutral_face: "\u{1F610}",
13
+ expressionless: "\u{1F611}",
14
+ no_mouth: "\u{1F636}",
15
+ heart_eyes: "\u{1F60D}",
16
+ sunglasses: "\u{1F60E}",
17
+ cry: "\u{1F622}",
18
+ sob: "\u{1F62D}",
19
+ scream: "\u{1F631}",
20
+ fearful: "\u{1F628}",
21
+ flushed: "\u{1F633}",
22
+ zzz: "\u{1F4A4}",
23
+ // Hand gestures
24
+ thumbsup: "\u{1F44D}",
25
+ thumbs_up: "\u{1F44D}",
26
+ thumbsdown: "\u{1F44E}",
27
+ thumbs_down: "\u{1F44E}",
28
+ ok_hand: "\u{1F44C}",
29
+ clap: "\u{1F44F}",
30
+ wave: "\u{1F44B}",
31
+ raised_hands: "\u{1F64C}",
32
+ pray: "\u{1F64F}",
33
+ muscle: "\u{1F4AA}",
34
+ point_right: "\u{1F449}",
35
+ point_left: "\u{1F448}",
36
+ point_up: "\u{1F446}",
37
+ point_down: "\u{1F447}",
38
+ // Signals & icons
39
+ check: "\u2705",
40
+ white_check_mark: "\u2705",
41
+ x: "\u274C",
42
+ heavy_check_mark: "\u2714",
43
+ ballot_box_with_check: "\u2611\uFE0F",
44
+ warning: "\u26A0\uFE0F",
45
+ no_entry: "\u26D4",
46
+ no_entry_sign: "\u{1F6AB}",
47
+ question: "\u2753",
48
+ exclamation: "\u2757",
49
+ bangbang: "\u203C\uFE0F",
50
+ information_source: "\u2139\uFE0F",
51
+ zap: "\u26A1",
52
+ bell: "\u{1F514}",
53
+ no_bell: "\u{1F515}",
54
+ // Tech
55
+ rocket: "\u{1F680}",
56
+ computer: "\u{1F4BB}",
57
+ desktop_computer: "\u{1F5A5}\uFE0F",
58
+ keyboard: "\u2328\uFE0F",
59
+ bulb: "\u{1F4A1}",
60
+ hammer: "\u{1F528}",
61
+ wrench: "\u{1F527}",
62
+ gear: "\u2699\uFE0F",
63
+ lock: "\u{1F512}",
64
+ unlock: "\u{1F513}",
65
+ key: "\u{1F511}",
66
+ package: "\u{1F4E6}",
67
+ link: "\u{1F517}",
68
+ paperclip: "\u{1F4CE}",
69
+ scissors: "\u2702\uFE0F",
70
+ hourglass: "\u23F3",
71
+ alarm_clock: "\u23F0",
72
+ stopwatch: "\u23F1\uFE0F",
73
+ hash: "#\uFE0F\u20E3",
74
+ // Files & docs
75
+ page_facing_up: "\u{1F4C4}",
76
+ pencil: "\u270F\uFE0F",
77
+ memo: "\u{1F4DD}",
78
+ book: "\u{1F4D6}",
79
+ books: "\u{1F4DA}",
80
+ bookmark: "\u{1F516}",
81
+ clipboard: "\u{1F4CB}",
82
+ chart_with_upwards_trend: "\u{1F4C8}",
83
+ chart_with_downwards_trend: "\u{1F4C9}",
84
+ bar_chart: "\u{1F4CA}",
85
+ // Status / energy
86
+ fire: "\u{1F525}",
87
+ sparkles: "\u2728",
88
+ star: "\u2B50",
89
+ star2: "\u{1F31F}",
90
+ tada: "\u{1F389}",
91
+ confetti_ball: "\u{1F38A}",
92
+ boom: "\u{1F4A5}",
93
+ rainbow: "\u{1F308}",
94
+ sunny: "\u2600\uFE0F",
95
+ cloud: "\u2601\uFE0F",
96
+ snowflake: "\u2744\uFE0F",
97
+ hot_face: "\u{1F975}",
98
+ cold_face: "\u{1F976}",
99
+ // Affection & symbols
100
+ heart: "\u2764\uFE0F",
101
+ broken_heart: "\u{1F494}",
102
+ hearts: "\u{1F495}",
103
+ fist: "\u270A",
104
+ trophy: "\u{1F3C6}",
105
+ medal: "\u{1F396}\uFE0F",
106
+ hundred: "\u{1F4AF}",
107
+ eye: "\u{1F441}\uFE0F",
108
+ eyes: "\u{1F440}",
109
+ // Coffee & food (slide signature)
110
+ coffee: "\u2615",
111
+ tea: "\u{1F375}",
112
+ beer: "\u{1F37A}",
113
+ pizza: "\u{1F355}",
114
+ // Arrows
115
+ arrow_right: "\u27A1\uFE0F",
116
+ arrow_left: "\u2B05\uFE0F",
117
+ arrow_up: "\u2B06\uFE0F",
118
+ arrow_down: "\u2B07\uFE0F",
119
+ arrow_upper_right: "\u2197\uFE0F",
120
+ arrow_lower_right: "\u2198\uFE0F"
121
+ };
122
+
123
+ // src/components/primitives/slide/plugins/emoji/index.ts
124
+ var SHORTCODE_RE = /:([a-z0-9_+-]+):/g;
125
+ function isInsideCodeOrPre(ancestors) {
126
+ for (let i = ancestors.length - 1; i >= 0; i--) {
127
+ const a = ancestors[i];
128
+ if (!a || a.type !== "element") continue;
129
+ if (a.tagName === "code" || a.tagName === "pre") return true;
130
+ }
131
+ return false;
132
+ }
133
+ function emojiPlugin(opts = {}) {
134
+ const map = { ...EMOJI_MAP, ...opts.extra ?? {} };
135
+ return {
136
+ name: "emoji",
137
+ async hastTransform(tree) {
138
+ const { visitParents } = await import('unist-util-visit-parents');
139
+ visitParents(
140
+ tree,
141
+ "text",
142
+ (node, ancestors) => {
143
+ if (isInsideCodeOrPre(ancestors)) return;
144
+ node.value = node.value.replace(SHORTCODE_RE, (match, code) => {
145
+ const replacement = map[code.toLowerCase()];
146
+ return replacement ?? match;
147
+ });
148
+ }
149
+ );
150
+ return tree;
151
+ }
152
+ };
153
+ }
154
+
155
+ export { EMOJI_MAP, emojiPlugin };
156
+ //# sourceMappingURL=index.js.map
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/primitives/slide/plugins/emoji/map.ts","../../../../src/components/primitives/slide/plugins/emoji/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 100 emoji shortcodes commonly used in tech presentations.\n *\n * Selection rationale: covers the top ~80% of GitHub markdown usage in code\n * docs, release notes, slides. Unicode-native (no external font dependency)\n * so the appearance follows the OS emoji font (Apple Color Emoji on macOS,\n * Segoe UI Emoji on Windows, Noto Color Emoji on Linux/Android).\n *\n * Adding entries: keep keys lowercase, kebab/underscore allowed, single Unicode\n * scalar value (sequences like 👨‍💻 are also OK).\n *\n * Twemoji variant: out of scope — `slide/plugins/emoji-twemoji` is a v0.5\n * candidate if demand emerges.\n */\nexport const EMOJI_MAP: Record<string, string> = {\n // Faces & reactions\n smile: \"\\u{1F600}\",\n grin: \"\\u{1F601}\",\n joy: \"\\u{1F602}\",\n rofl: \"\\u{1F923}\",\n wink: \"\\u{1F609}\",\n sweat_smile: \"\\u{1F605}\",\n blush: \"\\u{1F60A}\",\n thinking: \"\\u{1F914}\",\n neutral_face: \"\\u{1F610}\",\n expressionless: \"\\u{1F611}\",\n no_mouth: \"\\u{1F636}\",\n heart_eyes: \"\\u{1F60D}\",\n sunglasses: \"\\u{1F60E}\",\n cry: \"\\u{1F622}\",\n sob: \"\\u{1F62D}\",\n scream: \"\\u{1F631}\",\n fearful: \"\\u{1F628}\",\n flushed: \"\\u{1F633}\",\n zzz: \"\\u{1F4A4}\",\n\n // Hand gestures\n thumbsup: \"\\u{1F44D}\",\n thumbs_up: \"\\u{1F44D}\",\n thumbsdown: \"\\u{1F44E}\",\n thumbs_down: \"\\u{1F44E}\",\n ok_hand: \"\\u{1F44C}\",\n clap: \"\\u{1F44F}\",\n wave: \"\\u{1F44B}\",\n raised_hands: \"\\u{1F64C}\",\n pray: \"\\u{1F64F}\",\n muscle: \"\\u{1F4AA}\",\n point_right: \"\\u{1F449}\",\n point_left: \"\\u{1F448}\",\n point_up: \"\\u{1F446}\",\n point_down: \"\\u{1F447}\",\n\n // Signals & icons\n check: \"✅\",\n white_check_mark: \"✅\",\n x: \"❌\",\n heavy_check_mark: \"✔\",\n ballot_box_with_check: \"☑️\",\n warning: \"⚠️\",\n no_entry: \"⛔\",\n no_entry_sign: \"\\u{1F6AB}\",\n question: \"❓\",\n exclamation: \"❗\",\n bangbang: \"‼️\",\n information_source: \"ℹ️\",\n zap: \"⚡\",\n bell: \"\\u{1F514}\",\n no_bell: \"\\u{1F515}\",\n\n // Tech\n rocket: \"\\u{1F680}\",\n computer: \"\\u{1F4BB}\",\n desktop_computer: \"\\u{1F5A5}️\",\n keyboard: \"⌨️\",\n bulb: \"\\u{1F4A1}\",\n hammer: \"\\u{1F528}\",\n wrench: \"\\u{1F527}\",\n gear: \"⚙️\",\n lock: \"\\u{1F512}\",\n unlock: \"\\u{1F513}\",\n key: \"\\u{1F511}\",\n package: \"\\u{1F4E6}\",\n link: \"\\u{1F517}\",\n paperclip: \"\\u{1F4CE}\",\n scissors: \"✂️\",\n hourglass: \"⏳\",\n alarm_clock: \"⏰\",\n stopwatch: \"⏱️\",\n hash: \"#️⃣\",\n\n // Files & docs\n page_facing_up: \"\\u{1F4C4}\",\n pencil: \"✏️\",\n memo: \"\\u{1F4DD}\",\n book: \"\\u{1F4D6}\",\n books: \"\\u{1F4DA}\",\n bookmark: \"\\u{1F516}\",\n clipboard: \"\\u{1F4CB}\",\n chart_with_upwards_trend: \"\\u{1F4C8}\",\n chart_with_downwards_trend: \"\\u{1F4C9}\",\n bar_chart: \"\\u{1F4CA}\",\n\n // Status / energy\n fire: \"\\u{1F525}\",\n sparkles: \"✨\",\n star: \"⭐\",\n star2: \"\\u{1F31F}\",\n tada: \"\\u{1F389}\",\n confetti_ball: \"\\u{1F38A}\",\n boom: \"\\u{1F4A5}\",\n rainbow: \"\\u{1F308}\",\n sunny: \"☀️\",\n cloud: \"☁️\",\n snowflake: \"❄️\",\n hot_face: \"\\u{1F975}\",\n cold_face: \"\\u{1F976}\",\n\n // Affection & symbols\n heart: \"❤️\",\n broken_heart: \"\\u{1F494}\",\n hearts: \"\\u{1F495}\",\n fist: \"✊\",\n trophy: \"\\u{1F3C6}\",\n medal: \"\\u{1F396}️\",\n hundred: \"\\u{1F4AF}\",\n eye: \"\\u{1F441}️\",\n eyes: \"\\u{1F440}\",\n\n // Coffee & food (slide signature)\n coffee: \"☕\",\n tea: \"\\u{1F375}\",\n beer: \"\\u{1F37A}\",\n pizza: \"\\u{1F355}\",\n\n // Arrows\n arrow_right: \"➡️\",\n arrow_left: \"⬅️\",\n arrow_up: \"⬆️\",\n arrow_down: \"⬇️\",\n arrow_upper_right: \"↗️\",\n arrow_lower_right: \"↘️\",\n};\n","/**\n * Slide rich-content plugin — Unicode emoji shortcodes.\n *\n * Zero peer-deps for runtime (only unist-util-visit-parents which is part of\n * the existing slide stack tree). Substitutes `:shortcode:` patterns with the\n * matching Unicode emoji from the embedded map.\n *\n * EC-6: uses `visitParents` with an ancestor check so shortcodes inside\n * `<code>` / `<pre>` are NEVER replaced. This prevents corruption of code\n * samples that legitimately use `:colon:syntax` (Python type hints, YAML\n * keys, Ruby symbols, JSX self-closing, etc.).\n *\n * Unknown shortcodes pass through unchanged (`:foo:` stays as text).\n */\nimport type { Root as HastRoot } from \"hast\";\nimport type { SlidePlugin } from \"../../plugin.js\";\nimport { EMOJI_MAP } from \"./map.js\";\n\nconst SHORTCODE_RE = /:([a-z0-9_+-]+):/g;\n\n/**\n * Return true when any ancestor element is `<code>` or `<pre>`. Used to skip\n * emoji replacement inside code samples (EC-6).\n */\nfunction isInsideCodeOrPre(ancestors: ReadonlyArray<{ type: string; tagName?: string }>): boolean {\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const a = ancestors[i];\n if (!a || a.type !== \"element\") continue;\n if (a.tagName === \"code\" || a.tagName === \"pre\") return true;\n }\n return false;\n}\n\nexport interface EmojiPluginOptions {\n /** Override or extend the default emoji map. Merged on top of the built-in 100. */\n extra?: Record<string, string>;\n}\n\nexport function emojiPlugin(opts: EmojiPluginOptions = {}): SlidePlugin {\n const map = { ...EMOJI_MAP, ...(opts.extra ?? {}) };\n return {\n name: \"emoji\",\n async hastTransform(tree: HastRoot): Promise<HastRoot> {\n const { visitParents } = await import(\"unist-util-visit-parents\");\n visitParents(\n tree,\n \"text\",\n (node: { value: string }, ancestors: Array<{ type: string; tagName?: string }>) => {\n if (isInsideCodeOrPre(ancestors)) return;\n node.value = node.value.replace(SHORTCODE_RE, (match, code: string) => {\n const replacement = map[code.toLowerCase()];\n return replacement ?? match;\n });\n },\n );\n return tree;\n },\n };\n}\n\nexport { EMOJI_MAP };\n"]}
@@ -0,0 +1,13 @@
1
+ import { a as SlidePlugin } from '../../../plugin-Atb0VKtr.js';
2
+ import 'hast';
3
+ import 'mdast';
4
+ import 'react';
5
+ import 'zod';
6
+
7
+ interface MathPluginOptions {
8
+ /** KaTeX render options forwarded to `renderToString`. */
9
+ katexOptions?: Record<string, unknown>;
10
+ }
11
+ declare function mathPlugin(opts?: MathPluginOptions): SlidePlugin;
12
+
13
+ export { type MathPluginOptions, mathPlugin };
@@ -0,0 +1,145 @@
1
+ // src/components/primitives/slide/plugins/math/index.ts
2
+ var MATHML_TAG_NAMES = [
3
+ "span",
4
+ "div",
5
+ "math",
6
+ "semantics",
7
+ "annotation",
8
+ "annotation-xml",
9
+ // token elements
10
+ "mtext",
11
+ "mn",
12
+ "mo",
13
+ "mi",
14
+ "ms",
15
+ "mglyph",
16
+ // general layout
17
+ "mrow",
18
+ "mfrac",
19
+ "msqrt",
20
+ "mroot",
21
+ "mstyle",
22
+ "merror",
23
+ "mpadded",
24
+ "mphantom",
25
+ "menclose",
26
+ "mspace",
27
+ // scripts & limits
28
+ "msub",
29
+ "msup",
30
+ "msubsup",
31
+ "munder",
32
+ "mover",
33
+ "munderover",
34
+ "mmultiscripts",
35
+ "mprescripts",
36
+ // tables (matrices)
37
+ "mtable",
38
+ "mtr",
39
+ "mtd",
40
+ "mlabeledtr"
41
+ ];
42
+ var MATHML_ATTRIBUTES = {
43
+ "*": ["style", "className", "ariaHidden", "ariaLabel"],
44
+ span: ["style", "className"],
45
+ math: ["xmlns", "display"],
46
+ annotation: ["encoding"],
47
+ "annotation-xml": ["encoding"],
48
+ mfrac: ["linethickness"],
49
+ mspace: ["width", "height", "depth"],
50
+ mover: ["accent"],
51
+ munder: ["accentunder"],
52
+ mo: ["fence", "form", "lspace", "rspace", "stretchy", "symmetric"]
53
+ };
54
+ var INLINE_RE = /\$([^$\n]+)\$/g;
55
+ var BLOCK_RE = /\$\$([\s\S]+?)\$\$/g;
56
+ function mathPlugin(opts = {}) {
57
+ let peerDepMissing = false;
58
+ return {
59
+ name: "math",
60
+ sanitizeSchemaExtension: {
61
+ tagNames: MATHML_TAG_NAMES,
62
+ attributes: MATHML_ATTRIBUTES
63
+ },
64
+ async hastTransform(tree) {
65
+ if (peerDepMissing) return tree;
66
+ let katex;
67
+ let fromHtml;
68
+ let visit;
69
+ try {
70
+ katex = (await import('katex')).default;
71
+ fromHtml = (await import('hast-util-from-html')).fromHtml;
72
+ visit = (await import('unist-util-visit')).visit;
73
+ } catch (e) {
74
+ peerDepMissing = true;
75
+ throw new Error(
76
+ `[slide/plugins/math] peer-deps missing (katex / hast-util-from-html). Run: pnpm add katex hast-util-from-html. Math formulas remain as plain text. Error: ${e instanceof Error ? e.message : String(e)}`
77
+ );
78
+ }
79
+ visit(
80
+ tree,
81
+ "text",
82
+ (node, idx, parent) => {
83
+ if (!parent || idx === void 0) return;
84
+ if (parent.tagName === "code" || parent.tagName === "pre") return;
85
+ const value = node.value;
86
+ const replacements = [];
87
+ for (const m of value.matchAll(BLOCK_RE)) {
88
+ if (m.index === void 0) continue;
89
+ try {
90
+ replacements.push({
91
+ start: m.index,
92
+ end: m.index + m[0].length,
93
+ html: katex.renderToString(m[1], {
94
+ ...opts.katexOptions ?? {},
95
+ displayMode: true,
96
+ throwOnError: false
97
+ })
98
+ });
99
+ } catch {
100
+ }
101
+ }
102
+ for (const m of value.matchAll(INLINE_RE)) {
103
+ if (m.index === void 0) continue;
104
+ if (replacements.some((r) => (m.index ?? 0) >= r.start && (m.index ?? 0) < r.end))
105
+ continue;
106
+ try {
107
+ replacements.push({
108
+ start: m.index,
109
+ end: m.index + m[0].length,
110
+ html: katex.renderToString(m[1], {
111
+ ...opts.katexOptions ?? {},
112
+ displayMode: false,
113
+ throwOnError: false
114
+ })
115
+ });
116
+ } catch {
117
+ }
118
+ }
119
+ if (replacements.length === 0) return;
120
+ replacements.sort((a, b) => a.start - b.start);
121
+ const newChildren = [];
122
+ let cursor = 0;
123
+ for (const r of replacements) {
124
+ if (r.start > cursor) {
125
+ newChildren.push({ type: "text", value: value.slice(cursor, r.start) });
126
+ }
127
+ const fragment = fromHtml(r.html, { fragment: true });
128
+ newChildren.push(...fragment.children);
129
+ cursor = r.end;
130
+ }
131
+ if (cursor < value.length) {
132
+ newChildren.push({ type: "text", value: value.slice(cursor) });
133
+ }
134
+ parent.children.splice(idx, 1, ...newChildren);
135
+ return idx + newChildren.length;
136
+ }
137
+ );
138
+ return tree;
139
+ }
140
+ };
141
+ }
142
+
143
+ export { mathPlugin };
144
+ //# sourceMappingURL=index.js.map
145
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/primitives/slide/plugins/math/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Slide rich-content plugin — KaTeX math rendering.\n *\n * Peer-deps (optional): `katex`, `hast-util-from-html`, `unist-util-visit`.\n * Lazy + opt-in (ADR D10 / D13 of the rich-content plan).\n *\n * Detects `$inline$` and `$$block$$` patterns in hast text nodes and replaces\n * them with KaTeX-rendered HTML. Block math (display mode) is rendered with\n * `displayMode: true` for proper line height + numbering.\n *\n * EC-2: every peer-dep is loaded via dynamic `import()` wrapped in try/catch;\n * a missing peer-dep surfaces via the D16 path (PLUGIN_ERROR) and the math\n * source stays as plain text — slide doesn't break.\n *\n * EC-4: `sanitizeSchemaExtension` lists ~30 MathML tags + KaTeX-specific\n * attributes so the rendered output survives the security barrier (D17).\n *\n * Consumer setup:\n * import \"katex/dist/katex.min.css\";\n * <Slide markdown={md} plugins={[mathPlugin()]} />\n */\nimport type { Root as HastRoot } from \"hast\";\nimport type { SlidePlugin } from \"../../plugin.js\";\n\nexport interface MathPluginOptions {\n /** KaTeX render options forwarded to `renderToString`. */\n katexOptions?: Record<string, unknown>;\n}\n\n// EC-4: complete MathML core tag list — KaTeX emits these for fraction, sqrt,\n// matrix, sub/super, accents, etc. Plus `<span>`/`<div>` for layout chrome.\nconst MATHML_TAG_NAMES = [\n \"span\",\n \"div\",\n \"math\",\n \"semantics\",\n \"annotation\",\n \"annotation-xml\",\n // token elements\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"ms\",\n \"mglyph\",\n // general layout\n \"mrow\",\n \"mfrac\",\n \"msqrt\",\n \"mroot\",\n \"mstyle\",\n \"merror\",\n \"mpadded\",\n \"mphantom\",\n \"menclose\",\n \"mspace\",\n // scripts & limits\n \"msub\",\n \"msup\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"mmultiscripts\",\n \"mprescripts\",\n // tables (matrices)\n \"mtable\",\n \"mtr\",\n \"mtd\",\n \"mlabeledtr\",\n];\n\nconst MATHML_ATTRIBUTES: Record<string, string[]> = {\n \"*\": [\"style\", \"className\", \"ariaHidden\", \"ariaLabel\"],\n span: [\"style\", \"className\"],\n math: [\"xmlns\", \"display\"],\n annotation: [\"encoding\"],\n \"annotation-xml\": [\"encoding\"],\n mfrac: [\"linethickness\"],\n mspace: [\"width\", \"height\", \"depth\"],\n mover: [\"accent\"],\n munder: [\"accentunder\"],\n mo: [\"fence\", \"form\", \"lspace\", \"rspace\", \"stretchy\", \"symmetric\"],\n};\n\nconst INLINE_RE = /\\$([^$\\n]+)\\$/g;\nconst BLOCK_RE = /\\$\\$([\\s\\S]+?)\\$\\$/g;\n\nexport function mathPlugin(opts: MathPluginOptions = {}): SlidePlugin {\n let peerDepMissing = false;\n\n return {\n name: \"math\",\n sanitizeSchemaExtension: {\n tagNames: MATHML_TAG_NAMES,\n attributes: MATHML_ATTRIBUTES,\n },\n async hastTransform(tree: HastRoot): Promise<HastRoot> {\n if (peerDepMissing) return tree;\n // EC-2: peer-dep guard. Throw on missing import — D16 absorbs into errors[].\n // biome-ignore lint/suspicious/noExplicitAny: katex default export untyped here\n let katex: any;\n // biome-ignore lint/suspicious/noExplicitAny: hast-util-from-html fromHtml untyped here\n let fromHtml: any;\n // biome-ignore lint/suspicious/noExplicitAny: unist-util-visit\n let visit: any;\n try {\n katex = (await import(\"katex\")).default;\n fromHtml = (await import(\"hast-util-from-html\")).fromHtml;\n visit = (await import(\"unist-util-visit\")).visit;\n } catch (e) {\n peerDepMissing = true;\n throw new Error(\n `[slide/plugins/math] peer-deps missing (katex / hast-util-from-html). Run: pnpm add katex hast-util-from-html. Math formulas remain as plain text. Error: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n\n // Walk text nodes, find $..$ and $$..$$ patterns, replace with rendered HTML.\n visit(\n tree,\n \"text\",\n (\n node: { value: string },\n idx: number | undefined,\n parent: { children: unknown[]; tagName?: string } | undefined,\n ) => {\n if (!parent || idx === undefined) return;\n // Skip if the text is inside a code / pre block — math shouldn't\n // accidentally trigger on `$amount = 100$` inside a code sample.\n if (parent.tagName === \"code\" || parent.tagName === \"pre\") return;\n const value = node.value;\n const replacements: Array<{\n start: number;\n end: number;\n html: string;\n }> = [];\n // Block math first ($$..$$) so we can mask its ranges before inline scan.\n for (const m of value.matchAll(BLOCK_RE)) {\n if (m.index === undefined) continue;\n try {\n replacements.push({\n start: m.index,\n end: m.index + m[0].length,\n html: katex.renderToString(m[1], {\n ...(opts.katexOptions ?? {}),\n displayMode: true,\n throwOnError: false,\n }),\n });\n } catch {\n // Malformed TeX — KaTeX may throw with throwOnError:true. Skip.\n }\n }\n for (const m of value.matchAll(INLINE_RE)) {\n if (m.index === undefined) continue;\n // Avoid re-processing inline matches that fall inside a block range.\n if (replacements.some((r) => (m.index ?? 0) >= r.start && (m.index ?? 0) < r.end))\n continue;\n try {\n replacements.push({\n start: m.index,\n end: m.index + m[0].length,\n html: katex.renderToString(m[1], {\n ...(opts.katexOptions ?? {}),\n displayMode: false,\n throwOnError: false,\n }),\n });\n } catch {\n // skip\n }\n }\n if (replacements.length === 0) return;\n replacements.sort((a, b) => a.start - b.start);\n const newChildren: unknown[] = [];\n let cursor = 0;\n for (const r of replacements) {\n if (r.start > cursor) {\n newChildren.push({ type: \"text\", value: value.slice(cursor, r.start) });\n }\n const fragment = fromHtml(r.html, { fragment: true });\n newChildren.push(...fragment.children);\n cursor = r.end;\n }\n if (cursor < value.length) {\n newChildren.push({ type: \"text\", value: value.slice(cursor) });\n }\n parent.children.splice(idx, 1, ...newChildren);\n return idx + newChildren.length;\n },\n );\n return tree;\n },\n };\n}\n"]}