@bendyline/squisq 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/ContentContainer-DNx460y8.d.ts +88 -0
  2. package/dist/{Doc-1hmT1LZl.d.ts → Doc-Dwn5aHFC.d.ts} +115 -6
  3. package/dist/__tests__/dataTable.test.d.ts +2 -0
  4. package/dist/__tests__/dataTable.test.d.ts.map +1 -0
  5. package/dist/__tests__/dataTable.test.js +110 -0
  6. package/dist/__tests__/dataTable.test.js.map +1 -0
  7. package/dist/__tests__/markdownToDoc.test.js +75 -0
  8. package/dist/__tests__/markdownToDoc.test.js.map +1 -1
  9. package/dist/{chunk-6YJPI6VI.js → chunk-4X3JQXNM.js} +1 -1
  10. package/dist/chunk-DNJKBAI7.js +1 -0
  11. package/dist/chunk-DNJKBAI7.js.map +1 -0
  12. package/dist/{chunk-S4F2RY37.js → chunk-KEFWM2VC.js} +1 -1
  13. package/dist/chunk-KEFWM2VC.js.map +1 -0
  14. package/dist/{chunk-VP5SZTZ2.js → chunk-MIBRIRGJ.js} +2 -139
  15. package/dist/chunk-MIBRIRGJ.js.map +1 -0
  16. package/dist/{chunk-URU6QMLY.js → chunk-NJLL5XYC.js} +15 -8
  17. package/dist/chunk-NJLL5XYC.js.map +1 -0
  18. package/dist/chunk-QM5PKNPW.js +165 -0
  19. package/dist/chunk-QM5PKNPW.js.map +1 -0
  20. package/dist/{chunk-PJ7AODIM.js → chunk-T6WBC3ND.js} +459 -53
  21. package/dist/chunk-T6WBC3ND.js.map +1 -0
  22. package/dist/chunk-TSG2OSE6.js +145 -0
  23. package/dist/chunk-TSG2OSE6.js.map +1 -0
  24. package/dist/{chunk-O5HCIUAD.js → chunk-TVPKD76V.js} +81 -187
  25. package/dist/chunk-TVPKD76V.js.map +1 -0
  26. package/dist/contentExtractor-BNfVJV2U.d.ts +113 -0
  27. package/dist/doc/audioMapping.d.ts +36 -0
  28. package/dist/doc/audioMapping.d.ts.map +1 -0
  29. package/dist/doc/audioMapping.js +279 -0
  30. package/dist/doc/audioMapping.js.map +1 -0
  31. package/dist/doc/index.d.ts +1 -0
  32. package/dist/doc/index.d.ts.map +1 -1
  33. package/dist/doc/index.js +1 -0
  34. package/dist/doc/index.js.map +1 -1
  35. package/dist/doc/markdownToDoc.d.ts.map +1 -1
  36. package/dist/doc/markdownToDoc.js +126 -1
  37. package/dist/doc/markdownToDoc.js.map +1 -1
  38. package/dist/doc/templates/comparisonBar.d.ts.map +1 -1
  39. package/dist/doc/templates/comparisonBar.js +23 -5
  40. package/dist/doc/templates/comparisonBar.js.map +1 -1
  41. package/dist/doc/templates/coverBlock.d.ts.map +1 -1
  42. package/dist/doc/templates/coverBlock.js +3 -0
  43. package/dist/doc/templates/coverBlock.js.map +1 -1
  44. package/dist/doc/templates/dataTable.d.ts +14 -0
  45. package/dist/doc/templates/dataTable.d.ts.map +1 -0
  46. package/dist/doc/templates/dataTable.js +73 -0
  47. package/dist/doc/templates/dataTable.js.map +1 -0
  48. package/dist/doc/templates/dateEvent.d.ts.map +1 -1
  49. package/dist/doc/templates/dateEvent.js +4 -0
  50. package/dist/doc/templates/dateEvent.js.map +1 -1
  51. package/dist/doc/templates/definitionCard.d.ts.map +1 -1
  52. package/dist/doc/templates/definitionCard.js +4 -1
  53. package/dist/doc/templates/definitionCard.js.map +1 -1
  54. package/dist/doc/templates/factCard.d.ts.map +1 -1
  55. package/dist/doc/templates/factCard.js +4 -0
  56. package/dist/doc/templates/factCard.js.map +1 -1
  57. package/dist/doc/templates/fullBleedQuote.d.ts.map +1 -1
  58. package/dist/doc/templates/fullBleedQuote.js +2 -1
  59. package/dist/doc/templates/fullBleedQuote.js.map +1 -1
  60. package/dist/doc/templates/imageWithCaption.d.ts.map +1 -1
  61. package/dist/doc/templates/imageWithCaption.js +4 -0
  62. package/dist/doc/templates/imageWithCaption.js.map +1 -1
  63. package/dist/doc/templates/index.d.ts +1 -0
  64. package/dist/doc/templates/index.d.ts.map +1 -1
  65. package/dist/doc/templates/index.js +3 -0
  66. package/dist/doc/templates/index.js.map +1 -1
  67. package/dist/doc/templates/listBlock.d.ts.map +1 -1
  68. package/dist/doc/templates/listBlock.js +3 -0
  69. package/dist/doc/templates/listBlock.js.map +1 -1
  70. package/dist/doc/templates/mapBlock.d.ts.map +1 -1
  71. package/dist/doc/templates/mapBlock.js +3 -0
  72. package/dist/doc/templates/mapBlock.js.map +1 -1
  73. package/dist/doc/templates/photoGrid.d.ts.map +1 -1
  74. package/dist/doc/templates/photoGrid.js +3 -0
  75. package/dist/doc/templates/photoGrid.js.map +1 -1
  76. package/dist/doc/templates/pullQuote.d.ts.map +1 -1
  77. package/dist/doc/templates/pullQuote.js +4 -0
  78. package/dist/doc/templates/pullQuote.js.map +1 -1
  79. package/dist/doc/templates/quoteBlock.d.ts.map +1 -1
  80. package/dist/doc/templates/quoteBlock.js +4 -0
  81. package/dist/doc/templates/quoteBlock.js.map +1 -1
  82. package/dist/doc/templates/sectionHeader.d.ts.map +1 -1
  83. package/dist/doc/templates/sectionHeader.js +3 -2
  84. package/dist/doc/templates/sectionHeader.js.map +1 -1
  85. package/dist/doc/templates/statHighlight.d.ts.map +1 -1
  86. package/dist/doc/templates/statHighlight.js +4 -1
  87. package/dist/doc/templates/statHighlight.js.map +1 -1
  88. package/dist/doc/templates/titleBlock.d.ts.map +1 -1
  89. package/dist/doc/templates/titleBlock.js +3 -0
  90. package/dist/doc/templates/titleBlock.js.map +1 -1
  91. package/dist/doc/templates/twoColumn.d.ts.map +1 -1
  92. package/dist/doc/templates/twoColumn.js +7 -1
  93. package/dist/doc/templates/twoColumn.js.map +1 -1
  94. package/dist/doc/templates/videoPullQuote.d.ts.map +1 -1
  95. package/dist/doc/templates/videoPullQuote.js +4 -0
  96. package/dist/doc/templates/videoPullQuote.js.map +1 -1
  97. package/dist/doc/templates/videoWithCaption.d.ts.map +1 -1
  98. package/dist/doc/templates/videoWithCaption.js +3 -0
  99. package/dist/doc/templates/videoWithCaption.js.map +1 -1
  100. package/dist/doc/utils/themeUtils.d.ts +9 -0
  101. package/dist/doc/utils/themeUtils.d.ts.map +1 -1
  102. package/dist/doc/utils/themeUtils.js +26 -0
  103. package/dist/doc/utils/themeUtils.js.map +1 -1
  104. package/dist/markdown/convert.d.ts.map +1 -1
  105. package/dist/markdown/convert.js +16 -7
  106. package/dist/markdown/convert.js.map +1 -1
  107. package/dist/markdown/types.d.ts +4 -2
  108. package/dist/markdown/types.d.ts.map +1 -1
  109. package/dist/schemas/BlockTemplates.d.ts +20 -3
  110. package/dist/schemas/BlockTemplates.d.ts.map +1 -1
  111. package/dist/schemas/BlockTemplates.js +2 -2
  112. package/dist/schemas/BlockTemplates.js.map +1 -1
  113. package/dist/schemas/Doc.d.ts +93 -1
  114. package/dist/schemas/Doc.d.ts.map +1 -1
  115. package/dist/schemas/Doc.js.map +1 -1
  116. package/dist/schemas/themeLibrary.d.ts +2 -2
  117. package/dist/schemas/themeLibrary.d.ts.map +1 -1
  118. package/dist/schemas/themeLibrary.js +91 -26
  119. package/dist/schemas/themeLibrary.js.map +1 -1
  120. package/dist/story/index.d.ts +63 -5
  121. package/dist/story/index.js +17 -5
  122. package/dist/{themeLibrary-DR1VRo-8.d.ts → themeLibrary-DLJtGh8-.d.ts} +3 -3
  123. package/dist/transform/applyTransform.d.ts +22 -0
  124. package/dist/transform/applyTransform.d.ts.map +1 -0
  125. package/dist/transform/applyTransform.js +68 -0
  126. package/dist/transform/applyTransform.js.map +1 -0
  127. package/dist/transform/blockAnalyzer.d.ts +48 -0
  128. package/dist/transform/blockAnalyzer.d.ts.map +1 -0
  129. package/dist/transform/blockAnalyzer.js +135 -0
  130. package/dist/transform/blockAnalyzer.js.map +1 -0
  131. package/dist/transform/index.d.ts +6 -0
  132. package/dist/transform/index.d.ts.map +1 -0
  133. package/dist/transform/index.js +4 -0
  134. package/dist/transform/index.js.map +1 -0
  135. package/dist/transform/registry.d.ts +19 -0
  136. package/dist/transform/registry.d.ts.map +1 -0
  137. package/dist/transform/registry.js +41 -0
  138. package/dist/transform/registry.js.map +1 -0
  139. package/dist/transform/styles/dataDriven.d.ts +4 -0
  140. package/dist/transform/styles/dataDriven.d.ts.map +1 -0
  141. package/dist/transform/styles/dataDriven.js +15 -0
  142. package/dist/transform/styles/dataDriven.js.map +1 -0
  143. package/dist/transform/styles/documentary.d.ts +4 -0
  144. package/dist/transform/styles/documentary.d.ts.map +1 -0
  145. package/dist/transform/styles/documentary.js +15 -0
  146. package/dist/transform/styles/documentary.js.map +1 -0
  147. package/dist/transform/styles/index.d.ts +6 -0
  148. package/dist/transform/styles/index.d.ts.map +1 -0
  149. package/dist/transform/styles/index.js +6 -0
  150. package/dist/transform/styles/index.js.map +1 -0
  151. package/dist/transform/styles/magazine.d.ts +4 -0
  152. package/dist/transform/styles/magazine.d.ts.map +1 -0
  153. package/dist/transform/styles/magazine.js +15 -0
  154. package/dist/transform/styles/magazine.js.map +1 -0
  155. package/dist/transform/styles/minimal.d.ts +4 -0
  156. package/dist/transform/styles/minimal.d.ts.map +1 -0
  157. package/dist/transform/styles/minimal.js +15 -0
  158. package/dist/transform/styles/minimal.js.map +1 -0
  159. package/dist/transform/styles/narrative.d.ts +4 -0
  160. package/dist/transform/styles/narrative.d.ts.map +1 -0
  161. package/dist/transform/styles/narrative.js +15 -0
  162. package/dist/transform/styles/narrative.js.map +1 -0
  163. package/dist/transform/templateSelector.d.ts +30 -0
  164. package/dist/transform/templateSelector.d.ts.map +1 -0
  165. package/dist/transform/templateSelector.js +218 -0
  166. package/dist/transform/templateSelector.js.map +1 -0
  167. package/dist/transform/timingAllocator.d.ts +21 -0
  168. package/dist/transform/timingAllocator.d.ts.map +1 -0
  169. package/dist/transform/timingAllocator.js +111 -0
  170. package/dist/transform/timingAllocator.js.map +1 -0
  171. package/dist/transform/types.d.ts +86 -0
  172. package/dist/transform/types.d.ts.map +1 -0
  173. package/dist/transform/types.js +9 -0
  174. package/dist/transform/types.js.map +1 -0
  175. package/dist/{types-DHiv_Pnm.d.ts → types-DYAzisMG.d.ts} +4 -2
  176. package/package.json +6 -1
  177. package/src/__tests__/dataTable.test.ts +135 -0
  178. package/src/__tests__/markdownToDoc.test.ts +96 -0
  179. package/src/doc/audioMapping.ts +346 -0
  180. package/src/doc/index.ts +1 -0
  181. package/src/doc/markdownToDoc.ts +136 -2
  182. package/src/doc/templates/comparisonBar.ts +23 -5
  183. package/src/doc/templates/coverBlock.ts +3 -0
  184. package/src/doc/templates/dataTable.ts +84 -0
  185. package/src/doc/templates/dateEvent.ts +4 -0
  186. package/src/doc/templates/definitionCard.ts +4 -1
  187. package/src/doc/templates/factCard.ts +4 -0
  188. package/src/doc/templates/fullBleedQuote.ts +2 -1
  189. package/src/doc/templates/imageWithCaption.ts +4 -0
  190. package/src/doc/templates/index.ts +3 -0
  191. package/src/doc/templates/listBlock.ts +3 -0
  192. package/src/doc/templates/mapBlock.ts +3 -0
  193. package/src/doc/templates/photoGrid.ts +3 -0
  194. package/src/doc/templates/pullQuote.ts +4 -0
  195. package/src/doc/templates/quoteBlock.ts +4 -0
  196. package/src/doc/templates/sectionHeader.ts +3 -2
  197. package/src/doc/templates/statHighlight.ts +4 -1
  198. package/src/doc/templates/titleBlock.ts +3 -0
  199. package/src/doc/templates/twoColumn.ts +7 -1
  200. package/src/doc/templates/videoPullQuote.ts +4 -0
  201. package/src/doc/templates/videoWithCaption.ts +3 -0
  202. package/src/doc/utils/themeUtils.ts +29 -0
  203. package/src/markdown/convert.ts +17 -7
  204. package/src/markdown/types.ts +4 -2
  205. package/src/schemas/BlockTemplates.ts +22 -3
  206. package/src/schemas/Doc.ts +103 -1
  207. package/src/schemas/themeLibrary.ts +95 -26
  208. package/src/transform/applyTransform.ts +83 -0
  209. package/src/transform/blockAnalyzer.ts +181 -0
  210. package/src/transform/index.ts +20 -0
  211. package/src/transform/registry.ts +47 -0
  212. package/src/transform/styles/dataDriven.ts +16 -0
  213. package/src/transform/styles/documentary.ts +16 -0
  214. package/src/transform/styles/index.ts +5 -0
  215. package/src/transform/styles/magazine.ts +16 -0
  216. package/src/transform/styles/minimal.ts +16 -0
  217. package/src/transform/styles/narrative.ts +16 -0
  218. package/src/transform/templateSelector.ts +294 -0
  219. package/src/transform/timingAllocator.ts +126 -0
  220. package/src/transform/types.ts +105 -0
  221. package/dist/chunk-O5HCIUAD.js.map +0 -1
  222. package/dist/chunk-PJ7AODIM.js.map +0 -1
  223. package/dist/chunk-S4F2RY37.js.map +0 -1
  224. package/dist/chunk-URU6QMLY.js.map +0 -1
  225. package/dist/chunk-VP5SZTZ2.js.map +0 -1
  226. /package/dist/{chunk-6YJPI6VI.js.map → chunk-4X3JQXNM.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/Doc.ts","../src/schemas/Theme.ts"],"sourcesContent":["/**\n * Doc Schema\n *\n * Defines the JSON format for AI-scriptable visual docs that accompany\n * audio narration. A Doc describes a sequence of animated blocks\n * with text overlays, images, and transitions synchronized to audio.\n *\n * The format is designed to be:\n * 1. AI-generatable - Claude can create scripts from article content\n * 2. Browser-renderable - SVG + CSS animations (ES2015 compatible)\n * 3. Video-exportable - Frame-by-frame capture via Playwright\n *\n * Timing: Blocks are timed to match narration audio segments. Each article\n * has 3-6 MP3 files (intro + sections), and blocks are grouped by segment.\n */\n\n// ============================================\n// Core Types\n// ============================================\n\n/**\n * Configuration for the Start/resting block shown before playback begins.\n */\nexport interface StartBlockConfig {\n /** Hero image path (relative to article media) */\n heroSrc: string;\n /** Alt text for the hero image */\n heroAlt?: string;\n /** Title to display over the hero */\n title: string;\n /** Optional subtitle */\n subtitle?: string;\n /** Ambient motion for the hero image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** Photo credit / artist name for the hero image */\n heroCredit?: string;\n /** License identifier for the hero image */\n heroLicense?: string;\n}\n\n/**\n * A complete visual doc for an article.\n */\nexport interface Doc {\n /** Article ID this doc belongs to */\n articleId: string;\n\n /** Total duration in seconds (sum of all audio segments) */\n duration: number;\n\n /** Ordered list of blocks */\n blocks: Block[];\n\n /** Audio track configuration */\n audio: AudioTrack;\n\n /** Caption track for closed captions (generated by postprocess-doc) */\n captions?: CaptionTrack;\n\n /**\n * Start/resting block shown before playback begins.\n * Displays hero image with title - acts as a \"poster\" for the doc.\n * The player shows this until the user starts playback.\n */\n startBlock?: StartBlockConfig;\n\n /**\n * Persistent layers rendered on every block (behind and/or on top of content).\n * Used for consistent branding overlays, background gradients, etc.\n */\n persistentLayers?: import('./BlockTemplates.js').PersistentLayerConfig;\n\n /**\n * Optional theme identifier. Resolved at render time via `resolveTheme()`.\n * When omitted, the renderer uses the default theme ('documentary').\n */\n themeId?: string;\n\n /** Optional metadata */\n meta?: {\n generatedAt?: string;\n generatedBy?: string;\n version?: number;\n };\n\n /**\n * YAML frontmatter from the source markdown.\n * Carries rendering hints like `document-render-as` and custom metadata.\n */\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * A single block in the doc.\n *\n * Blocks can be flat (audio-synced timeline) or nested (markdown-driven hierarchy).\n * When derived from markdown, a block corresponds to a heading section:\n * - `sourceHeading` references the original MarkdownHeading node\n * - `contents` holds the body markdown between this heading and the next\n * - `children` holds sub-heading blocks (e.g., H2s under an H1)\n */\nexport interface Block {\n /** Unique identifier for this block */\n id: string;\n\n /** When this block appears (seconds from start) */\n startTime: number;\n\n /** How long this block is visible (seconds) */\n duration: number;\n\n /** Which audio segment this block belongs to (0-indexed) */\n audioSegment: number;\n\n /**\n * Pre-computed visual layers, rendered back-to-front.\n *\n * Optional — template-derived blocks typically omit this and compute\n * layers on demand via `getLayers()` from `@bendyline/squisq/doc`.\n * Raw blocks (e.g., hand-crafted by AI) may provide layers directly.\n */\n layers?: Layer[];\n\n /** Entry transition from previous block */\n transition?: Transition;\n\n /** Template name that generated this block (for debugging) */\n template?: string;\n\n /**\n * Display title for template rendering.\n * Extracted from the sourceHeading when the block is created by markdownToDoc().\n * Templates like sectionHeader and titleBlock read this for their title layer.\n */\n title?: string;\n\n // ── Markdown-driven hierarchy (optional) ──\n\n /**\n * Nested sub-blocks (heading hierarchy).\n * When a doc is derived from markdown, sub-headings within this\n * heading's section become child blocks. For example, H2 blocks\n * under an H1 block appear here.\n */\n children?: Block[];\n\n /**\n * Markdown body content for this block's section.\n * Contains the block-level markdown nodes (paragraphs, lists, code,\n * blockquotes, etc.) that appear between this heading and the next\n * heading or sub-heading. Empty for pure structural blocks.\n */\n contents?: import('../markdown/types.js').MarkdownBlockNode[];\n\n /**\n * Reference to the MarkdownHeading node that created this block.\n * Present when the block was derived from markdown via markdownToDoc().\n * Enables round-tripping back to markdown and provides heading depth.\n * Absent for the preamble block (content before the first heading).\n */\n sourceHeading?: import('../markdown/types.js').MarkdownHeading;\n\n /**\n * Template overrides extracted from markdown directives.\n * For example, `### Data Section {template=chart colorScheme=blue}`\n * would produce `{ template: 'chart', colorScheme: 'blue' }`.\n * Allows per-block customization of template selection and parameters.\n */\n templateOverrides?: Record<string, string>;\n}\n\n// ============================================\n// Layer Types\n// ============================================\n\n/**\n * A visual element within a block.\n * Layers are composited back-to-front (first layer is background).\n */\nexport type Layer = ImageLayer | TextLayer | ShapeLayer | MapLayer | VideoLayer | TableLayer;\n\ninterface BaseLayer {\n /** Unique identifier for this layer */\n id: string;\n\n /** Position and size */\n position: Position;\n\n /** Animation to apply */\n animation?: Animation;\n}\n\n/**\n * Image layer - displays an image with optional Ken Burns effect.\n */\nexport interface ImageLayer extends BaseLayer {\n type: 'image';\n content: {\n /** Path to image file (relative to article media dir) */\n src: string;\n /** Alt text for accessibility */\n alt: string;\n /** How to fit image in bounds */\n fit?: 'cover' | 'contain' | 'fill';\n /** Photo credit / artist name */\n credit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n license?: string;\n };\n}\n\n/**\n * Text layer - displays text with styling.\n */\nexport interface TextLayer extends BaseLayer {\n type: 'text';\n content: {\n /** Text to display (supports \\n for line breaks) */\n text: string;\n /** Text styling */\n style: TextStyle;\n };\n}\n\n/**\n * Shape layer - simple geometric shapes for visual accents.\n */\nexport interface ShapeLayer extends BaseLayer {\n type: 'shape';\n content: {\n /** Shape type */\n shape: 'rect' | 'circle' | 'line';\n /** Fill color (CSS color or 'none') */\n fill?: string;\n /** Stroke color */\n stroke?: string;\n /** Stroke width in pixels */\n strokeWidth?: number;\n /** Corner radius for rect */\n borderRadius?: number;\n };\n}\n\n/**\n * Map layer - displays a geographic map with optional markers.\n *\n * Maps are rendered as composite tile images from free tile providers.\n * Supported styles use open-source/free tiles requiring attribution.\n */\nexport interface MapLayer extends BaseLayer {\n type: 'map';\n content: {\n /** Center coordinates for the map */\n center: {\n lat: number;\n lng: number;\n };\n /** Zoom level (0-18, typically 8-14 for doc blocks) */\n zoom: number;\n /** Map tile style */\n style: MapTileStyle;\n /** Optional markers to display on the map */\n markers?: MapMarker[];\n /** Pre-rendered static image path (for video export reliability) */\n staticSrc?: string;\n /** Show attribution text (default: true) */\n showAttribution?: boolean;\n };\n}\n\n/**\n * Video layer - displays a short video clip, always muted (narration provides audio).\n *\n * Rendered via an HTML5 <video> element inside <foreignObject>, similar to how\n * ImageLayer uses <img> for Ken Burns effects. clipStart/clipEnd define the window\n * within the source video to play. During Playwright frame capture (seekTo mode),\n * the video is seeked programmatically rather than playing in real time.\n */\nexport interface VideoLayer extends BaseLayer {\n type: 'video';\n content: {\n /** Path to video file (relative to article media dir) */\n src: string;\n /** Path to poster frame (shown before video loads / during seek) */\n posterSrc?: string;\n /** Alt text for accessibility */\n alt: string;\n /** How to fit video in bounds */\n fit?: 'cover' | 'contain' | 'fill';\n /** Start time within the source video (seconds) */\n clipStart: number;\n /** End time within the source video (seconds) */\n clipEnd: number;\n /** Total source video duration (for validation) */\n sourceDuration?: number;\n /** Video credit / artist name */\n credit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n license?: string;\n };\n}\n\n/**\n * Table layer - renders a data table with themed styling.\n * Uses foreignObject inside SVG for HTML-based table layout.\n */\nexport interface TableLayer extends BaseLayer {\n type: 'table';\n content: {\n /** Header cell values */\n headers: string[];\n /** Data rows (array of cell value arrays) */\n rows: string[][];\n /** Per-column alignment */\n align?: (('left' | 'right' | 'center') | null)[];\n /** Visual styling */\n style: TableLayerStyle;\n };\n}\n\n/**\n * Styling options for a TableLayer.\n */\nexport interface TableLayerStyle {\n /** Header row background color */\n headerBackground: string;\n /** Header row text color */\n headerColor: string;\n /** Body cell background color */\n cellBackground: string;\n /** Body cell text color */\n cellColor: string;\n /** Border/divider color */\n borderColor: string;\n /** Font size in pixels */\n fontSize: number;\n /** Font family */\n fontFamily?: string;\n /** Header font family (falls back to fontFamily) */\n headerFontFamily?: string;\n /** Corner radius for the table container */\n borderRadius?: number;\n}\n\n/**\n * Available map tile styles from free/open-source providers.\n */\nexport type MapTileStyle =\n | 'terrain' // OpenTopoMap - topographic/terrain (default)\n | 'satellite' // ESRI World Imagery\n | 'road' // OpenStreetMap standard\n | 'toner' // Stadia Toner (high contrast B&W)\n | 'watercolor'; // Stadia Watercolor (artistic)\n\n/**\n * A marker to display on the map.\n */\nexport interface MapMarker {\n /** Marker latitude */\n lat: number;\n /** Marker longitude */\n lng: number;\n /** Optional label text */\n label?: string;\n /** Marker color (CSS color, default: #ef4444) */\n color?: string;\n /** Marker icon type */\n icon?: 'pin' | 'circle' | 'star';\n}\n\n// ============================================\n// Position & Styling\n// ============================================\n\n/**\n * Position and dimensions for a layer.\n * Values can be pixels (number) or percentages (string like \"50%\").\n */\nexport interface Position {\n /** X position (pixels or percentage) */\n x: number | string;\n /** Y position (pixels or percentage) */\n y: number | string;\n /** Width (pixels or percentage) */\n width?: number | string;\n /** Height (pixels or percentage) */\n height?: number | string;\n /** Anchor point for positioning */\n anchor?: 'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/**\n * Text styling options.\n */\nexport interface TextStyle {\n /** Font size in pixels */\n fontSize: number;\n /** Font family (defaults to system sans-serif) */\n fontFamily?: string;\n /** Font weight */\n fontWeight?: 'normal' | 'bold';\n /** Text color (CSS color) */\n color: string;\n /** Text alignment */\n textAlign?: 'left' | 'center' | 'right';\n /** Line height multiplier */\n lineHeight?: number;\n /** Add drop shadow for readability over images */\n shadow?: boolean;\n /** Background color for text box */\n background?: string;\n /** Padding around text (pixels) */\n padding?: number;\n /** Maximum number of lines before truncation (adds \"...\" to last line) */\n maxLines?: number;\n}\n\n// ============================================\n// Animation & Transitions\n// ============================================\n\n/**\n * Animation to apply to a layer.\n */\nexport interface Animation {\n /** Animation type */\n type: AnimationType;\n /** Duration in seconds (defaults to layer's block duration) */\n duration?: number;\n /** Delay before animation starts (seconds) */\n delay?: number;\n /** CSS easing function */\n easing?: string;\n /** For Ken Burns: zoom direction */\n direction?: 'in' | 'out';\n /** For pan animations: pan direction */\n panDirection?: 'left' | 'right' | 'up' | 'down';\n}\n\n/**\n * Available animation types.\n */\nexport type AnimationType =\n | 'none'\n | 'fadeIn'\n | 'fadeOut'\n | 'slowZoom' // Slow zoom with optional pan\n | 'zoomIn'\n | 'zoomOut'\n | 'panLeft'\n | 'panRight'\n | 'typewriter'; // Text appears letter by letter\n\n/**\n * Transition between blocks.\n */\nexport interface Transition {\n /** Transition type */\n type: TransitionType;\n /** Duration in seconds */\n duration: number;\n}\n\nexport type TransitionType =\n | 'cut' // Instant switch\n | 'fade' // Cross-fade\n | 'dissolve' // Soft dissolve\n | 'slideLeft' // New block enters from right\n | 'slideRight'; // New block enters from left\n\n// ============================================\n// Audio Configuration\n// ============================================\n\n/**\n * Audio track configuration.\n * Articles have multiple MP3 segments (intro + sections).\n */\nexport interface AudioTrack {\n /** Audio segments in playback order */\n segments: AudioSegment[];\n}\n\n// ============================================\n// Captions\n// ============================================\n\n/**\n * A single word with precise timing, used for word-level highlighting\n * in social-style captions. Populated from TTS timing data when available.\n */\nexport interface CaptionWord {\n /** The word text */\n text: string;\n /** Start time in seconds (relative to doc start) */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n}\n\n/**\n * A single caption phrase to display during playback.\n */\nexport interface CaptionPhrase {\n /** The text to display */\n text: string;\n /** Start time in seconds (relative to doc start) */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n /** Which audio segment this caption belongs to (0-indexed) */\n audioSegment: number;\n /**\n * Optional per-word timing from TTS timing data.\n * When present, enables precise word-level highlighting in social\n * caption style. When absent, word timing is interpolated evenly\n * across the phrase duration.\n */\n words?: CaptionWord[];\n}\n\n/**\n * Caption track for a doc.\n */\nexport interface CaptionTrack {\n /** Caption phrases in chronological order */\n phrases: CaptionPhrase[];\n /** When the captions were generated */\n generatedAt: string;\n /** Algorithm version for regeneration detection */\n version: number;\n}\n\n/**\n * A single audio segment (one MP3 file).\n */\nexport interface AudioSegment {\n /** Path to MP3 file (relative to article media dir) */\n src: string;\n /** Segment name (e.g., \"intro\", \"history\") */\n name: string;\n /** Duration in seconds */\n duration: number;\n /** Start time in overall timeline (calculated) */\n startTime: number;\n}\n\n// ============================================\n// Audio Timing Data (from TTS)\n// ============================================\n\n/**\n * A single word-level timing bookmark from TTS synthesis.\n * Used for precise audio-to-content mapping and word-level caption sync.\n */\nexport interface AudioBookmark {\n /** Unique identifier (e.g., \"word-0\", \"word-1\") */\n id: string;\n /** Timestamp in seconds from audio start */\n time: number;\n /** Character offset in the source text */\n charOffset: number;\n /** The word text at this position */\n textFragment?: string;\n}\n\n/**\n * Timing data for an audio segment, typically from a `.timing.json` file\n * generated alongside TTS audio. Contains the source text and per-word\n * timing bookmarks for precise content matching and caption sync.\n */\nexport interface AudioTimingData {\n /** The normalized plain text that was synthesized */\n sourceText: string;\n /** Word-level timing bookmarks */\n bookmarks: AudioBookmark[];\n /** Total audio duration in seconds */\n duration: number;\n}\n\n// ============================================\n// Helper Functions\n// ============================================\n\n/**\n * Calculate total duration from audio segments.\n */\nexport function calculateDuration(audio: AudioTrack): number {\n return audio.segments.reduce((sum, seg) => sum + seg.duration, 0);\n}\n\n/**\n * Find which audio segment is playing at a given time.\n */\nexport function getSegmentAtTime(audio: AudioTrack, time: number): number {\n let elapsed = 0;\n for (let i = 0; i < audio.segments.length; i++) {\n elapsed += audio.segments[i].duration;\n if (time < elapsed) return i;\n }\n return audio.segments.length - 1;\n}\n\n/**\n * Find which block should be visible at a given time.\n */\nexport function getBlockAtTime(blocks: Block[], time: number): Block | null {\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i];\n if (time >= block.startTime && time < block.startTime + block.duration) {\n return block;\n }\n }\n return blocks[0] || null;\n}\n\n/**\n * Find the caption phrase that should be displayed at a given time.\n */\nexport function getCaptionAtTime(\n captions: CaptionTrack | undefined,\n time: number,\n): CaptionPhrase | null {\n if (!captions || !captions.phrases.length) return null;\n\n for (const phrase of captions.phrases) {\n if (time >= phrase.startTime && time < phrase.endTime) {\n return phrase;\n }\n }\n return null;\n}\n\n/**\n * Validate a Doc for completeness.\n */\nexport function validateDoc(script: Doc): string[] {\n const errors: string[] = [];\n\n if (!script.articleId) {\n errors.push('Missing articleId');\n }\n\n if (!script.blocks || script.blocks.length === 0) {\n errors.push('No blocks defined');\n }\n\n if (!script.audio || !script.audio.segments || script.audio.segments.length === 0) {\n errors.push('No audio segments defined');\n }\n\n // Check for gaps in block coverage\n const totalDuration = script.duration;\n let covered = 0;\n for (const block of script.blocks) {\n if (block.startTime > covered + 0.1) {\n errors.push(`Gap in blocks: ${covered}s to ${block.startTime}s`);\n }\n covered = Math.max(covered, block.startTime + block.duration);\n }\n\n if (covered < totalDuration - 0.1) {\n errors.push(`Blocks end at ${covered}s but audio is ${totalDuration}s`);\n }\n\n return errors;\n}\n","/**\n * Theme System\n *\n * A Theme bundles color palette, typography, visual style, render-style\n * algorithm, and per-block color schemes into one JSON-serializable object.\n * Builders choose a theme from the built-in library or create a custom one\n * via `createTheme(base, overrides)`.\n *\n * Design principles:\n * - Fully JSON-serializable (no functions) — storable in config / APIs.\n * - Doc carries an optional `themeId` pointer; resolution happens at render time.\n * - `createTheme` deep-merges a base theme with partial overrides.\n */\n\nimport type { LayoutHints } from './LayoutStrategy.js';\nimport type { AnimationType, TransitionType } from './Doc.js';\nimport type { PersistentLayerConfig } from './BlockTemplates.js';\n\n// ============================================\n// Color Palette\n// ============================================\n\n/**\n * Core color palette for a theme. Every color is a CSS color string.\n */\nexport interface ThemeColorPalette {\n /** Primary accent color */\n primary: string;\n /** Secondary accent color */\n secondary: string;\n /** Background color (typically dark) */\n background: string;\n /** Lighter background for contrast panels */\n backgroundLight: string;\n /** Main text color */\n text: string;\n /** Muted/secondary text color */\n textMuted: string;\n /** Highlight/emphasis color */\n highlight: string;\n /** Warning/alert color */\n warning: string;\n}\n\n/**\n * A named color scheme used by templates for per-block color variation\n * (e.g., statHighlight or sectionHeader).\n */\nexport interface ThemeColorScheme {\n /** Background fill */\n bg: string;\n /** Primary text tint */\n text: string;\n /** Accent / secondary tint */\n accent: string;\n}\n\n// ============================================\n// Typography\n// ============================================\n\n/**\n * Typography settings for a theme.\n */\nexport interface ThemeTypography {\n /** Font family for body / description text */\n bodyFontFamily: string;\n /** Font family for titles and headings */\n titleFontFamily: string;\n /** Font family for code / monospaced text (optional) */\n monoFontFamily?: string;\n /** Multiplier applied to LayoutHints.titleScale (default 1.0) */\n titleScale?: number;\n /** Multiplier applied to LayoutHints.bodyScale (default 1.0) */\n bodyScale?: number;\n /** Default body line height (default 1.4) */\n lineHeight?: number;\n /** Default title line height */\n titleLineHeight?: number;\n /** Default title font weight */\n titleWeight?: 'normal' | 'bold';\n}\n\n// ============================================\n// Visual Style\n// ============================================\n\n/**\n * Global visual-style knobs that templates consult.\n */\nexport interface ThemeStyle {\n /** Default border radius for cards / shapes (px) */\n borderRadius?: number;\n /** Whether templates should default to text shadows */\n textShadow?: boolean;\n /** Darkness of overlay on image-backed blocks (0–1) */\n overlayOpacity?: number;\n /** Multiplier on all animation durations (1.0 = normal, <1 faster, >1 slower) */\n animationSpeed?: number;\n /** Default horizontal padding for text (percentage string, e.g. \"5%\") */\n blockPadding?: string;\n}\n\n// ============================================\n// Render Style\n// ============================================\n\n/**\n * Render-style algorithm preset. Controls layout tweaks, default animations,\n * transitions, and per-template behavioral hints.\n */\nexport interface RenderStyle {\n /** Identifier for this algorithmic approach (e.g. \"documentary\", \"magazine\") */\n name: string;\n /** Partial overrides merged onto the orientation-based LayoutHints */\n layoutOverrides?: Partial<LayoutHints>;\n /** Default entrance animation for text layers */\n defaultTextAnimation?: AnimationType;\n /** Default animation for background / image layers */\n defaultImageAnimation?: AnimationType;\n /** Whether to apply Ken Burns ambient motion to images by default */\n ambientMotion?: boolean;\n /** Default block-to-block transition */\n defaultTransition?: { type: TransitionType; duration?: number };\n /**\n * Per-template behavioral hints. Keys are template names, values are\n * string/number/boolean maps that templates can read to vary their output.\n *\n * @example\n * ```\n * { statHighlight: { entrance: 'dramatic' }, titleBlock: { showAccentLine: false } }\n * ```\n */\n templateHints?: Record<string, Record<string, string | number | boolean>>;\n}\n\n// ============================================\n// Theme\n// ============================================\n\n/**\n * A complete, JSON-serializable theme definition.\n */\nexport interface Theme {\n /** Unique identifier (e.g. \"documentary\") */\n id: string;\n /** Human-readable display name */\n name: string;\n /** Short description for theme pickers */\n description?: string;\n /** Color palette */\n colors: ThemeColorPalette;\n /** Typography settings */\n typography: ThemeTypography;\n /** Global visual-style knobs */\n style: ThemeStyle;\n /** Algorithmic render-style preset */\n renderStyle: RenderStyle;\n /**\n * Named color schemes for per-block color variation.\n * Templates reference these by name (e.g. \"blue\", \"warm\").\n */\n colorSchemes: Record<string, ThemeColorScheme>;\n /** Optional persistent layers baked into the theme */\n persistentLayers?: PersistentLayerConfig;\n}\n\n// ============================================\n// Deep-Partial helper type\n// ============================================\n\n/**\n * Recursively makes every property optional.\n */\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Deep-merge `source` into `target`, returning a new object.\n * Arrays are replaced wholesale (not concatenated).\n */\nfunction deepMerge<T extends Record<string, unknown>>(target: T, source: DeepPartial<T>): T {\n const result = { ...target } as Record<string, unknown>;\n for (const key of Object.keys(source)) {\n const srcVal = (source as Record<string, unknown>)[key];\n const tgtVal = (target as Record<string, unknown>)[key];\n if (\n srcVal !== null &&\n srcVal !== undefined &&\n typeof srcVal === 'object' &&\n !Array.isArray(srcVal) &&\n tgtVal !== null &&\n tgtVal !== undefined &&\n typeof tgtVal === 'object' &&\n !Array.isArray(tgtVal)\n ) {\n result[key] = deepMerge(\n tgtVal as Record<string, unknown>,\n srcVal as DeepPartial<Record<string, unknown>>,\n );\n } else if (srcVal !== undefined) {\n result[key] = srcVal;\n }\n }\n return result as T;\n}\n\n/**\n * Create a new theme by deep-merging overrides onto a base theme.\n * The returned theme gets its own `id` if one is provided in overrides,\n * otherwise keeps the base theme's `id` suffixed with \"-custom\".\n *\n * @example\n * ```ts\n * const myTheme = createTheme(THEMES.documentary, {\n * colors: { primary: '#ff0000' },\n * typography: { bodyFontFamily: '\"Inter\", sans-serif' },\n * });\n * ```\n */\nexport function createTheme(base: Theme, overrides: DeepPartial<Theme>): Theme {\n const merged = deepMerge(\n base as unknown as Record<string, unknown>,\n overrides as DeepPartial<Record<string, unknown>>,\n ) as unknown as Theme;\n if (!overrides.id && merged.id === base.id) {\n merged.id = `${base.id}-custom`;\n }\n return merged;\n}\n"],"mappings":";AA2kBO,SAAS,kBAAkB,OAA2B;AAC3D,SAAO,MAAM,SAAS,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,UAAU,CAAC;AAClE;AAKO,SAAS,iBAAiB,OAAmB,MAAsB;AACxE,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,QAAQ,KAAK;AAC9C,eAAW,MAAM,SAAS,CAAC,EAAE;AAC7B,QAAI,OAAO,QAAS,QAAO;AAAA,EAC7B;AACA,SAAO,MAAM,SAAS,SAAS;AACjC;AAKO,SAAS,eAAe,QAAiB,MAA4B;AAC1E,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,QAAQ,OAAO,CAAC;AACtB,QAAI,QAAQ,MAAM,aAAa,OAAO,MAAM,YAAY,MAAM,UAAU;AACtE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,OAAO,CAAC,KAAK;AACtB;AAKO,SAAS,iBACd,UACA,MACsB;AACtB,MAAI,CAAC,YAAY,CAAC,SAAS,QAAQ,OAAQ,QAAO;AAElD,aAAW,UAAU,SAAS,SAAS;AACrC,QAAI,QAAQ,OAAO,aAAa,OAAO,OAAO,SAAS;AACrD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,YAAY,QAAuB;AACjD,QAAM,SAAmB,CAAC;AAE1B,MAAI,CAAC,OAAO,WAAW;AACrB,WAAO,KAAK,mBAAmB;AAAA,EACjC;AAEA,MAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,GAAG;AAChD,WAAO,KAAK,mBAAmB;AAAA,EACjC;AAEA,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,MAAM,YAAY,OAAO,MAAM,SAAS,WAAW,GAAG;AACjF,WAAO,KAAK,2BAA2B;AAAA,EACzC;AAGA,QAAM,gBAAgB,OAAO;AAC7B,MAAI,UAAU;AACd,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,MAAM,YAAY,UAAU,KAAK;AACnC,aAAO,KAAK,kBAAkB,OAAO,QAAQ,MAAM,SAAS,GAAG;AAAA,IACjE;AACA,cAAU,KAAK,IAAI,SAAS,MAAM,YAAY,MAAM,QAAQ;AAAA,EAC9D;AAEA,MAAI,UAAU,gBAAgB,KAAK;AACjC,WAAO,KAAK,iBAAiB,OAAO,kBAAkB,aAAa,GAAG;AAAA,EACxE;AAEA,SAAO;AACT;;;ACheA,SAAS,UAA6C,QAAW,QAA2B;AAC1F,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAM,SAAU,OAAmC,GAAG;AACtD,UAAM,SAAU,OAAmC,GAAG;AACtD,QACE,WAAW,QACX,WAAW,UACX,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,MAAM,KACrB,WAAW,QACX,WAAW,UACX,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,MAAM,GACrB;AACA,aAAO,GAAG,IAAI;AAAA,QACZ;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,WAAW,QAAW;AAC/B,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAeO,SAAS,YAAY,MAAa,WAAsC;AAC7E,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,UAAU,MAAM,OAAO,OAAO,KAAK,IAAI;AAC1C,WAAO,KAAK,GAAG,KAAK,EAAE;AAAA,EACxB;AACA,SAAO;AACT;","names":[]}
@@ -1,11 +1,3 @@
1
- import {
2
- SeededRandom,
3
- hashString
4
- } from "./chunk-FVTQSNR7.js";
5
- import {
6
- estimateNarrationDuration
7
- } from "./chunk-3E5F2XMR.js";
8
-
9
1
  // src/generate/contentExtractor.ts
10
2
  var STAT_PATTERNS = [
11
3
  /\b(roughly |about |approximately |nearly |over |under |more than |less than )?([\d,]+(?:\.\d+)?)\s*%/gi,
@@ -655,138 +647,9 @@ function mapDefinition(data, base, opts) {
655
647
  };
656
648
  }
657
649
 
658
- // src/generate/slideshowGenerator.ts
659
- var DEFAULT_COLOR_SCHEMES = ["blue", "green", "purple", "orange", "red"];
660
- var DEFAULT_AMBIENT_MOTIONS = [
661
- "zoomIn",
662
- "zoomOut",
663
- "panLeft",
664
- "panRight"
665
- ];
666
- var DEFAULT_ACCENT_POSITIONS = ["left-strip", "right-strip", "bottom-strip"];
667
- function generateSlideshow(text, images = [], options = {}) {
668
- const {
669
- seed,
670
- title,
671
- subtitle,
672
- duration: explicitDuration,
673
- targetSlides,
674
- colorSchemes = DEFAULT_COLOR_SCHEMES,
675
- ambientMotions = DEFAULT_AMBIENT_MOTIONS,
676
- accentPositions = DEFAULT_ACCENT_POSITIONS,
677
- minSlideDuration = 4,
678
- maxSlideDuration = 15,
679
- themeId,
680
- isMarkdown = false,
681
- minConfidence = 0.3
682
- } = options;
683
- const plainText = isMarkdown ? stripMarkdown(text) : text;
684
- const rng = new SeededRandom(seed ?? hashString(plainText));
685
- const totalDuration = explicitDuration ?? estimateNarrationDuration(plainText);
686
- const extraction = extractContent(plainText, { minConfidence });
687
- const allElements = extraction.elements;
688
- const slideBudget = targetSlides ?? computeSlideBudget(totalDuration, allElements.length);
689
- const imageSlotCount = images.length > 0 ? Math.max(1, Math.round(slideBudget * 0.4)) : 0;
690
- const contentSlotCount = Math.max(0, slideBudget - imageSlotCount - (title ? 1 : 0));
691
- const sorted = [...allElements].sort((a, b) => b.confidence - a.confidence);
692
- const selected = sorted.slice(0, contentSlotCount);
693
- selected.sort((a, b) => a.sourcePosition - b.sourcePosition);
694
- const blocks = [];
695
- let slideIndex = 0;
696
- let colorIdx = 0;
697
- let accentIdx = 0;
698
- const totalSlideCount = (title ? 1 : 0) + selected.length + Math.min(imageSlotCount, images.length);
699
- const perSlide = totalSlideCount > 0 ? Math.min(maxSlideDuration, Math.max(minSlideDuration, totalDuration / totalSlideCount)) : minSlideDuration;
700
- if (title) {
701
- const titleBlock = {
702
- id: `slide-${slideIndex++}`,
703
- template: "titleBlock",
704
- title,
705
- subtitle,
706
- duration: Math.min(perSlide, 6),
707
- audioSegment: 0
708
- };
709
- blocks.push(titleBlock);
710
- }
711
- const standaloneImages = images.slice(0, Math.min(imageSlotCount, images.length));
712
- const accentPool = images.length > standaloneImages.length ? images.slice(standaloneImages.length) : [...images];
713
- const imageInterval = standaloneImages.length > 0 ? Math.max(1, Math.floor(selected.length / (standaloneImages.length + 1))) : Infinity;
714
- let imgIdx = 0;
715
- let sinceImage = 0;
716
- for (let i = 0; i < selected.length; i++) {
717
- if (sinceImage >= imageInterval && imgIdx < standaloneImages.length) {
718
- blocks.push(
719
- createImageSlide(standaloneImages[imgIdx], slideIndex++, perSlide, rng, ambientMotions)
720
- );
721
- imgIdx++;
722
- sinceImage = 0;
723
- }
724
- const element = selected[i];
725
- let accentImage;
726
- if (accentPool.length > 0) {
727
- const img = accentPool[i % accentPool.length];
728
- accentImage = {
729
- src: img.src,
730
- alt: img.alt ?? "",
731
- position: accentPositions[accentIdx % accentPositions.length],
732
- ambientMotion: rng.pickRequired(ambientMotions),
733
- credit: img.credit,
734
- license: img.license
735
- };
736
- accentIdx++;
737
- }
738
- const block = mapElementToBlock(element, {
739
- id: `slide-${slideIndex++}`,
740
- duration: perSlide,
741
- audioSegment: 0,
742
- colorScheme: colorSchemes[colorIdx % colorSchemes.length],
743
- accentImage
744
- });
745
- blocks.push(block);
746
- colorIdx++;
747
- sinceImage++;
748
- }
749
- while (imgIdx < standaloneImages.length) {
750
- blocks.push(
751
- createImageSlide(standaloneImages[imgIdx], slideIndex++, perSlide, rng, ambientMotions)
752
- );
753
- imgIdx++;
754
- }
755
- const doc = {
756
- articleId: title ?? "slideshow",
757
- duration: totalDuration,
758
- blocks,
759
- audio: {
760
- segments: [{ src: "", name: "main", duration: totalDuration, startTime: 0 }]
761
- },
762
- themeId
763
- };
764
- return doc;
765
- }
766
- function computeSlideBudget(duration, elementCount) {
767
- const byDuration = Math.round(duration / 60 * 5);
768
- const budget = Math.min(byDuration, elementCount + 4);
769
- return Math.max(3, Math.min(12, budget));
770
- }
771
- function createImageSlide(img, index, duration, rng, motions) {
772
- return {
773
- id: `slide-${index}`,
774
- template: "imageWithCaption",
775
- imageSrc: img.src,
776
- imageAlt: img.alt ?? "",
777
- caption: img.alt,
778
- duration,
779
- audioSegment: 0,
780
- ambientMotion: rng.pickRequired(motions),
781
- imageCredit: img.credit,
782
- imageLicense: img.license
783
- };
784
- }
785
-
786
650
  export {
787
651
  extractContent,
788
652
  stripMarkdown,
789
- mapElementToBlock,
790
- generateSlideshow
653
+ mapElementToBlock
791
654
  };
792
- //# sourceMappingURL=chunk-VP5SZTZ2.js.map
655
+ //# sourceMappingURL=chunk-MIBRIRGJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generate/contentExtractor.ts","../src/generate/templateMapper.ts"],"sourcesContent":["/**\n * Content Extractor\n *\n * Pattern-based extraction of compelling content from plain text.\n * Identifies statistics, dates, quotes, comparisons, facts, impact lines,\n * lists, and definitions that can be turned into slideshow slides.\n *\n * Extraction is purely regex-based — no AI calls, no runtime costs,\n * deterministic and reproducible.\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\n/** Kinds of content the extractor recognises. */\nexport type ExtractionType =\n | 'stat'\n | 'date'\n | 'quote'\n | 'comparison'\n | 'fact'\n | 'impactLine'\n | 'list'\n | 'definition';\n\n/** An extracted content element with source position. */\nexport interface ExtractedElement {\n type: ExtractionType;\n /** Full extracted text (usually the surrounding sentence). */\n text: string;\n /** Confidence score 0–1. */\n confidence: number;\n /** Character offset in the source text. */\n sourcePosition: number;\n /** Character offset where element ends. */\n endPosition: number;\n /** Type-specific structured payload. */\n data:\n | StatData\n | DateData\n | QuoteData\n | ComparisonData\n | FactData\n | ImpactLineData\n | ListData\n | DefinitionData;\n}\n\nexport interface StatData {\n type: 'stat';\n value: string;\n unit?: string;\n description: string;\n}\n\nexport interface DateData {\n type: 'date';\n date: string;\n description: string;\n mood?: 'neutral' | 'somber' | 'celebratory';\n}\n\nexport interface QuoteData {\n type: 'quote';\n quote: string;\n attribution?: string;\n}\n\nexport interface ComparisonData {\n type: 'comparison';\n left: { label: string; sublabel?: string };\n right: { label: string; sublabel?: string };\n header?: string;\n}\n\nexport interface FactData {\n type: 'fact';\n fact: string;\n explanation?: string;\n}\n\nexport interface ImpactLineData {\n type: 'impactLine';\n text: string;\n}\n\nexport interface ListData {\n type: 'list';\n items: string[];\n title?: string;\n}\n\nexport interface DefinitionData {\n type: 'definition';\n term: string;\n definition: string;\n origin?: string;\n}\n\n/** Options for `extractContent`. */\nexport interface ExtractionOptions {\n /** Minimum confidence threshold (default: 0.3). */\n minConfidence?: number;\n /** Which types to extract (default: all). */\n types?: ExtractionType[];\n /** Maximum extractions per type (default: unlimited). */\n maxPerType?: number;\n}\n\n/** Result of `extractContent`. */\nexport interface ExtractionResult {\n elements: ExtractedElement[];\n sourceLength: number;\n stats: {\n statCount: number;\n dateCount: number;\n quoteCount: number;\n comparisonCount: number;\n factCount: number;\n impactLineCount: number;\n listCount: number;\n definitionCount: number;\n totalCount: number;\n };\n}\n\n// ── Patterns ───────────────────────────────────────────────────────\n\nconst STAT_PATTERNS = [\n /\\b(roughly |about |approximately |nearly |over |under |more than |less than )?([\\d,]+(?:\\.\\d+)?)\\s*%/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|trillion|thousand|hundred)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(miles?|feet|ft|meters?|m|kilometers?|km|acres?|square miles?|sq mi|hectares?)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(x|times)\\b/gi,\n /\\b([$€£¥])\\s*([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|thousand)?\\b/gi,\n /\\b(\\d+)(st|nd|rd|th)\\s+(largest|smallest|oldest|newest|most|least)/gi,\n];\n\nconst DATE_PATTERNS = [\n /\\b(January|February|March|April|May|June|July|August|September|October|November|December)\\s+(\\d{1,2},?\\s+)?(\\d{4})\\b/gi,\n /\\b(\\d{4})s\\b|\\b(\\d{1,2})(st|nd|rd|th)\\s+century\\b/gi,\n /\\b(from\\s+)?(\\d{4})\\s*(to|–|-|through)\\s*(\\d{4})\\b/gi,\n /\\b(in|by|since|after|before|during)\\s+(\\d{4})\\b/gi,\n];\n\nconst QUOTE_PATTERNS = [\n /\"([^\"]{15,200})\"/g,\n /\\u201c([^\\u201d]{15,200})\\u201d/g,\n /(\\w+(?:\\s+\\w+)?)\\s+(?:said|stated|wrote|noted|observed|commented),?\\s*\"([^\"]+)\"/gi,\n];\n\nconst COMPARISON_PATTERNS = [\n /from\\s+(.{2,30})\\s+to\\s+(.{2,30})/gi,\n /(.{2,30})\\s+(?:vs\\.?|versus)\\s+(.{2,30})/gi,\n /(.{2,30})\\s+compared\\s+to\\s+(.{2,30})/gi,\n];\n\nconst FACT_INDICATORS = [\n 'largest',\n 'smallest',\n 'oldest',\n 'newest',\n 'first',\n 'last',\n 'most',\n 'least',\n 'only',\n 'unique',\n 'rare',\n 'famous',\n 'important',\n 'significant',\n 'notable',\n 'remarkable',\n 'extraordinary',\n 'crucial',\n 'essential',\n 'critical',\n 'major',\n 'key',\n 'discovered',\n 'founded',\n 'established',\n 'created',\n 'built',\n 'invented',\n 'developed',\n 'introduced',\n 'pioneered',\n 'transformed',\n 'revolutionized',\n 'changed',\n 'shaped',\n];\n\nconst SOMBER_INDICATORS = [\n 'disaster',\n 'tragedy',\n 'death',\n 'died',\n 'killed',\n 'destroyed',\n 'devastated',\n 'collapsed',\n 'lost',\n 'abandoned',\n 'declined',\n 'war',\n 'battle',\n 'conflict',\n 'earthquake',\n 'fire',\n 'flood',\n];\n\nconst CELEBRATORY_INDICATORS = [\n 'celebration',\n 'victory',\n 'success',\n 'achievement',\n 'triumph',\n 'opened',\n 'completed',\n 'inaugurated',\n 'launched',\n 'won',\n 'record',\n 'breakthrough',\n 'milestone',\n 'golden',\n 'grand',\n];\n\nconst IMPACT_VERBS = [\n 'check',\n 'watch',\n 'stop',\n 'prepare',\n 'imagine',\n 'consider',\n 'look',\n 'listen',\n 'notice',\n 'remember',\n 'discover',\n 'explore',\n 'visit',\n 'try',\n 'experience',\n 'avoid',\n 'beware',\n 'enjoy',\n];\n\nconst LIST_PATTERNS = [\n /(?:including|such as|namely|like|features?|offers?)\\s+([^.]{10,150})/gi,\n /(?:three|four|five|six|several|many|various)\\s+(?:main|key|primary|notable|popular|important)?\\s*\\w+(?:\\s+\\w+)?:\\s*([^.]{10,150})/gi,\n];\n\nconst DEFINITION_PATTERNS = [\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+is\\s+(?:a|an|the)\\s+([^.]{10,120})/g,\n /known\\s+as\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),\\s+([^.]{10,120})/gi,\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+refers?\\s+to\\s+([^.]{10,120})/g,\n /called\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),?\\s+(?:which|meaning|a)\\s+([^.]{10,120})/gi,\n];\n\n// ── Main ───────────────────────────────────────────────────────────\n\n/**\n * Extract compelling content from plain text.\n *\n * Call `stripMarkdown(text)` first if the input contains markdown.\n */\nexport function extractContent(text: string, options: ExtractionOptions = {}): ExtractionResult {\n const {\n minConfidence = 0.3,\n types = ['stat', 'date', 'quote', 'comparison', 'fact', 'impactLine', 'list', 'definition'],\n maxPerType,\n } = options;\n\n const elements: ExtractedElement[] = [];\n\n if (types.includes('stat')) elements.push(...extractStats(text));\n if (types.includes('date')) elements.push(...extractDates(text));\n if (types.includes('quote')) elements.push(...extractQuotes(text));\n if (types.includes('comparison')) elements.push(...extractComparisons(text));\n if (types.includes('fact')) elements.push(...extractFacts(text));\n if (types.includes('impactLine')) elements.push(...extractImpactLines(text));\n if (types.includes('list')) elements.push(...extractLists(text));\n if (types.includes('definition')) elements.push(...extractDefinitions(text));\n\n let filtered = elements.filter((e) => e.confidence >= minConfidence);\n\n if (maxPerType !== undefined) {\n const byType = new Map<ExtractionType, ExtractedElement[]>();\n for (const elem of filtered) {\n const list = byType.get(elem.type) || [];\n list.push(elem);\n byType.set(elem.type, list);\n }\n filtered = [];\n for (const [, list] of byType) {\n list.sort((a, b) => b.confidence - a.confidence);\n filtered.push(...list.slice(0, maxPerType));\n }\n }\n\n filtered.sort((a, b) => a.sourcePosition - b.sourcePosition);\n filtered = deduplicateOverlapping(filtered);\n\n return {\n elements: filtered,\n sourceLength: text.length,\n stats: {\n statCount: filtered.filter((e) => e.type === 'stat').length,\n dateCount: filtered.filter((e) => e.type === 'date').length,\n quoteCount: filtered.filter((e) => e.type === 'quote').length,\n comparisonCount: filtered.filter((e) => e.type === 'comparison').length,\n factCount: filtered.filter((e) => e.type === 'fact').length,\n impactLineCount: filtered.filter((e) => e.type === 'impactLine').length,\n listCount: filtered.filter((e) => e.type === 'list').length,\n definitionCount: filtered.filter((e) => e.type === 'definition').length,\n totalCount: filtered.length,\n },\n };\n}\n\n// ── Per-type extractors ────────────────────────────────────────────\n\nfunction extractStats(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of STAT_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const value = match[0].trim();\n\n elements.push({\n type: 'stat',\n text: context.sentence,\n confidence: calculateStatConfidence(value, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'stat', value, description: cleanDescription(context.sentence, value) },\n });\n }\n }\n return elements;\n}\n\nfunction extractDates(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DATE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const dateStr = match[0].trim();\n\n elements.push({\n type: 'date',\n text: context.sentence,\n confidence: calculateDateConfidence(dateStr, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: {\n type: 'date',\n date: dateStr,\n description: cleanDescription(context.sentence, dateStr),\n mood: detectMood(context.sentence),\n },\n });\n }\n }\n return elements;\n}\n\nfunction extractQuotes(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of QUOTE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const quote = match.length > 2 ? match[2] : match[1];\n const attribution = match.length > 2 ? match[1] : undefined;\n if (quote.length < 15 || quote.length > 200) continue;\n\n elements.push({\n type: 'quote',\n text: match[0],\n confidence: calculateQuoteConfidence(quote),\n sourcePosition: position,\n endPosition: position + match[0].length,\n data: { type: 'quote', quote: quote.trim(), attribution: attribution?.trim() },\n });\n }\n }\n return elements;\n}\n\nfunction extractComparisons(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of COMPARISON_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const left = match[1].trim();\n const right = match[2].trim();\n if (left.length < 2 || left.length > 30) continue;\n if (right.length < 2 || right.length > 30) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'comparison',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'comparison', left: { label: left }, right: { label: right } },\n });\n }\n }\n return elements;\n}\n\n/**\n * Iterate over sentence-like spans in the text, yielding trimmed text and\n * accurate start/end indices within the original string.\n *\n * Sentences are approximated as runs of non-[.!?] characters optionally\n * followed by [.!?] delimiters. Leading/trailing whitespace is excluded.\n */\nfunction forEachSentenceSpan(\n text: string,\n callback: (trimmed: string, start: number, end: number) => void,\n): void {\n const sentenceRegex = /[^.!?]+[.!?]*/g;\n let match: RegExpExecArray | null;\n\n while ((match = sentenceRegex.exec(text)) !== null) {\n const raw = match[0];\n const rawStart = match.index;\n const rawEnd = rawStart + raw.length;\n\n // Compute leading whitespace to exclude\n let leading = 0;\n while (leading < raw.length && /\\s/.test(raw[leading])) {\n leading++;\n }\n\n // Compute trailing whitespace or sentence-ending punctuation to exclude\n let trailing = 0;\n while (trailing < raw.length - leading && /[\\s.!?]/.test(raw[raw.length - 1 - trailing])) {\n trailing++;\n }\n\n const start = rawStart + leading;\n const end = rawEnd - trailing;\n if (end <= start) continue;\n\n const trimmed = text.slice(start, end);\n if (!trimmed) continue;\n\n callback(trimmed, start, end);\n }\n}\n\nfunction extractFacts(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length <= 20) return;\n\n const score = scoreSentenceAsFact(trimmed);\n if (score < 0.4) return;\n\n elements.push({\n type: 'fact',\n text: trimmed,\n confidence: score,\n sourcePosition: start,\n endPosition: end,\n data: { type: 'fact', fact: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractImpactLines(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length < 8 || trimmed.length > 60) return;\n\n let score = 0.3;\n const lower = trimmed.toLowerCase();\n\n for (const verb of IMPACT_VERBS) {\n if (lower.startsWith(verb)) {\n score += 0.25;\n break;\n }\n }\n\n if (end < text.length && text[end] === '!') score += 0.15;\n if (trimmed.length < 30) score += 0.1;\n if (trimmed.includes(',')) score -= 0.1;\n if (/^(and|but|or|the|a|an)\\b/i.test(trimmed)) score -= 0.15;\n if (score < 0.5) return;\n\n elements.push({\n type: 'impactLine',\n text: trimmed,\n confidence: Math.min(1, score),\n sourcePosition: start,\n endPosition: end,\n data: { type: 'impactLine', text: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractLists(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of LIST_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const listContent = match[1].trim();\n const items = listContent\n .split(/,\\s*(?:and|or)\\s+|,\\s+|\\s+and\\s+|\\s+or\\s+/)\n .map((item) => item.trim())\n .filter((item) => item.length >= 2 && item.length <= 60);\n\n if (items.length < 3 || items.length > 5) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'list',\n text: context.sentence,\n confidence: 0.65,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'list', items },\n });\n }\n }\n return elements;\n}\n\nfunction extractDefinitions(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DEFINITION_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const term = match[1].trim();\n const definition = match[2].trim();\n if (term.length < 3 || term.length > 30) continue;\n if (definition.length < 10 || definition.length > 120) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'definition',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'definition', term, definition },\n });\n }\n }\n return elements;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\nfunction getSentenceContext(\n text: string,\n matchStart: number,\n matchLength: number,\n): { sentence: string; start: number; end: number } {\n let start = matchStart;\n while (start > 0 && !/[.!?]/.test(text[start - 1])) start--;\n while (start < matchStart && /\\s/.test(text[start])) start++;\n\n let end = matchStart + matchLength;\n while (end < text.length && !/[.!?]/.test(text[end])) end++;\n if (end < text.length) end++;\n\n return { sentence: text.slice(start, end).trim(), start, end };\n}\n\nfunction calculateStatConfidence(value: string, context: string): number {\n let score = 0.5;\n if (value.includes('%')) score += 0.2;\n if (/million|billion|trillion/i.test(value)) score += 0.15;\n if (/increased|decreased|grew|dropped|rose|fell/i.test(context)) score += 0.15;\n if (context.length < 30) score -= 0.2;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateDateConfidence(date: string, context: string): number {\n let score = 0.5;\n if (\n /January|February|March|April|May|June|July|August|September|October|November|December/i.test(\n date,\n )\n )\n score += 0.2;\n if (/founded|established|built|opened|discovered|created/i.test(context)) score += 0.2;\n if (/happened|occurred|took place|began|ended/i.test(context)) score += 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateQuoteConfidence(quote: string): number {\n let score = 0.5;\n if (quote.length >= 30 && quote.length <= 150) score += 0.2;\n if (/\\b(I|we|our|my)\\b/i.test(quote)) score += 0.1;\n if (!/[.!?]$/.test(quote)) score -= 0.1;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction scoreSentenceAsFact(sentence: string): number {\n let score = 0.3;\n const lower = sentence.toLowerCase();\n for (const indicator of FACT_INDICATORS) {\n if (lower.includes(indicator)) {\n score += 0.1;\n break;\n }\n }\n const properNouns = sentence.match(/\\b[A-Z][a-z]+\\b/g);\n if (properNouns && properNouns.length >= 2) score += 0.15;\n if (/\\d/.test(sentence)) score += 0.1;\n if (sentence.endsWith('?')) score -= 0.3;\n if (sentence.length > 200) score -= 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction detectMood(sentence: string): 'neutral' | 'somber' | 'celebratory' {\n const lower = sentence.toLowerCase();\n for (const word of SOMBER_INDICATORS) {\n if (lower.includes(word)) return 'somber';\n }\n for (const word of CELEBRATORY_INDICATORS) {\n if (lower.includes(word)) return 'celebratory';\n }\n return 'neutral';\n}\n\nfunction cleanDescription(sentence: string, value: string): string {\n const prepositionPatterns = [\n new RegExp(`\\\\b[Ii]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Oo]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Dd]uring\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Bb]y\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ss]ince\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ff]rom\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n ];\n\n let desc = sentence;\n for (const pattern of prepositionPatterns) {\n desc = desc.replace(pattern, '');\n }\n if (desc === sentence) {\n desc = sentence.replace(value, '').trim();\n }\n\n desc = desc.replace(/^[,:\\-–—]+\\s*/, '').replace(/\\s*[,:\\-–—]+$/, '');\n desc = desc.replace(\n /\\s+(?:of|on|in|at|by|to|for|from|with|about|over|under|between|than|into|onto|upon)\\s*([,.])/gi,\n '$1',\n );\n desc = desc.replace(\n /\\s+(?:for|of|to|in|at|by|with|from|about|over|under|between|than|into|onto|upon)\\s*[.!?]?\\s*$/i,\n '.',\n );\n desc = desc.replace(/\\s{2,}/g, ' ').replace(/\\s+([,.])/g, '$1');\n desc = desc.charAt(0).toUpperCase() + desc.slice(1);\n return desc.trim();\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nconst TYPE_SPECIFICITY: Record<ExtractionType, number> = {\n stat: 5,\n date: 4,\n quote: 3,\n definition: 3,\n comparison: 2,\n list: 2,\n impactLine: 2,\n fact: 1,\n};\n\nfunction deduplicateOverlapping(elements: ExtractedElement[]): ExtractedElement[] {\n if (elements.length <= 1) return elements;\n\n const toRemove = new Set<number>();\n\n for (let i = 0; i < elements.length; i++) {\n if (toRemove.has(i)) continue;\n const a = elements[i];\n\n for (let j = i + 1; j < elements.length; j++) {\n if (toRemove.has(j)) continue;\n const b = elements[j];\n\n if (elementsOverlap(a, b)) {\n const aSpec = TYPE_SPECIFICITY[a.type];\n const bSpec = TYPE_SPECIFICITY[b.type];\n if (aSpec >= bSpec) {\n toRemove.add(j);\n } else {\n toRemove.add(i);\n break;\n }\n }\n }\n }\n\n return elements.filter((_, index) => !toRemove.has(index));\n}\n\nfunction elementsOverlap(a: ExtractedElement, b: ExtractedElement): boolean {\n if (a.sourcePosition === b.sourcePosition) return true;\n if (a.sourcePosition <= b.sourcePosition && a.endPosition >= b.endPosition) return true;\n if (b.sourcePosition <= a.sourcePosition && b.endPosition >= a.endPosition) return true;\n\n const overlapStart = Math.max(a.sourcePosition, b.sourcePosition);\n const overlapEnd = Math.min(a.endPosition, b.endPosition);\n if (overlapEnd > overlapStart) {\n const overlapLength = overlapEnd - overlapStart;\n const minLength = Math.min(a.endPosition - a.sourcePosition, b.endPosition - b.sourcePosition);\n if (overlapLength > minLength * 0.5) return true;\n }\n\n return false;\n}\n\n/**\n * Strip markdown formatting from text, returning plain prose.\n * Call this before `extractContent` if the input is markdown.\n */\nexport function stripMarkdown(markdown: string): string {\n return (\n markdown\n // Remove code blocks (before inline code)\n .replace(/```[\\s\\S]*?```/g, '')\n // Remove images (before links — `![` prefix distinguishes them)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '')\n // Remove links, keeping text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n .replace(/\\*([^*]+)\\*/g, '$1')\n .replace(/__([^_]+)__/g, '$1')\n .replace(/_([^_]+)_/g, '$1')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/^>\\s+/gm, '')\n .replace(/^[-*_]{3,}$/gm, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n","/**\n * Template Mapper\n *\n * Converts extracted content elements into squisq TemplateBlock inputs.\n * Each ExtractionType maps to a specific template from BlockTemplates.\n */\n\nimport type {\n ExtractedElement,\n StatData,\n DateData,\n QuoteData,\n ComparisonData,\n FactData,\n ImpactLineData,\n ListData,\n DefinitionData,\n} from './contentExtractor.js';\n\nimport type {\n StatHighlightInput,\n QuoteBlockInput,\n FactCardInput,\n TwoColumnInput,\n DateEventInput,\n FullBleedQuoteInput,\n ListBlockInput,\n DefinitionCardInput,\n TemplateBlock,\n ColorScheme,\n AccentImage,\n} from '../schemas/BlockTemplates.js';\n\n/** Options for mapping an element to a template block. */\nexport interface MapOptions {\n id: string;\n duration: number;\n audioSegment: number;\n colorScheme?: ColorScheme;\n accentImage?: AccentImage;\n sourceStartTime?: number;\n sourceDuration?: number;\n}\n\n/**\n * Convert an extracted element into the matching squisq TemplateBlock.\n */\nexport function mapElementToBlock(element: ExtractedElement, options: MapOptions): TemplateBlock {\n const base = {\n id: options.id,\n duration: options.duration,\n audioSegment: options.audioSegment,\n sourceStartTime: options.sourceStartTime,\n sourceDuration: options.sourceDuration,\n };\n\n switch (element.data.type) {\n case 'stat':\n return mapStat(element.data, base, options);\n case 'date':\n return mapDate(element.data, base, options);\n case 'quote':\n return mapQuote(element.data, base, options);\n case 'comparison':\n return mapComparison(element.data, base, options);\n case 'fact':\n return mapFact(element.data, base, options);\n case 'impactLine':\n return mapImpactLine(element.data, base, options);\n case 'list':\n return mapList(element.data, base, options);\n case 'definition':\n return mapDefinition(element.data, base, options);\n }\n}\n\n// ── Per-type mappers ───────────────────────────────────────────────\n\ntype Base = {\n id: string;\n duration: number;\n audioSegment: number;\n sourceStartTime?: number;\n sourceDuration?: number;\n};\n\nfunction mapStat(data: StatData, base: Base, opts: MapOptions): StatHighlightInput {\n return {\n ...base,\n template: 'statHighlight',\n stat: data.value,\n description: data.description,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDate(data: DateData, base: Base, opts: MapOptions): DateEventInput {\n return {\n ...base,\n template: 'dateEvent',\n date: data.date,\n description: data.description,\n mood: data.mood,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapQuote(data: QuoteData, base: Base, opts: MapOptions): QuoteBlockInput {\n return {\n ...base,\n template: 'quoteBlock',\n quote: data.quote,\n attribution: data.attribution,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapComparison(data: ComparisonData, base: Base, opts: MapOptions): TwoColumnInput {\n return {\n ...base,\n template: 'twoColumn',\n left: data.left,\n right: data.right,\n header: data.header,\n leftColor: opts.colorScheme,\n };\n}\n\nfunction mapFact(data: FactData, base: Base, opts: MapOptions): FactCardInput {\n return {\n ...base,\n template: 'factCard',\n fact: data.fact,\n explanation: data.explanation ?? '',\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapImpactLine(data: ImpactLineData, base: Base, opts: MapOptions): FullBleedQuoteInput {\n return {\n ...base,\n template: 'fullBleedQuote',\n text: data.text,\n colorScheme: opts.colorScheme,\n };\n}\n\nfunction mapList(data: ListData, base: Base, opts: MapOptions): ListBlockInput {\n return {\n ...base,\n template: 'listBlock',\n items: data.items,\n title: data.title,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDefinition(data: DefinitionData, base: Base, opts: MapOptions): DefinitionCardInput {\n return {\n ...base,\n template: 'definitionCard',\n term: data.term,\n definition: data.definition,\n origin: data.origin,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n"],"mappings":"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","names":[]}
@@ -196,29 +196,36 @@ function extractTemplateAnnotation(children) {
196
196
  }
197
197
  function parseAnnotationTokens(inner) {
198
198
  const tokens = inner.split(/\s+/);
199
- const template = tokens[0];
200
199
  const params = {};
201
- for (let i = 1; i < tokens.length; i++) {
200
+ const firstIsParam = tokens[0].indexOf("=") > 0;
201
+ const template = firstIsParam ? void 0 : tokens[0];
202
+ const startIdx = firstIsParam ? 0 : 1;
203
+ for (let i = startIdx; i < tokens.length; i++) {
202
204
  const eqIdx = tokens[i].indexOf("=");
203
205
  if (eqIdx > 0) {
204
206
  params[tokens[i].slice(0, eqIdx)] = tokens[i].slice(eqIdx + 1);
205
207
  }
206
208
  }
207
- const result = { template };
209
+ const result = {};
210
+ if (template) {
211
+ result.template = template;
212
+ }
208
213
  if (Object.keys(params).length > 0) {
209
214
  result.params = params;
210
215
  }
211
216
  return result;
212
217
  }
213
218
  function serializeTemplateAnnotation(annotation) {
214
- let result = `{[${annotation.template}`;
219
+ const parts = [];
220
+ if (annotation.template) {
221
+ parts.push(annotation.template);
222
+ }
215
223
  if (annotation.params) {
216
224
  for (const [key, value] of Object.entries(annotation.params)) {
217
- result += ` ${key}=${value}`;
225
+ parts.push(`${key}=${value}`);
218
226
  }
219
227
  }
220
- result += "]}";
221
- return result;
228
+ return `{[${parts.join(" ")}]}`;
222
229
  }
223
230
  function fromMdast(root, options) {
224
231
  const doParseHtml = options?.parseHtml !== false;
@@ -885,4 +892,4 @@ export {
885
892
  parseMarkdown,
886
893
  stringifyMarkdown
887
894
  };
888
- //# sourceMappingURL=chunk-URU6QMLY.js.map
895
+ //# sourceMappingURL=chunk-NJLL5XYC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markdown/parse.ts","../src/markdown/htmlParse.ts","../src/markdown/convert.ts","../src/markdown/stringify.ts"],"sourcesContent":["/**\n * Markdown → JSON Parser\n *\n * Parses a markdown string into a MarkdownDocument JSON structure\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport type { MarkdownDocument, ParseOptions } from './types.js';\nimport { fromMdast } from './convert.js';\nimport { parseFrontmatter } from './utils.js';\n\n// Cache the default processor (all extensions enabled) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n/**\n * Parse a markdown string into a MarkdownDocument.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to disable specific extensions.\n *\n * @param markdown - The markdown source text\n * @param options - Parser options (extensions, HTML parsing)\n * @returns A MarkdownDocument representing the parsed markdown\n *\n * @example\n * ```ts\n * const doc = parseMarkdown('# Hello\\n\\nWorld **bold** and *italic*');\n * // doc.type === 'document'\n * // doc.children[0].type === 'heading'\n * // doc.children[1].type === 'paragraph'\n * ```\n */\nexport function parseMarkdown(markdown: string, options?: ParseOptions): MarkdownDocument {\n // Use cached default processor when all extensions are enabled (the common case).\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n options.frontmatter !== false);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkFrontmatter, ['yaml']);\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested extensions.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified().use(remarkParse);\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n if (options?.frontmatter !== false) {\n processor = processor.use(remarkFrontmatter, ['yaml']);\n }\n }\n\n // Parse markdown → mdast tree (result is an mdast Root node)\n const mdastTree = processor.parse(markdown) as {\n type: string;\n children?: Array<{ type: string; value?: string }>;\n };\n\n // Convert mdast → MarkdownDocument\n const doc = fromMdast(mdastTree as Parameters<typeof fromMdast>[0], {\n parseHtml: options?.parseHtml,\n });\n\n // Extract YAML frontmatter if present\n if (options?.frontmatter !== false) {\n const yamlNode = mdastTree.children?.find((n) => n.type === 'yaml');\n if (yamlNode?.value) {\n const fm = parseFrontmatter(yamlNode.value);\n if (fm && Object.keys(fm).length > 0) {\n doc.frontmatter = fm;\n }\n }\n }\n\n return doc;\n}\n","/**\n * HTML ↔ HtmlNode Conversion\n *\n * Parses raw HTML strings into the squisq HtmlNode tree representation\n * using hast-util-from-html (parse5-backed), and serializes HtmlNode trees\n * back to HTML strings with a lightweight custom serializer.\n */\n\nimport { fromHtml } from 'hast-util-from-html';\nimport type { HtmlNode } from './types.js';\n\n// ============================================\n// Hast → HtmlNode conversion\n// ============================================\n\n/**\n * Mapping from hast DOM-style property names to HTML attribute names.\n * Only includes names where the property and attribute differ.\n */\nconst HAST_PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n httpEquiv: 'http-equiv',\n acceptCharset: 'accept-charset',\n};\n\n/** HTML void elements that are self-closing. */\nconst VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\n/**\n * Convert a hast property name to an HTML attribute name.\n */\nfunction propertyToAttribute(prop: string): string {\n if (HAST_PROP_TO_ATTR[prop]) return HAST_PROP_TO_ATTR[prop];\n\n // data-* attributes: dataFooBar → data-foo-bar\n if (prop.length > 4 && prop.startsWith('data') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'data-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // aria-* attributes: ariaLabel → aria-label\n if (prop.length > 4 && prop.startsWith('aria') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'aria-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // Most HTML attributes are lowercase (tabIndex → tabindex, readOnly → readonly, etc.)\n return prop.toLowerCase();\n}\n\n/**\n * Convert hast element properties to a flat HTML attribute map.\n */\nfunction convertHastProperties(properties: Record<string, unknown>): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (value === false || value === undefined || value === null) continue;\n\n const attrName = propertyToAttribute(key);\n\n if (value === true) {\n attrs[attrName] = '';\n } else if (Array.isArray(value)) {\n attrs[attrName] = value.join(' ');\n } else {\n attrs[attrName] = String(value);\n }\n }\n return attrs;\n}\n\n/**\n * Recursively convert hast child nodes to HtmlNode[].\n */\nfunction hastChildrenToNodes(children: unknown[]): HtmlNode[] {\n const result: HtmlNode[] = [];\n for (const child of children) {\n const node = hastNodeToHtmlNode(child as Record<string, unknown>);\n if (node) result.push(node);\n }\n return result;\n}\n\n/**\n * Convert a single hast node to an HtmlNode.\n */\nfunction hastNodeToHtmlNode(node: Record<string, unknown>): HtmlNode | null {\n switch (node.type) {\n case 'element': {\n const tagName = node.tagName as string;\n const properties = (node.properties ?? {}) as Record<string, unknown>;\n const children = (node.children ?? []) as unknown[];\n return {\n type: 'htmlElement',\n tagName,\n attributes: convertHastProperties(properties),\n children: hastChildrenToNodes(children),\n selfClosing: VOID_ELEMENTS.has(tagName) && children.length === 0,\n };\n }\n case 'text':\n return {\n type: 'htmlText',\n value: node.value as string,\n };\n case 'comment':\n return {\n type: 'htmlComment',\n value: node.value as string,\n };\n case 'doctype':\n // Doctype nodes are rare in markdown HTML; skip them\n return null;\n default:\n return null;\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Parse a raw HTML string into an HtmlNode[] tree.\n *\n * Uses the HTML5 parsing algorithm (via parse5) for correct handling of\n * self-closing tags, optional end tags, entity decoding, etc.\n *\n * @param html - Raw HTML string to parse\n * @returns Array of top-level HtmlNode elements\n */\nexport function parseHtmlToNodes(html: string): HtmlNode[] {\n if (!html || !html.trim()) return [];\n\n try {\n const tree = fromHtml(html, { fragment: true });\n return hastChildrenToNodes(tree.children as unknown[]);\n } catch {\n // If parsing fails, return the raw text as a single text node\n return [{ type: 'htmlText', value: html }];\n }\n}\n\n/**\n * Escape special characters in HTML attribute values.\n */\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/**\n * Serialize an HtmlNode back to an HTML string.\n */\nfunction stringifyNode(node: HtmlNode): string {\n switch (node.type) {\n case 'htmlElement': {\n const attrs = Object.entries(node.attributes)\n .map(([k, v]) => (v === '' ? k : `${k}=\"${escapeHtmlAttr(v)}\"`))\n .join(' ');\n const open = attrs ? `<${node.tagName} ${attrs}>` : `<${node.tagName}>`;\n\n if (node.selfClosing) return open;\n\n const inner = node.children.map(stringifyNode).join('');\n return `${open}${inner}</${node.tagName}>`;\n }\n case 'htmlText':\n return node.value;\n case 'htmlComment':\n return `<!--${node.value}-->`;\n }\n}\n\n/**\n * Serialize an HtmlNode[] tree back to an HTML string.\n *\n * @param nodes - Array of HtmlNode elements to serialize\n * @returns HTML string\n */\nexport function stringifyHtmlNodes(nodes: HtmlNode[]): string {\n return nodes.map(stringifyNode).join('');\n}\n","/**\n * mdast ↔ Squisq Markdown Node Conversion\n *\n * Converts between the remark/mdast AST format and the squisq\n * MarkdownDocument JSON format. This is the bridge layer that allows\n * us to use the battle-tested unified/remark ecosystem for parsing\n * and serialization while exposing our own clean, well-typed interfaces.\n *\n * The conversion is designed for perfect round-tripping:\n * toMdast(fromMdast(tree)) should produce an equivalent mdast tree.\n */\n\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownListItem,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownSourcePosition,\n HeadingTemplateAnnotation,\n MarkdownHeading,\n} from './types.js';\nimport { parseHtmlToNodes } from './htmlParse.js';\n\n// ============================================\n// Generic mdast node shape\n// ============================================\n\n/**\n * Minimal interface for mdast/unist nodes.\n * Using a generic shape avoids hard dependency on @types/mdast\n * and handles extension nodes (GFM, math, directive) uniformly.\n */\ninterface MdastPosition {\n start: { line: number; column: number; offset?: number };\n end: { line: number; column: number; offset?: number };\n}\n\ninterface MdastNode {\n type: string;\n position?: MdastPosition;\n children?: MdastNode[];\n value?: string;\n depth?: number;\n ordered?: boolean | null;\n start?: number | null;\n spread?: boolean | null;\n checked?: boolean | null;\n lang?: string | null;\n meta?: string | null;\n url?: string;\n title?: string | null;\n alt?: string;\n identifier?: string;\n label?: string;\n referenceType?: string;\n align?: (string | null)[];\n name?: string;\n attributes?: Record<string, string>;\n data?: Record<string, unknown>;\n}\n\n// ============================================\n// Position conversion\n// ============================================\n\nfunction convertPosition(pos?: MdastPosition): MarkdownSourcePosition | undefined {\n if (!pos) return undefined;\n return {\n start: {\n line: pos.start.line,\n column: pos.start.column,\n ...(pos.start.offset != null ? { offset: pos.start.offset } : {}),\n },\n end: {\n line: pos.end.line,\n column: pos.end.column,\n ...(pos.end.offset != null ? { offset: pos.end.offset } : {}),\n },\n };\n}\n\nfunction toMdastPosition(pos: MarkdownSourcePosition): MdastPosition {\n return {\n start: {\n line: pos.start.line,\n column: pos.start.column,\n ...(pos.start.offset != null ? { offset: pos.start.offset } : {}),\n },\n end: {\n line: pos.end.line,\n column: pos.end.column,\n ...(pos.end.offset != null ? { offset: pos.end.offset } : {}),\n },\n };\n}\n\n/** Conditionally include position in node. */\nfunction posField(\n pos?: MarkdownSourcePosition,\n): { position: MarkdownSourcePosition } | Record<string, never> {\n return pos ? { position: pos } : {};\n}\n\nfunction mdastPosField(\n pos?: MarkdownSourcePosition,\n): { position: MdastPosition } | Record<string, never> {\n return pos ? { position: toMdastPosition(pos) } : {};\n}\n\n// ============================================\n// mdast → Squisq (fromMdast)\n// ============================================\n\n/**\n * Extract plain text content from an mdast node tree.\n * Used for directive labels.\n */\nfunction extractText(node: MdastNode): string {\n if (node.value != null) return node.value;\n if (node.children) {\n return node.children.map(extractText).join('');\n }\n return '';\n}\n\n// ============================================\n// Template annotation helpers\n// ============================================\n\n/**\n * Regex matching a trailing `{[templateName key=value …]}` annotation.\n * Captures the content between `{[` and `]}`.\n */\nconst TEMPLATE_ANNOTATION_RE = /\\s*\\{\\[([^\\]]+)\\]\\}\\s*$/;\n\n/**\n * Extract a `{[templateName key=value …]}` annotation from a heading's\n * inline children. Mutates the children array in-place: strips the\n * annotation text from the last text node (or removes the node entirely).\n *\n * @returns The parsed annotation, or null if none found.\n */\nfunction extractTemplateAnnotation(\n children: MarkdownInlineNode[],\n): HeadingTemplateAnnotation | null {\n // Walk backwards to find the last text node\n for (let i = children.length - 1; i >= 0; i--) {\n const child = children[i];\n if (child.type === 'text') {\n const match = child.value.match(TEMPLATE_ANNOTATION_RE);\n if (match) {\n const inner = match[1].trim();\n const annotation = parseAnnotationTokens(inner);\n\n // Strip the matched portion from the text\n const stripped = child.value.slice(0, match.index!).replace(/\\s+$/, '');\n if (stripped) {\n (child as { value: string }).value = stripped;\n } else {\n // Remove the now-empty text node\n children.splice(i, 1);\n }\n return annotation;\n }\n // Only check the last text node (trailing position)\n break;\n }\n // If the last child isn't a text node, there's no annotation\n break;\n }\n return null;\n}\n\n/**\n * Parse the inner content of a `{[…]}` annotation into template + params.\n *\n * Input: `\"chart colorScheme=blue size=large\"`\n * Output: `{ template: 'chart', params: { colorScheme: 'blue', size: 'large' } }`\n */\nfunction parseAnnotationTokens(inner: string): HeadingTemplateAnnotation {\n const tokens = inner.split(/\\s+/);\n const params: Record<string, string> = {};\n\n // If the first token contains '=', there's no template name —\n // the annotation is purely key-value (e.g., `{[audio=intro.mp3]}`).\n const firstIsParam = tokens[0].indexOf('=') > 0;\n const template = firstIsParam ? undefined : tokens[0];\n const startIdx = firstIsParam ? 0 : 1;\n\n for (let i = startIdx; i < tokens.length; i++) {\n const eqIdx = tokens[i].indexOf('=');\n if (eqIdx > 0) {\n params[tokens[i].slice(0, eqIdx)] = tokens[i].slice(eqIdx + 1);\n }\n }\n\n const result: HeadingTemplateAnnotation = {};\n if (template) {\n result.template = template;\n }\n if (Object.keys(params).length > 0) {\n result.params = params;\n }\n return result;\n}\n\n/**\n * Serialize a HeadingTemplateAnnotation back to `{[templateName key=value …]}` text.\n */\nfunction serializeTemplateAnnotation(annotation: HeadingTemplateAnnotation): string {\n const parts: string[] = [];\n if (annotation.template) {\n parts.push(annotation.template);\n }\n if (annotation.params) {\n for (const [key, value] of Object.entries(annotation.params)) {\n parts.push(`${key}=${value}`);\n }\n }\n return `{[${parts.join(' ')}]}`;\n}\n\n/**\n * Convert an mdast Root node to a MarkdownDocument.\n *\n * @param root - The mdast root node (from remark-parse)\n * @param options - Conversion options\n * @returns A MarkdownDocument\n */\nexport function fromMdast(root: MdastNode, options?: { parseHtml?: boolean }): MarkdownDocument {\n const doParseHtml = options?.parseHtml !== false;\n return {\n type: 'document',\n children: convertBlockChildren(root.children ?? [], doParseHtml),\n ...posField(convertPosition(root.position)),\n };\n}\n\nfunction convertBlockChildren(children: MdastNode[], parseHtml: boolean): MarkdownBlockNode[] {\n const result: MarkdownBlockNode[] = [];\n for (const child of children) {\n const node = convertBlockNode(child, parseHtml);\n if (node) result.push(node);\n }\n return result;\n}\n\nfunction convertInlineChildren(children: MdastNode[], parseHtml: boolean): MarkdownInlineNode[] {\n const result: MarkdownInlineNode[] = [];\n for (const child of children) {\n const node = convertInlineNode(child, parseHtml);\n if (node) result.push(node);\n }\n return result;\n}\n\nfunction convertBlockNode(node: MdastNode, parseHtml: boolean): MarkdownBlockNode | null {\n const pos = convertPosition(node.position);\n\n switch (node.type) {\n case 'heading': {\n const headingChildren = convertInlineChildren(node.children ?? [], parseHtml);\n const annotation = extractTemplateAnnotation(headingChildren);\n const result: MarkdownHeading = {\n type: 'heading',\n depth: (node.depth ?? 1) as 1 | 2 | 3 | 4 | 5 | 6,\n children: headingChildren,\n ...posField(pos),\n };\n if (annotation) {\n result.templateAnnotation = annotation;\n }\n return result;\n }\n\n case 'paragraph':\n return {\n type: 'paragraph',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'blockquote':\n return {\n type: 'blockquote',\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'list':\n return {\n type: 'list',\n ordered: node.ordered ?? false,\n ...(node.start != null ? { start: node.start } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: (node.children ?? []).map((item) => convertListItem(item, parseHtml)),\n ...posField(pos),\n };\n\n case 'code':\n return {\n type: 'code',\n ...(node.lang != null ? { lang: node.lang } : {}),\n ...(node.meta != null ? { meta: node.meta } : {}),\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'thematicBreak':\n return {\n type: 'thematicBreak',\n ...posField(pos),\n };\n\n case 'table':\n return {\n type: 'table',\n ...(node.align\n ? { align: node.align.map((a) => a as 'left' | 'right' | 'center' | null) }\n : {}),\n children: (node.children ?? []).map((row, i) => convertTableRow(row, i === 0, parseHtml)),\n ...posField(pos),\n };\n\n case 'html':\n return {\n type: 'htmlBlock',\n rawHtml: node.value ?? '',\n htmlChildren: parseHtml ? parseHtmlToNodes(node.value ?? '') : [],\n ...posField(pos),\n };\n\n case 'math':\n return {\n type: 'math',\n value: node.value ?? '',\n ...(node.meta != null ? { meta: node.meta } : {}),\n ...posField(pos),\n };\n\n case 'definition':\n return {\n type: 'definition',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n url: node.url ?? '',\n ...(node.title != null ? { title: node.title } : {}),\n ...posField(pos),\n };\n\n case 'footnoteDefinition':\n return {\n type: 'footnoteDefinition',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'containerDirective': {\n const allChildren = node.children ?? [];\n let label: string | undefined;\n let contentChildren = allChildren;\n\n // Extract directive label (first paragraph with directiveLabel flag)\n if (\n allChildren.length > 0 &&\n allChildren[0].type === 'paragraph' &&\n allChildren[0].data?.directiveLabel === true\n ) {\n label = extractText(allChildren[0]);\n contentChildren = allChildren.slice(1);\n }\n\n return {\n type: 'containerDirective',\n name: node.name ?? '',\n ...(label ? { label } : {}),\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertBlockChildren(contentChildren, parseHtml),\n ...posField(pos),\n };\n }\n\n case 'leafDirective':\n return {\n type: 'leafDirective',\n name: node.name ?? '',\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'yaml':\n // YAML frontmatter nodes are handled separately in parse.ts — skip silently\n return null;\n\n default:\n // Unknown block node — skip with a warning\n if (typeof console !== 'undefined') {\n console.warn(`[squisq/markdown] Unknown mdast block node type: \"${node.type}\"`);\n }\n return null;\n }\n}\n\nfunction convertListItem(node: MdastNode, parseHtml: boolean): MarkdownListItem {\n const pos = convertPosition(node.position);\n return {\n type: 'listItem',\n ...(node.checked != null ? { checked: node.checked } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n}\n\nfunction convertTableRow(node: MdastNode, isHeader: boolean, parseHtml: boolean): MarkdownTableRow {\n const pos = convertPosition(node.position);\n return {\n type: 'tableRow',\n children: (node.children ?? []).map((cell) => convertTableCell(cell, isHeader, parseHtml)),\n ...posField(pos),\n };\n}\n\nfunction convertTableCell(\n node: MdastNode,\n isHeader: boolean,\n parseHtml: boolean,\n): MarkdownTableCell {\n const pos = convertPosition(node.position);\n return {\n type: 'tableCell',\n ...(isHeader ? { isHeader: true } : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n}\n\nfunction convertInlineNode(node: MdastNode, parseHtml: boolean): MarkdownInlineNode | null {\n const pos = convertPosition(node.position);\n\n switch (node.type) {\n case 'text':\n return {\n type: 'text',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'emphasis':\n return {\n type: 'emphasis',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'strong':\n return {\n type: 'strong',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'delete':\n return {\n type: 'delete',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'inlineCode':\n return {\n type: 'inlineCode',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'link':\n return {\n type: 'link',\n url: node.url ?? '',\n ...(node.title != null ? { title: node.title } : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'image':\n return {\n type: 'image',\n url: node.url ?? '',\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...(node.title != null ? { title: node.title } : {}),\n ...posField(pos),\n };\n\n case 'break':\n return {\n type: 'break',\n ...posField(pos),\n };\n\n case 'html':\n return {\n type: 'htmlInline',\n rawHtml: node.value ?? '',\n htmlChildren: parseHtml ? parseHtmlToNodes(node.value ?? '') : [],\n ...posField(pos),\n };\n\n case 'inlineMath':\n return {\n type: 'inlineMath',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'footnoteReference':\n return {\n type: 'footnoteReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n ...posField(pos),\n };\n\n case 'linkReference':\n return {\n type: 'linkReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: (node.referenceType ?? 'full') as 'full' | 'collapsed' | 'shortcut',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'imageReference':\n return {\n type: 'imageReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: (node.referenceType ?? 'full') as 'full' | 'collapsed' | 'shortcut',\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...posField(pos),\n };\n\n case 'textDirective':\n return {\n type: 'textDirective',\n name: node.name ?? '',\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n default:\n if (typeof console !== 'undefined') {\n console.warn(`[squisq/markdown] Unknown mdast inline node type: \"${node.type}\"`);\n }\n return null;\n }\n}\n\n// ============================================\n// Squisq → mdast (toMdast)\n// ============================================\n\n/**\n * Convert a MarkdownDocument back to an mdast Root node.\n *\n * @param doc - A MarkdownDocument\n * @returns An mdast Root node suitable for remark-stringify\n */\nexport function toMdast(doc: MarkdownDocument): MdastNode {\n return {\n type: 'root',\n children: doc.children.map((n) => blockToMdast(n)),\n ...mdastPosField(doc.position),\n };\n}\n\nfunction blockToMdast(node: MarkdownBlockNode): MdastNode {\n switch (node.type) {\n case 'heading': {\n const mdastChildren = node.children.map(inlineToMdast);\n if (node.templateAnnotation) {\n const suffix = serializeTemplateAnnotation(node.templateAnnotation);\n // Append to last text node, or create a new one\n const lastChild = mdastChildren[mdastChildren.length - 1];\n if (lastChild && lastChild.type === 'text') {\n lastChild.value = (lastChild.value ?? '') + ' ' + suffix;\n } else {\n mdastChildren.push({ type: 'text', value: ' ' + suffix });\n }\n }\n return {\n type: 'heading',\n depth: node.depth,\n children: mdastChildren,\n ...mdastPosField(node.position),\n };\n }\n\n case 'paragraph':\n return {\n type: 'paragraph',\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'blockquote':\n return {\n type: 'blockquote',\n children: node.children.map(blockToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'list':\n return {\n type: 'list',\n ordered: node.ordered,\n ...(node.start != null ? { start: node.start } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: node.children.map(listItemToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'code':\n return {\n type: 'code',\n ...(node.lang != null ? { lang: node.lang } : {}),\n ...(node.meta != null ? { meta: node.meta } : {}),\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'thematicBreak':\n return {\n type: 'thematicBreak',\n ...mdastPosField(node.position),\n };\n\n case 'table':\n return {\n type: 'table',\n ...(node.align ? { align: node.align } : {}),\n children: node.children.map(tableRowToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'htmlBlock':\n return {\n type: 'html',\n value: node.rawHtml,\n ...mdastPosField(node.position),\n };\n\n case 'math':\n return {\n type: 'math',\n value: node.value,\n ...(node.meta != null ? { meta: node.meta } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'definition':\n return {\n type: 'definition',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n url: node.url,\n ...(node.title != null ? { title: node.title } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'footnoteDefinition':\n return {\n type: 'footnoteDefinition',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n children: node.children.map(blockToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'containerDirective': {\n const children: MdastNode[] = [];\n\n // Reinject label as first paragraph with directiveLabel flag\n if (node.label) {\n children.push({\n type: 'paragraph',\n data: { directiveLabel: true },\n children: [{ type: 'text', value: node.label }],\n });\n }\n\n children.push(...node.children.map(blockToMdast));\n\n return {\n type: 'containerDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children,\n ...mdastPosField(node.position),\n };\n }\n\n case 'leafDirective':\n return {\n type: 'leafDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'definitionList':\n // Definition lists don't map to standard mdast.\n // Convert to HTML as fallback.\n return {\n type: 'html',\n value: definitionListToHtml(node),\n ...mdastPosField(node.position),\n };\n\n default:\n return { type: 'html', value: '' };\n }\n}\n\nfunction listItemToMdast(item: MarkdownListItem): MdastNode {\n return {\n type: 'listItem',\n ...(item.checked != null ? { checked: item.checked } : {}),\n ...(item.spread != null ? { spread: item.spread } : {}),\n children: item.children.map(blockToMdast),\n ...mdastPosField(item.position),\n };\n}\n\nfunction tableRowToMdast(row: MarkdownTableRow): MdastNode {\n return {\n type: 'tableRow',\n children: row.children.map(tableCellToMdast),\n ...mdastPosField(row.position),\n };\n}\n\nfunction tableCellToMdast(cell: MarkdownTableCell): MdastNode {\n return {\n type: 'tableCell',\n children: cell.children.map(inlineToMdast),\n ...mdastPosField(cell.position),\n };\n}\n\nfunction inlineToMdast(node: MarkdownInlineNode): MdastNode {\n switch (node.type) {\n case 'text':\n return {\n type: 'text',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'emphasis':\n return {\n type: 'emphasis',\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'strong':\n return {\n type: 'strong',\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'delete':\n return {\n type: 'delete',\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'inlineCode':\n return {\n type: 'inlineCode',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'link':\n return {\n type: 'link',\n url: node.url,\n ...(node.title != null ? { title: node.title } : {}),\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'image':\n return {\n type: 'image',\n url: node.url,\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...(node.title != null ? { title: node.title } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'break':\n return {\n type: 'break',\n ...mdastPosField(node.position),\n };\n\n case 'htmlInline':\n return {\n type: 'html',\n value: node.rawHtml,\n ...mdastPosField(node.position),\n };\n\n case 'inlineMath':\n return {\n type: 'inlineMath',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'footnoteReference':\n return {\n type: 'footnoteReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'linkReference':\n return {\n type: 'linkReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: node.referenceType,\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'imageReference':\n return {\n type: 'imageReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: node.referenceType,\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'textDirective':\n return {\n type: 'textDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children: node.children.map(inlineToMdast),\n ...mdastPosField(node.position),\n };\n\n default:\n return { type: 'text', value: '' };\n }\n}\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Convert a MarkdownDefinitionList to an HTML <dl> string.\n * Fallback for serialization since mdast doesn't support definition lists.\n */\nfunction definitionListToHtml(list: {\n children: Array<{ type: string; children?: unknown[] }>;\n}): string {\n let html = '<dl>\\n';\n for (const child of list.children) {\n if (child.type === 'definitionTerm') {\n // Extract plain text from inline children\n const text = (child.children as Array<{ value?: string; children?: unknown[] }>)\n .map((c) => c.value ?? '')\n .join('');\n html += ` <dt>${text}</dt>\\n`;\n } else if (child.type === 'definitionDescription') {\n html += ` <dd>...</dd>\\n`;\n }\n }\n html += '</dl>';\n return html;\n}\n","/**\n * JSON → Markdown Serializer\n *\n * Converts a MarkdownDocument JSON structure back to a markdown string\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkStringify from 'remark-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport type { MarkdownDocument, StringifyOptions } from './types.js';\nimport { toMdast } from './convert.js';\n\n// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n/**\n * Serialize a MarkdownDocument back to a markdown string.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to control formatting and disable extensions.\n *\n * @param doc - The MarkdownDocument to serialize\n * @param options - Serialization options (formatting, extensions)\n * @returns A markdown string\n *\n * @example\n * ```ts\n * const doc: MarkdownDocument = {\n * type: 'document',\n * children: [\n * { type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] },\n * { type: 'paragraph', children: [{ type: 'text', value: 'World' }] },\n * ],\n * };\n * const md = stringifyMarkdown(doc);\n * // '# Hello\\n\\nWorld\\n'\n * ```\n */\nexport function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOptions): string {\n // Convert MarkdownDocument → mdast tree\n const mdastTree = toMdast(doc);\n\n // Use cached default processor when all extensions and default formatting are used.\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n !options.bullet &&\n !options.bulletOrdered &&\n !options.emphasis &&\n !options.strong &&\n !options.rule &&\n !options.fence &&\n options.setext == null);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkStringify, {\n bullet: '-',\n bulletOrdered: '.',\n emphasis: '*',\n strong: '*',\n rule: '-',\n fence: '`',\n setext: false,\n });\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested options.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified();\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n\n processor = processor.use(remarkStringify, {\n bullet: options?.bullet ?? '-',\n bulletOrdered: options?.bulletOrdered ?? '.',\n emphasis: options?.emphasis ?? '*',\n strong: options?.strong ?? '*',\n rule: options?.rule ?? '-',\n fence: options?.fence ?? '`',\n setext: options?.setext ?? false,\n });\n }\n\n // Stringify mdast → markdown string\n const result = processor.stringify(mdastTree) as string;\n\n // remark-stringify escapes `[` in text nodes (to prevent link syntax),\n // which turns `{[template]}` into `{\\[template]}`. Unescape our annotation syntax.\n const cleaned = result.replace(/\\{\\\\\\[([^\\]]+)\\]\\}/g, '{[$1]}');\n\n // Prepend YAML frontmatter if present\n if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {\n const yamlLines = Object.entries(doc.frontmatter).map(\n ([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`,\n );\n return `---\\n${yamlLines.join('\\n')}\\n---\\n\\n${cleaned}`;\n }\n\n return cleaned;\n}\n"],"mappings":"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","names":["unified","remarkGfm","remarkMath","remarkDirective","defaultProcessor","unified","remarkGfm","remarkMath","remarkDirective"]}