@bendyline/squisq 1.3.0 → 1.4.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 (419) hide show
  1. package/dist/{Doc-Ch4tc3vv.d.ts → Doc-BeSeH2iX.d.ts} +118 -41
  2. package/dist/ImageEditDoc-BTIvw0oq.d.ts +84 -0
  3. package/dist/__tests__/colorUtils.test.d.ts +2 -0
  4. package/dist/__tests__/colorUtils.test.d.ts.map +1 -0
  5. package/dist/__tests__/colorUtils.test.js +94 -0
  6. package/dist/__tests__/colorUtils.test.js.map +1 -0
  7. package/dist/__tests__/getLayers.test.js +31 -7
  8. package/dist/__tests__/getLayers.test.js.map +1 -1
  9. package/dist/__tests__/iconResolve.test.d.ts +2 -0
  10. package/dist/__tests__/iconResolve.test.d.ts.map +1 -0
  11. package/dist/__tests__/iconResolve.test.js +99 -0
  12. package/dist/__tests__/iconResolve.test.js.map +1 -0
  13. package/dist/__tests__/imageEditState.test.d.ts +2 -0
  14. package/dist/__tests__/imageEditState.test.d.ts.map +1 -0
  15. package/dist/__tests__/imageEditState.test.js +61 -0
  16. package/dist/__tests__/imageEditState.test.js.map +1 -0
  17. package/dist/__tests__/imageEditVersions.test.d.ts +2 -0
  18. package/dist/__tests__/imageEditVersions.test.d.ts.map +1 -0
  19. package/dist/__tests__/imageEditVersions.test.js +141 -0
  20. package/dist/__tests__/imageEditVersions.test.js.map +1 -0
  21. package/dist/__tests__/inferDocumentTitle.test.d.ts +2 -0
  22. package/dist/__tests__/inferDocumentTitle.test.d.ts.map +1 -0
  23. package/dist/__tests__/inferDocumentTitle.test.js +42 -0
  24. package/dist/__tests__/inferDocumentTitle.test.js.map +1 -0
  25. package/dist/__tests__/jsonForm.chooseControl.test.d.ts +2 -0
  26. package/dist/__tests__/jsonForm.chooseControl.test.d.ts.map +1 -0
  27. package/dist/__tests__/jsonForm.chooseControl.test.js +74 -0
  28. package/dist/__tests__/jsonForm.chooseControl.test.js.map +1 -0
  29. package/dist/__tests__/jsonForm.evaluateWhen.test.d.ts +2 -0
  30. package/dist/__tests__/jsonForm.evaluateWhen.test.d.ts.map +1 -0
  31. package/dist/__tests__/jsonForm.evaluateWhen.test.js +51 -0
  32. package/dist/__tests__/jsonForm.evaluateWhen.test.js.map +1 -0
  33. package/dist/__tests__/jsonForm.inferSchema.test.d.ts +2 -0
  34. package/dist/__tests__/jsonForm.inferSchema.test.d.ts.map +1 -0
  35. package/dist/__tests__/jsonForm.inferSchema.test.js +23 -0
  36. package/dist/__tests__/jsonForm.inferSchema.test.js.map +1 -0
  37. package/dist/__tests__/jsonForm.pointer.test.d.ts +2 -0
  38. package/dist/__tests__/jsonForm.pointer.test.d.ts.map +1 -0
  39. package/dist/__tests__/jsonForm.pointer.test.js +94 -0
  40. package/dist/__tests__/jsonForm.pointer.test.js.map +1 -0
  41. package/dist/__tests__/markdownIcons.test.d.ts +2 -0
  42. package/dist/__tests__/markdownIcons.test.d.ts.map +1 -0
  43. package/dist/__tests__/markdownIcons.test.js +93 -0
  44. package/dist/__tests__/markdownIcons.test.js.map +1 -0
  45. package/dist/__tests__/markdownToDoc.test.js +15 -4
  46. package/dist/__tests__/markdownToDoc.test.js.map +1 -1
  47. package/dist/__tests__/recommendTemplates.test.d.ts +2 -0
  48. package/dist/__tests__/recommendTemplates.test.d.ts.map +1 -0
  49. package/dist/__tests__/recommendTemplates.test.js +158 -0
  50. package/dist/__tests__/recommendTemplates.test.js.map +1 -0
  51. package/dist/__tests__/scopedContentContainer.test.d.ts +2 -0
  52. package/dist/__tests__/scopedContentContainer.test.d.ts.map +1 -0
  53. package/dist/__tests__/scopedContentContainer.test.js +74 -0
  54. package/dist/__tests__/scopedContentContainer.test.js.map +1 -0
  55. package/dist/__tests__/slideshowGenerator.test.js +1 -1
  56. package/dist/__tests__/slideshowGenerator.test.js.map +1 -1
  57. package/dist/__tests__/templateAnnotationParse.test.d.ts +2 -0
  58. package/dist/__tests__/templateAnnotationParse.test.d.ts.map +1 -0
  59. package/dist/__tests__/templateAnnotationParse.test.js +42 -0
  60. package/dist/__tests__/templateAnnotationParse.test.js.map +1 -0
  61. package/dist/__tests__/templates.test.js +90 -9
  62. package/dist/__tests__/templates.test.js.map +1 -1
  63. package/dist/__tests__/themeCompile.test.d.ts +2 -0
  64. package/dist/__tests__/themeCompile.test.d.ts.map +1 -0
  65. package/dist/__tests__/themeCompile.test.js +134 -0
  66. package/dist/__tests__/themeCompile.test.js.map +1 -0
  67. package/dist/__tests__/themeValidator.test.d.ts +2 -0
  68. package/dist/__tests__/themeValidator.test.d.ts.map +1 -0
  69. package/dist/__tests__/themeValidator.test.js +107 -0
  70. package/dist/__tests__/themeValidator.test.js.map +1 -0
  71. package/dist/__tests__/versions.test.d.ts +2 -0
  72. package/dist/__tests__/versions.test.d.ts.map +1 -0
  73. package/dist/__tests__/versions.test.js +339 -0
  74. package/dist/__tests__/versions.test.js.map +1 -0
  75. package/dist/{chunk-CYQHG27J.js → chunk-2TMAWB4Y.js} +74 -1
  76. package/dist/chunk-2TMAWB4Y.js.map +1 -0
  77. package/dist/{chunk-OASJKWO5.js → chunk-3K5OG6KI.js} +92 -7
  78. package/dist/chunk-3K5OG6KI.js.map +1 -0
  79. package/dist/chunk-57CVQCFX.js +193 -0
  80. package/dist/chunk-57CVQCFX.js.map +1 -0
  81. package/dist/{chunk-QM5PKNPW.js → chunk-5WFRKURX.js} +1 -1
  82. package/dist/chunk-5WFRKURX.js.map +1 -0
  83. package/dist/chunk-B4EEON3N.js +1317 -0
  84. package/dist/chunk-B4EEON3N.js.map +1 -0
  85. package/dist/chunk-DBS6RIFL.js +81 -0
  86. package/dist/chunk-DBS6RIFL.js.map +1 -0
  87. package/dist/{chunk-MIBRIRGJ.js → chunk-FR2RBTKO.js} +3 -3
  88. package/dist/{chunk-MIBRIRGJ.js.map → chunk-FR2RBTKO.js.map} +1 -1
  89. package/dist/chunk-H3AXU4MJ.js +179 -0
  90. package/dist/chunk-H3AXU4MJ.js.map +1 -0
  91. package/dist/chunk-JELIRVDP.js +24 -0
  92. package/dist/chunk-JELIRVDP.js.map +1 -0
  93. package/dist/chunk-K32VJONL.js +193 -0
  94. package/dist/chunk-K32VJONL.js.map +1 -0
  95. package/dist/{chunk-6BAVXFYC.js → chunk-KSWJR66U.js} +218 -42
  96. package/dist/chunk-KSWJR66U.js.map +1 -0
  97. package/dist/{chunk-TSG2OSE6.js → chunk-MYIH7FWD.js} +3 -3
  98. package/dist/chunk-MYIH7FWD.js.map +1 -0
  99. package/dist/chunk-VLJJHDAC.js +218 -0
  100. package/dist/chunk-VLJJHDAC.js.map +1 -0
  101. package/dist/chunk-WMOI7FN3.js +182 -0
  102. package/dist/chunk-WMOI7FN3.js.map +1 -0
  103. package/dist/chunk-ZTX4PFFT.js +520 -0
  104. package/dist/chunk-ZTX4PFFT.js.map +1 -0
  105. package/dist/doc/audioMapping.d.ts.map +1 -1
  106. package/dist/doc/audioMapping.js +42 -7
  107. package/dist/doc/audioMapping.js.map +1 -1
  108. package/dist/doc/getLayers.d.ts.map +1 -1
  109. package/dist/doc/getLayers.js +29 -18
  110. package/dist/doc/getLayers.js.map +1 -1
  111. package/dist/doc/markdownToDoc.d.ts.map +1 -1
  112. package/dist/doc/markdownToDoc.js +40 -6
  113. package/dist/doc/markdownToDoc.js.map +1 -1
  114. package/dist/doc/templates/featureBlock.d.ts +18 -0
  115. package/dist/doc/templates/featureBlock.d.ts.map +1 -0
  116. package/dist/doc/templates/featureBlock.js +180 -0
  117. package/dist/doc/templates/featureBlock.js.map +1 -0
  118. package/dist/doc/templates/fullBleedQuote.d.ts +12 -0
  119. package/dist/doc/templates/fullBleedQuote.d.ts.map +1 -1
  120. package/dist/doc/templates/fullBleedQuote.js +12 -0
  121. package/dist/doc/templates/fullBleedQuote.js.map +1 -1
  122. package/dist/doc/templates/index.d.ts +28 -4
  123. package/dist/doc/templates/index.d.ts.map +1 -1
  124. package/dist/doc/templates/index.js +45 -12
  125. package/dist/doc/templates/index.js.map +1 -1
  126. package/dist/doc/templates/listBlock.d.ts.map +1 -1
  127. package/dist/doc/templates/listBlock.js +22 -5
  128. package/dist/doc/templates/listBlock.js.map +1 -1
  129. package/dist/doc/templates/statHighlight.d.ts +12 -0
  130. package/dist/doc/templates/statHighlight.d.ts.map +1 -1
  131. package/dist/doc/templates/statHighlight.js +12 -0
  132. package/dist/doc/templates/statHighlight.js.map +1 -1
  133. package/dist/doc/templates/titleBlock.d.ts +11 -0
  134. package/dist/doc/templates/titleBlock.d.ts.map +1 -1
  135. package/dist/doc/templates/titleBlock.js +8 -0
  136. package/dist/doc/templates/titleBlock.js.map +1 -1
  137. package/dist/doc/utils/themeUtils.d.ts +4 -4
  138. package/dist/doc/utils/themeUtils.d.ts.map +1 -1
  139. package/dist/doc/utils/themeUtils.js +8 -7
  140. package/dist/doc/utils/themeUtils.js.map +1 -1
  141. package/dist/generate/slideshowGenerator.js +1 -1
  142. package/dist/generate/slideshowGenerator.js.map +1 -1
  143. package/dist/generate/templateMapper.js +2 -2
  144. package/dist/generate/templateMapper.js.map +1 -1
  145. package/dist/icons/iconData.d.ts +26 -0
  146. package/dist/icons/iconData.d.ts.map +1 -0
  147. package/dist/icons/iconData.js +12 -0
  148. package/dist/icons/iconData.js.map +1 -0
  149. package/dist/icons/index.d.ts +12 -0
  150. package/dist/icons/index.d.ts.map +1 -0
  151. package/dist/icons/index.js +10 -0
  152. package/dist/icons/index.js.map +1 -0
  153. package/dist/icons/resolve.d.ts +61 -0
  154. package/dist/icons/resolve.d.ts.map +1 -0
  155. package/dist/icons/resolve.js +130 -0
  156. package/dist/icons/resolve.js.map +1 -0
  157. package/dist/imageEdit/ImageEditVersionManager.d.ts +31 -0
  158. package/dist/imageEdit/ImageEditVersionManager.d.ts.map +1 -0
  159. package/dist/imageEdit/ImageEditVersionManager.js +39 -0
  160. package/dist/imageEdit/ImageEditVersionManager.js.map +1 -0
  161. package/dist/imageEdit/export.d.ts +33 -0
  162. package/dist/imageEdit/export.d.ts.map +1 -0
  163. package/dist/imageEdit/export.js +253 -0
  164. package/dist/imageEdit/export.js.map +1 -0
  165. package/dist/imageEdit/index.d.ts +14 -0
  166. package/dist/imageEdit/index.d.ts.map +1 -0
  167. package/dist/imageEdit/index.js +22 -0
  168. package/dist/imageEdit/index.js.map +1 -0
  169. package/dist/imageEdit/persistence.d.ts +15 -0
  170. package/dist/imageEdit/persistence.d.ts.map +1 -0
  171. package/dist/imageEdit/persistence.js +49 -0
  172. package/dist/imageEdit/persistence.js.map +1 -0
  173. package/dist/imageEdit/state.d.ts +42 -0
  174. package/dist/imageEdit/state.d.ts.map +1 -0
  175. package/dist/imageEdit/state.js +98 -0
  176. package/dist/imageEdit/state.js.map +1 -0
  177. package/dist/imageEdit/versionPaths.d.ts +27 -0
  178. package/dist/imageEdit/versionPaths.d.ts.map +1 -0
  179. package/dist/imageEdit/versionPaths.js +57 -0
  180. package/dist/imageEdit/versionPaths.js.map +1 -0
  181. package/dist/imageEdit/versions.d.ts +67 -0
  182. package/dist/imageEdit/versions.d.ts.map +1 -0
  183. package/dist/imageEdit/versions.js +161 -0
  184. package/dist/imageEdit/versions.js.map +1 -0
  185. package/dist/index.d.ts +4 -0
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +4 -0
  188. package/dist/index.js.map +1 -1
  189. package/dist/jsonForm/chooseControl.d.ts +20 -0
  190. package/dist/jsonForm/chooseControl.d.ts.map +1 -0
  191. package/dist/jsonForm/chooseControl.js +82 -0
  192. package/dist/jsonForm/chooseControl.js.map +1 -0
  193. package/dist/jsonForm/evaluateWhen.d.ts +12 -0
  194. package/dist/jsonForm/evaluateWhen.d.ts.map +1 -0
  195. package/dist/jsonForm/evaluateWhen.js +65 -0
  196. package/dist/jsonForm/evaluateWhen.js.map +1 -0
  197. package/dist/jsonForm/index.d.ts +14 -0
  198. package/dist/jsonForm/index.d.ts.map +1 -0
  199. package/dist/jsonForm/index.js +12 -0
  200. package/dist/jsonForm/index.js.map +1 -0
  201. package/dist/jsonForm/inferSchema.d.ts +12 -0
  202. package/dist/jsonForm/inferSchema.d.ts.map +1 -0
  203. package/dist/jsonForm/inferSchema.js +15 -0
  204. package/dist/jsonForm/inferSchema.js.map +1 -0
  205. package/dist/jsonForm/pointer.d.ts +26 -0
  206. package/dist/jsonForm/pointer.d.ts.map +1 -0
  207. package/dist/jsonForm/pointer.js +102 -0
  208. package/dist/jsonForm/pointer.js.map +1 -0
  209. package/dist/jsonForm/types.d.ts +120 -0
  210. package/dist/jsonForm/types.d.ts.map +1 -0
  211. package/dist/jsonForm/types.js +10 -0
  212. package/dist/jsonForm/types.js.map +1 -0
  213. package/dist/markdown/convert.d.ts +11 -1
  214. package/dist/markdown/convert.d.ts.map +1 -1
  215. package/dist/markdown/convert.js +135 -5
  216. package/dist/markdown/convert.js.map +1 -1
  217. package/dist/markdown/index.d.ts +1 -1
  218. package/dist/markdown/index.d.ts.map +1 -1
  219. package/dist/markdown/index.js +1 -1
  220. package/dist/markdown/index.js.map +1 -1
  221. package/dist/markdown/types.d.ts +17 -2
  222. package/dist/markdown/types.d.ts.map +1 -1
  223. package/dist/markdown/utils.d.ts +40 -0
  224. package/dist/markdown/utils.d.ts.map +1 -1
  225. package/dist/markdown/utils.js +155 -0
  226. package/dist/markdown/utils.js.map +1 -1
  227. package/dist/recommend/index.d.ts +16 -0
  228. package/dist/recommend/index.d.ts.map +1 -0
  229. package/dist/recommend/index.js +15 -0
  230. package/dist/recommend/index.js.map +1 -0
  231. package/dist/recommend/templates.d.ts +39 -0
  232. package/dist/recommend/templates.d.ts.map +1 -0
  233. package/dist/recommend/templates.js +235 -0
  234. package/dist/recommend/templates.js.map +1 -0
  235. package/dist/schemas/BlockTemplates.d.ts +46 -8
  236. package/dist/schemas/BlockTemplates.d.ts.map +1 -1
  237. package/dist/schemas/BlockTemplates.js +3 -3
  238. package/dist/schemas/BlockTemplates.js.map +1 -1
  239. package/dist/schemas/ImageEditDoc.d.ts +81 -0
  240. package/dist/schemas/ImageEditDoc.d.ts.map +1 -0
  241. package/dist/schemas/ImageEditDoc.js +20 -0
  242. package/dist/schemas/ImageEditDoc.js.map +1 -0
  243. package/dist/schemas/Theme.d.ts +70 -31
  244. package/dist/schemas/Theme.d.ts.map +1 -1
  245. package/dist/schemas/Theme.js +48 -16
  246. package/dist/schemas/Theme.js.map +1 -1
  247. package/dist/schemas/colorUtils.d.ts +51 -0
  248. package/dist/schemas/colorUtils.d.ts.map +1 -0
  249. package/dist/schemas/colorUtils.js +154 -0
  250. package/dist/schemas/colorUtils.js.map +1 -0
  251. package/dist/schemas/fontStacks.d.ts +57 -0
  252. package/dist/schemas/fontStacks.d.ts.map +1 -0
  253. package/dist/schemas/fontStacks.js +227 -0
  254. package/dist/schemas/fontStacks.js.map +1 -0
  255. package/dist/schemas/index.d.ts +5 -0
  256. package/dist/schemas/index.d.ts.map +1 -1
  257. package/dist/schemas/index.js +4 -0
  258. package/dist/schemas/index.js.map +1 -1
  259. package/dist/schemas/themeCompile.d.ts +45 -0
  260. package/dist/schemas/themeCompile.d.ts.map +1 -0
  261. package/dist/schemas/themeCompile.js +160 -0
  262. package/dist/schemas/themeCompile.js.map +1 -0
  263. package/dist/schemas/themeLibrary.d.ts +11 -15
  264. package/dist/schemas/themeLibrary.d.ts.map +1 -1
  265. package/dist/schemas/themeLibrary.js +18 -566
  266. package/dist/schemas/themeLibrary.js.map +1 -1
  267. package/dist/schemas/themeValidator.d.ts +26 -0
  268. package/dist/schemas/themeValidator.d.ts.map +1 -0
  269. package/dist/schemas/themeValidator.js +352 -0
  270. package/dist/schemas/themeValidator.js.map +1 -0
  271. package/dist/schemas/themes/bold.json +48 -0
  272. package/dist/schemas/themes/cinematic.json +54 -0
  273. package/dist/schemas/themes/documentary.json +49 -0
  274. package/dist/schemas/themes/gezellig.json +44 -0
  275. package/dist/schemas/themes/index.d.ts +26 -0
  276. package/dist/schemas/themes/index.d.ts.map +1 -0
  277. package/dist/schemas/themes/index.js +49 -0
  278. package/dist/schemas/themes/index.js.map +1 -0
  279. package/dist/schemas/themes/magazine.json +44 -0
  280. package/dist/schemas/themes/minimalist.json +45 -0
  281. package/dist/schemas/themes/morning-light.json +42 -0
  282. package/dist/schemas/themes/standard-dark.json +50 -0
  283. package/dist/schemas/themes/standard.json +42 -0
  284. package/dist/schemas/themes/tech-dark.json +48 -0
  285. package/dist/schemas/themes/warm-earth.json +50 -0
  286. package/dist/storage/ScopedContentContainer.d.ts +44 -0
  287. package/dist/storage/ScopedContentContainer.d.ts.map +1 -0
  288. package/dist/storage/ScopedContentContainer.js +94 -0
  289. package/dist/storage/ScopedContentContainer.js.map +1 -0
  290. package/dist/storage/index.d.ts +1 -0
  291. package/dist/storage/index.d.ts.map +1 -1
  292. package/dist/storage/index.js +1 -0
  293. package/dist/storage/index.js.map +1 -1
  294. package/dist/story/index.d.ts +53 -13
  295. package/dist/story/index.js +12 -4
  296. package/dist/themeLibrary-BHjJ0Qx6.d.ts +40 -0
  297. package/dist/{types-CkNttVSF.d.ts → types-BdZkdJ3z.d.ts} +17 -2
  298. package/dist/types-D98li1gV.d.ts +93 -0
  299. package/dist/versions/DocumentVersionManager.d.ts +31 -0
  300. package/dist/versions/DocumentVersionManager.d.ts.map +1 -0
  301. package/dist/versions/DocumentVersionManager.js +33 -0
  302. package/dist/versions/DocumentVersionManager.js.map +1 -0
  303. package/dist/versions/index.d.ts +29 -0
  304. package/dist/versions/index.d.ts.map +1 -0
  305. package/dist/versions/index.js +28 -0
  306. package/dist/versions/index.js.map +1 -0
  307. package/dist/versions/operations.d.ts +44 -0
  308. package/dist/versions/operations.d.ts.map +1 -0
  309. package/dist/versions/operations.js +190 -0
  310. package/dist/versions/operations.js.map +1 -0
  311. package/dist/versions/paths.d.ts +29 -0
  312. package/dist/versions/paths.d.ts.map +1 -0
  313. package/dist/versions/paths.js +66 -0
  314. package/dist/versions/paths.js.map +1 -0
  315. package/dist/versions/timestamp.d.ts +22 -0
  316. package/dist/versions/timestamp.d.ts.map +1 -0
  317. package/dist/versions/timestamp.js +44 -0
  318. package/dist/versions/timestamp.js.map +1 -0
  319. package/dist/versions/types.d.ts +92 -0
  320. package/dist/versions/types.d.ts.map +1 -0
  321. package/dist/versions/types.js +11 -0
  322. package/dist/versions/types.js.map +1 -0
  323. package/package.json +28 -1
  324. package/src/__tests__/colorUtils.test.ts +112 -0
  325. package/src/__tests__/getLayers.test.ts +32 -7
  326. package/src/__tests__/iconResolve.test.ts +122 -0
  327. package/src/__tests__/imageEditState.test.ts +74 -0
  328. package/src/__tests__/imageEditVersions.test.ts +169 -0
  329. package/src/__tests__/inferDocumentTitle.test.ts +50 -0
  330. package/src/__tests__/jsonForm.chooseControl.test.ts +86 -0
  331. package/src/__tests__/jsonForm.evaluateWhen.test.ts +59 -0
  332. package/src/__tests__/jsonForm.inferSchema.test.ts +25 -0
  333. package/src/__tests__/jsonForm.pointer.test.ts +103 -0
  334. package/src/__tests__/markdownIcons.test.ts +104 -0
  335. package/src/__tests__/markdownToDoc.test.ts +19 -4
  336. package/src/__tests__/recommendTemplates.test.ts +183 -0
  337. package/src/__tests__/scopedContentContainer.test.ts +89 -0
  338. package/src/__tests__/slideshowGenerator.test.ts +1 -1
  339. package/src/__tests__/templateAnnotationParse.test.ts +42 -0
  340. package/src/__tests__/templates.test.ts +93 -9
  341. package/src/__tests__/themeCompile.test.ts +156 -0
  342. package/src/__tests__/themeValidator.test.ts +125 -0
  343. package/src/__tests__/versions.test.ts +415 -0
  344. package/src/doc/audioMapping.ts +55 -11
  345. package/src/doc/getLayers.ts +32 -21
  346. package/src/doc/markdownToDoc.ts +45 -7
  347. package/src/doc/templates/featureBlock.ts +202 -0
  348. package/src/doc/templates/fullBleedQuote.ts +13 -0
  349. package/src/doc/templates/index.ts +50 -16
  350. package/src/doc/templates/listBlock.ts +24 -5
  351. package/src/doc/templates/statHighlight.ts +13 -0
  352. package/src/doc/templates/titleBlock.ts +9 -0
  353. package/src/doc/utils/themeUtils.ts +8 -7
  354. package/src/generate/slideshowGenerator.ts +1 -1
  355. package/src/generate/templateMapper.ts +2 -2
  356. package/src/icons/iconData.ts +31 -0
  357. package/src/icons/index.ts +18 -0
  358. package/src/icons/resolve.ts +139 -0
  359. package/src/imageEdit/ImageEditVersionManager.ts +73 -0
  360. package/src/imageEdit/export.ts +326 -0
  361. package/src/imageEdit/index.ts +71 -0
  362. package/src/imageEdit/persistence.ts +62 -0
  363. package/src/imageEdit/state.ts +112 -0
  364. package/src/imageEdit/versionPaths.ts +57 -0
  365. package/src/imageEdit/versions.ts +231 -0
  366. package/src/index.ts +4 -0
  367. package/src/jsonForm/chooseControl.ts +88 -0
  368. package/src/jsonForm/evaluateWhen.ts +59 -0
  369. package/src/jsonForm/index.ts +22 -0
  370. package/src/jsonForm/inferSchema.ts +25 -0
  371. package/src/jsonForm/pointer.ts +109 -0
  372. package/src/jsonForm/types.ts +167 -0
  373. package/src/markdown/convert.ts +145 -5
  374. package/src/markdown/index.ts +3 -0
  375. package/src/markdown/types.ts +19 -2
  376. package/src/markdown/utils.ts +159 -0
  377. package/src/recommend/index.ts +16 -0
  378. package/src/recommend/templates.ts +273 -0
  379. package/src/schemas/BlockTemplates.ts +50 -7
  380. package/src/schemas/ImageEditDoc.ts +95 -0
  381. package/src/schemas/Theme.ts +98 -31
  382. package/src/schemas/colorUtils.ts +186 -0
  383. package/src/schemas/fontStacks.ts +257 -0
  384. package/src/schemas/index.ts +12 -0
  385. package/src/schemas/themeCompile.ts +203 -0
  386. package/src/schemas/themeLibrary.ts +18 -597
  387. package/src/schemas/themeValidator.ts +388 -0
  388. package/src/schemas/themes/bold.json +48 -0
  389. package/src/schemas/themes/cinematic.json +54 -0
  390. package/src/schemas/themes/documentary.json +49 -0
  391. package/src/schemas/themes/gezellig.json +44 -0
  392. package/src/schemas/themes/index.ts +53 -0
  393. package/src/schemas/themes/magazine.json +44 -0
  394. package/src/schemas/themes/minimalist.json +45 -0
  395. package/src/schemas/themes/morning-light.json +42 -0
  396. package/src/schemas/themes/standard-dark.json +50 -0
  397. package/src/schemas/themes/standard.json +42 -0
  398. package/src/schemas/themes/tech-dark.json +48 -0
  399. package/src/schemas/themes/warm-earth.json +50 -0
  400. package/src/storage/ScopedContentContainer.ts +113 -0
  401. package/src/storage/index.ts +1 -0
  402. package/src/versions/DocumentVersionManager.ts +70 -0
  403. package/src/versions/index.ts +54 -0
  404. package/src/versions/operations.ts +230 -0
  405. package/src/versions/paths.ts +66 -0
  406. package/src/versions/timestamp.ts +48 -0
  407. package/src/versions/types.ts +91 -0
  408. package/dist/chunk-6BAVXFYC.js.map +0 -1
  409. package/dist/chunk-7GF6RDED.js +0 -82
  410. package/dist/chunk-7GF6RDED.js.map +0 -1
  411. package/dist/chunk-CYQHG27J.js.map +0 -1
  412. package/dist/chunk-F22AE6BY.js +0 -121
  413. package/dist/chunk-F22AE6BY.js.map +0 -1
  414. package/dist/chunk-FIQR4AJG.js +0 -517
  415. package/dist/chunk-FIQR4AJG.js.map +0 -1
  416. package/dist/chunk-OASJKWO5.js.map +0 -1
  417. package/dist/chunk-QM5PKNPW.js.map +0 -1
  418. package/dist/chunk-TSG2OSE6.js.map +0 -1
  419. package/dist/themeLibrary-CEo2D4b6.d.ts +0 -44
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ toPointer,
4
+ pointerSegments,
5
+ getByPointer,
6
+ setByPointer,
7
+ resolveRef,
8
+ } from '../jsonForm/index.js';
9
+
10
+ describe('toPointer', () => {
11
+ it('keeps pointer-form paths intact', () => {
12
+ expect(toPointer('/a/b')).toBe('/a/b');
13
+ expect(toPointer('/0/1')).toBe('/0/1');
14
+ });
15
+ it('converts dotted-form to pointer-form', () => {
16
+ expect(toPointer('user.role')).toBe('/user/role');
17
+ expect(toPointer('a')).toBe('/a');
18
+ });
19
+ it('returns empty for root', () => {
20
+ expect(toPointer('')).toBe('');
21
+ expect(toPointer('/')).toBe('');
22
+ });
23
+ it('escapes ~ and / per RFC 6901', () => {
24
+ // dotted form does not allow / inside a segment; we test pointer-form decoding instead.
25
+ expect(pointerSegments('/a~1b')).toEqual(['a/b']);
26
+ expect(pointerSegments('/a~0b')).toEqual(['a~b']);
27
+ });
28
+ });
29
+
30
+ describe('getByPointer', () => {
31
+ const data = { user: { role: 'admin', tags: ['a', 'b'] } };
32
+ it('resolves nested object paths', () => {
33
+ expect(getByPointer(data, '/user/role')).toBe('admin');
34
+ expect(getByPointer(data, 'user.role')).toBe('admin');
35
+ });
36
+ it('resolves array indices', () => {
37
+ expect(getByPointer(data, '/user/tags/1')).toBe('b');
38
+ });
39
+ it('returns undefined for missing paths', () => {
40
+ expect(getByPointer(data, '/user/missing')).toBeUndefined();
41
+ expect(getByPointer(data, '/user/tags/9')).toBeUndefined();
42
+ });
43
+ it('returns the data itself for empty pointer', () => {
44
+ expect(getByPointer(data, '')).toBe(data);
45
+ });
46
+ });
47
+
48
+ describe('setByPointer', () => {
49
+ it('writes a leaf value immutably', () => {
50
+ const before = { a: 1, b: 2 };
51
+ const after = setByPointer(before, '/a', 99);
52
+ expect(after).toEqual({ a: 99, b: 2 });
53
+ expect(before).toEqual({ a: 1, b: 2 });
54
+ });
55
+ it('clones intermediate objects, shares siblings', () => {
56
+ const before = { x: { y: 1 }, z: { w: 2 } };
57
+ const after = setByPointer(before, '/x/y', 7);
58
+ expect(after).toEqual({ x: { y: 7 }, z: { w: 2 } });
59
+ expect(after.z).toBe(before.z);
60
+ expect(after.x).not.toBe(before.x);
61
+ });
62
+ it('writes into array indices', () => {
63
+ const before = { items: ['a', 'b', 'c'] };
64
+ const after = setByPointer(before, '/items/1', 'B');
65
+ expect(after.items).toEqual(['a', 'B', 'c']);
66
+ expect(after.items).not.toBe(before.items);
67
+ });
68
+ it('creates intermediate containers when missing', () => {
69
+ const after = setByPointer({}, '/a/b/0/c', 'leaf');
70
+ expect(after).toEqual({ a: { b: [{ c: 'leaf' }] } });
71
+ });
72
+ it('round-trips with getByPointer', () => {
73
+ const before = { a: { b: [{ c: 1 }, { c: 2 }] } };
74
+ const after = setByPointer(before, '/a/b/1/c', 42);
75
+ expect(getByPointer(after, '/a/b/1/c')).toBe(42);
76
+ expect(getByPointer(after, '/a/b/0/c')).toBe(1);
77
+ });
78
+ });
79
+
80
+ describe('resolveRef', () => {
81
+ const root = {
82
+ type: 'object',
83
+ properties: {
84
+ pet: { $ref: '#/$defs/Pet' },
85
+ },
86
+ $defs: {
87
+ Pet: { type: 'object', properties: { name: { type: 'string' } } },
88
+ },
89
+ };
90
+ it('returns referenced subschema for a local $ref', () => {
91
+ const ref = { $ref: '#/$defs/Pet' };
92
+ const resolved = resolveRef(ref, root);
93
+ expect(resolved?.type).toBe('object');
94
+ expect(resolved?.properties?.name?.type).toBe('string');
95
+ });
96
+ it('returns the schema unchanged when no $ref present', () => {
97
+ const node = { type: 'string' };
98
+ expect(resolveRef(node, root)).toBe(node);
99
+ });
100
+ it('returns undefined for unknown refs', () => {
101
+ expect(resolveRef({ $ref: '#/$defs/Nope' }, root)).toBeUndefined();
102
+ });
103
+ });
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseMarkdown, stringifyMarkdown } from '../markdown/index.js';
3
+
4
+ describe('inline icon parsing', () => {
5
+ it('detects a unique bare {[name]} as an inlineIcon node', () => {
6
+ const doc = parseMarkdown('Hello {[github]} world');
7
+ const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
8
+ expect(para.children.length).toBe(3);
9
+ expect(para.children[0]).toMatchObject({ type: 'text', value: 'Hello ' });
10
+ expect(para.children[1]).toMatchObject({
11
+ type: 'inlineIcon',
12
+ token: 'github',
13
+ family: 'brands',
14
+ name: 'github',
15
+ });
16
+ expect(para.children[2]).toMatchObject({ type: 'text', value: ' world' });
17
+ });
18
+
19
+ it('resolves qualified tokens', () => {
20
+ const doc = parseMarkdown('Q: {[fa-solid:user]}');
21
+ const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
22
+ const icon = para.children.find((c) => c.type === 'inlineIcon');
23
+ expect(icon).toMatchObject({
24
+ type: 'inlineIcon',
25
+ token: 'fa-solid:user',
26
+ family: 'solid',
27
+ name: 'user',
28
+ });
29
+ });
30
+
31
+ it('leaves unknown / ambiguous tokens as literal text', () => {
32
+ // `user` is ambiguous (multiple families), `notathing` is unknown —
33
+ // both should round-trip as plain text so authors can write them.
34
+ const doc = parseMarkdown('a {[user]} b {[notathing]} c');
35
+ const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
36
+ expect(para.children).toHaveLength(1);
37
+ expect(para.children[0]).toMatchObject({
38
+ type: 'text',
39
+ value: 'a {[user]} b {[notathing]} c',
40
+ });
41
+ });
42
+
43
+ it('round-trips bare icons byte-stable through stringify', () => {
44
+ // Bare names contain no colon, so the serializer can re-emit them
45
+ // verbatim. (Qualified tokens like `fa-solid:user` get the colon
46
+ // escaped as `\:` for safety against remark-directive — that form
47
+ // re-parses to the same token, just isn't byte-stable.)
48
+ const src = 'Built with {[github]} and {[face-smile]}.';
49
+ expect(stringifyMarkdown(parseMarkdown(src)).trim()).toBe(src);
50
+ });
51
+
52
+ it('round-trips qualified icons semantically through stringify', () => {
53
+ // `{[fa-solid:user]}` may serialize as `{[fa-solid\:user]}` — both
54
+ // forms parse back to the same inlineIcon node, so we verify
55
+ // semantic equivalence rather than byte equality.
56
+ const src = 'Q: {[fa-solid:user]} end';
57
+ const round1 = stringifyMarkdown(parseMarkdown(src));
58
+ const reparsed = parseMarkdown(round1);
59
+ const para = reparsed.children[0] as unknown as { children: Array<Record<string, unknown>> };
60
+ const icon = para.children.find((c) => c.type === 'inlineIcon');
61
+ expect(icon).toMatchObject({
62
+ type: 'inlineIcon',
63
+ token: 'fa-solid:user',
64
+ family: 'solid',
65
+ name: 'user',
66
+ });
67
+ });
68
+ });
69
+
70
+ describe('inline icons inside headings', () => {
71
+ it('parses the trailing template annotation as the template, body icon as inlineIcon', () => {
72
+ const doc = parseMarkdown('# Repo {[github]} status {[sectionHeader]}\n');
73
+ const heading = doc.children[0] as unknown as {
74
+ type: string;
75
+ templateAnnotation?: { template?: string };
76
+ children: Array<Record<string, unknown>>;
77
+ };
78
+ expect(heading.type).toBe('heading');
79
+ expect(heading.templateAnnotation?.template).toBe('sectionHeader');
80
+ // Children should be: text('Repo '), inlineIcon(github), text(' status')
81
+ const icon = heading.children.find((c) => c.type === 'inlineIcon');
82
+ expect(icon).toMatchObject({ type: 'inlineIcon', name: 'github' });
83
+ });
84
+
85
+ it('round-trips heading template + inline icon', () => {
86
+ const src = '# Repo {[github]} status {[sectionHeader]}\n';
87
+ expect(stringifyMarkdown(parseMarkdown(src))).toBe(src);
88
+ });
89
+
90
+ it('round-trips a heading where the whole trailing annotation is an icon (not a template)', () => {
91
+ // `# Foo {[github]}` — `github` is a valid icon (not a known template),
92
+ // so the bare token should NOT be treated as a template annotation
93
+ // unless it also happens to be in the template registry. The parser
94
+ // is liberal here — `extractTemplateAnnotation` runs first and would
95
+ // capture `{[github]}` as a template annotation; we accept that as
96
+ // the documented precedence.
97
+ const src = '# Foo {[github]}\n';
98
+ const out = stringifyMarkdown(parseMarkdown(src));
99
+ // The annotation extraction happens first, so the round-trip is
100
+ // either `# Foo {[github]}` (when serialized back via template
101
+ // suffix) — verify the output still matches the source.
102
+ expect(out).toBe(src);
103
+ });
104
+ });
@@ -142,12 +142,12 @@ describe('markdownToDoc', () => {
142
142
  const md = parseMarkdown('# Test');
143
143
  const doc = markdownToDoc(md, {
144
144
  articleId: 'custom-article',
145
- defaultTemplate: 'titleBlock',
145
+ defaultTemplate: 'title',
146
146
  defaultDuration: 10,
147
147
  });
148
148
 
149
149
  expect(doc.articleId).toBe('custom-article');
150
- expect(doc.blocks[0].template).toBe('titleBlock');
150
+ expect(doc.blocks[0].template).toBe('title');
151
151
  expect(doc.blocks[0].duration).toBe(10);
152
152
  });
