@bendyline/squisq 0.1.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +85 -0
  2. package/dist/{themeLibrary-DySHPcgj.d.ts → Doc-1hmT1LZl.d.ts} +1 -42
  3. package/dist/__tests__/animationUtils.test.d.ts +2 -0
  4. package/dist/__tests__/animationUtils.test.d.ts.map +1 -0
  5. package/dist/__tests__/animationUtils.test.js +60 -0
  6. package/dist/__tests__/animationUtils.test.js.map +1 -0
  7. package/dist/__tests__/contentExtractor.test.d.ts +2 -0
  8. package/dist/__tests__/contentExtractor.test.d.ts.map +1 -0
  9. package/dist/__tests__/contentExtractor.test.js +151 -0
  10. package/dist/__tests__/contentExtractor.test.js.map +1 -0
  11. package/dist/__tests__/geohash.test.d.ts +2 -0
  12. package/dist/__tests__/geohash.test.d.ts.map +1 -0
  13. package/dist/__tests__/geohash.test.js +87 -0
  14. package/dist/__tests__/geohash.test.js.map +1 -0
  15. package/dist/__tests__/getLayers.test.d.ts +2 -0
  16. package/dist/__tests__/getLayers.test.d.ts.map +1 -0
  17. package/dist/__tests__/getLayers.test.js +167 -0
  18. package/dist/__tests__/getLayers.test.js.map +1 -0
  19. package/dist/__tests__/haversine.test.d.ts +2 -0
  20. package/dist/__tests__/haversine.test.d.ts.map +1 -0
  21. package/dist/__tests__/haversine.test.js +56 -0
  22. package/dist/__tests__/haversine.test.js.map +1 -0
  23. package/dist/__tests__/localForageAdapter.test.d.ts +8 -0
  24. package/dist/__tests__/localForageAdapter.test.d.ts.map +1 -0
  25. package/dist/__tests__/localForageAdapter.test.js +130 -0
  26. package/dist/__tests__/localForageAdapter.test.js.map +1 -0
  27. package/dist/__tests__/markdown.test.d.ts +2 -0
  28. package/dist/__tests__/markdown.test.d.ts.map +1 -0
  29. package/dist/__tests__/markdown.test.js +654 -0
  30. package/dist/__tests__/markdown.test.js.map +1 -0
  31. package/dist/__tests__/markdownToDoc.test.d.ts +2 -0
  32. package/dist/__tests__/markdownToDoc.test.d.ts.map +1 -0
  33. package/dist/__tests__/markdownToDoc.test.js +313 -0
  34. package/dist/__tests__/markdownToDoc.test.js.map +1 -0
  35. package/dist/__tests__/seededRandom.test.d.ts +2 -0
  36. package/dist/__tests__/seededRandom.test.d.ts.map +1 -0
  37. package/dist/__tests__/seededRandom.test.js +125 -0
  38. package/dist/__tests__/seededRandom.test.js.map +1 -0
  39. package/dist/__tests__/slideshowGenerator.test.d.ts +2 -0
  40. package/dist/__tests__/slideshowGenerator.test.d.ts.map +1 -0
  41. package/dist/__tests__/slideshowGenerator.test.js +114 -0
  42. package/dist/__tests__/slideshowGenerator.test.js.map +1 -0
  43. package/dist/__tests__/storage.test.d.ts +2 -0
  44. package/dist/__tests__/storage.test.d.ts.map +1 -0
  45. package/dist/__tests__/storage.test.js +51 -0
  46. package/dist/__tests__/storage.test.js.map +1 -0
  47. package/dist/__tests__/templates.test.d.ts +2 -0
  48. package/dist/__tests__/templates.test.d.ts.map +1 -0
  49. package/dist/__tests__/templates.test.js +217 -0
  50. package/dist/__tests__/templates.test.js.map +1 -0
  51. package/dist/__tests__/timing.test.d.ts +2 -0
  52. package/dist/__tests__/timing.test.d.ts.map +1 -0
  53. package/dist/__tests__/timing.test.js +131 -0
  54. package/dist/__tests__/timing.test.js.map +1 -0
  55. package/dist/chunk-3E5F2XMR.js +129 -0
  56. package/dist/chunk-3E5F2XMR.js.map +1 -0
  57. package/dist/chunk-6YJPI6VI.js +35 -0
  58. package/dist/chunk-6YJPI6VI.js.map +1 -0
  59. package/dist/chunk-FVTQSNR7.js +100 -0
  60. package/dist/chunk-FVTQSNR7.js.map +1 -0
  61. package/dist/chunk-V543AI2F.js +1 -0
  62. package/dist/chunk-V543AI2F.js.map +1 -0
  63. package/dist/chunk-VP5SZTZ2.js +792 -0
  64. package/dist/chunk-VP5SZTZ2.js.map +1 -0
  65. package/dist/doc/docToMarkdown.d.ts +41 -0
  66. package/dist/doc/docToMarkdown.d.ts.map +1 -0
  67. package/dist/doc/docToMarkdown.js +88 -0
  68. package/dist/doc/docToMarkdown.js.map +1 -0
  69. package/dist/doc/getLayers.d.ts +65 -0
  70. package/dist/doc/getLayers.d.ts.map +1 -0
  71. package/dist/doc/getLayers.js +103 -0
  72. package/dist/doc/getLayers.js.map +1 -0
  73. package/dist/doc/index.d.ts +9 -3
  74. package/dist/doc/index.d.ts.map +1 -0
  75. package/dist/doc/index.js +6 -125
  76. package/dist/doc/index.js.map +1 -1
  77. package/dist/doc/markdownToDoc.d.ts +68 -0
  78. package/dist/doc/markdownToDoc.d.ts.map +1 -0
  79. package/dist/doc/markdownToDoc.js +225 -0
  80. package/dist/doc/markdownToDoc.js.map +1 -0
  81. package/dist/doc/templates/accentImage.d.ts +59 -0
  82. package/dist/doc/templates/accentImage.d.ts.map +1 -0
  83. package/dist/doc/templates/accentImage.js +275 -0
  84. package/dist/doc/templates/accentImage.js.map +1 -0
  85. package/dist/doc/templates/captionUtils.d.ts +20 -0
  86. package/dist/doc/templates/captionUtils.d.ts.map +1 -0
  87. package/dist/doc/templates/captionUtils.js +29 -0
  88. package/dist/doc/templates/captionUtils.js.map +1 -0
  89. package/dist/doc/templates/comparisonBar.d.ts +13 -0
  90. package/dist/doc/templates/comparisonBar.d.ts.map +1 -0
  91. package/dist/doc/templates/comparisonBar.js +127 -0
  92. package/dist/doc/templates/comparisonBar.js.map +1 -0
  93. package/dist/doc/templates/coverBlock.d.ts +51 -0
  94. package/dist/doc/templates/coverBlock.d.ts.map +1 -0
  95. package/dist/doc/templates/coverBlock.js +125 -0
  96. package/dist/doc/templates/coverBlock.js.map +1 -0
  97. package/dist/doc/templates/dateEvent.d.ts +15 -0
  98. package/dist/doc/templates/dateEvent.d.ts.map +1 -0
  99. package/dist/doc/templates/dateEvent.js +110 -0
  100. package/dist/doc/templates/dateEvent.js.map +1 -0
  101. package/dist/doc/templates/definitionCard.d.ts +13 -0
  102. package/dist/doc/templates/definitionCard.d.ts.map +1 -0
  103. package/dist/doc/templates/definitionCard.js +114 -0
  104. package/dist/doc/templates/definitionCard.js.map +1 -0
  105. package/dist/doc/templates/factCard.d.ts +15 -0
  106. package/dist/doc/templates/factCard.d.ts.map +1 -0
  107. package/dist/doc/templates/factCard.js +100 -0
  108. package/dist/doc/templates/factCard.js.map +1 -0
  109. package/dist/doc/templates/fullBleedQuote.d.ts +13 -0
  110. package/dist/doc/templates/fullBleedQuote.d.ts.map +1 -0
  111. package/dist/doc/templates/fullBleedQuote.js +54 -0
  112. package/dist/doc/templates/fullBleedQuote.js.map +1 -0
  113. package/dist/doc/templates/imageWithCaption.d.ts +13 -0
  114. package/dist/doc/templates/imageWithCaption.d.ts.map +1 -0
  115. package/dist/doc/templates/imageWithCaption.js +142 -0
  116. package/dist/doc/templates/imageWithCaption.js.map +1 -0
  117. package/dist/doc/templates/index.d.ts +104 -0
  118. package/dist/doc/templates/index.d.ts.map +1 -0
  119. package/dist/doc/templates/index.js +442 -0
  120. package/dist/doc/templates/index.js.map +1 -0
  121. package/dist/doc/templates/listBlock.d.ts +13 -0
  122. package/dist/doc/templates/listBlock.d.ts.map +1 -0
  123. package/dist/doc/templates/listBlock.js +89 -0
  124. package/dist/doc/templates/listBlock.js.map +1 -0
  125. package/dist/doc/templates/mapBlock.d.ts +17 -0
  126. package/dist/doc/templates/mapBlock.d.ts.map +1 -0
  127. package/dist/doc/templates/mapBlock.js +112 -0
  128. package/dist/doc/templates/mapBlock.js.map +1 -0
  129. package/dist/doc/templates/persistentLayers.d.ts +39 -0
  130. package/dist/doc/templates/persistentLayers.d.ts.map +1 -0
  131. package/dist/doc/templates/persistentLayers.js +447 -0
  132. package/dist/doc/templates/persistentLayers.js.map +1 -0
  133. package/dist/doc/templates/photoGrid.d.ts +20 -0
  134. package/dist/doc/templates/photoGrid.d.ts.map +1 -0
  135. package/dist/doc/templates/photoGrid.js +172 -0
  136. package/dist/doc/templates/photoGrid.js.map +1 -0
  137. package/dist/doc/templates/pullQuote.d.ts +13 -0
  138. package/dist/doc/templates/pullQuote.d.ts.map +1 -0
  139. package/dist/doc/templates/pullQuote.js +109 -0
  140. package/dist/doc/templates/pullQuote.js.map +1 -0
  141. package/dist/doc/templates/quoteBlock.d.ts +15 -0
  142. package/dist/doc/templates/quoteBlock.d.ts.map +1 -0
  143. package/dist/doc/templates/quoteBlock.js +97 -0
  144. package/dist/doc/templates/quoteBlock.js.map +1 -0
  145. package/dist/doc/templates/sectionHeader.d.ts +14 -0
  146. package/dist/doc/templates/sectionHeader.d.ts.map +1 -0
  147. package/dist/doc/templates/sectionHeader.js +96 -0
  148. package/dist/doc/templates/sectionHeader.js.map +1 -0
  149. package/dist/doc/templates/statHighlight.d.ts +15 -0
  150. package/dist/doc/templates/statHighlight.d.ts.map +1 -0
  151. package/dist/doc/templates/statHighlight.js +99 -0
  152. package/dist/doc/templates/statHighlight.js.map +1 -0
  153. package/dist/doc/templates/titleBlock.d.ts +13 -0
  154. package/dist/doc/templates/titleBlock.d.ts.map +1 -0
  155. package/dist/doc/templates/titleBlock.js +92 -0
  156. package/dist/doc/templates/titleBlock.js.map +1 -0
  157. package/dist/doc/templates/twoColumn.d.ts +15 -0
  158. package/dist/doc/templates/twoColumn.d.ts.map +1 -0
  159. package/dist/doc/templates/twoColumn.js +219 -0
  160. package/dist/doc/templates/twoColumn.js.map +1 -0
  161. package/dist/doc/templates/videoPullQuote.d.ts +20 -0
  162. package/dist/doc/templates/videoPullQuote.d.ts.map +1 -0
  163. package/dist/doc/templates/videoPullQuote.js +118 -0
  164. package/dist/doc/templates/videoPullQuote.js.map +1 -0
  165. package/dist/doc/templates/videoWithCaption.d.ts +20 -0
  166. package/dist/doc/templates/videoWithCaption.d.ts.map +1 -0
  167. package/dist/doc/templates/videoWithCaption.js +104 -0
  168. package/dist/doc/templates/videoWithCaption.js.map +1 -0
  169. package/dist/doc/utils/animationUtils.d.ts +34 -0
  170. package/dist/doc/utils/animationUtils.d.ts.map +1 -0
  171. package/dist/doc/utils/animationUtils.js +110 -0
  172. package/dist/doc/utils/animationUtils.js.map +1 -0
  173. package/dist/doc/utils/themeUtils.d.ts +55 -0
  174. package/dist/doc/utils/themeUtils.d.ts.map +1 -0
  175. package/dist/doc/utils/themeUtils.js +109 -0
  176. package/dist/doc/utils/themeUtils.js.map +1 -0
  177. package/dist/generate/contentExtractor.d.ts +112 -0
  178. package/dist/generate/contentExtractor.d.ts.map +1 -0
  179. package/dist/generate/contentExtractor.js +638 -0
  180. package/dist/generate/contentExtractor.js.map +1 -0
  181. package/dist/generate/index.d.ts +7 -0
  182. package/dist/generate/index.d.ts.map +1 -0
  183. package/dist/generate/index.js +4 -0
  184. package/dist/generate/index.js.map +1 -0
  185. package/dist/generate/slideshowGenerator.d.ts +72 -0
  186. package/dist/generate/slideshowGenerator.d.ts.map +1 -0
  187. package/dist/generate/slideshowGenerator.js +159 -0
  188. package/dist/generate/slideshowGenerator.js.map +1 -0
  189. package/dist/generate/templateMapper.d.ts +23 -0
  190. package/dist/generate/templateMapper.d.ts.map +1 -0
  191. package/dist/generate/templateMapper.js +114 -0
  192. package/dist/generate/templateMapper.js.map +1 -0
  193. package/dist/index.d.ts +9 -8
  194. package/dist/index.d.ts.map +1 -0
  195. package/dist/index.js +8 -216
  196. package/dist/index.js.map +1 -1
  197. package/dist/markdown/convert.d.ts +71 -0
  198. package/dist/markdown/convert.d.ts.map +1 -0
  199. package/dist/markdown/convert.js +746 -0
  200. package/dist/markdown/convert.js.map +1 -0
  201. package/dist/markdown/htmlParse.d.ts +26 -0
  202. package/dist/markdown/htmlParse.d.ts.map +1 -0
  203. package/dist/markdown/htmlParse.js +195 -0
  204. package/dist/markdown/htmlParse.js.map +1 -0
  205. package/dist/markdown/index.d.ts +19 -225
  206. package/dist/markdown/index.d.ts.map +1 -0
  207. package/dist/markdown/index.js +33 -32
  208. package/dist/markdown/index.js.map +1 -1
  209. package/dist/markdown/parse.d.ts +27 -0
  210. package/dist/markdown/parse.d.ts.map +1 -0
  211. package/dist/markdown/parse.js +90 -0
  212. package/dist/markdown/parse.js.map +1 -0
  213. package/dist/markdown/stringify.d.ts +32 -0
  214. package/dist/markdown/stringify.d.ts.map +1 -0
  215. package/dist/markdown/stringify.js +108 -0
  216. package/dist/markdown/stringify.js.map +1 -0
  217. package/dist/markdown/types.d.ts +496 -0
  218. package/dist/markdown/types.d.ts.map +1 -0
  219. package/dist/markdown/types.js +17 -0
  220. package/dist/markdown/types.js.map +1 -0
  221. package/dist/markdown/utils.d.ts +78 -0
  222. package/dist/markdown/utils.d.ts.map +1 -0
  223. package/dist/markdown/utils.js +149 -0
  224. package/dist/markdown/utils.js.map +1 -0
  225. package/dist/random/SeededRandom.d.ts +50 -0
  226. package/dist/random/SeededRandom.d.ts.map +1 -0
  227. package/dist/random/SeededRandom.js +115 -0
  228. package/dist/random/SeededRandom.js.map +1 -0
  229. package/dist/random/index.d.ts +2 -0
  230. package/dist/random/index.d.ts.map +1 -0
  231. package/dist/random/index.js +2 -0
  232. package/dist/random/index.js.map +1 -0
  233. package/dist/schemas/BlockTemplates.d.ts +583 -0
  234. package/dist/schemas/BlockTemplates.d.ts.map +1 -0
  235. package/dist/schemas/BlockTemplates.js +72 -0
  236. package/dist/schemas/BlockTemplates.js.map +1 -0
  237. package/dist/schemas/Doc.d.ts +411 -0
  238. package/dist/schemas/Doc.d.ts.map +1 -0
  239. package/dist/schemas/Doc.js +90 -0
  240. package/dist/schemas/Doc.js.map +1 -0
  241. package/dist/schemas/LayoutStrategy.d.ts +84 -0
  242. package/dist/schemas/LayoutStrategy.d.ts.map +1 -0
  243. package/dist/schemas/LayoutStrategy.js +123 -0
  244. package/dist/schemas/LayoutStrategy.js.map +1 -0
  245. package/dist/schemas/MediaProvider.d.ts +65 -0
  246. package/dist/schemas/MediaProvider.d.ts.map +1 -0
  247. package/dist/schemas/MediaProvider.js +13 -0
  248. package/dist/schemas/MediaProvider.js.map +1 -0
  249. package/dist/schemas/Theme.d.ts +163 -0
  250. package/dist/schemas/Theme.d.ts.map +1 -0
  251. package/dist/schemas/Theme.js +62 -0
  252. package/dist/schemas/Theme.js.map +1 -0
  253. package/dist/schemas/Types.d.ts +14 -0
  254. package/dist/schemas/Types.d.ts.map +1 -0
  255. package/dist/schemas/Types.js +5 -0
  256. package/dist/schemas/Types.js.map +1 -0
  257. package/dist/schemas/Viewport.d.ts +74 -0
  258. package/dist/schemas/Viewport.d.ts.map +1 -0
  259. package/dist/schemas/Viewport.js +63 -0
  260. package/dist/schemas/Viewport.js.map +1 -0
  261. package/dist/schemas/index.d.ts +10 -70
  262. package/dist/schemas/index.d.ts.map +1 -0
  263. package/dist/schemas/index.js +7 -60
  264. package/dist/schemas/index.js.map +1 -1
  265. package/dist/schemas/themeLibrary.d.ts +41 -0
  266. package/dist/schemas/themeLibrary.d.ts.map +1 -0
  267. package/dist/schemas/themeLibrary.js +466 -0
  268. package/dist/schemas/themeLibrary.js.map +1 -0
  269. package/dist/spatial/Geohash.d.ts +85 -0
  270. package/dist/spatial/Geohash.d.ts.map +1 -0
  271. package/dist/spatial/Geohash.js +142 -0
  272. package/dist/spatial/Geohash.js.map +1 -0
  273. package/dist/spatial/Haversine.d.ts +24 -0
  274. package/dist/spatial/Haversine.d.ts.map +1 -0
  275. package/dist/spatial/Haversine.js +48 -0
  276. package/dist/spatial/Haversine.js.map +1 -0
  277. package/dist/spatial/index.d.ts +3 -111
  278. package/dist/spatial/index.d.ts.map +1 -0
  279. package/dist/spatial/index.js +2 -24
  280. package/dist/spatial/index.js.map +1 -1
  281. package/dist/storage/LocalForageAdapter.d.ts +36 -0
  282. package/dist/storage/LocalForageAdapter.d.ts.map +1 -0
  283. package/dist/storage/LocalForageAdapter.js +82 -0
  284. package/dist/storage/LocalForageAdapter.js.map +1 -0
  285. package/dist/storage/LocalStorageAdapter.d.ts +16 -0
  286. package/dist/storage/LocalStorageAdapter.d.ts.map +1 -0
  287. package/dist/storage/LocalStorageAdapter.js +50 -0
  288. package/dist/storage/LocalStorageAdapter.js.map +1 -0
  289. package/dist/storage/MemoryStorageAdapter.d.ts +15 -0
  290. package/dist/storage/MemoryStorageAdapter.d.ts.map +1 -0
  291. package/dist/storage/MemoryStorageAdapter.js +34 -0
  292. package/dist/storage/MemoryStorageAdapter.js.map +1 -0
  293. package/dist/storage/Storage.d.ts +23 -0
  294. package/dist/storage/Storage.d.ts.map +1 -0
  295. package/dist/storage/Storage.js +9 -0
  296. package/dist/storage/Storage.js.map +1 -0
  297. package/dist/storage/index.d.ts +6 -91
  298. package/dist/storage/index.d.ts.map +1 -0
  299. package/dist/storage/index.js +3 -10
  300. package/dist/storage/index.js.map +1 -1
  301. package/dist/story/index.d.ts +3 -2
  302. package/dist/themeLibrary-DR1VRo-8.d.ts +44 -0
  303. package/dist/timing/index.d.ts +4 -0
  304. package/dist/timing/index.d.ts.map +1 -0
  305. package/dist/timing/index.js +3 -0
  306. package/dist/timing/index.js.map +1 -0
  307. package/dist/timing/narrationTiming.d.ts +54 -0
  308. package/dist/timing/narrationTiming.d.ts.map +1 -0
  309. package/dist/timing/narrationTiming.js +190 -0
  310. package/dist/timing/narrationTiming.js.map +1 -0
  311. package/dist/timing/readingTime.d.ts +54 -0
  312. package/dist/timing/readingTime.d.ts.map +1 -0
  313. package/dist/timing/readingTime.js +49 -0
  314. package/dist/timing/readingTime.js.map +1 -0
  315. package/package.json +19 -5
  316. package/src/__tests__/contentExtractor.test.ts +183 -0
  317. package/src/__tests__/seededRandom.test.ts +142 -0
  318. package/src/__tests__/slideshowGenerator.test.ts +128 -0
  319. package/src/__tests__/timing.test.ts +167 -0
  320. package/src/generate/contentExtractor.ts +808 -0
  321. package/src/generate/index.ts +21 -0
  322. package/src/generate/slideshowGenerator.ts +273 -0
  323. package/src/generate/templateMapper.ts +170 -0
  324. package/src/index.ts +3 -0
  325. package/src/random/SeededRandom.ts +127 -0
  326. package/src/random/index.ts +1 -0
  327. package/src/timing/index.ts +17 -0
  328. package/src/timing/narrationTiming.ts +220 -0
  329. package/src/timing/readingTime.ts +94 -0
