@bendyline/squisq 1.3.0 → 1.4.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 (533) 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/{chunk-CYQHG27J.js → chunk-2TMAWB4Y.js} +74 -1
  4. package/dist/chunk-2TMAWB4Y.js.map +1 -0
  5. package/dist/{chunk-OASJKWO5.js → chunk-3K5OG6KI.js} +92 -7
  6. package/dist/chunk-3K5OG6KI.js.map +1 -0
  7. package/dist/chunk-57CVQCFX.js +193 -0
  8. package/dist/chunk-57CVQCFX.js.map +1 -0
  9. package/dist/{chunk-QM5PKNPW.js → chunk-5WFRKURX.js} +1 -1
  10. package/dist/chunk-5WFRKURX.js.map +1 -0
  11. package/dist/chunk-B4EEON3N.js +1317 -0
  12. package/dist/chunk-B4EEON3N.js.map +1 -0
  13. package/dist/chunk-DBS6RIFL.js +81 -0
  14. package/dist/chunk-DBS6RIFL.js.map +1 -0
  15. package/dist/{chunk-MIBRIRGJ.js → chunk-FR2RBTKO.js} +3 -3
  16. package/dist/{chunk-MIBRIRGJ.js.map → chunk-FR2RBTKO.js.map} +1 -1
  17. package/dist/chunk-H3AXU4MJ.js +179 -0
  18. package/dist/chunk-H3AXU4MJ.js.map +1 -0
  19. package/dist/chunk-JELIRVDP.js +24 -0
  20. package/dist/chunk-JELIRVDP.js.map +1 -0
  21. package/dist/chunk-K32VJONL.js +193 -0
  22. package/dist/chunk-K32VJONL.js.map +1 -0
  23. package/dist/{chunk-6BAVXFYC.js → chunk-KSWJR66U.js} +218 -42
  24. package/dist/chunk-KSWJR66U.js.map +1 -0
  25. package/dist/{chunk-TSG2OSE6.js → chunk-MYIH7FWD.js} +3 -3
  26. package/dist/chunk-MYIH7FWD.js.map +1 -0
  27. package/dist/chunk-VLJJHDAC.js +218 -0
  28. package/dist/chunk-VLJJHDAC.js.map +1 -0
  29. package/dist/chunk-WMOI7FN3.js +182 -0
  30. package/dist/chunk-WMOI7FN3.js.map +1 -0
  31. package/dist/chunk-ZTX4PFFT.js +520 -0
  32. package/dist/chunk-ZTX4PFFT.js.map +1 -0
  33. package/dist/doc/index.d.ts +5 -10
  34. package/dist/doc/index.js +145 -7
  35. package/dist/doc/index.js.map +1 -1
  36. package/dist/generate/index.d.ts +99 -7
  37. package/dist/generate/index.js +16 -3
  38. package/dist/generate/index.js.map +1 -1
  39. package/dist/icons/index.d.ts +87 -0
  40. package/dist/icons/index.js +17 -0
  41. package/dist/icons/index.js.map +1 -0
  42. package/dist/imageEdit/index.d.ts +216 -0
  43. package/dist/imageEdit/index.js +54 -0
  44. package/dist/imageEdit/index.js.map +1 -0
  45. package/dist/index.d.ts +21 -9
  46. package/dist/index.js +461 -8
  47. package/dist/index.js.map +1 -1
  48. package/dist/jsonForm/index.d.ts +191 -0
  49. package/dist/jsonForm/index.js +27 -0
  50. package/dist/jsonForm/index.js.map +1 -0
  51. package/dist/markdown/index.d.ts +265 -19
  52. package/dist/markdown/index.js +39 -33
  53. package/dist/markdown/index.js.map +1 -1
  54. package/dist/random/index.d.ts +51 -2
  55. package/dist/random/index.js +9 -1
  56. package/dist/random/index.js.map +1 -1
  57. package/dist/recommend/index.d.ts +42 -0
  58. package/dist/recommend/index.js +10 -0
  59. package/dist/recommend/index.js.map +1 -0
  60. package/dist/schemas/index.d.ts +188 -10
  61. package/dist/schemas/index.js +120 -7
  62. package/dist/schemas/index.js.map +1 -1
  63. package/dist/spatial/index.d.ts +111 -3
  64. package/dist/spatial/index.js +24 -2
  65. package/dist/spatial/index.js.map +1 -1
  66. package/dist/storage/index.d.ts +159 -9
  67. package/dist/storage/index.js +20 -5
  68. package/dist/storage/index.js.map +1 -1
  69. package/dist/story/index.d.ts +53 -13
  70. package/dist/story/index.js +12 -4
  71. package/dist/themeLibrary-BHjJ0Qx6.d.ts +40 -0
  72. package/dist/timing/index.d.ts +109 -4
  73. package/dist/timing/index.js +23 -2
  74. package/dist/timing/index.js.map +1 -1
  75. package/dist/transform/index.d.ts +175 -6
  76. package/dist/transform/index.js +490 -3
  77. package/dist/transform/index.js.map +1 -1
  78. package/dist/{types-CkNttVSF.d.ts → types-BdZkdJ3z.d.ts} +17 -2
  79. package/dist/types-D98li1gV.d.ts +93 -0
  80. package/dist/versions/index.d.ts +128 -0
  81. package/dist/versions/index.js +35 -0
  82. package/dist/versions/index.js.map +1 -0
  83. package/package.json +28 -1
  84. package/src/__tests__/colorUtils.test.ts +112 -0
  85. package/src/__tests__/getLayers.test.ts +32 -7
  86. package/src/__tests__/iconResolve.test.ts +122 -0
  87. package/src/__tests__/imageEditState.test.ts +74 -0
  88. package/src/__tests__/imageEditVersions.test.ts +169 -0
  89. package/src/__tests__/inferDocumentTitle.test.ts +50 -0
  90. package/src/__tests__/jsonForm.chooseControl.test.ts +86 -0
  91. package/src/__tests__/jsonForm.evaluateWhen.test.ts +59 -0
  92. package/src/__tests__/jsonForm.inferSchema.test.ts +25 -0
  93. package/src/__tests__/jsonForm.pointer.test.ts +103 -0
  94. package/src/__tests__/markdownIcons.test.ts +104 -0
  95. package/src/__tests__/markdownToDoc.test.ts +19 -4
  96. package/src/__tests__/recommendTemplates.test.ts +183 -0
  97. package/src/__tests__/scopedContentContainer.test.ts +89 -0
  98. package/src/__tests__/slideshowGenerator.test.ts +1 -1
  99. package/src/__tests__/templateAnnotationParse.test.ts +42 -0
  100. package/src/__tests__/templates.test.ts +93 -9
  101. package/src/__tests__/themeCompile.test.ts +156 -0
  102. package/src/__tests__/themeValidator.test.ts +125 -0
  103. package/src/__tests__/versions.test.ts +415 -0
  104. package/src/doc/audioMapping.ts +55 -11
  105. package/src/doc/getLayers.ts +32 -21
  106. package/src/doc/markdownToDoc.ts +45 -7
  107. package/src/doc/templates/featureBlock.ts +202 -0
  108. package/src/doc/templates/fullBleedQuote.ts +13 -0
  109. package/src/doc/templates/index.ts +50 -16
  110. package/src/doc/templates/listBlock.ts +24 -5
  111. package/src/doc/templates/statHighlight.ts +13 -0
  112. package/src/doc/templates/titleBlock.ts +9 -0
  113. package/src/doc/utils/themeUtils.ts +8 -7
  114. package/src/generate/slideshowGenerator.ts +1 -1
  115. package/src/generate/templateMapper.ts +2 -2
  116. package/src/icons/iconData.ts +31 -0
  117. package/src/icons/index.ts +18 -0
  118. package/src/icons/resolve.ts +139 -0
  119. package/src/imageEdit/ImageEditVersionManager.ts +73 -0
  120. package/src/imageEdit/export.ts +326 -0
  121. package/src/imageEdit/index.ts +71 -0
  122. package/src/imageEdit/persistence.ts +62 -0
  123. package/src/imageEdit/state.ts +112 -0
  124. package/src/imageEdit/versionPaths.ts +57 -0
  125. package/src/imageEdit/versions.ts +231 -0
  126. package/src/index.ts +4 -0
  127. package/src/jsonForm/chooseControl.ts +88 -0
  128. package/src/jsonForm/evaluateWhen.ts +59 -0
  129. package/src/jsonForm/index.ts +22 -0
  130. package/src/jsonForm/inferSchema.ts +25 -0
  131. package/src/jsonForm/pointer.ts +109 -0
  132. package/src/jsonForm/types.ts +167 -0
  133. package/src/markdown/convert.ts +145 -5
  134. package/src/markdown/index.ts +3 -0
  135. package/src/markdown/types.ts +19 -2
  136. package/src/markdown/utils.ts +159 -0
  137. package/src/recommend/index.ts +16 -0
  138. package/src/recommend/templates.ts +273 -0
  139. package/src/schemas/BlockTemplates.ts +50 -7
  140. package/src/schemas/ImageEditDoc.ts +95 -0
  141. package/src/schemas/Theme.ts +98 -31
  142. package/src/schemas/colorUtils.ts +186 -0
  143. package/src/schemas/fontStacks.ts +257 -0
  144. package/src/schemas/index.ts +12 -0
  145. package/src/schemas/themeCompile.ts +203 -0
  146. package/src/schemas/themeLibrary.ts +18 -597
  147. package/src/schemas/themeValidator.ts +388 -0
  148. package/src/schemas/themes/bold.json +48 -0
  149. package/src/schemas/themes/cinematic.json +54 -0
  150. package/src/schemas/themes/documentary.json +49 -0
  151. package/src/schemas/themes/gezellig.json +44 -0
  152. package/src/schemas/themes/index.ts +53 -0
  153. package/src/schemas/themes/magazine.json +44 -0
  154. package/src/schemas/themes/minimalist.json +45 -0
  155. package/src/schemas/themes/morning-light.json +42 -0
  156. package/src/schemas/themes/standard-dark.json +50 -0
  157. package/src/schemas/themes/standard.json +42 -0
  158. package/src/schemas/themes/tech-dark.json +48 -0
  159. package/src/schemas/themes/warm-earth.json +50 -0
  160. package/src/storage/ScopedContentContainer.ts +113 -0
  161. package/src/storage/index.ts +1 -0
  162. package/src/versions/DocumentVersionManager.ts +70 -0
  163. package/src/versions/index.ts +54 -0
  164. package/src/versions/operations.ts +230 -0
  165. package/src/versions/paths.ts +66 -0
  166. package/src/versions/timestamp.ts +48 -0
  167. package/src/versions/types.ts +91 -0
  168. package/dist/__tests__/animationUtils.test.d.ts +0 -2
  169. package/dist/__tests__/animationUtils.test.d.ts.map +0 -1
  170. package/dist/__tests__/animationUtils.test.js +0 -60
  171. package/dist/__tests__/animationUtils.test.js.map +0 -1
  172. package/dist/__tests__/contentContainer.test.d.ts +0 -2
  173. package/dist/__tests__/contentContainer.test.d.ts.map +0 -1
  174. package/dist/__tests__/contentContainer.test.js +0 -234
  175. package/dist/__tests__/contentContainer.test.js.map +0 -1
  176. package/dist/__tests__/contentExtractor.test.d.ts +0 -2
  177. package/dist/__tests__/contentExtractor.test.d.ts.map +0 -1
  178. package/dist/__tests__/contentExtractor.test.js +0 -151
  179. package/dist/__tests__/contentExtractor.test.js.map +0 -1
  180. package/dist/__tests__/coverBlock.test.d.ts +0 -2
  181. package/dist/__tests__/coverBlock.test.d.ts.map +0 -1
  182. package/dist/__tests__/coverBlock.test.js +0 -197
  183. package/dist/__tests__/coverBlock.test.js.map +0 -1
  184. package/dist/__tests__/dataTable.test.d.ts +0 -2
  185. package/dist/__tests__/dataTable.test.d.ts.map +0 -1
  186. package/dist/__tests__/dataTable.test.js +0 -110
  187. package/dist/__tests__/dataTable.test.js.map +0 -1
  188. package/dist/__tests__/geohash.test.d.ts +0 -2
  189. package/dist/__tests__/geohash.test.d.ts.map +0 -1
  190. package/dist/__tests__/geohash.test.js +0 -87
  191. package/dist/__tests__/geohash.test.js.map +0 -1
  192. package/dist/__tests__/getLayers.test.d.ts +0 -2
  193. package/dist/__tests__/getLayers.test.d.ts.map +0 -1
  194. package/dist/__tests__/getLayers.test.js +0 -167
  195. package/dist/__tests__/getLayers.test.js.map +0 -1
  196. package/dist/__tests__/haversine.test.d.ts +0 -2
  197. package/dist/__tests__/haversine.test.d.ts.map +0 -1
  198. package/dist/__tests__/haversine.test.js +0 -56
  199. package/dist/__tests__/haversine.test.js.map +0 -1
  200. package/dist/__tests__/localForageAdapter.test.d.ts +0 -8
  201. package/dist/__tests__/localForageAdapter.test.d.ts.map +0 -1
  202. package/dist/__tests__/localForageAdapter.test.js +0 -130
  203. package/dist/__tests__/localForageAdapter.test.js.map +0 -1
  204. package/dist/__tests__/markdown.test.d.ts +0 -2
  205. package/dist/__tests__/markdown.test.d.ts.map +0 -1
  206. package/dist/__tests__/markdown.test.js +0 -715
  207. package/dist/__tests__/markdown.test.js.map +0 -1
  208. package/dist/__tests__/markdownToDoc.test.d.ts +0 -2
  209. package/dist/__tests__/markdownToDoc.test.d.ts.map +0 -1
  210. package/dist/__tests__/markdownToDoc.test.js +0 -447
  211. package/dist/__tests__/markdownToDoc.test.js.map +0 -1
  212. package/dist/__tests__/seededRandom.test.d.ts +0 -2
  213. package/dist/__tests__/seededRandom.test.d.ts.map +0 -1
  214. package/dist/__tests__/seededRandom.test.js +0 -125
  215. package/dist/__tests__/seededRandom.test.js.map +0 -1
  216. package/dist/__tests__/slideshowGenerator.test.d.ts +0 -2
  217. package/dist/__tests__/slideshowGenerator.test.d.ts.map +0 -1
  218. package/dist/__tests__/slideshowGenerator.test.js +0 -114
  219. package/dist/__tests__/slideshowGenerator.test.js.map +0 -1
  220. package/dist/__tests__/storage.test.d.ts +0 -2
  221. package/dist/__tests__/storage.test.d.ts.map +0 -1
  222. package/dist/__tests__/storage.test.js +0 -51
  223. package/dist/__tests__/storage.test.js.map +0 -1
  224. package/dist/__tests__/surface.test.d.ts +0 -2
  225. package/dist/__tests__/surface.test.d.ts.map +0 -1
  226. package/dist/__tests__/surface.test.js +0 -57
  227. package/dist/__tests__/surface.test.js.map +0 -1
  228. package/dist/__tests__/templates.test.d.ts +0 -2
  229. package/dist/__tests__/templates.test.d.ts.map +0 -1
  230. package/dist/__tests__/templates.test.js +0 -217
  231. package/dist/__tests__/templates.test.js.map +0 -1
  232. package/dist/__tests__/timing.test.d.ts +0 -2
  233. package/dist/__tests__/timing.test.d.ts.map +0 -1
  234. package/dist/__tests__/timing.test.js +0 -131
  235. package/dist/__tests__/timing.test.js.map +0 -1
  236. package/dist/chunk-6BAVXFYC.js.map +0 -1
  237. package/dist/chunk-7GF6RDED.js +0 -82
  238. package/dist/chunk-7GF6RDED.js.map +0 -1
  239. package/dist/chunk-CYQHG27J.js.map +0 -1
  240. package/dist/chunk-F22AE6BY.js +0 -121
  241. package/dist/chunk-F22AE6BY.js.map +0 -1
  242. package/dist/chunk-FIQR4AJG.js +0 -517
  243. package/dist/chunk-FIQR4AJG.js.map +0 -1
  244. package/dist/chunk-OASJKWO5.js.map +0 -1
  245. package/dist/chunk-QM5PKNPW.js.map +0 -1
  246. package/dist/chunk-TSG2OSE6.js.map +0 -1
  247. package/dist/doc/audioMapping.d.ts +0 -36
  248. package/dist/doc/audioMapping.d.ts.map +0 -1
  249. package/dist/doc/audioMapping.js +0 -315
  250. package/dist/doc/audioMapping.js.map +0 -1
  251. package/dist/doc/docToMarkdown.d.ts +0 -41
  252. package/dist/doc/docToMarkdown.d.ts.map +0 -1
  253. package/dist/doc/docToMarkdown.js +0 -88
  254. package/dist/doc/docToMarkdown.js.map +0 -1
  255. package/dist/doc/getLayers.d.ts +0 -65
  256. package/dist/doc/getLayers.d.ts.map +0 -1
  257. package/dist/doc/getLayers.js +0 -103
  258. package/dist/doc/getLayers.js.map +0 -1
  259. package/dist/doc/index.d.ts.map +0 -1
  260. package/dist/doc/markdownToDoc.d.ts +0 -75
  261. package/dist/doc/markdownToDoc.d.ts.map +0 -1
  262. package/dist/doc/markdownToDoc.js +0 -398
  263. package/dist/doc/markdownToDoc.js.map +0 -1
  264. package/dist/doc/templates/accentImage.d.ts +0 -59
  265. package/dist/doc/templates/accentImage.d.ts.map +0 -1
  266. package/dist/doc/templates/accentImage.js +0 -275
  267. package/dist/doc/templates/accentImage.js.map +0 -1
  268. package/dist/doc/templates/captionUtils.d.ts +0 -20
  269. package/dist/doc/templates/captionUtils.d.ts.map +0 -1
  270. package/dist/doc/templates/captionUtils.js +0 -29
  271. package/dist/doc/templates/captionUtils.js.map +0 -1
  272. package/dist/doc/templates/comparisonBar.d.ts +0 -13
  273. package/dist/doc/templates/comparisonBar.d.ts.map +0 -1
  274. package/dist/doc/templates/comparisonBar.js +0 -145
  275. package/dist/doc/templates/comparisonBar.js.map +0 -1
  276. package/dist/doc/templates/coverBlock.d.ts +0 -51
  277. package/dist/doc/templates/coverBlock.d.ts.map +0 -1
  278. package/dist/doc/templates/coverBlock.js +0 -153
  279. package/dist/doc/templates/coverBlock.js.map +0 -1
  280. package/dist/doc/templates/dataTable.d.ts +0 -14
  281. package/dist/doc/templates/dataTable.d.ts.map +0 -1
  282. package/dist/doc/templates/dataTable.js +0 -73
  283. package/dist/doc/templates/dataTable.js.map +0 -1
  284. package/dist/doc/templates/dateEvent.d.ts +0 -15
  285. package/dist/doc/templates/dateEvent.d.ts.map +0 -1
  286. package/dist/doc/templates/dateEvent.js +0 -114
  287. package/dist/doc/templates/dateEvent.js.map +0 -1
  288. package/dist/doc/templates/definitionCard.d.ts +0 -13
  289. package/dist/doc/templates/definitionCard.d.ts.map +0 -1
  290. package/dist/doc/templates/definitionCard.js +0 -117
  291. package/dist/doc/templates/definitionCard.js.map +0 -1
  292. package/dist/doc/templates/factCard.d.ts +0 -15
  293. package/dist/doc/templates/factCard.d.ts.map +0 -1
  294. package/dist/doc/templates/factCard.js +0 -104
  295. package/dist/doc/templates/factCard.js.map +0 -1
  296. package/dist/doc/templates/fullBleedQuote.d.ts +0 -13
  297. package/dist/doc/templates/fullBleedQuote.d.ts.map +0 -1
  298. package/dist/doc/templates/fullBleedQuote.js +0 -55
  299. package/dist/doc/templates/fullBleedQuote.js.map +0 -1
  300. package/dist/doc/templates/imageWithCaption.d.ts +0 -13
  301. package/dist/doc/templates/imageWithCaption.d.ts.map +0 -1
  302. package/dist/doc/templates/imageWithCaption.js +0 -146
  303. package/dist/doc/templates/imageWithCaption.js.map +0 -1
  304. package/dist/doc/templates/index.d.ts +0 -105
  305. package/dist/doc/templates/index.d.ts.map +0 -1
  306. package/dist/doc/templates/index.js +0 -445
  307. package/dist/doc/templates/index.js.map +0 -1
  308. package/dist/doc/templates/listBlock.d.ts +0 -13
  309. package/dist/doc/templates/listBlock.d.ts.map +0 -1
  310. package/dist/doc/templates/listBlock.js +0 -92
  311. package/dist/doc/templates/listBlock.js.map +0 -1
  312. package/dist/doc/templates/mapBlock.d.ts +0 -17
  313. package/dist/doc/templates/mapBlock.d.ts.map +0 -1
  314. package/dist/doc/templates/mapBlock.js +0 -115
  315. package/dist/doc/templates/mapBlock.js.map +0 -1
  316. package/dist/doc/templates/persistentLayers.d.ts +0 -39
  317. package/dist/doc/templates/persistentLayers.d.ts.map +0 -1
  318. package/dist/doc/templates/persistentLayers.js +0 -447
  319. package/dist/doc/templates/persistentLayers.js.map +0 -1
  320. package/dist/doc/templates/photoGrid.d.ts +0 -20
  321. package/dist/doc/templates/photoGrid.d.ts.map +0 -1
  322. package/dist/doc/templates/photoGrid.js +0 -175
  323. package/dist/doc/templates/photoGrid.js.map +0 -1
  324. package/dist/doc/templates/pullQuote.d.ts +0 -13
  325. package/dist/doc/templates/pullQuote.d.ts.map +0 -1
  326. package/dist/doc/templates/pullQuote.js +0 -113
  327. package/dist/doc/templates/pullQuote.js.map +0 -1
  328. package/dist/doc/templates/quoteBlock.d.ts +0 -15
  329. package/dist/doc/templates/quoteBlock.d.ts.map +0 -1
  330. package/dist/doc/templates/quoteBlock.js +0 -101
  331. package/dist/doc/templates/quoteBlock.js.map +0 -1
  332. package/dist/doc/templates/sectionHeader.d.ts +0 -14
  333. package/dist/doc/templates/sectionHeader.d.ts.map +0 -1
  334. package/dist/doc/templates/sectionHeader.js +0 -97
  335. package/dist/doc/templates/sectionHeader.js.map +0 -1
  336. package/dist/doc/templates/statHighlight.d.ts +0 -15
  337. package/dist/doc/templates/statHighlight.d.ts.map +0 -1
  338. package/dist/doc/templates/statHighlight.js +0 -102
  339. package/dist/doc/templates/statHighlight.js.map +0 -1
  340. package/dist/doc/templates/titleBlock.d.ts +0 -13
  341. package/dist/doc/templates/titleBlock.d.ts.map +0 -1
  342. package/dist/doc/templates/titleBlock.js +0 -95
  343. package/dist/doc/templates/titleBlock.js.map +0 -1
  344. package/dist/doc/templates/twoColumn.d.ts +0 -15
  345. package/dist/doc/templates/twoColumn.d.ts.map +0 -1
  346. package/dist/doc/templates/twoColumn.js +0 -225
  347. package/dist/doc/templates/twoColumn.js.map +0 -1
  348. package/dist/doc/templates/videoPullQuote.d.ts +0 -20
  349. package/dist/doc/templates/videoPullQuote.d.ts.map +0 -1
  350. package/dist/doc/templates/videoPullQuote.js +0 -122
  351. package/dist/doc/templates/videoPullQuote.js.map +0 -1
  352. package/dist/doc/templates/videoWithCaption.d.ts +0 -20
  353. package/dist/doc/templates/videoWithCaption.d.ts.map +0 -1
  354. package/dist/doc/templates/videoWithCaption.js +0 -107
  355. package/dist/doc/templates/videoWithCaption.js.map +0 -1
  356. package/dist/doc/utils/animationUtils.d.ts +0 -34
  357. package/dist/doc/utils/animationUtils.d.ts.map +0 -1
  358. package/dist/doc/utils/animationUtils.js +0 -110
  359. package/dist/doc/utils/animationUtils.js.map +0 -1
  360. package/dist/doc/utils/themeUtils.d.ts +0 -64
  361. package/dist/doc/utils/themeUtils.d.ts.map +0 -1
  362. package/dist/doc/utils/themeUtils.js +0 -135
  363. package/dist/doc/utils/themeUtils.js.map +0 -1
  364. package/dist/generate/contentExtractor.d.ts +0 -112
  365. package/dist/generate/contentExtractor.d.ts.map +0 -1
  366. package/dist/generate/contentExtractor.js +0 -638
  367. package/dist/generate/contentExtractor.js.map +0 -1
  368. package/dist/generate/index.d.ts.map +0 -1
  369. package/dist/generate/slideshowGenerator.d.ts +0 -72
  370. package/dist/generate/slideshowGenerator.d.ts.map +0 -1
  371. package/dist/generate/slideshowGenerator.js +0 -159
  372. package/dist/generate/slideshowGenerator.js.map +0 -1
  373. package/dist/generate/templateMapper.d.ts +0 -23
  374. package/dist/generate/templateMapper.d.ts.map +0 -1
  375. package/dist/generate/templateMapper.js +0 -114
  376. package/dist/generate/templateMapper.js.map +0 -1
  377. package/dist/index.d.ts.map +0 -1
  378. package/dist/markdown/convert.d.ts +0 -71
  379. package/dist/markdown/convert.d.ts.map +0 -1
  380. package/dist/markdown/convert.js +0 -845
  381. package/dist/markdown/convert.js.map +0 -1
  382. package/dist/markdown/htmlParse.d.ts +0 -26
  383. package/dist/markdown/htmlParse.d.ts.map +0 -1
  384. package/dist/markdown/htmlParse.js +0 -195
  385. package/dist/markdown/htmlParse.js.map +0 -1
  386. package/dist/markdown/index.d.ts.map +0 -1
  387. package/dist/markdown/parse.d.ts +0 -27
  388. package/dist/markdown/parse.d.ts.map +0 -1
  389. package/dist/markdown/parse.js +0 -90
  390. package/dist/markdown/parse.js.map +0 -1
  391. package/dist/markdown/stringify.d.ts +0 -32
  392. package/dist/markdown/stringify.d.ts.map +0 -1
  393. package/dist/markdown/stringify.js +0 -108
  394. package/dist/markdown/stringify.js.map +0 -1
  395. package/dist/markdown/types.d.ts +0 -516
  396. package/dist/markdown/types.d.ts.map +0 -1
  397. package/dist/markdown/types.js +0 -17
  398. package/dist/markdown/types.js.map +0 -1
  399. package/dist/markdown/utils.d.ts +0 -78
  400. package/dist/markdown/utils.d.ts.map +0 -1
  401. package/dist/markdown/utils.js +0 -153
  402. package/dist/markdown/utils.js.map +0 -1
  403. package/dist/random/SeededRandom.d.ts +0 -50
  404. package/dist/random/SeededRandom.d.ts.map +0 -1
  405. package/dist/random/SeededRandom.js +0 -115
  406. package/dist/random/SeededRandom.js.map +0 -1
  407. package/dist/random/index.d.ts.map +0 -1
  408. package/dist/schemas/BlockTemplates.d.ts +0 -600
  409. package/dist/schemas/BlockTemplates.d.ts.map +0 -1
  410. package/dist/schemas/BlockTemplates.js +0 -72
  411. package/dist/schemas/BlockTemplates.js.map +0 -1
  412. package/dist/schemas/Doc.d.ts +0 -503
  413. package/dist/schemas/Doc.d.ts.map +0 -1
  414. package/dist/schemas/Doc.js +0 -90
  415. package/dist/schemas/Doc.js.map +0 -1
  416. package/dist/schemas/LayoutStrategy.d.ts +0 -84
  417. package/dist/schemas/LayoutStrategy.d.ts.map +0 -1
  418. package/dist/schemas/LayoutStrategy.js +0 -123
  419. package/dist/schemas/LayoutStrategy.js.map +0 -1
  420. package/dist/schemas/MediaProvider.d.ts +0 -65
  421. package/dist/schemas/MediaProvider.d.ts.map +0 -1
  422. package/dist/schemas/MediaProvider.js +0 -13
  423. package/dist/schemas/MediaProvider.js.map +0 -1
  424. package/dist/schemas/Theme.d.ts +0 -194
  425. package/dist/schemas/Theme.d.ts.map +0 -1
  426. package/dist/schemas/Theme.js +0 -97
  427. package/dist/schemas/Theme.js.map +0 -1
  428. package/dist/schemas/Types.d.ts +0 -14
  429. package/dist/schemas/Types.d.ts.map +0 -1
  430. package/dist/schemas/Types.js +0 -5
  431. package/dist/schemas/Types.js.map +0 -1
  432. package/dist/schemas/Viewport.d.ts +0 -74
  433. package/dist/schemas/Viewport.d.ts.map +0 -1
  434. package/dist/schemas/Viewport.js +0 -63
  435. package/dist/schemas/Viewport.js.map +0 -1
  436. package/dist/schemas/index.d.ts.map +0 -1
  437. package/dist/schemas/themeLibrary.d.ts +0 -41
  438. package/dist/schemas/themeLibrary.d.ts.map +0 -1
  439. package/dist/schemas/themeLibrary.js +0 -597
  440. package/dist/schemas/themeLibrary.js.map +0 -1
  441. package/dist/spatial/Geohash.d.ts +0 -85
  442. package/dist/spatial/Geohash.d.ts.map +0 -1
  443. package/dist/spatial/Geohash.js +0 -142
  444. package/dist/spatial/Geohash.js.map +0 -1
  445. package/dist/spatial/Haversine.d.ts +0 -24
  446. package/dist/spatial/Haversine.d.ts.map +0 -1
  447. package/dist/spatial/Haversine.js +0 -48
  448. package/dist/spatial/Haversine.js.map +0 -1
  449. package/dist/spatial/index.d.ts.map +0 -1
  450. package/dist/storage/ContentContainer.d.ts +0 -87
  451. package/dist/storage/ContentContainer.d.ts.map +0 -1
  452. package/dist/storage/ContentContainer.js +0 -131
  453. package/dist/storage/ContentContainer.js.map +0 -1
  454. package/dist/storage/LocalForageAdapter.d.ts +0 -36
  455. package/dist/storage/LocalForageAdapter.d.ts.map +0 -1
  456. package/dist/storage/LocalForageAdapter.js +0 -82
  457. package/dist/storage/LocalForageAdapter.js.map +0 -1
  458. package/dist/storage/LocalStorageAdapter.d.ts +0 -16
  459. package/dist/storage/LocalStorageAdapter.d.ts.map +0 -1
  460. package/dist/storage/LocalStorageAdapter.js +0 -50
  461. package/dist/storage/LocalStorageAdapter.js.map +0 -1
  462. package/dist/storage/MediaProviderFromContainer.d.ts +0 -21
  463. package/dist/storage/MediaProviderFromContainer.d.ts.map +0 -1
  464. package/dist/storage/MediaProviderFromContainer.js +0 -79
  465. package/dist/storage/MediaProviderFromContainer.js.map +0 -1
  466. package/dist/storage/MemoryStorageAdapter.d.ts +0 -15
  467. package/dist/storage/MemoryStorageAdapter.d.ts.map +0 -1
  468. package/dist/storage/MemoryStorageAdapter.js +0 -34
  469. package/dist/storage/MemoryStorageAdapter.js.map +0 -1
  470. package/dist/storage/Storage.d.ts +0 -23
  471. package/dist/storage/Storage.d.ts.map +0 -1
  472. package/dist/storage/Storage.js +0 -9
  473. package/dist/storage/Storage.js.map +0 -1
  474. package/dist/storage/index.d.ts.map +0 -1
  475. package/dist/themeLibrary-CEo2D4b6.d.ts +0 -44
  476. package/dist/timing/index.d.ts.map +0 -1
  477. package/dist/timing/narrationTiming.d.ts +0 -54
  478. package/dist/timing/narrationTiming.d.ts.map +0 -1
  479. package/dist/timing/narrationTiming.js +0 -190
  480. package/dist/timing/narrationTiming.js.map +0 -1
  481. package/dist/timing/readingTime.d.ts +0 -54
  482. package/dist/timing/readingTime.d.ts.map +0 -1
  483. package/dist/timing/readingTime.js +0 -49
  484. package/dist/timing/readingTime.js.map +0 -1
  485. package/dist/transform/applyTransform.d.ts +0 -22
  486. package/dist/transform/applyTransform.d.ts.map +0 -1
  487. package/dist/transform/applyTransform.js +0 -68
  488. package/dist/transform/applyTransform.js.map +0 -1
  489. package/dist/transform/blockAnalyzer.d.ts +0 -48
  490. package/dist/transform/blockAnalyzer.d.ts.map +0 -1
  491. package/dist/transform/blockAnalyzer.js +0 -135
  492. package/dist/transform/blockAnalyzer.js.map +0 -1
  493. package/dist/transform/index.d.ts.map +0 -1
  494. package/dist/transform/registry.d.ts +0 -19
  495. package/dist/transform/registry.d.ts.map +0 -1
  496. package/dist/transform/registry.js +0 -41
  497. package/dist/transform/registry.js.map +0 -1
  498. package/dist/transform/styles/dataDriven.d.ts +0 -4
  499. package/dist/transform/styles/dataDriven.d.ts.map +0 -1
  500. package/dist/transform/styles/dataDriven.js +0 -15
  501. package/dist/transform/styles/dataDriven.js.map +0 -1
  502. package/dist/transform/styles/documentary.d.ts +0 -4
  503. package/dist/transform/styles/documentary.d.ts.map +0 -1
  504. package/dist/transform/styles/documentary.js +0 -15
  505. package/dist/transform/styles/documentary.js.map +0 -1
  506. package/dist/transform/styles/index.d.ts +0 -6
  507. package/dist/transform/styles/index.d.ts.map +0 -1
  508. package/dist/transform/styles/index.js +0 -6
  509. package/dist/transform/styles/index.js.map +0 -1
  510. package/dist/transform/styles/magazine.d.ts +0 -4
  511. package/dist/transform/styles/magazine.d.ts.map +0 -1
  512. package/dist/transform/styles/magazine.js +0 -15
  513. package/dist/transform/styles/magazine.js.map +0 -1
  514. package/dist/transform/styles/minimal.d.ts +0 -4
  515. package/dist/transform/styles/minimal.d.ts.map +0 -1
  516. package/dist/transform/styles/minimal.js +0 -15
  517. package/dist/transform/styles/minimal.js.map +0 -1
  518. package/dist/transform/styles/narrative.d.ts +0 -4
  519. package/dist/transform/styles/narrative.d.ts.map +0 -1
  520. package/dist/transform/styles/narrative.js +0 -15
  521. package/dist/transform/styles/narrative.js.map +0 -1
  522. package/dist/transform/templateSelector.d.ts +0 -30
  523. package/dist/transform/templateSelector.d.ts.map +0 -1
  524. package/dist/transform/templateSelector.js +0 -218
  525. package/dist/transform/templateSelector.js.map +0 -1
  526. package/dist/transform/timingAllocator.d.ts +0 -21
  527. package/dist/transform/timingAllocator.d.ts.map +0 -1
  528. package/dist/transform/timingAllocator.js +0 -111
  529. package/dist/transform/timingAllocator.js.map +0 -1
  530. package/dist/transform/types.d.ts +0 -86
  531. package/dist/transform/types.d.ts.map +0 -1
  532. package/dist/transform/types.js +0 -9
  533. package/dist/transform/types.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Icons barrel — subpath `@bendyline/squisq/icons`.