153
153
 
@@ -363,11 +363,11 @@ describe('template annotation in markdownToDoc', () => {
363
363
  });
364
364
 
365
365
  it('nested headings preserve their own annotations', () => {
366
- const input = '# Chapter {[titleBlock]}\n\nIntro\n\n## Section {[chart]}\n\nData';
366
+ const input = '# Chapter {[title]}\n\nIntro\n\n## Section {[chart]}\n\nData';
367
367
  const md = parseMarkdown(input);
368
368
  const doc = markdownToDoc(md);
369
369
 
370
- expect(doc.blocks[0].template).toBe('titleBlock');
370
+ expect(doc.blocks[0].template).toBe('title');
371
371
  expect(doc.blocks[0].children).toHaveLength(1);
372
372
  expect(doc.blocks[0].children![0].template).toBe('chart');
373
373
  });
@@ -509,6 +509,21 @@ describe('auto cover block generation', () => {
509
509
  expect(doc.startBlock!.heroSrc).toBe('deep.png');
510
510
  });
511
511
 
512
+ it('picks raw HTML <img> as heroSrc (WYSIWYG resize case)', () => {
513
+ // The WYSIWYG editor serializes resized images as raw `<img src width>`
514
+ // because markdown shorthand has no width syntax. The cover scanner
515
+ // must see through that, or every resized image silently drops out
516
+ // of exports.
517
+ const md = parseMarkdown(
518
+ '# Title\n\n<img alt="resized" src="resized.jpg" width="194">\n\nText.',
519
+ );
520
+ const doc = markdownToDoc(md);
521
+
522
+ expect(doc.startBlock).toBeDefined();
523
+ expect(doc.startBlock!.heroSrc).toBe('resized.jpg');
524
+ expect(doc.startBlock!.heroAlt).toBe('resized');
525
+ });
526
+
512
527
  it('works without images (no heroSrc)', () => {
513
528
  const md = parseMarkdown('# Title\n\nPlain text, no images.');
514
529
  const doc = markdownToDoc(md);
@@ -0,0 +1,183 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { parseMarkdown } from '../markdown/parse.js';
3
+ import { profileBlockContents, recommendTemplatesForBlock } from '../recommend/templates.js';
4
+
5
+ const ALL_TEMPLATES = [
6
+ 'title',
7
+ 'sectionHeader',
8
+ 'statHighlight',
9
+ 'quote',
10
+ 'factCard',
11
+ 'twoColumn',
12
+ 'dateEvent',
13
+ 'imageWithCaption',
14
+ 'leftFeature',
15
+ 'rightFeature',
16
+ 'map',
17
+ 'fullBleedQuote',
18
+ 'list',
19
+ 'photoGrid',
20
+ 'definitionCard',
21
+ 'comparisonBar',
22
+ 'pullQuote',
23
+ 'videoWithCaption',
24
+ 'videoPullQuote',
25
+ 'dataTable',
26
+ ];
27
+
28
+ function profileOf(source: string) {
29
+ const doc = parseMarkdown(source);
30
+ return profileBlockContents(doc.children);
31
+ }
32
+
33
+ describe('profileBlockContents', () => {
34
+ it('returns an empty profile for no nodes', () => {
35
+ const p = profileBlockContents([]);
36
+ expect(p.wordCount).toBe(0);
37
+ expect(p.hasImage).toBe(false);
38
+ expect(p.hasVideo).toBe(false);
39
+ });
40
+
41
+ it('detects a single markdown image', () => {
42
+ const p = profileOf('![alt](https://example.com/a.png)');
43
+ expect(p.imageCount).toBe(1);
44
+ expect(p.hasImage).toBe(true);
45
+ });
46
+
47
+ it('detects an HTML <img> tag', () => {
48
+ const p = profileOf('<img src="a.png" alt="x" />');
49
+ expect(p.imageCount).toBe(1);
50
+ expect(p.hasImage).toBe(true);
51
+ });
52
+
53
+ it('detects a blockquote', () => {
54
+ const p = profileOf('> The mountain was calling me.');
55
+ expect(p.hasBlockquote).toBe(true);
56
+ });
57
+
58
+ it('detects a bullet list', () => {
59
+ const p = profileOf('- one\n- two\n- three');
60
+ expect(p.hasList).toBe(true);
61
+ });
62
+
63
+ it('detects a GFM table', () => {
64
+ const p = profileOf('| a | b |\n| --- | --- |\n| 1 | 2 |');
65
+ expect(p.hasTable).toBe(true);
66
+ });
67
+
68
+ it('detects a date string in body text', () => {
69
+ const p = profileOf('Construction began in July 1899.');
70
+ expect(p.hasDate).toBe(true);
71
+ });
72
+
73
+ it('detects an ISO date', () => {
74
+ const p = profileOf('Launched on 2024-03-15.');
75
+ expect(p.hasDate).toBe(true);
76
+ });
77
+
78
+ it('detects a prominent number paragraph', () => {
79
+ const p = profileOf('$2.3M raised');
80
+ expect(p.hasNumberHighlight).toBe(true);
81
+ });
82
+
83
+ it('detects a YouTube embed via link', () => {
84
+ const p = profileOf('[Watch](https://www.youtube.com/watch?v=abc)');
85
+ expect(p.hasVideo).toBe(true);
86
+ });
87
+
88
+ it('detects a <video> tag', () => {
89
+ const p = profileOf('<video src="x.mp4"></video>');
90
+ expect(p.hasVideo).toBe(true);
91
+ });
92
+
93
+ it('counts multiple images', () => {
94
+ const p = profileOf('![a](a.png)\n\n![b](b.png)\n\n![c](c.png)');
95
+ expect(p.imageCount).toBe(3);
96
+ });
97
+ });
98
+
99
+ describe('recommendTemplatesForBlock', () => {
100
+ it('falls back to universal defaults when nothing is detected', () => {
101
+ const profile = profileBlockContents([]);
102
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
103
+ expect(recommended).toEqual(['title', 'sectionHeader', 'factCard', 'twoColumn']);
104
+ });
105
+
106
+ it('keeps recommended ordering aligned with the input list', () => {
107
+ const profile = profileOf('![a](a.png)');
108
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
109
+ // imageWithCaption comes before leftFeature/rightFeature in ALL_TEMPLATES
110
+ expect(recommended.indexOf('imageWithCaption')).toBeLessThan(
111
+ recommended.indexOf('leftFeature'),
112
+ );
113
+ });
114
+
115
+ it('recommends image templates for blocks with one image (no photoGrid)', () => {
116
+ const profile = profileOf('![a](a.png)');
117
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
118
+ expect(recommended).toContain('imageWithCaption');
119
+ expect(recommended).toContain('leftFeature');
120
+ expect(recommended).toContain('rightFeature');
121
+ expect(recommended).not.toContain('photoGrid');
122
+ });
123
+
124
+ it('adds photoGrid when 2+ images present', () => {
125
+ const profile = profileOf('![a](a.png)\n\n![b](b.png)');
126
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
127
+ expect(recommended).toContain('photoGrid');
128
+ });
129
+
130
+ it('recommends quote templates for blocks with blockquotes', () => {
131
+ const profile = profileOf('> something memorable');
132
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
133
+ expect(recommended).toContain('quote');
134
+ expect(recommended).toContain('pullQuote');
135
+ expect(recommended).toContain('fullBleedQuote');
136
+ });
137
+
138
+ it('recommends dateEvent for blocks with date strings', () => {
139
+ const profile = profileOf('On March 5, 2024, the ribbon was cut.');
140
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
141
+ expect(recommended).toContain('dateEvent');
142
+ });
143
+
144
+ it('recommends statHighlight for blocks with prominent numbers', () => {
145
+ const profile = profileOf('$2.3M raised');
146
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
147
+ expect(recommended).toContain('statHighlight');
148
+ });
149
+
150
+ it('recommends dataTable + comparisonBar for blocks with tables', () => {
151
+ const profile = profileOf('| a | b |\n| --- | --- |\n| 1 | 2 |');
152
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
153
+ expect(recommended).toContain('dataTable');
154
+ expect(recommended).toContain('comparisonBar');
155
+ });
156
+
157
+ it('omits universal generics (factCard, twoColumn) when specific signal fires', () => {
158
+ const profile = profileOf('> something memorable');
159
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
160
+ // title + sectionHeader still come along, but factCard/twoColumn do not
161
+ expect(recommended).toContain('title');
162
+ expect(recommended).toContain('sectionHeader');
163
+ expect(recommended).not.toContain('factCard');
164
+ expect(recommended).not.toContain('twoColumn');
165
+ });
166
+
167
+ it('combines multiple signals additively', () => {
168
+ const profile = profileOf('![a](a.png)\n\n> a great line\n\n- alpha\n- beta');
169
+ const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
170
+ expect(recommended).toContain('imageWithCaption');
171
+ expect(recommended).toContain('quote');
172
+ expect(recommended).toContain('list');
173
+ });
174
+
175
+ it('places unrecommended templates in the rest bucket', () => {
176
+ const profile = profileOf('![a](a.png)');
177
+ const { recommended, rest } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
178
+ expect(rest).toContain('map');
179
+ expect(rest).toContain('definitionCard');
180
+ // No template appears in both buckets
181
+ for (const name of recommended) expect(rest).not.toContain(name);
182
+ });
183
+ });
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { MemoryContentContainer } from '../storage/ContentContainer';
3
+ import { ScopedContentContainer, scopeContainer } from '../storage/ScopedContentContainer';
4
+
5
+ const enc = (s: string) => new TextEncoder().encode(s);
6
+ const dec = (b: ArrayBuffer | null) => (b ? new TextDecoder().decode(b) : null);
7
+
8
+ describe('ScopedContentContainer', () => {
9
+ let parent: MemoryContentContainer;
10
+
11
+ beforeEach(() => {
12
+ parent = new MemoryContentContainer();
13
+ });
14
+
15
+ it('writeFile + readFile route through the prefix', async () => {
16
+ const scoped = scopeContainer(parent, 'image_files');
17
+ await scoped.writeFile('state.json', enc('{}'), 'application/json');
18
+
19
+ expect(dec(await parent.readFile('image_files/state.json'))).toBe('{}');
20
+ expect(dec(await scoped.readFile('state.json'))).toBe('{}');
21
+ });
22
+
23
+ it('exists + removeFile honor the prefix', async () => {
24
+ const scoped = scopeContainer(parent, 'image_files');
25
+ await scoped.writeFile('a.txt', enc('a'));
26
+ expect(await scoped.exists('a.txt')).toBe(true);
27
+ expect(await parent.exists('image_files/a.txt')).toBe(true);
28
+
29
+ await scoped.removeFile('a.txt');
30
+ expect(await scoped.exists('a.txt')).toBe(false);
31
+ expect(await parent.exists('image_files/a.txt')).toBe(false);
32
+ });
33
+
34
+ it('listFiles returns prefix-stripped paths and ignores siblings', async () => {
35
+ const scoped = scopeContainer(parent, 'image_files');
36
+ await parent.writeFile('outside.txt', enc('x'));
37
+ await parent.writeFile('image_files_sibling/peek.txt', enc('y')); // not in scope
38
+ await scoped.writeFile('a.txt', enc('1'));
39
+ await scoped.writeFile('sub/b.txt', enc('2'));
40
+
41
+ const list = await scoped.listFiles();
42
+ const paths = list.map((e) => e.path).sort();
43
+ expect(paths).toEqual(['a.txt', 'sub/b.txt']);
44
+ });
45
+
46
+ it('listFiles with prefix joins the scope prefix', async () => {
47
+ const scoped = scopeContainer(parent, 'sidecar');
48
+ await scoped.writeFile('assets/a.png', enc('a'));
49
+ await scoped.writeFile('assets/b.png', enc('b'));
50
+ await scoped.writeFile('state.json', enc('{}'));
51
+
52
+ const list = await scoped.listFiles('assets/');
53
+ expect(list.map((e) => e.path).sort()).toEqual(['assets/a.png', 'assets/b.png']);
54
+ });
55
+
56
+ it('nested scopes compose', async () => {
57
+ const a = scopeContainer(parent, 'a');
58
+ const ab = scopeContainer(a, 'b');
59
+ await ab.writeFile('c.txt', enc('hi'));
60
+ expect(dec(await parent.readFile('a/b/c.txt'))).toBe('hi');
61
+ expect(dec(await ab.readFile('c.txt'))).toBe('hi');
62
+ });
63
+
64
+ it('normalizes leading/trailing slashes in prefix and paths', async () => {
65
+ const scoped = scopeContainer(parent, '/sidecar/');
66
+ expect(scoped.prefix).toBe('sidecar');
67
+ await scoped.writeFile('/x.txt', enc('x'));
68
+ expect(dec(await parent.readFile('sidecar/x.txt'))).toBe('x');
69
+ });
70
+
71
+ it('rejects empty / dotted prefixes', () => {
72
+ expect(() => scopeContainer(parent, '')).toThrow();
73
+ expect(() => scopeContainer(parent, '/')).toThrow();
74
+ expect(() => scopeContainer(parent, 'a/../b')).toThrow();
75
+ expect(() => scopeContainer(parent, './a')).toThrow();
76
+ });
77
+
78
+ it('getDocumentPath / readDocument always return null', async () => {
79
+ const scoped = scopeContainer(parent, 'sidecar');
80
+ await scoped.writeFile('index.md', enc('# hi'));
81
+ expect(await scoped.getDocumentPath()).toBeNull();
82
+ expect(await scoped.readDocument()).toBeNull();
83
+ });
84
+
85
+ it('writeDocument throws', async () => {
86
+ const scoped = new ScopedContentContainer(parent, 'sidecar');
87
+ await expect(scoped.writeDocument()).rejects.toThrow();
88
+ });
89
+ });
@@ -34,7 +34,7 @@ describe('generateSlideshow', () => {
34
34
  expect(doc.articleId).toBe('Mount Rainier');
35
35
  const first = doc.blocks[0];
36
36
  if (isTemplateBlock(first)) {
37
- expect(first.template).toBe('titleBlock');
37
+ expect(first.template).toBe('title');
38
38
  }
39
39
  });
40
40
 
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseMarkdown } from '../markdown/parse.js';
3
+ import { markdownToDoc } from '../doc/markdownToDoc.js';
4
+
5
+ describe('Template annotation in Heading parse path', () => {
6
+ it('parseMarkdown extracts {[template]} from a heading', () => {
7
+ const md = '## Getting Started {[comparisonBar]}';
8
+ const doc = parseMarkdown(md);
9
+ const heading = doc.children[0];
10
+ expect(heading.type).toBe('heading');
11
+ if (heading.type !== 'heading') return;
12
+ expect(heading.templateAnnotation).toEqual({ template: 'comparisonBar' });
13
+ });
14
+
15
+ it('markdownToDoc preserves templateAnnotation on the resulting block', () => {
16
+ const md = '# Title\n\n## Getting Started {[comparisonBar]}\n\nBody.';
17
+ const parsed = parseMarkdown(md);
18
+ const doc = markdownToDoc(parsed, { articleId: 'test' });
19
+ // The h1 is the root; first child is the h2 with annotation
20
+ const h1 = doc.blocks[0];
21
+ expect(h1.sourceHeading?.depth).toBe(1);
22
+ const h2 = h1.children?.[0];
23
+ expect(h2?.sourceHeading?.depth).toBe(2);
24
+ expect(h2?.sourceHeading?.templateAnnotation).toEqual({ template: 'comparisonBar' });
25
+ });
26
+
27
+ it('tolerates accidentally-doubled trailing `]}` (user typo)', () => {
28
+ const md = '## Getting Started {[comparisonBar]}]}';
29
+ const doc = parseMarkdown(md);
30
+ const heading = doc.children[0];
31
+ if (heading.type !== 'heading') throw new Error('expected heading');
32
+ expect(heading.templateAnnotation).toEqual({ template: 'comparisonBar' });
33
+ });
34
+
35
+ it('still rejects non-trailing annotations (regex stays anchored)', () => {
36
+ const md = '## The {[chart]} section'; // word continues after — not an annotation
37
+ const doc = parseMarkdown(md);
38
+ const heading = doc.children[0];
39
+ if (heading.type !== 'heading') throw new Error('expected heading');
40
+ expect(heading.templateAnnotation).toBeUndefined();
41
+ });
42
+ });