@@ -0,0 +1,21 @@
1
+ export { extractContent, stripMarkdown } from './contentExtractor.js';
2
+ export type {
3
+ ExtractionType,
4
+ ExtractedElement,
5
+ StatData,
6
+ DateData,
7
+ QuoteData,
8
+ ComparisonData,
9
+ FactData,
10
+ ImpactLineData,
11
+ ListData,
12
+ DefinitionData,
13
+ ExtractionOptions,
14
+ ExtractionResult,
15
+ } from './contentExtractor.js';
16
+
17
+ export { mapElementToBlock } from './templateMapper.js';
18
+ export type { MapOptions } from './templateMapper.js';
19
+
20
+ export { generateSlideshow } from './slideshowGenerator.js';
21
+ export type { SlideshowDoc, SlideshowImage, SlideshowOptions } from './slideshowGenerator.js';
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Slideshow Generator
3
+ *
4
+ * Turns plain text + optional images into a complete squisq `Doc`.
5
+ *
6
+ * Pipeline:
7
+ * 1. Strip markdown (if needed)
8
+ * 2. Extract compelling content (stats, dates, quotes, …)
9
+ * 3. Estimate narration timing
10
+ * 4. Map extractions → template blocks
11
+ * 5. Interleave image slides
12
+ * 6. Return a ready-to-render `Doc`
13
+ */
14
+
15
+ import type { Doc } from '../schemas/Doc.js';
16
+ import type {
17
+ TemplateBlock,
18
+ ImageWithCaptionInput,
19
+ TitleBlockInput,
20
+ ColorScheme,
21
+ AccentImage,
22
+ AccentPosition,
23
+ } from '../schemas/BlockTemplates.js';
24
+ import { extractContent, stripMarkdown } from './contentExtractor.js';
25
+ import { mapElementToBlock } from './templateMapper.js';
26
+ import { estimateNarrationDuration } from '../timing/narrationTiming.js';
27
+ import { SeededRandom, hashString } from '../random/SeededRandom.js';
28
+
29
+ // ── Public types ───────────────────────────────────────────────────
30
+
31
+ /** A Doc variant whose blocks are TemplateBlocks (no startTime required). */
32
+ export type SlideshowDoc = Omit<Doc, 'blocks'> & {
33
+ blocks: TemplateBlock[];
34
+ };
35
+
36
+ /** Image metadata passed to the generator. */
37
+ export interface SlideshowImage {
38
+ /** Image source path or URL. */
39
+ src: string;
40
+ /** Alt text for accessibility. */
41
+ alt?: string;
42
+ /** Photo credit. */
43
+ credit?: string;
44
+ /** License identifier. */
45
+ license?: string;
46
+ }
47
+
48
+ /** Options for `generateSlideshow`. */
49
+ export interface SlideshowOptions {
50
+ /** Deterministic seed (default: hash of the text). */
51
+ seed?: number;
52
+ /** Title slide text. Omit to skip the title slide. */
53
+ title?: string;
54
+ /** Subtitle for the title slide. */
55
+ subtitle?: string;
56
+ /** Explicit total duration in seconds. When omitted, estimated from text. */
57
+ duration?: number;
58
+ /** Target number of content slides (default: 5–8, auto-scaled). */
59
+ targetSlides?: number;
60
+ /** Color schemes to rotate (default: ['blue','green','purple','orange','red']). */
61
+ colorSchemes?: ColorScheme[];
62
+ /** Ambient motions to rotate on images. */
63
+ ambientMotions?: Array<'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight'>;
64
+ /** Accent positions to rotate on text slides. */
65
+ accentPositions?: AccentPosition[];
66
+ /** Minimum slide duration in seconds (default: 4). */
67
+ minSlideDuration?: number;
68
+ /** Maximum slide duration in seconds (default: 15). */
69
+ maxSlideDuration?: number;
70
+ /** Theme identifier to embed in the Doc. */
71
+ themeId?: string;
72
+ /** Whether the input text is markdown (default: false). */
73
+ isMarkdown?: boolean;
74
+ /** Minimum extraction confidence (default: 0.3). */
75
+ minConfidence?: number;
76
+ }
77
+
78
+ const DEFAULT_COLOR_SCHEMES: ColorScheme[] = ['blue', 'green', 'purple', 'orange', 'red'];
79
+ const DEFAULT_AMBIENT_MOTIONS: Array<'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight'> = [
80
+ 'zoomIn',
81
+ 'zoomOut',
82
+ 'panLeft',
83
+ 'panRight',
84
+ ];
85
+ const DEFAULT_ACCENT_POSITIONS: AccentPosition[] = ['left-strip', 'right-strip', 'bottom-strip'];
86
+
87
+ // ── Main ───────────────────────────────────────────────────────────
88
+
89
+ /**
90
+ * Generate a slideshow `Doc` from text and optional images.
91
+ *
92
+ * ```ts
93
+ * const doc = generateSlideshow(articleText, myImages, {
94
+ * title: 'Mount Rainier',
95
+ * themeId: 'documentary',
96
+ * });
97
+ * // doc is a complete Doc ready for DocPlayer / LinearDocView
98
+ * ```
99
+ */
100
+ export function generateSlideshow(
101
+ text: string,
102
+ images: SlideshowImage[] = [],
103
+ options: SlideshowOptions = {},
104
+ ): SlideshowDoc {
105
+ const {
106
+ seed,
107
+ title,
108
+ subtitle,
109
+ duration: explicitDuration,
110
+ targetSlides,
111
+ colorSchemes = DEFAULT_COLOR_SCHEMES,
112
+ ambientMotions = DEFAULT_AMBIENT_MOTIONS,
113
+ accentPositions = DEFAULT_ACCENT_POSITIONS,
114
+ minSlideDuration = 4,
115
+ maxSlideDuration = 15,
116
+ themeId,
117
+ isMarkdown = false,
118
+ minConfidence = 0.3,
119
+ } = options;
120
+
121
+ const plainText = isMarkdown ? stripMarkdown(text) : text;
122
+ const rng = new SeededRandom(seed ?? hashString(plainText));
123
+
124
+ // 1. Estimate total duration
125
+ const totalDuration = explicitDuration ?? estimateNarrationDuration(plainText);
126
+
127
+ // 2. Extract compelling content
128
+ const extraction = extractContent(plainText, { minConfidence });
129
+ const allElements = extraction.elements;
130
+
131
+ // 3. Decide slide budget
132
+ const slideBudget = targetSlides ?? computeSlideBudget(totalDuration, allElements.length);
133
+
134
+ // Allocate slots between content and images
135
+ const imageSlotCount = images.length > 0 ? Math.max(1, Math.round(slideBudget * 0.4)) : 0;
136
+ const contentSlotCount = Math.max(0, slideBudget - imageSlotCount - (title ? 1 : 0));
137
+
138
+ // Select top elements by confidence, then restore source order
139
+ const sorted = [...allElements].sort((a, b) => b.confidence - a.confidence);
140
+ const selected = sorted.slice(0, contentSlotCount);
141
+ selected.sort((a, b) => a.sourcePosition - b.sourcePosition);
142
+
143
+ // 4. Build slides
144
+ const blocks: TemplateBlock[] = [];
145
+ let slideIndex = 0;
146
+ let colorIdx = 0;
147
+ let accentIdx = 0;
148
+
149
+ // Determine per-slide duration
150
+ const totalSlideCount =
151
+ (title ? 1 : 0) + selected.length + Math.min(imageSlotCount, images.length);
152
+ const perSlide =
153
+ totalSlideCount > 0
154
+ ? Math.min(maxSlideDuration, Math.max(minSlideDuration, totalDuration / totalSlideCount))
155
+ : minSlideDuration;
156
+
157
+ // Title slide
158
+ if (title) {
159
+ const titleBlock: TitleBlockInput = {
160
+ id: `slide-${slideIndex++}`,
161
+ template: 'titleBlock',
162
+ title,
163
+ subtitle,
164
+ duration: Math.min(perSlide, 6),
165
+ audioSegment: 0,
166
+ };
167
+ blocks.push(titleBlock);
168
+ }
169
+
170
+ // Prepare image pool for standalone + accent images
171
+ const standaloneImages = images.slice(0, Math.min(imageSlotCount, images.length));
172
+ const accentPool =
173
+ images.length > standaloneImages.length ? images.slice(standaloneImages.length) : [...images]; // reuse if not enough
174
+
175
+ // Interleave pattern: insert an image slide every N content slides
176
+ const imageInterval =
177
+ standaloneImages.length > 0
178
+ ? Math.max(1, Math.floor(selected.length / (standaloneImages.length + 1)))
179
+ : Infinity;
180
+ let imgIdx = 0;
181
+ let sinceImage = 0;
182
+
183
+ for (let i = 0; i < selected.length; i++) {
184
+ // Maybe insert a standalone image slide before this content slide
185
+ if (sinceImage >= imageInterval && imgIdx < standaloneImages.length) {
186
+ blocks.push(
187
+ createImageSlide(standaloneImages[imgIdx], slideIndex++, perSlide, rng, ambientMotions),
188
+ );
189
+ imgIdx++;
190
+ sinceImage = 0;
191
+ }
192
+
193
+ const element = selected[i];
194
+
195
+ // Optional accent image
196
+ let accentImage: AccentImage | undefined;
197
+ if (accentPool.length > 0) {
198
+ const img = accentPool[i % accentPool.length];
199
+ accentImage = {
200
+ src: img.src,
201
+ alt: img.alt ?? '',
202
+ position: accentPositions[accentIdx % accentPositions.length],
203
+ ambientMotion: rng.pickRequired(ambientMotions),
204
+ credit: img.credit,
205
+ license: img.license,
206
+ };
207
+ accentIdx++;
208
+ }
209
+
210
+ const block = mapElementToBlock(element, {
211
+ id: `slide-${slideIndex++}`,
212
+ duration: perSlide,
213
+ audioSegment: 0,
214
+ colorScheme: colorSchemes[colorIdx % colorSchemes.length],
215
+ accentImage,
216
+ });
217
+ blocks.push(block);
218
+ colorIdx++;
219
+ sinceImage++;
220
+ }
221
+
222
+ // Append any remaining standalone images
223
+ while (imgIdx < standaloneImages.length) {
224
+ blocks.push(
225
+ createImageSlide(standaloneImages[imgIdx], slideIndex++, perSlide, rng, ambientMotions),
226
+ );
227
+ imgIdx++;
228
+ }
229
+
230
+ // 5. Assemble Doc
231
+ const doc: SlideshowDoc = {
232
+ articleId: title ?? 'slideshow',
233
+ duration: totalDuration,
234
+ blocks,
235
+ audio: {
236
+ segments: [{ src: '', name: 'main', duration: totalDuration, startTime: 0 }],
237
+ },
238
+ themeId,
239
+ };
240
+
241
+ return doc;
242
+ }
243
+
244
+ // ── Helpers ────────────────────────────────────────────────────────
245
+
246
+ function computeSlideBudget(duration: number, elementCount: number): number {
247
+ // Aim for ~5 slides per minute of content, clamped 3–12
248
+ const byDuration = Math.round((duration / 60) * 5);
249
+ // But don't exceed available elements + a few image slots
250
+ const budget = Math.min(byDuration, elementCount + 4);
251
+ return Math.max(3, Math.min(12, budget));
252
+ }
253
+
254
+ function createImageSlide(
255
+ img: SlideshowImage,
256
+ index: number,
257
+ duration: number,
258
+ rng: SeededRandom,
259
+ motions: Array<'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight'>,
260
+ ): ImageWithCaptionInput {
261
+ return {
262
+ id: `slide-${index}`,
263
+ template: 'imageWithCaption',
264
+ imageSrc: img.src,
265
+ imageAlt: img.alt ?? '',
266
+ caption: img.alt,
267
+ duration,
268
+ audioSegment: 0,
269
+ ambientMotion: rng.pickRequired(motions),
270
+ imageCredit: img.credit,
271
+ imageLicense: img.license,
272
+ };
273
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Template Mapper
3
+ *
4
+ * Converts extracted content elements into squisq TemplateBlock inputs.
5
+ * Each ExtractionType maps to a specific template from BlockTemplates.
6
+ */
7
+
8
+ import type {
9
+ ExtractedElement,
10
+ StatData,
11
+ DateData,
12
+ QuoteData,
13
+ ComparisonData,
14
+ FactData,
15
+ ImpactLineData,
16
+ ListData,
17
+ DefinitionData,
18
+ } from './contentExtractor.js';
19
+
20
+ import type {
21
+ StatHighlightInput,
22
+ QuoteBlockInput,
23
+ FactCardInput,
24
+ TwoColumnInput,
25
+ DateEventInput,
26
+ FullBleedQuoteInput,
27
+ ListBlockInput,
28
+ DefinitionCardInput,
29
+ TemplateBlock,
30
+ ColorScheme,
31
+ AccentImage,
32
+ } from '../schemas/BlockTemplates.js';
33
+
34
+ /** Options for mapping an element to a template block. */
35
+ export interface MapOptions {
36
+ id: string;
37
+ duration: number;
38
+ audioSegment: number;
39
+ colorScheme?: ColorScheme;
40
+ accentImage?: AccentImage;
41
+ sourceStartTime?: number;
42
+ sourceDuration?: number;
43
+ }
44
+
45
+ /**
46
+ * Convert an extracted element into the matching squisq TemplateBlock.
47
+ */
48
+ export function mapElementToBlock(element: ExtractedElement, options: MapOptions): TemplateBlock {
49
+ const base = {
50
+ id: options.id,
51
+ duration: options.duration,
52
+ audioSegment: options.audioSegment,
53
+ sourceStartTime: options.sourceStartTime,
54
+ sourceDuration: options.sourceDuration,
55
+ };
56
+
57
+ switch (element.data.type) {
58
+ case 'stat':
59
+ return mapStat(element.data, base, options);
60
+ case 'date':
61
+ return mapDate(element.data, base, options);
62
+ case 'quote':
63
+ return mapQuote(element.data, base, options);
64
+ case 'comparison':
65
+ return mapComparison(element.data, base, options);
66
+ case 'fact':
67
+ return mapFact(element.data, base, options);
68
+ case 'impactLine':
69
+ return mapImpactLine(element.data, base, options);
70
+ case 'list':
71
+ return mapList(element.data, base, options);
72
+ case 'definition':
73
+ return mapDefinition(element.data, base, options);
74
+ }
75
+ }
76
+
77
+ // ── Per-type mappers ───────────────────────────────────────────────
78
+
79
+ type Base = {
80
+ id: string;
81
+ duration: number;
82
+ audioSegment: number;
83
+ sourceStartTime?: number;
84
+ sourceDuration?: number;
85
+ };
86
+
87
+ function mapStat(data: StatData, base: Base, opts: MapOptions): StatHighlightInput {
88
+ return {
89
+ ...base,
90
+ template: 'statHighlight',
91
+ stat: data.value,
92
+ description: data.description,
93
+ colorScheme: opts.colorScheme,
94
+ accentImage: opts.accentImage,
95
+ };
96
+ }
97
+
98
+ function mapDate(data: DateData, base: Base, opts: MapOptions): DateEventInput {
99
+ return {
100
+ ...base,
101
+ template: 'dateEvent',
102
+ date: data.date,
103
+ description: data.description,
104
+ mood: data.mood,
105
+ accentImage: opts.accentImage,
106
+ };
107
+ }
108
+
109
+ function mapQuote(data: QuoteData, base: Base, opts: MapOptions): QuoteBlockInput {
110
+ return {
111
+ ...base,
112
+ template: 'quoteBlock',
113
+ quote: data.quote,
114
+ attribution: data.attribution,
115
+ accentImage: opts.accentImage,
116
+ };
117
+ }
118
+
119
+ function mapComparison(data: ComparisonData, base: Base, opts: MapOptions): TwoColumnInput {
120
+ return {
121
+ ...base,
122
+ template: 'twoColumn',
123
+ left: data.left,
124
+ right: data.right,
125
+ header: data.header,
126
+ leftColor: opts.colorScheme,
127
+ };
128
+ }
129
+
130
+ function mapFact(data: FactData, base: Base, opts: MapOptions): FactCardInput {
131
+ return {
132
+ ...base,
133
+ template: 'factCard',
134
+ fact: data.fact,
135
+ explanation: data.explanation ?? '',
136
+ accentImage: opts.accentImage,
137
+ };
138
+ }
139
+
140
+ function mapImpactLine(data: ImpactLineData, base: Base, opts: MapOptions): FullBleedQuoteInput {
141
+ return {
142
+ ...base,
143
+ template: 'fullBleedQuote',
144
+ text: data.text,
145
+ colorScheme: opts.colorScheme,
146
+ };
147
+ }
148
+
149
+ function mapList(data: ListData, base: Base, opts: MapOptions): ListBlockInput {
150
+ return {
151
+ ...base,
152
+ template: 'listBlock',
153
+ items: data.items,
154
+ title: data.title,
155
+ colorScheme: opts.colorScheme,
156
+ accentImage: opts.accentImage,
157
+ };
158
+ }
159
+
160
+ function mapDefinition(data: DefinitionData, base: Base, opts: MapOptions): DefinitionCardInput {
161
+ return {
162
+ ...base,
163
+ template: 'definitionCard',
164
+ term: data.term,
165
+ definition: data.definition,
166
+ origin: data.origin,
167
+ colorScheme: opts.colorScheme,
168
+ accentImage: opts.accentImage,
169
+ };
170
+ }
package/src/index.ts CHANGED
@@ -3,3 +3,6 @@ export * from './doc/index.js';
3
3
  export * from './spatial/index.js';
4
4
  export * from './storage/index.js';
5
5
  export * from './markdown/index.js';
6
+ export * from './timing/index.js';
7
+ export * from './random/index.js';
8
+ export * from './generate/index.js';
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Seeded pseudo-random number generator (Mulberry32 algorithm).
3
+ *
4
+ * Provides deterministic, reproducible randomness from an integer seed.
5
+ * Useful for consistent slideshow generation, shuffling, and selection.
6
+ *
7
+ * ```ts
8
+ * const rng = new SeededRandom(42);
9
+ * rng.next(); // 0.0–1.0
10
+ * rng.nextInt(10); // 0–9
11
+ * rng.pick(['a', 'b']); // 'a' or 'b'
12
+ * ```
13
+ */
14
+ export class SeededRandom {
15
+ private state: number;
16
+
17
+ constructor(seed: number) {
18
+ this.state = seed >>> 0;
19
+ if (this.state === 0) {
20
+ this.state = 0xdeadbeef;
21
+ }
22
+ }
23
+
24
+ /** Random float in [0, 1). */
25
+ next(): number {
26
+ let t = (this.state += 0x6d2b79f5);
27
+ t = Math.imul(t ^ (t >>> 15), t | 1);
28
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
29
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
30
+ }
31
+
32
+ /** Random integer in [0, max). */
33
+ nextInt(max: number): number {
34
+ return Math.floor(this.next() * max);
35
+ }
36
+
37
+ /** Random integer in [min, max). */
38
+ nextIntRange(min: number, max: number): number {
39
+ return min + Math.floor(this.next() * (max - min));
40
+ }
41
+
42
+ /** Random boolean with given probability of `true` (default 0.5). */
43
+ nextBool(probability: number = 0.5): boolean {
44
+ return this.next() < probability;
45
+ }
46
+
47
+ /** Pick a random element, or `undefined` if array is empty. */
48
+ pick<T>(array: T[]): T | undefined {
49
+ if (array.length === 0) return undefined;
50
+ return array[this.nextInt(array.length)];
51
+ }
52
+
53
+ /** Pick a random element; throws if empty. */
54
+ pickRequired<T>(array: T[]): T {
55
+ if (array.length === 0) {
56
+ throw new Error('Cannot pick from empty array');
57
+ }
58
+ return array[this.nextInt(array.length)];
59
+ }
60
+
61
+ /** Pick `count` unique random elements (fewer if array is smaller). */
62
+ pickMultiple<T>(array: T[], count: number): T[] {
63
+ if (count >= array.length) {
64
+ return this.shuffle([...array]);
65
+ }
66
+ const result: T[] = [];
67
+ const available = [...array];
68
+ for (let i = 0; i < count && available.length > 0; i++) {
69
+ const index = this.nextInt(available.length);
70
+ result.push(available[index]);
71
+ available.splice(index, 1);
72
+ }
73
+ return result;
74
+ }
75
+
76
+ /** Fisher-Yates shuffle **in place**. Returns the same array. */
77
+ shuffle<T>(array: T[]): T[] {
78
+ for (let i = array.length - 1; i > 0; i--) {
79
+ const j = this.nextInt(i + 1);
80
+ [array[i], array[j]] = [array[j], array[i]];
81
+ }
82
+ return array;
83
+ }
84
+
85
+ /** Return a **new** shuffled copy. */
86
+ shuffled<T>(array: readonly T[]): T[] {
87
+ return this.shuffle([...array]);
88
+ }
89
+
90
+ /** Weighted random selection. Returns `undefined` if items is empty. */
91
+ pickWeighted<T>(items: { item: T; weight: number }[]): T | undefined {
92
+ if (items.length === 0) return undefined;
93
+
94
+ const totalWeight = items.reduce((sum, { weight }) => sum + weight, 0);
95
+ if (totalWeight <= 0) return items[0]?.item;
96
+
97
+ let random = this.next() * totalWeight;
98
+ for (const { item, weight } of items) {
99
+ random -= weight;
100
+ if (random <= 0) return item;
101
+ }
102
+ return items[items.length - 1]?.item;
103
+ }
104
+
105
+ /** Current internal state (for debugging / serialization). */
106
+ getState(): number {
107
+ return this.state;
108
+ }
109
+
110
+ /** Create an independent sub-stream keyed by a modifier. */
111
+ derive(modifier: string | number): SeededRandom {
112
+ const hash = typeof modifier === 'string' ? hashString(modifier) : modifier >>> 0;
113
+ return new SeededRandom(this.state ^ hash);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Simple string → 32-bit hash (djb2 algorithm).
119
+ * Useful for turning a document ID into a numeric seed.
120
+ */
121
+ export function hashString(str: string): number {
122
+ let hash = 5381;
123
+ for (let i = 0; i < str.length; i++) {
124
+ hash = ((hash << 5) + hash) ^ str.charCodeAt(i);
125
+ }
126
+ return hash >>> 0;
127
+ }
@@ -0,0 +1 @@
1
+ export { SeededRandom, hashString } from './SeededRandom.js';
@@ -0,0 +1,17 @@
1
+ export {
2
+ DEFAULT_WORDS_PER_SECOND,
3
+ estimateSpokenWordCount,
4
+ countSpokenWords,
5
+ estimateTimeFromText,
6
+ calculatePrefixDuration,
7
+ estimateNarrationDuration,
8
+ } from './narrationTiming.js';
9
+
10
+ export { estimateReadingTime, estimateNarrationTime } from './readingTime.js';
11
+
12
+ export type {
13
+ ReadingTimeEstimate,
14
+ ReadingTimeOptions,
15
+ NarrationTimeEstimate,
16
+ NarrationTimeOptions,
17
+ } from './readingTime.js';