@bendyline/squisq 2.1.0 → 2.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 (446) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +24 -0
  3. package/dist/{ContentContainer-BXUlIf18.d.ts → ContentContainer-B2w9sUoL.d.ts} +15 -0
  4. package/dist/{Doc-DynJleo-.d.ts → Doc-BubIpWsp.d.ts} +1 -1
  5. package/dist/{ImageEditDoc-Ca4Tr9uN.d.ts → ImageEditDoc-CGxk5CHc.d.ts} +1 -1
  6. package/dist/{annotationCoercion-Dr_DY9Hx.d.ts → annotationCoercion-PtRQhk5V.d.ts} +1 -1
  7. package/dist/chunk-2L55443L.js +0 -0
  8. package/dist/{chunk-VWCJ2WXC.js → chunk-2ZWIXGAC.js} +0 -1
  9. package/dist/{chunk-VYFRI7R6.js → chunk-32R2RZAO.js} +72 -3
  10. package/dist/chunk-3IGPAPQ7.js +645 -0
  11. package/dist/{chunk-QE7TWGVT.js → chunk-4VOD55SX.js} +0 -1
  12. package/dist/{chunk-3E5F2XMR.js → chunk-67L6WRJN.js} +0 -1
  13. package/dist/{chunk-5DKBFXPY.js → chunk-6ECMNUIP.js} +26 -18
  14. package/dist/{chunk-JI77MGAK.js → chunk-7ARJZYQY.js} +159 -120
  15. package/dist/{chunk-DBS6RIFL.js → chunk-7N4G32LG.js} +0 -1
  16. package/dist/{chunk-JOQYPRDZ.js → chunk-7YEZFHLS.js} +10 -8
  17. package/dist/{chunk-2UQOHYE2.js → chunk-7ZAAICW4.js} +0 -1
  18. package/dist/{chunk-66QO7XFB.js → chunk-ACZOVWX3.js} +117 -7
  19. package/dist/{chunk-5J4HKRJF.js → chunk-BAOV476U.js} +0 -1
  20. package/dist/chunk-BCCXTMN5.js +699 -0
  21. package/dist/{chunk-JHZWYRSW.js → chunk-C33GTPUZ.js} +11 -4
  22. package/dist/{chunk-QUUPYOSP.js → chunk-CB45YGAM.js} +2 -3
  23. package/dist/{chunk-XNLB57UD.js → chunk-DYPMOPIW.js} +18 -2
  24. package/dist/{chunk-VR7UPJ7C.js → chunk-FCG3BIZN.js} +12 -1
  25. package/dist/{chunk-5UYI3RJB.js → chunk-FDYDYL77.js} +54 -40
  26. package/dist/{chunk-OTKVFVL6.js → chunk-I2O6W7DX.js} +116 -44
  27. package/dist/{chunk-Q7LROV2H.js → chunk-NKNMGIIN.js} +0 -1
  28. package/dist/chunk-OPO5K4DZ.js +241 -0
  29. package/dist/{chunk-TUWMK62B.js → chunk-PUS54YU6.js} +45 -2
  30. package/dist/chunk-QKPXHDLR.js +0 -0
  31. package/dist/{chunk-GZY66SDN.js → chunk-R3KTTOMS.js} +0 -1
  32. package/dist/{chunk-ILWDXLUK.js → chunk-RS5AP3J4.js} +49 -10
  33. package/dist/chunk-WV5ARNDA.js +0 -0
  34. package/dist/{chunk-4X3JQXNM.js → chunk-XOFT65EX.js} +1 -2
  35. package/dist/{chunk-FVTQSNR7.js → chunk-XRW6P7IF.js} +13 -5
  36. package/dist/chunk-XWNNA3KE.js +0 -0
  37. package/dist/chunk-YZNGMV3G.js +0 -0
  38. package/dist/{chunk-R2YZTCLO.js → chunk-ZQKZSJAX.js} +1 -2
  39. package/dist/doc/index.d.ts +27 -12
  40. package/dist/doc/index.js +16 -17
  41. package/dist/generate/index.d.ts +2 -2
  42. package/dist/generate/index.js +2 -3
  43. package/dist/icons/index.js +3 -4
  44. package/dist/icons/inlineIconMarker.js +1 -2
  45. package/dist/imageEdit/index.d.ts +48 -17
  46. package/dist/imageEdit/index.js +3 -4
  47. package/dist/index.d.ts +11 -11
  48. package/dist/index.js +60 -33
  49. package/dist/jsonForm/index.d.ts +10 -3
  50. package/dist/jsonForm/index.js +8 -7
  51. package/dist/markdown/index.d.ts +75 -11
  52. package/dist/markdown/index.js +32 -7
  53. package/dist/{materializePageSection-DDzubQXo.d.ts → materializePageSection-wSLA2A1g.d.ts} +2 -2
  54. package/dist/narration/index.d.ts +16 -3
  55. package/dist/narration/index.js +13 -14
  56. package/dist/random/index.d.ts +11 -2
  57. package/dist/random/index.js +2 -3
  58. package/dist/recommend/index.d.ts +1 -1
  59. package/dist/recommend/index.js +3 -4
  60. package/dist/schemas/index.d.ts +5 -5
  61. package/dist/schemas/index.js +5 -6
  62. package/dist/spatial/index.d.ts +2 -1
  63. package/dist/spatial/index.js +1 -2
  64. package/dist/storage/index.d.ts +4 -2
  65. package/dist/storage/index.js +1 -2
  66. package/dist/{themeLibrary-B72m0W1y.d.ts → themeLibrary-DU9mUmVl.d.ts} +1 -1
  67. package/dist/timing/index.js +3 -4
  68. package/dist/transform/index.d.ts +3 -3
  69. package/dist/transform/index.js +6 -7
  70. package/dist/{types-C4kiRn_k.d.ts → types-8QjefM9J.d.ts} +38 -2
  71. package/dist/{types-BwMPl2CB.d.ts → types-CUNc9biN.d.ts} +24 -1
  72. package/dist/versions/index.d.ts +14 -11
  73. package/dist/versions/index.js +2 -3
  74. package/package.json +4 -2
  75. package/dist/chunk-2UQOHYE2.js.map +0 -1
  76. package/dist/chunk-3E5F2XMR.js.map +0 -1
  77. package/dist/chunk-4X3JQXNM.js.map +0 -1
  78. package/dist/chunk-5DKBFXPY.js.map +0 -1
  79. package/dist/chunk-5J4HKRJF.js.map +0 -1
  80. package/dist/chunk-5UYI3RJB.js.map +0 -1
  81. package/dist/chunk-66QO7XFB.js.map +0 -1
  82. package/dist/chunk-AMIR72JP.js +0 -380
  83. package/dist/chunk-AMIR72JP.js.map +0 -1
  84. package/dist/chunk-DBS6RIFL.js.map +0 -1
  85. package/dist/chunk-DNJKBAI7.js +0 -1
  86. package/dist/chunk-DNJKBAI7.js.map +0 -1
  87. package/dist/chunk-ET53IIEP.js +0 -1
  88. package/dist/chunk-ET53IIEP.js.map +0 -1
  89. package/dist/chunk-FVTQSNR7.js.map +0 -1
  90. package/dist/chunk-GZY66SDN.js.map +0 -1
  91. package/dist/chunk-ILWDXLUK.js.map +0 -1
  92. package/dist/chunk-JHZWYRSW.js.map +0 -1
  93. package/dist/chunk-JI77MGAK.js.map +0 -1
  94. package/dist/chunk-JOQYPRDZ.js.map +0 -1
  95. package/dist/chunk-OTKVFVL6.js.map +0 -1
  96. package/dist/chunk-PGGGKYWG.js +0 -1
  97. package/dist/chunk-PGGGKYWG.js.map +0 -1
  98. package/dist/chunk-Q7LROV2H.js.map +0 -1
  99. package/dist/chunk-QE7TWGVT.js.map +0 -1
  100. package/dist/chunk-QUUPYOSP.js.map +0 -1
  101. package/dist/chunk-R2GC6RLD.js +0 -344
  102. package/dist/chunk-R2GC6RLD.js.map +0 -1
  103. package/dist/chunk-R2YZTCLO.js.map +0 -1
  104. package/dist/chunk-S7A5D7ZJ.js +0 -174
  105. package/dist/chunk-S7A5D7ZJ.js.map +0 -1
  106. package/dist/chunk-TUWMK62B.js.map +0 -1
  107. package/dist/chunk-V543AI2F.js +0 -1
  108. package/dist/chunk-V543AI2F.js.map +0 -1
  109. package/dist/chunk-VR7UPJ7C.js.map +0 -1
  110. package/dist/chunk-VWCJ2WXC.js.map +0 -1
  111. package/dist/chunk-VYFRI7R6.js.map +0 -1
  112. package/dist/chunk-XNLB57UD.js.map +0 -1
  113. package/dist/chunk-ZLQKNOZQ.js +0 -1
  114. package/dist/chunk-ZLQKNOZQ.js.map +0 -1
  115. package/dist/doc/index.js.map +0 -1
  116. package/dist/generate/index.js.map +0 -1
  117. package/dist/icons/index.js.map +0 -1
  118. package/dist/icons/inlineIconMarker.js.map +0 -1
  119. package/dist/imageEdit/index.js.map +0 -1
  120. package/dist/index.js.map +0 -1
  121. package/dist/jsonForm/index.js.map +0 -1
  122. package/dist/markdown/index.js.map +0 -1
  123. package/dist/narration/index.js.map +0 -1
  124. package/dist/random/index.js.map +0 -1
  125. package/dist/recommend/index.js.map +0 -1
  126. package/dist/schemas/index.js.map +0 -1
  127. package/dist/spatial/index.js.map +0 -1
  128. package/dist/storage/index.js.map +0 -1
  129. package/dist/timing/index.js.map +0 -1
  130. package/dist/transform/index.js.map +0 -1
  131. package/dist/versions/index.js.map +0 -1
  132. package/src/__tests__/animationUtils.test.ts +0 -117
  133. package/src/__tests__/annotationCoercion.test.ts +0 -243
  134. package/src/__tests__/applyNarrationTiming.test.ts +0 -190
  135. package/src/__tests__/applyRenderStyle.test.ts +0 -146
  136. package/src/__tests__/asciiDiagramBattle.test.ts +0 -246
  137. package/src/__tests__/asciiDiagramDetect.test.ts +0 -162
  138. package/src/__tests__/asciiDiagramMapping.test.ts +0 -38
  139. package/src/__tests__/asciiDiagramParse.test.ts +0 -233
  140. package/src/__tests__/asciiDiagramPipeline.test.ts +0 -273
  141. package/src/__tests__/asciiDiagramRender.test.ts +0 -162
  142. package/src/__tests__/asciiDiagramRepair.test.ts +0 -84
  143. package/src/__tests__/asciiTimelineDetect.test.ts +0 -157
  144. package/src/__tests__/asciiTimelineParse.test.ts +0 -205
  145. package/src/__tests__/asciiTimelinePipeline.test.ts +0 -349
  146. package/src/__tests__/asciiTimelineRender.test.ts +0 -376
  147. package/src/__tests__/atmosphereLayers.test.ts +0 -108
  148. package/src/__tests__/attrTokens.test.ts +0 -167
  149. package/src/__tests__/colorUtils.test.ts +0 -112
  150. package/src/__tests__/contentContainer.test.ts +0 -325
  151. package/src/__tests__/contentExtractor.test.ts +0 -243
  152. package/src/__tests__/coverBlock.test.ts +0 -310
  153. package/src/__tests__/customTemplateSample.test.ts +0 -144
  154. package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -221
  155. package/src/__tests__/customThemesFrontmatter.test.ts +0 -146
  156. package/src/__tests__/dataTable.test.ts +0 -145
  157. package/src/__tests__/diagramBlock.test.ts +0 -122
  158. package/src/__tests__/diagramLayout.test.ts +0 -134
  159. package/src/__tests__/diagramText.test.ts +0 -26
  160. package/src/__tests__/docToMarkdownMedia.test.ts +0 -83
  161. package/src/__tests__/drawing.test.ts +0 -109
  162. package/src/__tests__/drawingBlock.test.ts +0 -124
  163. package/src/__tests__/drawingLayout.test.ts +0 -171
  164. package/src/__tests__/fixtures/asciiDiagramBattle.ts +0 -512
  165. package/src/__tests__/fixtures/asciiDiagrams.ts +0 -287
  166. package/src/__tests__/fixtures/asciiTimelines.ts +0 -35
  167. package/src/__tests__/fixtures/treeviewFixtures.ts +0 -243
  168. package/src/__tests__/geohash.test.ts +0 -110
  169. package/src/__tests__/getLayers.test.ts +0 -243
  170. package/src/__tests__/getLayersFallback.test.ts +0 -103
  171. package/src/__tests__/haversine.test.ts +0 -63
  172. package/src/__tests__/iconResolve.test.ts +0 -122
  173. package/src/__tests__/imageEditExportSvg.test.ts +0 -70
  174. package/src/__tests__/imageEditState.test.ts +0 -74
  175. package/src/__tests__/imageEditVersions.test.ts +0 -243
  176. package/src/__tests__/inferDocumentTitle.test.ts +0 -50
  177. package/src/__tests__/inlineIconMarker.test.ts +0 -36
  178. package/src/__tests__/inputDescriptors.test.ts +0 -162
  179. package/src/__tests__/jsonForm.chooseControl.test.ts +0 -86
  180. package/src/__tests__/jsonForm.evaluateWhen.test.ts +0 -59
  181. package/src/__tests__/jsonForm.inferSchema.test.ts +0 -25
  182. package/src/__tests__/jsonForm.pointer.test.ts +0 -149
  183. package/src/__tests__/jsonForm.tokens.test.ts +0 -84
  184. package/src/__tests__/layoutBlock.test.ts +0 -83
  185. package/src/__tests__/layoutLayout.test.ts +0 -169
  186. package/src/__tests__/localForageAdapter.test.ts +0 -155
  187. package/src/__tests__/majorApiTypes.test.ts +0 -18
  188. package/src/__tests__/markdown.test.ts +0 -1009
  189. package/src/__tests__/markdownIcons.test.ts +0 -104
  190. package/src/__tests__/markdownSanitize.test.ts +0 -81
  191. package/src/__tests__/markdownToDoc.test.ts +0 -957
  192. package/src/__tests__/matchFontFamily.test.ts +0 -114
  193. package/src/__tests__/materializeBlockLayers.test.ts +0 -167
  194. package/src/__tests__/materializePageSections.test.ts +0 -404
  195. package/src/__tests__/materializeTestUtils.ts +0 -17
  196. package/src/__tests__/mediaAnnotations.test.ts +0 -91
  197. package/src/__tests__/mediaSchedule.test.ts +0 -118
  198. package/src/__tests__/narrationAlign.test.ts +0 -150
  199. package/src/__tests__/narrationFeatures.test.ts +0 -71
  200. package/src/__tests__/narrationNuclei.test.ts +0 -45
  201. package/src/__tests__/narrationPacing.test.ts +0 -189
  202. package/src/__tests__/narrationScript.test.ts +0 -149
  203. package/src/__tests__/narrationSessionRepro.test.ts +0 -62
  204. package/src/__tests__/narrationSidecar.test.ts +0 -88
  205. package/src/__tests__/narrationSyllables.test.ts +0 -65
  206. package/src/__tests__/narrationTestSignals.ts +0 -167
  207. package/src/__tests__/narrationVad.test.ts +0 -66
  208. package/src/__tests__/pageStyle.test.ts +0 -258
  209. package/src/__tests__/pandocAttrParse.test.ts +0 -187
  210. package/src/__tests__/recommendTemplates.test.ts +0 -183
  211. package/src/__tests__/resolveDocTheme.test.ts +0 -69
  212. package/src/__tests__/rootExports.test.ts +0 -61
  213. package/src/__tests__/scopedContentContainer.test.ts +0 -109
  214. package/src/__tests__/seededRandom.test.ts +0 -142
  215. package/src/__tests__/shapeGeometry.test.ts +0 -138
  216. package/src/__tests__/shapeVocabulary.test.ts +0 -133
  217. package/src/__tests__/storage.test.ts +0 -60
  218. package/src/__tests__/structuredData.test.ts +0 -249
  219. package/src/__tests__/surface.test.ts +0 -68
  220. package/src/__tests__/templateAnnotationParse.test.ts +0 -173
  221. package/src/__tests__/templateInputs.test.ts +0 -69
  222. package/src/__tests__/templateMetadata.test.ts +0 -32
  223. package/src/__tests__/templates.test.ts +0 -710
  224. package/src/__tests__/themeCompile.test.ts +0 -225
  225. package/src/__tests__/themeValidator.test.ts +0 -131
  226. package/src/__tests__/timing.test.ts +0 -167
  227. package/src/__tests__/transformProvenance.test.ts +0 -160
  228. package/src/__tests__/transformV2.test.ts +0 -163
  229. package/src/__tests__/transitionNormalize.test.ts +0 -61
  230. package/src/__tests__/treeviewBattle.test.ts +0 -76
  231. package/src/__tests__/treeviewDetect.test.ts +0 -168
  232. package/src/__tests__/treeviewParse.test.ts +0 -118
  233. package/src/__tests__/treeviewPipeline.test.ts +0 -183
  234. package/src/__tests__/treeviewRender.test.ts +0 -91
  235. package/src/__tests__/validateDoc.test.ts +0 -260
  236. package/src/__tests__/versions.test.ts +0 -459
  237. package/src/doc/annotationBlocks.ts +0 -117
  238. package/src/doc/applyNarrationTiming.ts +0 -244
  239. package/src/doc/asciiDiagram/chars.ts +0 -210
  240. package/src/doc/asciiDiagram/detect.ts +0 -175
  241. package/src/doc/asciiDiagram/index.ts +0 -38
  242. package/src/doc/asciiDiagram/mapping.ts +0 -225
  243. package/src/doc/asciiDiagram/parse.ts +0 -1095
  244. package/src/doc/asciiDiagram/render.ts +0 -963
  245. package/src/doc/asciiDiagram/repair.ts +0 -400
  246. package/src/doc/asciiDiagram/types.ts +0 -65
  247. package/src/doc/asciiTimeline/detect.ts +0 -138
  248. package/src/doc/asciiTimeline/index.ts +0 -26
  249. package/src/doc/asciiTimeline/mapping.ts +0 -43
  250. package/src/doc/asciiTimeline/parse.ts +0 -870
  251. package/src/doc/asciiTimeline/render.ts +0 -427
  252. package/src/doc/asciiTimeline/types.ts +0 -73
  253. package/src/doc/asciiTimeline/wrappedFlow.ts +0 -427
  254. package/src/doc/audioMapping.ts +0 -442
  255. package/src/doc/customTemplatesFrontmatter.ts +0 -273
  256. package/src/doc/customThemesFrontmatter.ts +0 -95
  257. package/src/doc/docToMarkdown.ts +0 -284
  258. package/src/doc/index.ts +0 -161
  259. package/src/doc/markdownToDoc.ts +0 -1054
  260. package/src/doc/materializeBlockLayers.ts +0 -465
  261. package/src/doc/mediaAnnotations.ts +0 -120
  262. package/src/doc/page/PageSection.ts +0 -174
  263. package/src/doc/page/materializePageSection.ts +0 -616
  264. package/src/doc/page/resolvePageBlock.ts +0 -108
  265. package/src/doc/page/sectionExtractors.ts +0 -466
  266. package/src/doc/pageCss.ts +0 -628
  267. package/src/doc/resolveDocTheme.ts +0 -56
  268. package/src/doc/standaloneAnnotation.ts +0 -58
  269. package/src/doc/structuredData.ts +0 -301
  270. package/src/doc/templateInputs.ts +0 -424
  271. package/src/doc/templates/__tests__/customTemplate.test.ts +0 -170
  272. package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +0 -124
  273. package/src/doc/templates/__tests__/listBlock.test.ts +0 -64
  274. package/src/doc/templates/accentImage.ts +0 -362
  275. package/src/doc/templates/captionUtils.ts +0 -56
  276. package/src/doc/templates/comparisonBar.ts +0 -173
  277. package/src/doc/templates/coverBlock.ts +0 -255
  278. package/src/doc/templates/customTemplate.ts +0 -51
  279. package/src/doc/templates/dataTable.ts +0 -102
  280. package/src/doc/templates/dateEvent.ts +0 -147
  281. package/src/doc/templates/definitionCard.ts +0 -140
  282. package/src/doc/templates/diagramBlock.ts +0 -403
  283. package/src/doc/templates/diagramLayout.ts +0 -159
  284. package/src/doc/templates/drawingBlock.ts +0 -325
  285. package/src/doc/templates/drawingLayout.ts +0 -539
  286. package/src/doc/templates/factCard.ts +0 -146
  287. package/src/doc/templates/fallbackBlock.ts +0 -123
  288. package/src/doc/templates/featureBlock.ts +0 -209
  289. package/src/doc/templates/fullBleedQuote.ts +0 -83
  290. package/src/doc/templates/imageWithCaption.ts +0 -186
  291. package/src/doc/templates/index.ts +0 -642
  292. package/src/doc/templates/inputDescriptors.ts +0 -451
  293. package/src/doc/templates/layoutBlock.ts +0 -43
  294. package/src/doc/templates/layoutLayout.ts +0 -243
  295. package/src/doc/templates/listBlock.ts +0 -207
  296. package/src/doc/templates/mapBlock.ts +0 -138
  297. package/src/doc/templates/metadata.ts +0 -130
  298. package/src/doc/templates/persistentLayers.ts +0 -526
  299. package/src/doc/templates/photoGrid.ts +0 -190
  300. package/src/doc/templates/pullQuote.ts +0 -141
  301. package/src/doc/templates/quoteBlock.ts +0 -141
  302. package/src/doc/templates/registry.ts +0 -88
  303. package/src/doc/templates/sectionHeader.ts +0 -120
  304. package/src/doc/templates/statHighlight.ts +0 -146
  305. package/src/doc/templates/templateNames.ts +0 -22
  306. package/src/doc/templates/timelineBlock.ts +0 -613
  307. package/src/doc/templates/titleBlock.ts +0 -150
  308. package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +0 -487
  309. package/src/doc/templates/tokens/resolveTokens.ts +0 -453
  310. package/src/doc/templates/treeBlock.ts +0 -131
  311. package/src/doc/templates/twoColumn.ts +0 -263
  312. package/src/doc/templates/videoPullQuote.ts +0 -142
  313. package/src/doc/templates/videoWithCaption.ts +0 -135
  314. package/src/doc/treeview/chars.ts +0 -72
  315. package/src/doc/treeview/detect.ts +0 -145
  316. package/src/doc/treeview/index.ts +0 -30
  317. package/src/doc/treeview/mapping.ts +0 -95
  318. package/src/doc/treeview/parse.ts +0 -201
  319. package/src/doc/treeview/render.ts +0 -41
  320. package/src/doc/treeview/types.ts +0 -43
  321. package/src/doc/utils/animationUtils.ts +0 -399
  322. package/src/doc/utils/applyRenderStyle.ts +0 -106
  323. package/src/doc/utils/diagramText.ts +0 -78
  324. package/src/doc/utils/imageTreatment.ts +0 -70
  325. package/src/doc/utils/nearest.ts +0 -59
  326. package/src/doc/utils/shapeGeometry.ts +0 -614
  327. package/src/doc/utils/themeUtils.ts +0 -243
  328. package/src/doc/validate.ts +0 -575
  329. package/src/generate/contentExtractor.ts +0 -921
  330. package/src/generate/index.ts +0 -18
  331. package/src/generate/templateMapper.ts +0 -177
  332. package/src/icons/iconData.ts +0 -31
  333. package/src/icons/index.ts +0 -26
  334. package/src/icons/inlineIconMarker.ts +0 -73
  335. package/src/icons/resolve.ts +0 -139
  336. package/src/imageEdit/ImageEditVersionManager.ts +0 -73
  337. package/src/imageEdit/export.ts +0 -410
  338. package/src/imageEdit/index.ts +0 -72
  339. package/src/imageEdit/persistence.ts +0 -113
  340. package/src/imageEdit/state.ts +0 -112
  341. package/src/imageEdit/versionPaths.ts +0 -44
  342. package/src/imageEdit/versions.ts +0 -152
  343. package/src/index.ts +0 -15
  344. package/src/internal/immutable.ts +0 -58
  345. package/src/jsonForm/chooseControl.ts +0 -88
  346. package/src/jsonForm/evaluateWhen.ts +0 -59
  347. package/src/jsonForm/index.ts +0 -32
  348. package/src/jsonForm/inferSchema.ts +0 -25
  349. package/src/jsonForm/pointer.ts +0 -154
  350. package/src/jsonForm/tokens.ts +0 -78
  351. package/src/jsonForm/types.ts +0 -167
  352. package/src/markdown/annotationCoercion.ts +0 -295
  353. package/src/markdown/attrTokens.ts +0 -243
  354. package/src/markdown/convert.ts +0 -1353
  355. package/src/markdown/htmlParse.ts +0 -212
  356. package/src/markdown/index.ts +0 -152
  357. package/src/markdown/parse.ts +0 -103
  358. package/src/markdown/sanitize.ts +0 -318
  359. package/src/markdown/stringify.ts +0 -207
  360. package/src/markdown/types.ts +0 -710
  361. package/src/markdown/utils.ts +0 -459
  362. package/src/narration/align.ts +0 -514
  363. package/src/narration/features.ts +0 -231
  364. package/src/narration/index.ts +0 -86
  365. package/src/narration/nuclei.ts +0 -118
  366. package/src/narration/pacing.ts +0 -211
  367. package/src/narration/script.ts +0 -239
  368. package/src/narration/session.ts +0 -100
  369. package/src/narration/sidecar.ts +0 -187
  370. package/src/narration/syllables.ts +0 -85
  371. package/src/narration/trace.ts +0 -50
  372. package/src/narration/types.ts +0 -299
  373. package/src/narration/vad.ts +0 -131
  374. package/src/ngeohash.d.ts +0 -11
  375. package/src/random/SeededRandom.ts +0 -127
  376. package/src/random/index.ts +0 -1
  377. package/src/recommend/index.ts +0 -16
  378. package/src/recommend/numberHighlight.ts +0 -25
  379. package/src/recommend/templates.ts +0 -393
  380. package/src/schemas/BlockTemplates.ts +0 -1046
  381. package/src/schemas/CustomTemplates.ts +0 -126
  382. package/src/schemas/Doc.ts +0 -1073
  383. package/src/schemas/ImageEditDoc.ts +0 -102
  384. package/src/schemas/LayoutStrategy.ts +0 -178
  385. package/src/schemas/Media.ts +0 -179
  386. package/src/schemas/MediaProvider.ts +0 -70
  387. package/src/schemas/PageStyle.ts +0 -213
  388. package/src/schemas/Theme.ts +0 -413
  389. package/src/schemas/Transitions.ts +0 -224
  390. package/src/schemas/Types.ts +0 -15
  391. package/src/schemas/Viewport.ts +0 -91
  392. package/src/schemas/colorUtils.ts +0 -219
  393. package/src/schemas/fontStacks.ts +0 -339
  394. package/src/schemas/index.ts +0 -31
  395. package/src/schemas/pageStyleDefaults.ts +0 -204
  396. package/src/schemas/themeCompile.ts +0 -244
  397. package/src/schemas/themeConstants.ts +0 -2
  398. package/src/schemas/themeLibrary.ts +0 -60
  399. package/src/schemas/themeValidator.ts +0 -609
  400. package/src/schemas/themes/bold.json +0 -77
  401. package/src/schemas/themes/cinematic.json +0 -143
  402. package/src/schemas/themes/documentary.json +0 -125
  403. package/src/schemas/themes/gezellig.json +0 -123
  404. package/src/schemas/themes/index.ts +0 -53
  405. package/src/schemas/themes/magazine.json +0 -126
  406. package/src/schemas/themes/minimalist.json +0 -74
  407. package/src/schemas/themes/morning-light.json +0 -117
  408. package/src/schemas/themes/standard-dark.json +0 -109
  409. package/src/schemas/themes/standard.json +0 -98
  410. package/src/schemas/themes/tech-dark.json +0 -143
  411. package/src/schemas/themes/warm-earth.json +0 -74
  412. package/src/spatial/Geohash.ts +0 -196
  413. package/src/spatial/Haversine.ts +0 -59
  414. package/src/spatial/index.ts +0 -2
  415. package/src/storage/ContentContainer.ts +0 -220
  416. package/src/storage/LocalForageAdapter.ts +0 -103
  417. package/src/storage/LocalStorageAdapter.ts +0 -57
  418. package/src/storage/MediaProviderFromContainer.ts +0 -92
  419. package/src/storage/MemoryStorageAdapter.ts +0 -37
  420. package/src/storage/ScopedContentContainer.ts +0 -143
  421. package/src/storage/Storage.ts +0 -28
  422. package/src/storage/index.ts +0 -9
  423. package/src/timing/index.ts +0 -17
  424. package/src/timing/narrationTiming.ts +0 -220
  425. package/src/timing/readingTime.ts +0 -94
  426. package/src/transform/applyTransform.ts +0 -122
  427. package/src/transform/blockAnalyzer.ts +0 -181
  428. package/src/transform/index.ts +0 -23
  429. package/src/transform/registry.ts +0 -217
  430. package/src/transform/styles/dataDriven.ts +0 -18
  431. package/src/transform/styles/documentary.ts +0 -18
  432. package/src/transform/styles/index.ts +0 -5
  433. package/src/transform/styles/magazine.ts +0 -19
  434. package/src/transform/styles/minimal.ts +0 -18
  435. package/src/transform/styles/narrative.ts +0 -19
  436. package/src/transform/templateSelector.ts +0 -389
  437. package/src/transform/timingAllocator.ts +0 -257
  438. package/src/transform/types.ts +0 -158
  439. package/src/versions/DocumentVersionManager.ts +0 -70
  440. package/src/versions/gitignore.ts +0 -34
  441. package/src/versions/index.ts +0 -56
  442. package/src/versions/operations.ts +0 -144
  443. package/src/versions/paths.ts +0 -51
  444. package/src/versions/snapshotStore.ts +0 -202
  445. package/src/versions/timestamp.ts +0 -48
  446. package/src/versions/types.ts +0 -93