3
+ *
4
+ * Exposes the FontAwesome Free catalog (`ICONS`, `IconEntry`,
5
+ * `IconFamily`) and the lookup helpers (`resolveIcon`,
6
+ * `canonicalIconToken`, `looksLikeIconToken`).
7
+ */
8
+
9
+ export { ICONS } from './iconData.js';
10
+ export type { IconEntry, IconFamily } from './iconData.js';
11
+ export {
12
+ resolveIcon,
13
+ canonicalIconToken,
14
+ looksLikeIconToken,
15
+ suggestIcons,
16
+ iconGlyph,
17
+ } from './resolve.js';
18
+ export type { IconSuggestion } from './resolve.js';
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Icon Resolver
3
+ *
4
+ * Looks up a user-supplied token against the FontAwesome Free catalog.
5
+ *
6
+ * Tokens come in two forms:
7
+ * - **Bare** (`github`) — resolves only when the name is unique across
8
+ * families. The picker prefers this form for friendliness.
9
+ * - **Qualified** (`fa-brands:github`, `fa-solid:user`) — required when
10
+ * the name collides across families (e.g. `user` ships in both solid
11
+ * and regular).
12
+ *
13
+ * Returning `null` from `resolveIcon` is a deliberate signal: callers
14
+ * should render the source token verbatim so authors can write
15
+ * `{[notathing]}` in prose without it being silently swallowed.
16
+ */
17
+
18
+ import { ICONS } from './iconData.js';
19
+ import type { IconEntry, IconFamily } from './iconData.js';
20
+
21
+ // Index built once per module: name → IconEntry[]. Multi-entry buckets
22
+ // indicate cross-family collisions that require a qualified token.
23
+ const BY_NAME = new Map<string, IconEntry[]>();
24
+ for (const entry of ICONS) {
25
+ const bucket = BY_NAME.get(entry.name);
26
+ if (bucket) bucket.push(entry);
27
+ else BY_NAME.set(entry.name, [entry]);
28
+ }
29
+
30
+ const FAMILY_PREFIXES: Record<string, IconFamily> = {
31
+ 'fa-brands': 'brands',
32
+ 'fa-solid': 'solid',
33
+ 'fa-regular': 'regular',
34
+ brands: 'brands',
35
+ solid: 'solid',
36
+ regular: 'regular',
37
+ };
38
+
39
+ /**
40
+ * True when a token looks like an icon reference. Used by the markdown
41
+ * parser to decide whether a `{[…]}` body in a heading is worth probing
42
+ * (the heading-template path already extracted the trailing annotation
43
+ * by the time inline detection runs).
44
+ */
45
+ export function looksLikeIconToken(token: string): boolean {
46
+ return /^[a-zA-Z0-9_:-]+$/.test(token);
47
+ }
48
+
49
+ /**
50
+ * Resolve a token to an icon. Returns `null` when the token is unknown
51
+ * OR when the bare form collides — callers should render `null` results
52
+ * as literal markdown text so authors can opt out by qualifying.
53
+ */
54
+ export function resolveIcon(token: string): IconEntry | null {
55
+ if (!looksLikeIconToken(token)) return null;
56
+ // Qualified form: `<family-prefix>:<name>`.
57
+ const colon = token.indexOf(':');
58
+ if (colon > 0) {
59
+ const prefix = token.slice(0, colon);
60
+ const name = token.slice(colon + 1);
61
+ const family = FAMILY_PREFIXES[prefix];
62
+ if (!family) return null;
63
+ const bucket = BY_NAME.get(name);
64
+ return bucket?.find((e) => e.family === family) ?? null;
65
+ }
66
+ // Bare form: must be unique across families.
67
+ const bucket = BY_NAME.get(token);
68
+ if (!bucket || bucket.length !== 1) return null;
69
+ return bucket[0];
70
+ }
71
+
72
+ /**
73
+ * The canonical "shortest unambiguous" token for an icon — bare if the
74
+ * name is unique, otherwise the qualified form. Used when serializing
75
+ * a resolved icon back to markdown so we don't gratuitously qualify
76
+ * names that don't need it.
77
+ */
78
+ export function canonicalIconToken(entry: IconEntry): string {
79
+ const bucket = BY_NAME.get(entry.name);
80
+ if (bucket && bucket.length === 1) return entry.name;
81
+ return `fa-${entry.family}:${entry.name}`;
82
+ }
83
+
84
+ /**
85
+ * Render the icon's Unicode codepoint as a single-character string.
86
+ * Returns `''` for entries without a codepoint (defensive — FA always
87
+ * has one). Used by editors and previews that want to display the
88
+ * actual glyph using the FontAwesome font; pair with the right
89
+ * font-family/font-weight for the icon's family.
90
+ */
91
+ export function iconGlyph(entry: IconEntry): string {
92
+ if (!entry.unicode) return '';
93
+ const code = parseInt(entry.unicode, 16);
94
+ if (!Number.isFinite(code)) return '';
95
+ return String.fromCodePoint(code);
96
+ }
97
+
98
+ /** Single autocomplete suggestion. `score` ranks lower-is-better:
99
+ * 0 = name starts-with query, 1 = name contains query,
100
+ * 2 = keyword contains query. */
101
+ export interface IconSuggestion {
102
+ entry: IconEntry;
103
+ token: string;
104
+ score: number;
105
+ }
106
+
107
+ /**
108
+ * Rank icons against a partial token typed by the user. Used by the
109
+ * Monaco completion provider in editor-react and re-exported so any
110
+ * host can build its own typeahead.
111
+ */
112
+ export function suggestIcons(query: string, limit = 50): IconSuggestion[] {
113
+ const q = query.trim().toLowerCase();
114
+ const matches: IconSuggestion[] = [];
115
+ for (const icon of ICONS) {
116
+ let score = -1;
117
+ if (!q) {
118
+ // Empty query — surface a few common icons. We just walk in
119
+ // order; the limit cuts it off.
120
+ score = 3;
121
+ } else if (icon.name.startsWith(q)) {
122
+ score = 0;
123
+ } else if (icon.name.includes(q)) {
124
+ score = 1;
125
+ } else if (icon.keywords.includes(q)) {
126
+ score = 2;
127
+ }
128
+ if (score < 0) continue;
129
+ matches.push({ entry: icon, token: canonicalIconToken(icon), score });
130
+ if (matches.length > 400) break; // hard cap before sort
131
+ }
132
+ matches.sort(
133
+ (a, b) =>
134
+ a.score - b.score || a.token.length - b.token.length || a.token.localeCompare(b.token),
135
+ );
136
+ return matches.slice(0, limit);
137
+ }
138
+
139
+ export type { IconEntry, IconFamily };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Convenience wrapper around the image-edit version operations that
3
+ * captures a {@link ContentContainer} reference and an optional basename
4
+ * override. Mirrors `DocumentVersionManager` in shape.
5
+ */
6
+
7
+ import type { ContentContainer } from '../storage/ContentContainer.js';
8
+ import type { CoalesceOptions, PrunePolicy, Version } from '../versions/types.js';
9
+ import {
10
+ coalesceImageEditVersions,
11
+ listImageEditVersions,
12
+ pruneImageEditVersions,
13
+ readImageEditVersion,
14
+ revertToImageEditVersion,
15
+ saveImageEditVersion,
16
+ type RevertImageEditOptions,
17
+ type SaveImageEditVersionOptions,
18
+ type SaveImageEditVersionResult,
19
+ } from './versions.js';
20
+ import type { ImageEditDoc } from '../schemas/ImageEditDoc.js';
21
+
22
+ export interface ImageEditVersionManagerOptions {
23
+ /** Override the basename used in version filenames. Defaults to `'state'`. */
24
+ basename?: string;
25
+ /** Override the source filename inside the sidecar. Defaults to `state.json`. */
26
+ stateFilename?: string;
27
+ }
28
+
29
+ export class ImageEditVersionManager {
30
+ private readonly container: ContentContainer;
31
+ private readonly basename: string | undefined;
32
+ private readonly stateFilename: string | undefined;
33
+
34
+ constructor(container: ContentContainer, options: ImageEditVersionManagerOptions = {}) {
35
+ this.container = container;
36
+ this.basename = options.basename;
37
+ this.stateFilename = options.stateFilename;
38
+ }
39
+
40
+ saveVersion(options: SaveImageEditVersionOptions = {}): Promise<SaveImageEditVersionResult> {
41
+ return saveImageEditVersion(this.container, {
42
+ basename: this.basename,
43
+ stateFilename: this.stateFilename,
44
+ ...options,
45
+ });
46
+ }
47
+
48
+ listVersions(): Promise<Version[]> {
49
+ return listImageEditVersions(this.container, this.basename);
50
+ }
51
+
52
+ readVersion(version: Version | string): Promise<ImageEditDoc | null> {
53
+ return readImageEditVersion(this.container, version);
54
+ }
55
+
56
+ revertToVersion(
57
+ version: Version | string,
58
+ options: RevertImageEditOptions = {},
59
+ ): Promise<{ reverted: boolean; snapshotted: Version | null }> {
60
+ return revertToImageEditVersion(this.container, version, {
61
+ stateFilename: this.stateFilename,
62
+ ...options,
63
+ });
64
+ }
65
+
66
+ pruneVersions(policy: PrunePolicy): Promise<Version[]> {
67
+ return pruneImageEditVersions(this.container, policy, this.basename);
68
+ }
69
+
70
+ coalesceVersions(options: CoalesceOptions = {}): Promise<Version[]> {
71
+ return coalesceImageEditVersions(this.container, options, this.basename);
72
+ }
73
+ }
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Rasterize an {@link ImageEditDoc} to a PNG / JPEG / WebP {@link Blob}.
3
+ *
4
+ * The pipeline:
5
+ * 1. Build an SVG string mirroring the canvas + layers, with image
6
+ * hrefs inlined as `data:` URLs (so the canvas isn't tainted).
7
+ * 2. Wrap the SVG in a `Blob` and load it through an `Image`.
8
+ * 3. Draw it onto a canvas (`OffscreenCanvas` when available,
9
+ * otherwise a detached `HTMLCanvasElement`).
10
+ * 4. Convert the canvas to the requested format.
11
+ *
12
+ * Browser-only — calls `Image`, `URL.createObjectURL`, and canvas APIs.
13
+ * Returns a Promise that rejects on SVG parse / load failures.
14
+ */
15
+
16
+ import type { ContentContainer } from '../storage/ContentContainer.js';
17
+ import type { ImageEditCanvas, ImageEditDoc, ImageEditLayer } from '../schemas/ImageEditDoc.js';
18
+ import type { Position } from '../schemas/Doc.js';
19
+
20
+ /** Supported export formats. */
21
+ export type ImageEditExportFormat = 'png' | 'jpeg' | 'webp';
22
+
23
+ /** Options for {@link exportImageEditDoc}. */
24
+ export interface ImageEditExportOptions {
25
+ /** Output format. Defaults to `'png'`. */
26
+ format?: ImageEditExportFormat;
27
+ /** JPEG/WebP quality 0..1. Defaults to `0.92`. Ignored for PNG. */
28
+ quality?: number;
29
+ /** Resolution scale factor. Defaults to `1` (canvas pixels = output pixels). */
30
+ scale?: number;
31
+ }
32
+
33
+ const FORMAT_MIME: Record<ImageEditExportFormat, string> = {
34
+ png: 'image/png',
35
+ jpeg: 'image/jpeg',
36
+ webp: 'image/webp',
37
+ };
38
+
39
+ /**
40
+ * Export the doc to a raster {@link Blob}. Asset bytes referenced by image
41
+ * layers are read from `container` and inlined as data URLs.
42
+ */
43
+ export async function exportImageEditDoc(
44
+ doc: ImageEditDoc,
45
+ container: ContentContainer,
46
+ options: ImageEditExportOptions = {},
47
+ ): Promise<Blob> {
48
+ const format: ImageEditExportFormat = options.format ?? 'png';
49
+ const quality = options.quality ?? 0.92;
50
+ const scale = options.scale ?? 1;
51
+ const mime = FORMAT_MIME[format];
52
+
53
+ const svg = await buildSvgString(doc, container);
54
+ const outW = Math.max(1, Math.round(doc.canvas.width * scale));
55
+ const outH = Math.max(1, Math.round(doc.canvas.height * scale));
56
+
57
+ const img = await loadSvgImage(svg);
58
+ const canvas = createRasterCanvas(outW, outH);
59
+ const ctx = canvas.getContext('2d');
60
+ if (!ctx) throw new Error('exportImageEditDoc: 2D canvas context unavailable');
61
+
62
+ // For JPEG (no alpha), pre-fill with the canvas background or white so
63
+ // transparent regions aren't rendered as black.
64
+ if (format === 'jpeg') {
65
+ const bg =
66
+ doc.canvas.background && doc.canvas.background !== 'transparent'
67
+ ? doc.canvas.background
68
+ : '#ffffff';
69
+ ctx.fillStyle = bg;
70
+ ctx.fillRect(0, 0, outW, outH);
71
+ }
72
+
73
+ ctx.drawImage(img as CanvasImageSource, 0, 0, outW, outH);
74
+ return canvasToBlob(canvas, mime, quality);
75
+ }
76
+
77
+ // ============================================
78
+ // SVG construction
79
+ // ============================================
80
+
81
+ async function buildSvgString(doc: ImageEditDoc, container: ContentContainer): Promise<string> {
82
+ const { width, height, background } = doc.canvas;
83
+ const bgRect =
84
+ background && background !== 'transparent'
85
+ ? `<rect width="${width}" height="${height}" fill="${escapeAttr(background)}"/>`
86
+ : '';
87
+
88
+ const layerXml: string[] = [];
89
+ for (const layer of doc.layers) {
90
+ if (layer.visible === false) continue;
91
+ layerXml.push(await renderLayerToSvg(layer, doc.canvas, container));
92
+ }
93
+
94
+ return (
95
+ `<?xml version="1.0" encoding="UTF-8"?>` +
96
+ `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"` +
97
+ ` width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">` +
98
+ bgRect +
99
+ layerXml.join('') +
100
+ `</svg>`
101
+ );
102
+ }
103
+
104
+ async function renderLayerToSvg(
105
+ layer: ImageEditLayer,
106
+ canvas: ImageEditCanvas,
107
+ container: ContentContainer,
108
+ ): Promise<string> {
109
+ const opacity = layer.opacity ?? 1;
110
+ const opacityAttr = opacity < 1 ? ` opacity="${opacity}"` : '';
111
+ const inner = await renderLayerInner(layer, canvas, container);
112
+ return `<g data-layer-id="${escapeAttr(layer.id)}"${opacityAttr}>${inner}</g>`;
113
+ }
114
+
115
+ async function renderLayerInner(
116
+ layer: ImageEditLayer,
117
+ canvas: ImageEditCanvas,
118
+ container: ContentContainer,
119
+ ): Promise<string> {
120
+ const { x, y, w, h } = resolveBox(layer.position, canvas);
121
+
122
+ if (layer.type === 'image') {
123
+ const dataUrl = await loadAssetAsDataUrl(container, layer.content.src);
124
+ return (
125
+ `<image x="${x}" y="${y}" width="${w}" height="${h}"` +
126
+ ` preserveAspectRatio="${preserveAspectRatioFor(layer.content.fit)}"` +
127
+ ` xlink:href="${escapeAttr(dataUrl)}"/>`
128
+ );
129
+ }
130
+
131
+ if (layer.type === 'shape') {
132
+ const c = layer.content;
133
+ const fill = c.fill ?? 'none';
134
+ const stroke = c.stroke ? ` stroke="${escapeAttr(c.stroke)}"` : '';
135
+ const strokeWidth = c.strokeWidth ? ` stroke-width="${c.strokeWidth}"` : '';
136
+ if (c.shape === 'rect') {
137
+ const rx = c.borderRadius ? ` rx="${c.borderRadius}" ry="${c.borderRadius}"` : '';
138
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${escapeAttr(fill)}"${stroke}${strokeWidth}${rx}/>`;
139
+ }
140
+ if (c.shape === 'circle') {
141
+ const r = Math.min(w, h) / 2;
142
+ return `<circle cx="${x + w / 2}" cy="${y + h / 2}" r="${r}" fill="${escapeAttr(fill)}"${stroke}${strokeWidth}/>`;
143
+ }
144
+ // line
145
+ return `<line x1="${x}" y1="${y}" x2="${x + w}" y2="${y + h}"${stroke}${strokeWidth}/>`;
146
+ }
147
+
148
+ // text
149
+ const c = layer.content;
150
+ const style = c.style;
151
+ const lineHeight = style.lineHeight ?? 1.4;
152
+ const lineHeightPx = style.fontSize * lineHeight;
153
+ const lines = (c.text ?? '').split('\n');
154
+ const tspans = lines
155
+ .map(
156
+ (line, i) => `<tspan x="${x}" dy="${i === 0 ? 0 : lineHeightPx}">${escapeText(line)}</tspan>`,
157
+ )
158
+ .join('');
159
+ const fontFamily = style.fontFamily ?? 'sans-serif';
160
+ const fontWeight = style.fontWeight ?? 'normal';
161
+ const textAnchor =
162
+ style.textAlign === 'center' ? 'middle' : style.textAlign === 'right' ? 'end' : 'start';
163
+ return (
164
+ `<text x="${x}" y="${y + style.fontSize}" font-family="${escapeAttr(fontFamily)}"` +
165
+ ` font-size="${style.fontSize}" font-weight="${fontWeight}"` +
166
+ ` fill="${escapeAttr(style.color)}" text-anchor="${textAnchor}">` +
167
+ tspans +
168
+ `</text>`
169
+ );
170
+ }
171
+
172
+ function preserveAspectRatioFor(fit?: 'cover' | 'contain' | 'fill'): string {
173
+ if (fit === 'cover') return 'xMidYMid slice';
174
+ if (fit === 'fill') return 'none';
175
+ return 'xMidYMid meet'; // contain (default)
176
+ }
177
+
178
+ function resolveBox(
179
+ position: Position,
180
+ canvas: ImageEditCanvas,
181
+ ): { x: number; y: number; w: number; h: number } {
182
+ const x = resolveValue(position.x, canvas.width);
183
+ const y = resolveValue(position.y, canvas.height);
184
+ const w =
185
+ position.width !== undefined ? resolveValue(position.width, canvas.width) : canvas.width;
186
+ const h =
187
+ position.height !== undefined ? resolveValue(position.height, canvas.height) : canvas.height;
188
+ const offset = anchorOffset(position.anchor, w, h);
189
+ return { x: x + offset.x, y: y + offset.y, w, h };
190
+ }
191
+
192
+ function resolveValue(v: number | string, base: number): number {
193
+ if (typeof v === 'number') return v;
194
+ if (v.endsWith('%')) return (parseFloat(v) / 100) * base;
195
+ return parseFloat(v) || 0;
196
+ }
197
+
198
+ function anchorOffset(
199
+ anchor: Position['anchor'] | undefined,
200
+ w: number,
201
+ h: number,
202
+ ): { x: number; y: number } {
203
+ switch (anchor) {
204
+ case 'center':
205
+ return { x: -w / 2, y: -h / 2 };
206
+ case 'top-right':
207
+ return { x: -w, y: 0 };
208
+ case 'bottom-left':
209
+ return { x: 0, y: -h };
210
+ case 'bottom-right':
211
+ return { x: -w, y: -h };
212
+ case 'top-left':
213
+ default:
214
+ return { x: 0, y: 0 };
215
+ }
216
+ }
217
+
218
+ // ============================================
219
+ // Asset inlining
220
+ // ============================================
221
+
222
+ async function loadAssetAsDataUrl(container: ContentContainer, src: string): Promise<string> {
223
+ // Already a data: or absolute URL — pass through.
224
+ if (/^(data:|https?:|blob:)/.test(src)) return src;
225
+ const data = await container.readFile(src);
226
+ if (!data) throw new Error(`exportImageEditDoc: missing asset "${src}"`);
227
+ const list = await container.listFiles(src);
228
+ const mime = list.find((e) => e.path === src)?.mimeType ?? guessMime(src);
229
+ return arrayBufferToDataUrl(data, mime);
230
+ }
231
+
232
+ function arrayBufferToDataUrl(data: ArrayBuffer, mime: string): string {
233
+ const bytes = new Uint8Array(data);
234
+ let binary = '';
235
+ // chunk to avoid call-stack limits with large images
236
+ const CHUNK = 0x8000;
237
+ for (let i = 0; i < bytes.length; i += CHUNK) {
238
+ binary += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK)));
239
+ }
240
+ // btoa exists in browsers and modern Node (>= 16); fall back to Buffer for older runtimes.
241
+ const b64 = typeof btoa === 'function' ? btoa(binary) : Buffer.from(bytes).toString('base64');
242
+ return `data:${mime};base64,${b64}`;
243
+ }
244
+
245
+ function guessMime(path: string): string {
246
+ const dot = path.lastIndexOf('.');
247
+ const ext = dot >= 0 ? path.slice(dot).toLowerCase() : '';
248
+ switch (ext) {
249
+ case '.png':
250
+ return 'image/png';
251
+ case '.jpg':
252
+ case '.jpeg':
253
+ return 'image/jpeg';
254
+ case '.webp':
255
+ return 'image/webp';
256
+ case '.gif':
257
+ return 'image/gif';
258
+ case '.svg':
259
+ return 'image/svg+xml';
260
+ default:
261
+ return 'application/octet-stream';
262
+ }
263
+ }
264
+
265
+ // ============================================
266
+ // SVG → Image → Canvas → Blob
267
+ // ============================================
268
+
269
+ async function loadSvgImage(svg: string): Promise<HTMLImageElement> {
270
+ const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
271
+ const url = URL.createObjectURL(blob);
272
+ try {
273
+ return await new Promise<HTMLImageElement>((resolve, reject) => {
274
+ const img = new Image();
275
+ img.onload = () => resolve(img);
276
+ img.onerror = () => reject(new Error('exportImageEditDoc: SVG image failed to load'));
277
+ img.src = url;
278
+ });
279
+ } finally {
280
+ URL.revokeObjectURL(url);
281
+ }
282
+ }
283
+
284
+ type RasterCanvas = HTMLCanvasElement | OffscreenCanvas;
285
+
286
+ function createRasterCanvas(width: number, height: number): RasterCanvas {
287
+ if (typeof OffscreenCanvas !== 'undefined') {
288
+ return new OffscreenCanvas(width, height);
289
+ }
290
+ const c = document.createElement('canvas');
291
+ c.width = width;
292
+ c.height = height;
293
+ return c;
294
+ }
295
+
296
+ async function canvasToBlob(canvas: RasterCanvas, mime: string, quality: number): Promise<Blob> {
297
+ if ('convertToBlob' in canvas) {
298
+ return canvas.convertToBlob({ type: mime, quality });
299
+ }
300
+ return new Promise<Blob>((resolve, reject) => {
301
+ canvas.toBlob(
302
+ (blob) => {
303
+ if (blob) resolve(blob);
304
+ else reject(new Error('exportImageEditDoc: canvas.toBlob returned null'));
305
+ },
306
+ mime,
307
+ quality,
308
+ );
309
+ });
310
+ }
311
+
312
+ // ============================================
313
+ // XML escaping
314
+ // ============================================
315
+
316
+ function escapeAttr(s: string): string {
317
+ return s
318
+ .replace(/&/g, '&amp;')
319
+ .replace(/"/g, '&quot;')
320
+ .replace(/</g, '&lt;')
321
+ .replace(/>/g, '&gt;');
322
+ }
323
+
324
+ function escapeText(s: string): string {
325
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
326
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Image-editor primitives: schema re-exports, state helpers, persistence,
3
+ * version history, and SVG → raster export.
4
+ *
5
+ * See `@bendyline/squisq/imageEdit` for the public subpath entry.
6
+ */
7
+
8
+ // Schema (re-exported from schemas for ergonomic single-import)
9
+ export type {
10
+ ImageEditDoc,
11
+ ImageEditLayer,
12
+ ImageEditLayerKind,
13
+ ImageEditCanvas,
14
+ ImageEditMeta,
15
+ EditorLayerMeta,
16
+ } from '../schemas/ImageEditDoc.js';
17
+
18
+ // State helpers
19
+ export {
20
+ IMAGE_EDIT_STATE_FILENAME,
21
+ IMAGE_EDIT_ASSETS_PREFIX,
22
+ createEmptyImageEditDoc,
23
+ addLayer,
24
+ removeLayer,
25
+ reorderLayer,
26
+ updateLayer,
27
+ setCanvas,
28
+ touch,
29
+ } from './state.js';
30
+
31
+ // Persistence
32
+ export { readImageEditDoc, writeImageEditDoc } from './persistence.js';
33
+
34
+ // Version paths
35
+ export {
36
+ IMAGE_EDIT_VERSIONS_PREFIX,
37
+ IMAGE_EDIT_DEFAULT_BASENAME,
38
+ buildImageEditVersionPath,
39
+ parseImageEditVersionPath,
40
+ } from './versionPaths.js';
41
+
42
+ // Version operations
43
+ export {
44
+ saveImageEditVersion,
45
+ listImageEditVersions,
46
+ readImageEditVersion,
47
+ readImageEditVersionText,
48
+ revertToImageEditVersion,
49
+ pruneImageEditVersions,
50
+ coalesceImageEditVersions,
51
+ type SaveImageEditVersionOptions,
52
+ type SaveImageEditVersionResult,
53
+ type RevertImageEditOptions,
54
+ } from './versions.js';
55
+
56
+ // Manager
57
+ export {
58
+ ImageEditVersionManager,
59
+ type ImageEditVersionManagerOptions,
60
+ } from './ImageEditVersionManager.js';
61
+
62
+ // Note: shared version types (`Version`, `PrunePolicy`, `CoalesceOptions`)
63
+ // are re-exported from `@bendyline/squisq/versions` and the package root.
64
+ // Import them from there to avoid duplicating the export surface.
65
+
66
+ // Export
67
+ export {
68
+ exportImageEditDoc,
69
+ type ImageEditExportFormat,
70
+ type ImageEditExportOptions,
71
+ } from './export.js';
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Load and save {@link ImageEditDoc} JSON to a sidecar
3
+ * {@link ContentContainer} (typically a {@link ScopedContentContainer}
4
+ * rooted at `<basename>_files/`).
5
+ */
6
+
7
+ import type { ContentContainer } from '../storage/ContentContainer.js';
8
+ import type { ImageEditDoc } from '../schemas/ImageEditDoc.js';
9
+ import { IMAGE_EDIT_STATE_FILENAME } from './state.js';
10
+
11
+ const decoder = new TextDecoder();
12
+ const encoder = new TextEncoder();
13
+
14
+ /**
15
+ * Read `state.json` from the sidecar and parse it. Returns `null` when
16
+ * the file does not exist; throws on parse / shape errors.
17
+ */
18
+ export async function readImageEditDoc(
19
+ container: ContentContainer,
20
+ filename: string = IMAGE_EDIT_STATE_FILENAME,
21
+ ): Promise<ImageEditDoc | null> {
22
+ const data = await container.readFile(filename);
23
+ if (!data) return null;
24
+ const text = decoder.decode(data);
25
+ let parsed: unknown;
26
+ try {
27
+ parsed = JSON.parse(text);
28
+ } catch (err: unknown) {
29
+ const msg = err instanceof Error ? err.message : String(err);
30
+ throw new Error(`readImageEditDoc: ${filename} is not valid JSON: ${msg}`);
31
+ }
32
+ assertImageEditDoc(parsed, filename);
33
+ return parsed;
34
+ }
35
+
36
+ /** Serialize `doc` to JSON and write it to the sidecar. */
37
+ export async function writeImageEditDoc(
38
+ container: ContentContainer,
39
+ doc: ImageEditDoc,
40
+ filename: string = IMAGE_EDIT_STATE_FILENAME,
41
+ ): Promise<void> {
42
+ const text = JSON.stringify(doc, null, 2);
43
+ await container.writeFile(filename, encoder.encode(text), 'application/json');
44
+ }
45
+
46
+ function assertImageEditDoc(value: unknown, filename: string): asserts value is ImageEditDoc {
47
+ if (!value || typeof value !== 'object') {
48
+ throw new Error(`readImageEditDoc: ${filename} root must be an object`);
49
+ }
50
+ const v = value as Partial<ImageEditDoc>;
51
+ if (v.version !== 1) {
52
+ throw new Error(
53
+ `readImageEditDoc: ${filename} has unsupported schema version ${String(v.version)} (expected 1)`,
54
+ );
55
+ }
56
+ if (!v.canvas || typeof v.canvas.width !== 'number' || typeof v.canvas.height !== 'number') {
57
+ throw new Error(`readImageEditDoc: ${filename} canvas.width/height must be numbers`);
58
+ }
59
+ if (!Array.isArray(v.layers)) {
60
+ throw new Error(`readImageEditDoc: ${filename} layers must be an array`);
61
+ }
62
+ }