@@ -1 +0,0 @@
1
- //# sourceMappingURL=chunk-PGGGKYWG.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
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\n// A single side of a comparison (\"X\" in \"from X to Y\"). Deliberately excludes\n// sentence/clause terminators (. ! ? ;) and newlines so a capture group can\n// never swallow text from an adjacent sentence. Without this, greedy `.{2,30}`\n// matches across a period — e.g. \"…contradicted from witness to witness.\n// Robertson said…\" yields right=\"witness. Robertson\", producing a nonsensical\n// \"witness → witness. Robertson\" slide. Commas and colons are allowed (ranges\n// like \"9:00 to 17:00\", ratios); downstream label truncation trims commas.\nconst COMPARISON_SIDE = '[^.!?;\\\\r\\\\n]{2,30}';\n\nconst COMPARISON_PATTERNS = [\n new RegExp(`from\\\\s+(${COMPARISON_SIDE})\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+(?:vs\\\\.?|versus)\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+compared\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n];\n\n// Words whose trailing period is an abbreviation, not a sentence end. Used by\n// isSentenceBoundary() so names like \"George W. Tibbetts\" or \"Quong Chong & Co.\"\n// don't get split into fragments. Matched only when the entire preceding word\n// equals one of these (case-insensitive), so \"canal.\" / \"taco.\" are unaffected.\nconst SENTENCE_ABBREVIATIONS = new Set([\n 'mr',\n 'mrs',\n 'ms',\n 'dr',\n 'prof',\n 'st',\n 'mt',\n 'jr',\n 'sr',\n 'gen',\n 'col',\n 'capt',\n 'lt',\n 'sgt',\n 'rev',\n 'hon',\n 'sen',\n 'rep',\n 'gov',\n 'pres',\n 'vs',\n 'etc',\n 'inc',\n 'co',\n 'ltd',\n 'corp',\n 'al',\n 'fig',\n 'eg',\n 'ie',\n 'approx',\n 'dept',\n 'univ',\n 'assn',\n 'bros',\n 'ave',\n 'blvd',\n 'rd',\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 // Trim a trailing clause introduced by a comma + space (\"retired\n // airliners, arranged in rows\" → \"retired airliners\"). A greedy side\n // match often swallows the words after the compared thing; the comma is a\n // clause boundary, not part of the value. Numeric grouping commas\n // (\"4,000\") survive because those are comma+digit, not comma+space.\n const trimClause = (s: string) => s.replace(/,\\s+.*$/, '').trim();\n const left = trimClause(match[1].trim());\n const right = trimClause(match[2].trim());\n if (left.length < 2 || left.length > 30) continue;\n if (right.length < 2 || right.length > 30) continue;\n // Reject \"from X to X\"-style idioms — \"from witness to witness\",\n // \"from day to day\", \"from generation to generation\". These mean \"it\n // varied\", not \"A versus B\", and render as a pointless \"X → X\" card.\n // The signature is a word repeated across the connector: the last word\n // of the left side equals the first word of the right side. (Greedy\n // matching may append trailing context to the right side, so a whole-\n // string equality check is not enough.)\n const wordsOf = (s: string) =>\n s\n .toLowerCase()\n .split(/\\s+/)\n .map((w) => w.replace(/[^a-z0-9]+/g, ''))\n .filter(Boolean);\n const leftWords = wordsOf(left);\n const rightWords = wordsOf(right);\n if (\n leftWords.length > 0 &&\n rightWords.length > 0 &&\n leftWords[leftWords.length - 1] === rightWords[0]\n ) {\n continue;\n }\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 * Whether the `. ! ?` at `index` is a real sentence terminator rather than a\n * period inside an abbreviation (\"George W. Tibbetts\", \"M. DeWitt\", \"Mr.\",\n * \"U.S.\"), a single-letter initial, a decimal (\"3.5\"), or an ellipsis. Naive\n * splitting on every period produces broken fragments — e.g. the standalone\n * slide \"They were met at George W\" from \"…at George W. Tibbetts' store…\".\n * Returns false for any non-terminator character so callers can scan freely.\n */\nfunction isSentenceBoundary(text: string, index: number): boolean {\n const ch = text[index];\n if (ch === '!' || ch === '?') return true;\n if (ch !== '.') return false;\n\n // Ellipsis: defer the boundary to the final dot in a run of dots.\n if (text[index + 1] === '.') return false;\n // Decimal number: digit \".\" digit → \"3.5\".\n if (/[0-9]/.test(text[index - 1] ?? '') && /[0-9]/.test(text[index + 1] ?? '')) return false;\n\n // The run of letters immediately preceding the period.\n let w = index - 1;\n while (w >= 0 && /[A-Za-z]/.test(text[w])) w--;\n const word = text.slice(w + 1, index);\n if (word.length === 1) return false; // single-letter initial: \"George W.\"\n if (word && SENTENCE_ABBREVIATIONS.has(word.toLowerCase())) return false;\n\n // A genuine sentence starts with an uppercase letter, digit, or opening\n // quote/bracket. A lowercase continuation means the period was mid-sentence\n // (an abbreviation we didn't enumerate).\n let j = index + 1;\n while (j < text.length && /\\s/.test(text[j])) j++;\n if (j >= text.length) return true; // end of text\n return !/[a-z]/.test(text[j]);\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 * Boundaries are detected by isSentenceBoundary() so abbreviations, initials,\n * decimals, and ellipses do not split a sentence. Leading whitespace and\n * trailing whitespace/terminators are excluded from each span.\n */\nfunction forEachSentenceSpan(\n text: string,\n callback: (trimmed: string, start: number, end: number) => void,\n): void {\n const emit = (rawStart: number, rawEnd: number) => {\n let start = rawStart;\n while (start < rawEnd && /\\s/.test(text[start])) start++;\n let end = rawEnd;\n while (end > start && /[\\s.!?]/.test(text[end - 1])) end--;\n if (end <= start) return;\n const trimmed = text.slice(start, end);\n if (trimmed) callback(trimmed, start, end);\n };\n\n let spanStart = 0;\n let i = 0;\n while (i < text.length) {\n if (isSentenceBoundary(text, i)) {\n // Consume any consecutive terminators (\"?!\", \"…\") as one boundary.\n let end = i + 1;\n while (end < text.length && /[.!?]/.test(text[end])) end++;\n emit(spanStart, end);\n spanStart = end;\n i = end;\n } else {\n i++;\n }\n }\n if (spanStart < text.length) emit(spanStart, text.length);\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 && !isSentenceBoundary(text, start - 1)) start--;\n while (start < matchStart && /\\s/.test(text[start])) start++;\n\n let end = matchStart + matchLength;\n while (end < text.length && !isSentenceBoundary(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 /** Doc-timeline SECONDS when the extracted content is spoken. */\n sourceStartTime?: number;\n sourceDuration?: number;\n /** Provenance: the source block the element was extracted from. */\n sourceBlockId?: string;\n /** Provenance: char offset of the extraction in the source plain text. */\n sourceCharOffset?: 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 sourceBlockId: options.sourceBlockId,\n sourceCharOffset: options.sourceCharOffset,\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: 'quote',\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: 'list',\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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas/Transitions.ts"],"sourcesContent":["/**\n * Shared transition vocabulary for block-to-block slide changes.\n *\n * The canonical names are based on PowerPoint / PresentationML transition\n * names where possible, with a few legacy renderer aliases kept so older\n * Squisq documents continue to work.\n */\n\nexport const DEFAULT_TRANSITION_DURATION_SECONDS = 0.7;\n\nexport const TRANSITION_TYPES = [\n // Existing / common transitions\n 'cut',\n 'fade',\n 'dissolve',\n 'push',\n 'wipe',\n 'split',\n 'reveal',\n 'randomBars',\n 'shape',\n 'uncover',\n 'cover',\n 'zoom',\n 'pan',\n\n // PowerPoint subtle / exciting transitions\n 'flash',\n 'fallOver',\n 'drape',\n 'curtains',\n 'wind',\n 'prestige',\n 'fracture',\n 'crush',\n 'peelOff',\n 'pageCurl',\n 'airplane',\n 'origami',\n 'checkerboard',\n 'blinds',\n 'clock',\n 'ripple',\n 'honeycomb',\n 'glitter',\n 'vortex',\n 'shred',\n 'switch',\n 'flip',\n 'gallery',\n 'cube',\n 'doors',\n 'box',\n 'ferrisWheel',\n 'conveyor',\n 'rotate',\n 'window',\n 'orbit',\n 'flyThrough',\n 'morph',\n\n // PresentationML/Open XML precise names and legacy aliases\n 'checker',\n 'circle',\n 'comb',\n 'diamond',\n 'newsflash',\n 'plus',\n 'pull',\n 'random',\n 'randomBar',\n 'strips',\n 'wedge',\n 'wheel',\n 'prism',\n 'warp',\n 'ferris',\n 'flythrough',\n 'wheelReverse',\n 'pageCurlDouble',\n 'pageCurlSingle',\n 'slideLeft',\n 'slideRight',\n 'slideUp',\n 'slideDown',\n] as const;\n\nexport type TransitionType = (typeof TRANSITION_TYPES)[number];\n\nexport const TRANSITION_DIRECTIONS = [\n 'left',\n 'right',\n 'up',\n 'down',\n 'in',\n 'out',\n 'horizontal',\n 'vertical',\n] as const;\n\nexport type TransitionDirection = (typeof TRANSITION_DIRECTIONS)[number];\n\n/**\n * Transition between blocks.\n *\n * A block's transition is the lead-in from the previous block. The same\n * transition drives the entering block and the previous block's exit.\n */\nexport interface Transition {\n /** Transition type */\n type: TransitionType;\n /** Duration in seconds. Defaults to DEFAULT_TRANSITION_DURATION_SECONDS. */\n duration?: number;\n /** Optional direction/variant for directional transitions. */\n direction?: TransitionDirection;\n}\n\nconst TRANSITION_TYPE_SET = new Set<string>(TRANSITION_TYPES);\n\nconst TRANSITION_TYPE_ALIASES: Record<string, TransitionType> = {\n none: 'cut',\n randombars: 'randomBars',\n randombar: 'randomBar',\n // Self-referential spellings (`checkerboard`, `shape`, `uncover`) are\n // intentionally absent: an exact valid type returns early from\n // `normalizeTransitionType`, and any case variant resolves through\n // `NORMALIZED_TRANSITION_TYPES`. Only the *plural* aliases need an entry.\n checkerboards: 'checkerboard',\n shapes: 'shape',\n ferris: 'ferrisWheel',\n ferriswheel: 'ferrisWheel',\n flythrough: 'flyThrough',\n flythru: 'flyThrough',\n pagecurl: 'pageCurl',\n pagecurldouble: 'pageCurlDouble',\n pagecurlsingle: 'pageCurlSingle',\n wheelreverse: 'wheelReverse',\n slideleft: 'slideLeft',\n slideright: 'slideRight',\n slideup: 'slideUp',\n slidedown: 'slideDown',\n};\n\nconst NORMALIZED_TRANSITION_TYPES = new Map<string, TransitionType>(\n TRANSITION_TYPES.map((type) => [normalizeToken(type), type]),\n);\n\nexport function isTransitionType(value: string): value is TransitionType {\n return TRANSITION_TYPE_SET.has(value);\n}\n\nexport function normalizeTransitionType(value: string): TransitionType | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n if (isTransitionType(trimmed)) return trimmed;\n const normalized = normalizeToken(trimmed);\n return TRANSITION_TYPE_ALIASES[normalized] ?? NORMALIZED_TRANSITION_TYPES.get(normalized) ?? null;\n}\n\nexport function normalizeTransitionDirection(value: string): TransitionDirection | null {\n const normalized = normalizeToken(value);\n switch (normalized) {\n case 'left':\n case 'l':\n return 'left';\n case 'right':\n case 'r':\n return 'right';\n case 'up':\n case 'u':\n return 'up';\n case 'down':\n case 'd':\n return 'down';\n case 'in':\n return 'in';\n case 'out':\n return 'out';\n case 'horizontal':\n case 'horz':\n case 'h':\n return 'horizontal';\n case 'vertical':\n case 'vert':\n case 'v':\n return 'vertical';\n default:\n return null;\n }\n}\n\nexport function resolveTransitionDuration(\n transition: Transition | undefined,\n fallback: number = DEFAULT_TRANSITION_DURATION_SECONDS,\n): number {\n const duration = transition?.duration;\n return duration != null && Number.isFinite(duration) && duration > 0 ? duration : fallback;\n}\n\n/**\n * Resolve the effective transition for a block: the authored value wins\n * (markdown frontmatter and transform styles both write `block.transition`\n * upstream), then the theme's `renderStyle.defaultTransition`, then\n * `undefined` (the player's built-in default).\n *\n * Block 0 never gets a theme default — an entrance transition on the first\n * block would dim the opening frames of every exported video.\n *\n * The theme parameter is structural (not `Theme`) to avoid a schema import\n * cycle; pass any resolved theme.\n */\nexport function resolveBlockTransition(\n block: { transition?: Transition },\n theme: { renderStyle: { defaultTransition?: Transition } },\n blockIndex: number,\n): Transition | undefined {\n if (block.transition) return block.transition;\n if (blockIndex === 0) return undefined;\n return theme.renderStyle.defaultTransition;\n}\n\nfunction normalizeToken(value: string): string {\n return value.toLowerCase().replace(/[\\s_-]+/g, '');\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas/Doc.ts","../src/schemas/themeCompile.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\nimport type { Transition } from './Transitions.js';\nexport type { Transition, TransitionType, TransitionDirection } from './Transitions.js';\n\n// ============================================\n// Core Types\n// ============================================\n\n/**\n * A directed connection from one block to another.\n *\n * Populated from a heading's Pandoc-style `{connectsTo=…}` attribute\n * (e.g. `## Step 1 {#step1 connectsTo=foo:flow,bar}`). Each item is\n * `target` or `target:type`. Used by diagram-style layouts that draw\n * edges between blocks.\n */\nexport interface BlockConnection {\n /** Target block id (matches another block's `id`). */\n target: string;\n /** Optional connection type/label (e.g., \"flow\", \"requires\"). */\n type?: string;\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). When omitted, a theme-driven background is used. */\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 structural problem detected while converting or validating a document.\n *\n * Diagnostics ride on `Doc.diagnostics` so any consumer — the editor, the\n * CLI `validate` command, or an agent re-parsing its own output — gets the\n * same feedback without watching the console. Conversion never throws for\n * content problems; it degrades gracefully and records a diagnostic.\n */\nexport interface DocDiagnostic {\n /** `error` = the author's intent could not be honored (e.g. unparseable\n * data fence); `warning` = something looks wrong but rendering proceeds\n * with a fallback (e.g. unknown template name); `info` = nothing is\n * broken, but the author may want to know (e.g. a redundant annotation). */\n severity: 'error' | 'warning' | 'info';\n /** Stable machine-readable code (e.g. `unknown-template`, `duplicate-id`,\n * `unresolved-connection`, `data-fence-parse`, `missing-asset`). */\n code: string;\n /** Human-readable description, including a suggestion when one exists. */\n message: string;\n /** Id of the block the problem belongs to, when attributable. */\n blockId?: string;\n /** 1-based line number in the markdown source, when known. */\n line?: number;\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 * User-defined block templates inlined into this doc.\n *\n * Populated from the markdown frontmatter key\n * `squisq-custom-templates`. The template-expansion pipeline\n * (`expandDocBlocks` in `doc/templates/index.ts`) merges these into\n * the registry before walking blocks, so a heading annotated\n * `{[myhero]}` resolves against a doc-defined template named `myhero`.\n *\n * Library templates (stored in localStorage on the editor side) are\n * NOT auto-loaded here — applying a library template to a block\n * copies its definition into this list so the doc remains\n * self-sufficient for SSR and export.\n */\n customTemplates?: import('./CustomTemplates.js').CustomTemplateDefinition[];\n\n /**\n * User-defined themes inlined into this doc.\n *\n * The theme analog of {@link Doc.customTemplates}. Populated from the\n * markdown frontmatter key `squisq-custom-themes`. Exactly one is active\n * at a time — selected by id via `squisq-theme` / {@link Doc.themeId},\n * the doc-level counterpart of a block's `{[name]}` template annotation —\n * but the payload is a list so a doc can carry a small catalog and stay\n * self-sufficient for SSR / export. `resolveThemeForDoc(doc, id)` resolves\n * against this list first (pure, doc-scoped) before built-ins, mirroring\n * how `buildRegistry` resolves custom templates.\n */\n customThemes?: import('./Theme.js').Theme[];\n\n /**\n * Structural problems found while building this doc (unknown templates,\n * unparseable data fences, duplicate ids, unresolved connections, …).\n * Populated by `markdownToDoc()`; extended by `validateMarkdownDoc()`.\n * Absent when the document is clean.\n */\n diagnostics?: DocDiagnostic[];\n\n /**\n * Document-spanning timed media (e.g. a full-length narration MP3/MP4),\n * authored as a media annotation in the preamble (before the first\n * heading) with `anchor=document`. Each clip is timed from the document\n * start and may play across every block. See `resolveMediaSchedule`.\n */\n documentMedia?: import('./Media.js').MediaClip[];\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 *\n * Four adjacent fields carry template information — do not confuse them:\n * - `template` — the *resolved* template id this block renders with.\n * - `templateOverrides` — raw `{[…]}` inline params (always strings).\n * - `templateData` — structured inputs from a data fence / GFM table\n * (parsed types, not strings).\n * - `sourceAnnotation.template` — the raw *requested* template name for a\n * standalone-annotation block (before alias resolution).\n * Merge order at render time: template defaults → `templateData` →\n * `templateOverrides`.\n */\nexport interface Block {\n /** Unique identifier for this block.\n * Defaults to a slug derived from the heading text. May be overridden by\n * a Pandoc-style `{#custom-id}` attribute on the heading. */\n id: string;\n\n /** When this block appears (seconds from start).\n * Overridden by a heading attribute `startTime=…` when present. */\n startTime: number;\n\n /** How long this block is visible (seconds).\n * Overridden by a heading attribute `duration=…` when present. */\n duration: number;\n\n /** Which audio segment this block belongs to (0-indexed) */\n audioSegment: number;\n\n /**\n * Transform provenance: the source block id this block was derived\n * from (see `BaseTemplateBlock.sourceBlockId`). Present on blocks a\n * transform produced; lets a persisted transformed Doc keep its typed\n * link back to the source content.\n */\n sourceBlockId?: string;\n /** All source block ids when this block summarizes several. */\n sourceBlockIds?: string[];\n /** Char offset of the extraction inside the source block's plain text. */\n sourceCharOffset?: 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 `materializeBlockLayers()` 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 /** Resolved template id this block renders with (see the quartet note above). */\n template?: string;\n\n /**\n * True when `template` was chosen by content-aware auto-picking\n * (`markdownToDoc`'s `autoTemplates`) rather than authored. Ephemeral:\n * `docToMarkdown` does not materialize auto-picked templates as heading\n * annotations, keeping the markdown round-trip lossless.\n */\n autoTemplate?: boolean;\n\n /**\n * True when this block was produced from a *standalone* `{[templateName …]}`\n * annotation paragraph in another block's body (rather than from a heading).\n * Such blocks have no `sourceHeading`; `docToMarkdown` re-emits their\n * annotation as a synthesized paragraph before their `contents` so the\n * markdown round-trips. See `annotationBlocks.ts`.\n *\n * Presence-marker: only ever `true` (never `false`). Test with\n * `block.standaloneAnnotation` presence, not as a boolean toggle.\n */\n standaloneAnnotation?: true;\n\n /**\n * The annotation a standalone block was built from — the raw (un-resolved)\n * template name and its params, in author order. Round-tripped by\n * `docToMarkdown` (via `serializeAnnotation`) and read by validation as the\n * raw requested template name (symmetric with\n * `sourceHeading.templateAnnotation`). Present only when\n * `standaloneAnnotation` is true.\n */\n sourceAnnotation?: { template?: string; params?: Record<string, 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 a heading's `{[templateName key=value]}`\n * annotation. Only carries template-specific params (e.g. `colorScheme=blue`).\n * Block-level metadata lives in `x` / `y` / `connectsTo` / `metadata` and is\n * sourced from the Pandoc-style `{#id .class key=value}` attribute block instead.\n */\n templateOverrides?: Record<string, string>;\n\n /**\n * Structured template inputs sourced from the block's body content:\n * a ```json data / ```yaml data fence under the heading, or — for the\n * `dataTable` template — the first GFM table in the section. Unlike\n * `templateOverrides` (always strings), values here keep their parsed\n * types (arrays, numbers, nested objects). Merge order at render time:\n * template defaults → `templateData` → `templateOverrides`.\n */\n templateData?: Record<string, unknown>;\n\n // ── Block-level metadata from Pandoc-style `{#id .class key=value}` ──\n\n /**\n * X coordinate for diagram-style positioning (in author-defined units).\n * Sourced from a heading attribute `x=…`.\n */\n x?: number;\n\n /**\n * Y coordinate for diagram-style positioning (in author-defined units).\n * Sourced from a heading attribute `y=…`.\n */\n y?: number;\n\n /**\n * Outgoing connections to other blocks, by id. Sourced from a heading\n * attribute `connectsTo=target1,target2:type,target3`.\n */\n connectsTo?: BlockConnection[];\n\n /**\n * CSS-style class tokens from a heading's Pandoc attribute (e.g. `{.important .v2}`).\n * Parsed and stored; downstream consumers may use them for styling or filtering.\n */\n classes?: string[];\n\n /**\n * Free-form metadata for heading attribute keys outside the typed registry\n * (anything that isn't `x` / `y` / `connectsTo` / `startTime` / `duration`).\n * Values are raw strings exactly as authored.\n */\n metadata?: Record<string, string>;\n\n /**\n * Timed media clips attached to this block, authored as body-level\n * `{[audio …]}` / `{[video …]}` annotations. Each clip is timed relative\n * to this block's `startTime` (via `startAt`) and optionally spills past\n * the block's end. See `MediaClip` / `resolveMediaSchedule`.\n */\n media?: import('./Media.js').MediaClip[];\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 =\n | ImageLayer\n | TextLayer\n | ShapeLayer\n | PathLayer\n | MapLayer\n | VideoLayer\n | TableLayer\n | TreeLayer\n | MermaidLayer;\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 * Photographic treatment applied to an image layer — a theme-level \"grade\"\n * so the same photo looks native in every theme. Rendered as CSS filter\n * functions, which behave identically in the browser player and headless\n * frame capture (video/still export).\n */\nexport interface ImageTreatment {\n /**\n * mono: desaturate toward archival black & white.\n * duotone: single-hue tint (hue taken from `color`).\n * warm / cool: gentle temperature grade.\n */\n type: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';\n /** Blend strength 0..1. Default 0.6. */\n strength?: number;\n /** Duotone tint color (themes default this to their primary). */\n color?: string;\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 /** Theme-derived photographic grade (see ImageTreatment). */\n treatment?: ImageTreatment;\n /** Gaussian blur radius in px (background/atmosphere imagery). */\n blur?: number;\n };\n}\n\n/**\n * Text layer - displays text with styling.\n */\nexport interface TextLayer extends BaseLayer {\n type: 'text';\n content: {\n /**\n * Plain text (supports \\n for line breaks). Source of truth for plain\n * consumers — PDF/markdown export, search, accessibility — and the SVG\n * `<text>` fallback. When `html` is set, this is its plain-text\n * projection and must be kept in sync.\n */\n text: string;\n /**\n * Optional sanitized **inline** HTML for rich formatting (bold/italic/\n * links, and — for layout textboxes — headings/lists). When present the\n * renderer draws it via `<foreignObject>` instead of SVG `<text>`. Treated\n * as untrusted and re-sanitized at render time. See `RichTextLayer` in\n * `@bendyline/squisq-react`.\n */\n html?: string;\n /** Text styling */\n style: TextStyle;\n };\n}\n\n/**\n * A two-stop linear gradient fill, shared by shape/path fills and text\n * backgrounds. When present it overrides the solid `fill` color.\n *\n * `angle` is in degrees: 0 = top→bottom, 90 = left→right, increasing\n * clockwise. Defaults to 0. Rendered as an SVG `<linearGradient>` in\n * objectBoundingBox units so it scales with the layer's box.\n */\nexport interface LinearGradient {\n from: string;\n to: string;\n angle?: number;\n}\n\n/** Border line style, mapped to an SVG stroke-dasharray by the renderer. */\nexport type BorderStyle = 'solid' | 'dashed' | 'dotted';\n\n/**\n * Shape layer - simple geometric shapes for visual accents.\n */\n/**\n * Repeating SVG pattern fill for a shape (dots, grid, diagonal lines).\n * Rendered as a native `<pattern>` def — fully vector and export-safe.\n */\nexport interface ShapePattern {\n kind: 'dots' | 'grid' | 'diagonal';\n /** Pattern ink color. */\n color: string;\n /** Tile size in px (default 24). */\n size?: number;\n /** Pattern opacity 0–1 (default 1; tint via a translucent color or this). */\n opacity?: number;\n}\n\n/**\n * Procedural filter applied to a shape. `noise` renders static film grain\n * via SVG feTurbulence — deliberately not animated so frame capture and\n * the live player agree.\n */\nexport interface ShapeFilter {\n type: 'noise';\n /** feTurbulence base frequency (default 0.8 — fine grain). */\n baseFrequency?: number;\n /** Grain opacity 0–1 (default 0.05). */\n opacity?: number;\n}\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 /** Fill opacity 0–1 (applies to solid and gradient fills). */\n fillOpacity?: number;\n /** Gradient fill; overrides `fill` when set. */\n gradient?: LinearGradient;\n /** Repeating pattern fill; overrides `fill`/`gradient` when set. */\n pattern?: ShapePattern;\n /** Procedural filter (film grain). */\n filter?: ShapeFilter;\n /** Stroke color */\n stroke?: string;\n /** Stroke width in pixels */\n strokeWidth?: number;\n /** Border line style (solid/dashed/dotted). Default solid. */\n borderStyle?: BorderStyle;\n /** Corner radius for rect */\n borderRadius?: number;\n };\n}\n\n/**\n * End-of-line marker style for a path/connector endpoint.\n * `arrow` is a filled triangle (the classic arrowhead); `open` is a stroked\n * V; `diamond`/`circle`/`square` are filled glyphs; `none` draws nothing.\n */\nexport type MarkerStyle = 'none' | 'arrow' | 'open' | 'diamond' | 'circle' | 'square';\n\n/**\n * Path layer - renders an SVG `<path>` for arbitrary curves, connectors,\n * arrows, or filled regions. Used by the diagram template for edges\n * between nodes, the drawing template for non-rect/circle/line shapes, and\n * available to any template that needs a custom geometry.\n *\n * The `position` field carries the layer's bounding box (so animations\n * and clipping work the same as for other layers), but the actual\n * geometry is encoded in the `d` attribute using absolute SVG path\n * coordinates relative to the block viewport.\n */\nexport interface PathLayer extends BaseLayer {\n type: 'path';\n content: {\n /** SVG path `d` attribute (e.g. \"M 10 10 L 90 90\"). Absolute viewBox coords. */\n d: string;\n /**\n * Named shape kind (e.g. `'diamond'`, `'star'`, `'arrow-right'`) for\n * path layers that represent one of Squisq's standard shapes. When\n * set, the renderer re-derives `d` from the layer's `position` box\n * (resolved against the viewport) instead of using the stored `d` —\n * so the shape moves, resizes, and adapts to any aspect ratio like\n * the native rect/circle/line layers do. Plain paths (connectors,\n * freehand) leave this unset and keep their absolute `d`.\n */\n shapeKind?: string;\n /** Stroke color (default: theme text color). */\n stroke?: string;\n /** Stroke width in pixels (default: 2). */\n strokeWidth?: number;\n /** Optional fill color (default: 'none' — pure connectors). */\n fill?: string;\n /** Fill opacity 0–1 (applies to solid and gradient fills). */\n fillOpacity?: number;\n /** Gradient fill; overrides `fill` when set. */\n gradient?: LinearGradient;\n /**\n * Border line style (solid/dashed/dotted). A convenience over\n * `dasharray` for named shapes; when set the renderer derives the\n * dash pattern from it (scaled by stroke width).\n */\n borderStyle?: BorderStyle;\n /** Optional stroke dash pattern (SVG `stroke-dasharray` syntax). */\n dasharray?: string;\n /** Marker at the path start. */\n startMarker?: MarkerStyle;\n /** Marker at the path end. */\n endMarker?: MarkerStyle;\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 /**\n * Seconds into the owning block before this video begins playing\n * (the block-relative `startAt`). Default 0 — plays from block start.\n */\n startAt?: number;\n /**\n * When true, the video keeps playing past the block's end (the\n * playback scheduler re-homes it to the player level). Default false.\n */\n spillover?: boolean;\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 * One node in a `TreeLayer` — a JSON-serializable hierarchy item (mirrors\n * the treeview codec's `TreeItem`). Rendered as an indented filesystem-style\n * row with a folder/file icon and connector rails.\n */\nexport interface TreeLayerItem {\n id: string;\n label: string;\n /** True renders a folder (container) icon; else a file (leaf) icon. */\n isDir?: boolean;\n /** Trailing comment shown muted after the label. */\n comment?: string;\n children: TreeLayerItem[];\n}\n\n/**\n * A hierarchical treeview rendered as interactive HTML inside a\n * `<foreignObject>` (like `TableLayer`): folder/file icons, connector\n * rails, and collapse chevrons. Interactive (collapse/expand) in the live\n * React player; captured fully-expanded and static in frame/PDF export.\n */\nexport interface TreeLayer extends BaseLayer {\n type: 'tree';\n content: {\n items: TreeLayerItem[];\n style: TreeLayerStyle;\n };\n}\n\n/** Styling options for a TreeLayer. */\nexport interface TreeLayerStyle {\n /** Leaf (file) row text color. */\n rowColor: string;\n /** Container (folder) row text color. */\n dirColor: string;\n /** Connector rail / branch color. */\n connectorColor: string;\n /** Icon color. */\n iconColor: string;\n /** Muted comment color. */\n commentColor: string;\n /** Font size in pixels. */\n fontSize: number;\n /** Row label font family. */\n fontFamily?: string;\n /** Monospace font for the connector rails. */\n monoFontFamily?: string;\n /** Indent step per depth level, in pixels. */\n indentPx: number;\n /** FontAwesome icon token for containers (default `folder`). */\n folderIcon?: string;\n /** FontAwesome icon token for leaves (default `file`). */\n fileIcon?: string;\n}\n\n/**\n * Mermaid diagram layer — renders an authored `mermaid` code fence through\n * the official Mermaid runtime while keeping the fence source authoritative.\n * Like video/table/tree layers, the browser renderer hosts the diagram in an\n * SVG `<foreignObject>` so it participates in slide layout and frame capture.\n */\nexport interface MermaidLayer extends BaseLayer {\n type: 'mermaid';\n content: {\n /** Complete Mermaid source, excluding the Markdown fence delimiters. */\n source: string;\n /** Theme-derived panel color behind the generated SVG. */\n background?: string;\n /** Theme-derived foreground used by loading/error states. */\n foreground?: string;\n /** Inner panel padding in pixels. */\n padding?: number;\n };\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 /** Font style. Also the inherited baseline for rich `content.html`. */\n fontStyle?: 'normal' | 'italic';\n /** Text color (CSS color) */\n color: string;\n /** Horizontal text alignment within the layer's position box. */\n textAlign?: 'left' | 'center' | 'right';\n /**\n * Vertical alignment of the text within the layer's position box.\n * Requires a `position.height` to have an effect. When omitted, the\n * vertical baseline is derived from `position.anchor` (legacy behavior).\n */\n verticalAlign?: 'top' | 'middle' | 'bottom';\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 /** Background opacity 0–1 (applies to solid and gradient backgrounds). */\n backgroundOpacity?: number;\n /** Gradient background; overrides `background` when set. */\n backgroundGradient?: LinearGradient;\n /** Border (stroke) color for the text box. */\n borderColor?: string;\n /** Border width in pixels. A border renders when > 0 and a color is set. */\n borderWidth?: number;\n /** Border line style (solid/dashed/dotted). Default solid. */\n borderStyle?: BorderStyle;\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// 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. Optional so that conversion stays\n * deterministic — `markdownToDoc()` only sets it when the caller\n * supplies a timestamp via `captionsGeneratedAt`. */\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 * Theme Compilation\n *\n * Turns a partial Theme (typically authored by the customizer panel from\n * a few seed colors and preset choices) into a complete, validated Theme\n * by:\n *\n * 1. Filling unspecified fields from a hardcoded `STARTER_THEME`.\n * 2. Deriving missing color slots from `seedColors` via OKLCh math.\n * 3. Validating the result.\n *\n * Built-in themes ship as fully-specified Theme JSON and skip this step.\n */\n\nimport type {\n Theme,\n DeepPartial,\n ThemeColorPalette,\n ThemeColorScheme,\n ThemeSeedColors,\n FontFamily,\n} from './Theme.js';\nimport { THEME_SCHEMA_VERSION, createTheme } from './Theme.js';\nimport { assertTheme } from './themeValidator.js';\nimport { defaultPageStyle } from './pageStyleDefaults.js';\nimport {\n oklchLighten,\n oklchDarken,\n oklchSetChroma,\n pickContrastingText,\n relativeLuminance,\n deriveScale,\n isHex,\n} from './colorUtils.js';\n\n/** Internal default theme used to fill in fields the customizer doesn't expose. */\nconst STARTER_BODY_FONT: FontFamily = { stackId: 'system-sans' };\nconst STARTER_TITLE_FONT: FontFamily = { stackId: 'system-serif' };\nconst STARTER_MONO_FONT: FontFamily = { stackId: 'system-mono' };\n\nconst STARTER_COLOR_SCHEMES: Record<string, ThemeColorScheme> = {\n blue: { bg: '#1a365d', text: '#63b3ed', accent: '#90cdf4' },\n green: { bg: '#22543d', text: '#9ae6b4', accent: '#68d391' },\n purple: { bg: '#44337a', text: '#d6bcfa', accent: '#b794f4' },\n red: { bg: '#742a2a', text: '#fc8181', accent: '#feb2b2' },\n orange: { bg: '#744210', text: '#fbd38d', accent: '#f6ad55' },\n teal: { bg: '#234e52', text: '#81e6d9', accent: '#4fd1c5' },\n};\n\nexport const STARTER_THEME: Theme = {\n schemaVersion: THEME_SCHEMA_VERSION,\n id: 'custom',\n name: 'Custom Theme',\n description: 'Customizer starter — gets overridden by user choices.',\n colors: {\n primary: '#3182ce',\n secondary: '#4a5568',\n background: '#1a202c',\n backgroundLight: '#2d3748',\n text: '#f7fafc',\n textMuted: '#a0aec0',\n highlight: '#4299e1',\n warning: '#fc8181',\n },\n typography: {\n bodyFont: STARTER_BODY_FONT,\n titleFont: STARTER_TITLE_FONT,\n monoFont: STARTER_MONO_FONT,\n titleWeight: 'bold',\n },\n style: {\n textShadow: true,\n overlayOpacity: 0.45,\n animationSpeed: 1.0,\n borderRadius: 6,\n },\n renderStyle: {\n name: 'standard',\n defaultTextAnimation: 'fadeIn',\n defaultImageAnimation: 'slowZoom',\n ambientMotion: true,\n defaultTransition: { type: 'fade', duration: 0.7 },\n },\n colorSchemes: STARTER_COLOR_SCHEMES,\n};\n\n/**\n * Derive a full `ThemeColorPalette` from `seedColors`, with any explicit\n * `partialColors` taking precedence over derived values. Background and\n * text are guessed from luminance when the user gave only a primary.\n */\nexport function deriveColorPalette(\n seeds: ThemeSeedColors,\n partialColors: Partial<ThemeColorPalette> = {},\n opts: { contrast?: 'subtle' | 'balanced' | 'high' } = {},\n): ThemeColorPalette {\n const spread = opts.contrast === 'high' ? 0.22 : opts.contrast === 'subtle' ? 0.08 : 0.15;\n\n const primary = seeds.primary;\n const secondary = seeds.secondary ?? oklchSetChroma(oklchLighten(primary, 0.05), 0.5);\n const accent = seeds.accent ?? oklchLighten(primary, spread);\n\n // Decide light vs dark surface from the explicit background, the seed background,\n // or default to dark.\n const bgSeed = partialColors.background ?? seeds.background;\n let background: string;\n if (bgSeed) {\n background = bgSeed;\n } else {\n // Pick a dark or light background that contrasts with primary\n background = relativeLuminance(primary) > 0.5 ? '#0a0a0a' : '#1a202c';\n }\n const isLightSurface = relativeLuminance(background) > 0.5;\n\n const backgroundLight =\n partialColors.backgroundLight ??\n (isLightSurface ? oklchDarken(background, 0.04) : oklchLighten(background, 0.04));\n\n const text =\n partialColors.text ?? seeds.text ?? pickContrastingText(background, '#f7fafc', '#1a202c');\n const textMuted =\n partialColors.textMuted ??\n (isLightSurface ? oklchLighten(text, 0.25) : oklchDarken(text, 0.25));\n\n const highlight = partialColors.highlight ?? accent;\n const warning = partialColors.warning ?? '#fc8181';\n\n return {\n primary: partialColors.primary ?? primary,\n secondary: partialColors.secondary ?? secondary,\n background,\n backgroundLight,\n text,\n textMuted,\n highlight,\n warning,\n };\n}\n\n/**\n * Expand one accent color into a full `{bg, text, accent}` color scheme via\n * the OKLCh scale — dark saturated bg, light readable text, the picked accent.\n * Falls back to a neutral scheme when the input isn't a valid hex. Shared by\n * the editor's accent list and file-import theme inference so both derive\n * identical schemes from the same accent.\n */\nexport function accentToColorScheme(accent: string): ThemeColorScheme {\n if (!isHex(accent)) return { bg: '#1a202c', text: '#e2e8f0', accent: '#63b3ed' };\n const scale = deriveScale(accent, 0.3);\n return { bg: scale.darker2, text: scale.lighter2, accent: scale.base };\n}\n\n/** Map a contrast preset to numeric spread used by `deriveColorPalette`. */\nexport type ContrastPreset = 'subtle' | 'balanced' | 'high';\n\nexport interface CompileOptions {\n /** Contrast level for OKLCh derivation (default 'balanced'). */\n contrast?: ContrastPreset;\n /**\n * Base theme to inherit from. When provided, the partial is deep-merged\n * over this theme instead of the neutral `STARTER_THEME`, so the compiled\n * theme inherits the base's render style, color schemes, typography, and\n * persistent layers — and only the fields the user changed are overridden.\n * The base's id is recorded on the result as `basedOn`.\n */\n base?: Theme;\n}\n\n/**\n * Compile a partial Theme into a complete one. Fills missing fields from\n * the base (`opts.base` when given, else `STARTER_THEME`), derives missing\n * color slots from `seedColors` (when present), and validates the result.\n */\nexport function compileTheme(partial: DeepPartial<Theme>, opts: CompileOptions = {}): Theme {\n // Step 1: deep-merge over the base (a chosen theme, or the neutral starter)\n const base = opts.base ?? STARTER_THEME;\n const merged = createTheme(base, partial);\n merged.schemaVersion = THEME_SCHEMA_VERSION;\n // Record which theme this was derived from so a customizer can re-inherit.\n if (opts.base && !merged.basedOn) merged.basedOn = opts.base.id;\n\n // Step 1b: typography fonts are discriminated unions ({stackId} | {custom}),\n // so deep-merge would leave stale keys from the starter when the partial\n // switches form. Replace each font wholesale when explicitly provided.\n const partialTypography = partial.typography;\n if (partialTypography) {\n if (partialTypography.titleFont !== undefined) {\n merged.typography.titleFont = partialTypography.titleFont as FontFamily;\n }\n if (partialTypography.bodyFont !== undefined) {\n merged.typography.bodyFont = partialTypography.bodyFont as FontFamily;\n }\n if (partialTypography.monoFont !== undefined) {\n merged.typography.monoFont = partialTypography.monoFont as FontFamily;\n }\n }\n\n // Step 1c: colorSchemes is a map the user edits wholesale (add / remove\n // accents). Deep-merge can't delete keys, so replace it outright when the\n // partial provides it — mirroring the font replacement above. This lets a\n // removed accent actually disappear rather than lingering from the base.\n if (partial.colorSchemes !== undefined) {\n merged.colorSchemes = partial.colorSchemes as Record<string, ThemeColorScheme>;\n }\n\n // Step 2: derive missing color slots from seeds, if present\n if (merged.seedColors) {\n const partialColors = (partial.colors ?? {}) as Partial<ThemeColorPalette>;\n merged.colors = deriveColorPalette(merged.seedColors, partialColors, {\n contrast: opts.contrast,\n });\n }\n\n // Step 2b: fill page art direction from existing fields when absent, so\n // every compiled theme carries an explicit, serializable pageStyle.\n if (!merged.pageStyle) {\n merged.pageStyle = defaultPageStyle(merged);\n }\n\n // Step 3: validate\n return assertTheme(merged, `compiled theme \"${merged.id}\"`);\n}\n\n/**\n * Parse a JSON string into a validated Theme. Throws on invalid input.\n */\nexport function parseTheme(json: string): Theme {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Invalid theme JSON: ${msg}`);\n }\n return assertTheme(parsed, 'parsed theme');\n}\n\n/**\n * Serialize a Theme to a stable, pretty-printed JSON string. Round-trips\n * cleanly through `parseTheme`.\n */\nexport function serializeTheme(theme: Theme): string {\n return JSON.stringify(theme, null, 2);\n}\n"],"mappings":"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","names":[]}
@@ -1,344 +0,0 @@
1
- import {
2
- toMdast
3
- } from "./chunk-66QO7XFB.js";
4
- import {
5
- formatFrontmatterValue
6
- } from "./chunk-AMIR72JP.js";
7
-
8
- // src/markdown/stringify.ts
9
- import { unified } from "unified";
10
- import remarkStringify from "remark-stringify";
11
- import remarkGfm from "remark-gfm";
12
- import remarkMath from "remark-math";
13
- import remarkDirective from "remark-directive";
14
- var defaultProcessor;
15
- var DQ_RUN = `"(?:[^"\\\\]|\\\\.)*"`;
16
- var SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
17
- var ESCAPED_TEMPLATE_SPAN_RE = new RegExp(
18
- `\\{\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^\\]\\\\])*)\\]\\}`,
19
- "g"
20
- );
21
- var HEADING_LINE_RE = /^#{1,6} .*$/gm;
22
- var TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(
23
- `\\{(?!\\\\?\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^}\\\\])*)\\}(?=\\s*(?:\\{\\[.*)?$)`
24
- );
25
- var UNESCAPE_PUNCT_RE = /\\([\\[\]:#.,+=/?;@%$(){}'"-])/g;
26
- function unescapeMarkdownPunct(text) {
27
- return text.replace(UNESCAPE_PUNCT_RE, "$1");
28
- }
29
- function stringifyMarkdown(doc, options) {
30
- const mdastTree = toMdast(doc);
31
- const useDefaults = !options || options.gfm !== false && options.math !== false && options.directive !== false && !options.bullet && !options.bulletOrdered && !options.emphasis && !options.strong && !options.rule && !options.fence && options.setext == null;
32
- let processor;
33
- if (useDefaults) {
34
- if (!defaultProcessor) {
35
- defaultProcessor = unified().use(remarkGfm).use(remarkMath).use(remarkDirective).use(remarkStringify, {
36
- bullet: "-",
37
- bulletOrdered: ".",
38
- emphasis: "*",
39
- strong: "*",
40
- rule: "-",
41
- fence: "`",
42
- setext: false
43
- });
44
- }
45
- processor = defaultProcessor;
46
- } else {
47
- processor = unified();
48
- if (options?.gfm !== false) {
49
- processor = processor.use(remarkGfm);
50
- }
51
- if (options?.math !== false) {
52
- processor = processor.use(remarkMath);
53
- }
54
- if (options?.directive !== false) {
55
- processor = processor.use(remarkDirective);
56
- }
57
- processor = processor.use(remarkStringify, {
58
- bullet: options?.bullet ?? "-",
59
- bulletOrdered: options?.bulletOrdered ?? ".",
60
- emphasis: options?.emphasis ?? "*",
61
- strong: options?.strong ?? "*",
62
- rule: options?.rule ?? "-",
63
- fence: options?.fence ?? "`",
64
- setext: options?.setext ?? false
65
- });
66
- }
67
- const result = processor.stringify(mdastTree);
68
- let cleaned = result.replace(
69
- ESCAPED_TEMPLATE_SPAN_RE,
70
- (_m, inner) => `{[${unescapeMarkdownPunct(inner)}]}`
71
- );
72
- cleaned = cleaned.replace(
73
- HEADING_LINE_RE,
74
- (line) => line.replace(
75
- TRAILING_ESCAPED_PANDOC_SPAN_RE,
76
- (_m, inner) => `{${unescapeMarkdownPunct(inner)}}`
77
- )
78
- );
79
- cleaned = cleaned.replace(/\{(?!\\?\[)[^}]*\}/g, (match) => match.replace(/\\:/g, ":"));
80
- if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {
81
- const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {
82
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
83
- return `${k}: ${formatFrontmatterValue(v)}`;
84
- }
85
- return `${k}: ${JSON.stringify(v)}`;
86
- });
87
- return `---
88
- ${yamlLines.join("\n")}
89
- ---
90
-
91
- ${cleaned}`;
92
- }
93
- return cleaned;
94
- }
95
-
96
- // src/markdown/sanitize.ts
97
- var SAFE_LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
98
- var SAFE_MEDIA_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "blob"]);
99
- var NEVER_ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["javascript", "vbscript", "data"]);
100
- var SAFE_DATA_MEDIA_RE = /^data:(?:image\/(?!svg\+xml)[a-z0-9.+-]+|audio\/[a-z0-9.+-]+|video\/[a-z0-9.+-]+);/i;
101
- var SAFE_TAGS = /* @__PURE__ */ new Set([
102
- "a",
103
- "abbr",
104
- "b",
105
- "blockquote",
106
- "br",
107
- "caption",
108
- "cite",
109
- "code",
110
- "col",
111
- "colgroup",
112
- "data",
113
- "dd",
114
- "del",
115
- "details",
116
- "dfn",
117
- "div",
118
- "dl",
119
- "dt",
120
- "em",
121
- "figcaption",
122
- "figure",
123
- "h1",
124
- "h2",
125
- "h3",
126
- "h4",
127
- "h5",
128
- "h6",
129
- "hr",
130
- "i",
131
- "img",
132
- "kbd",
133
- "li",
134
- "mark",
135
- "ol",
136
- "p",
137
- "pre",
138
- "q",
139
- "s",
140
- "samp",
141
- "small",
142
- "source",
143
- "span",
144
- "strong",
145
- "sub",
146
- "summary",
147
- "sup",
148
- "table",
149
- "tbody",
150
- "td",
151
- "tfoot",
152
- "th",
153
- "thead",
154
- "time",
155
- "tr",
156
- "track",
157
- "u",
158
- "ul",
159
- "var",
160
- "video",
161
- "audio"
162
- ]);
163
- var DROP_WITH_CONTENT_TAGS = /* @__PURE__ */ new Set([
164
- "base",
165
- "button",
166
- "embed",
167
- "form",
168
- "iframe",
169
- "input",
170
- "link",
171
- "meta",
172
- "object",
173
- "option",
174
- "script",
175
- "select",
176
- "style",
177
- "svg",
178
- "template",
179
- "textarea"
180
- ]);
181
- var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "id", "title", "role"]);
182
- var TAG_ATTRS = {
183
- a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
184
- audio: /* @__PURE__ */ new Set(["src", "controls", "preload", "muted", "loop"]),
185
- col: /* @__PURE__ */ new Set(["span", "width"]),
186
- colgroup: /* @__PURE__ */ new Set(["span"]),
187
- data: /* @__PURE__ */ new Set(["value"]),
188
- img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading", "decoding"]),
189
- li: /* @__PURE__ */ new Set(["value"]),
190
- ol: /* @__PURE__ */ new Set(["start", "type"]),
191
- q: /* @__PURE__ */ new Set(["cite"]),
192
- source: /* @__PURE__ */ new Set(["src", "type", "media"]),
193
- td: /* @__PURE__ */ new Set(["colspan", "rowspan", "align"]),
194
- th: /* @__PURE__ */ new Set(["colspan", "rowspan", "align", "scope"]),
195
- time: /* @__PURE__ */ new Set(["datetime"]),
196
- track: /* @__PURE__ */ new Set(["src", "kind", "label", "srclang", "default"]),
197
- video: /* @__PURE__ */ new Set([
198
- "src",
199
- "poster",
200
- "width",
201
- "height",
202
- "controls",
203
- "preload",
204
- "muted",
205
- "loop",
206
- "playsinline"
207
- ])
208
- };
209
- var SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;
210
- var SAFE_OL_TYPES = /* @__PURE__ */ new Set(["1", "a", "A", "i", "I"]);
211
- var SAFE_PRELOAD_VALUES = /* @__PURE__ */ new Set(["none", "metadata", "auto"]);
212
- var SAFE_TRACK_KINDS = /* @__PURE__ */ new Set(["subtitles", "captions", "descriptions", "chapters", "metadata"]);
213
- function sanitizeUrl(url, kind = "link", options) {
214
- if (typeof url !== "string") return null;
215
- const trimmed = url.trim();
216
- if (!trimmed) return null;
217
- const compact = stripUrlSchemeNoise(trimmed);
218
- const colon = compact.indexOf(":");
219
- const firstPathChar = firstIndexOfAny(compact, ["/", "?", "#"]);
220
- const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);
221
- if (!hasScheme) return trimmed;
222
- const scheme = compact.slice(0, colon).toLowerCase();
223
- if (kind === "media") {
224
- if (scheme === "data") return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;
225
- return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;
226
- }
227
- if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;
228
- if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {
229
- return trimmed;
230
- }
231
- return null;
232
- }
233
- function sanitizeHtmlNodes(nodes) {
234
- const out = [];
235
- for (const node of nodes) {
236
- out.push(...sanitizeHtmlNode(node));
237
- }
238
- return out;
239
- }
240
- function sanitizeHtmlNode(node) {
241
- switch (node.type) {
242
- case "htmlText":
243
- return [node];
244
- case "htmlComment":
245
- return [];
246
- case "htmlElement":
247
- return sanitizeHtmlElement(node);
248
- }
249
- }
250
- function sanitizeHtmlElement(node) {
251
- const tag = node.tagName.toLowerCase();
252
- if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];
253
- const children = sanitizeHtmlNodes(node.children);
254
- if (!SAFE_TAGS.has(tag)) return children;
255
- return [
256
- {
257
- type: "htmlElement",
258
- tagName: tag,
259
- attributes: sanitizeAttrs(tag, node.attributes),
260
- children,
261
- selfClosing: node.selfClosing
262
- }
263
- ];
264
- }
265
- function sanitizeAttrs(tag, attrs) {
266
- const out = {};
267
- for (const [rawName, rawValue] of Object.entries(attrs)) {
268
- const name = rawName.toLowerCase();
269
- if (!isAllowedAttr(tag, name)) continue;
270
- const value = String(rawValue);
271
- const safeValue = sanitizeAttrValue(tag, name, value);
272
- if (safeValue === null) continue;
273
- out[name] = safeValue;
274
- }
275
- if (tag === "a" && out.target === "_blank") {
276
- out.rel = ensureRelTokens(out.rel, ["noopener", "noreferrer"]);
277
- }
278
- return out;
279
- }
280
- function isAllowedAttr(tag, name) {
281
- if (!SAFE_ATTR_NAME_RE.test(name)) return false;
282
- if (name.startsWith("on")) return false;
283
- if (name.startsWith("aria-") || name.startsWith("data-")) return true;
284
- if (GLOBAL_ATTRS.has(name)) return true;
285
- return TAG_ATTRS[tag]?.has(name) ?? false;
286
- }
287
- function sanitizeAttrValue(tag, name, value) {
288
- if (name === "href") return sanitizeUrl(value, "link");
289
- if (name === "src" || name === "poster") return sanitizeUrl(value, "media");
290
- if ((name === "width" || name === "height" || name === "span" || name === "colspan" || name === "rowspan" || name === "value") && !isNonNegativeInteger(value)) {
291
- return null;
292
- }
293
- if (name === "start" && !isInteger(value)) return null;
294
- if (name === "type" && tag === "ol" && !SAFE_OL_TYPES.has(value)) return null;
295
- if (name === "preload" && !SAFE_PRELOAD_VALUES.has(value)) return null;
296
- if (name === "kind" && tag === "track" && !SAFE_TRACK_KINDS.has(value)) return null;
297
- if (name === "align" && value !== "left" && value !== "right" && value !== "center") return null;
298
- if (name === "target")
299
- return value === "_blank" || value === "_self" || value === "_parent" || value === "_top" ? value : null;
300
- if (name === "rel") return sanitizeRel(value);
301
- return value;
302
- }
303
- function sanitizeRel(value) {
304
- return relTokens(value).join(" ");
305
- }
306
- function ensureRelTokens(value, required) {
307
- const tokens = new Set(value ? relTokens(value) : []);
308
- for (const token of required) tokens.add(token);
309
- return Array.from(tokens).join(" ");
310
- }
311
- function relTokens(value) {
312
- const tokens = value.split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean).filter((token) => /^[a-z0-9_-]+$/.test(token));
313
- return Array.from(new Set(tokens));
314
- }
315
- function isInteger(value) {
316
- return /^-?\d+$/.test(value.trim());
317
- }
318
- function isNonNegativeInteger(value) {
319
- return /^\d+$/.test(value.trim());
320
- }
321
- function firstIndexOfAny(value, needles) {
322
- let result = -1;
323
- for (const needle of needles) {
324
- const index = value.indexOf(needle);
325
- if (index >= 0 && (result < 0 || index < result)) result = index;
326
- }
327
- return result;
328
- }
329
- function stripUrlSchemeNoise(value) {
330
- let out = "";
331
- for (const char of value) {
332
- const code = char.charCodeAt(0);
333
- if (code <= 32 || code === 127 || char.trim() === "") continue;
334
- out += char;
335
- }
336
- return out;
337
- }
338
-
339
- export {
340
- stringifyMarkdown,
341
- sanitizeUrl,
342
- sanitizeHtmlNodes
343
- };
344
- //# sourceMappingURL=chunk-R2GC6RLD.js.map