@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.
- package/dist/{Doc-Ch4tc3vv.d.ts → Doc-BeSeH2iX.d.ts} +118 -41
- package/dist/ImageEditDoc-BTIvw0oq.d.ts +84 -0
- package/dist/{chunk-CYQHG27J.js → chunk-2TMAWB4Y.js} +74 -1
- package/dist/chunk-2TMAWB4Y.js.map +1 -0
- package/dist/{chunk-OASJKWO5.js → chunk-3K5OG6KI.js} +92 -7
- package/dist/chunk-3K5OG6KI.js.map +1 -0
- package/dist/chunk-57CVQCFX.js +193 -0
- package/dist/chunk-57CVQCFX.js.map +1 -0
- package/dist/{chunk-QM5PKNPW.js → chunk-5WFRKURX.js} +1 -1
- package/dist/chunk-5WFRKURX.js.map +1 -0
- package/dist/chunk-B4EEON3N.js +1317 -0
- package/dist/chunk-B4EEON3N.js.map +1 -0
- package/dist/chunk-DBS6RIFL.js +81 -0
- package/dist/chunk-DBS6RIFL.js.map +1 -0
- package/dist/{chunk-MIBRIRGJ.js → chunk-FR2RBTKO.js} +3 -3
- package/dist/{chunk-MIBRIRGJ.js.map → chunk-FR2RBTKO.js.map} +1 -1
- package/dist/chunk-H3AXU4MJ.js +179 -0
- package/dist/chunk-H3AXU4MJ.js.map +1 -0
- package/dist/chunk-JELIRVDP.js +24 -0
- package/dist/chunk-JELIRVDP.js.map +1 -0
- package/dist/chunk-K32VJONL.js +193 -0
- package/dist/chunk-K32VJONL.js.map +1 -0
- package/dist/{chunk-6BAVXFYC.js → chunk-KSWJR66U.js} +218 -42
- package/dist/chunk-KSWJR66U.js.map +1 -0
- package/dist/{chunk-TSG2OSE6.js → chunk-MYIH7FWD.js} +3 -3
- package/dist/chunk-MYIH7FWD.js.map +1 -0
- package/dist/chunk-VLJJHDAC.js +218 -0
- package/dist/chunk-VLJJHDAC.js.map +1 -0
- package/dist/chunk-WMOI7FN3.js +182 -0
- package/dist/chunk-WMOI7FN3.js.map +1 -0
- package/dist/chunk-ZTX4PFFT.js +520 -0
- package/dist/chunk-ZTX4PFFT.js.map +1 -0
- package/dist/doc/index.d.ts +5 -10
- package/dist/doc/index.js +145 -7
- package/dist/doc/index.js.map +1 -1
- package/dist/generate/index.d.ts +99 -7
- package/dist/generate/index.js +16 -3
- package/dist/generate/index.js.map +1 -1
- package/dist/icons/index.d.ts +87 -0
- package/dist/icons/index.js +17 -0
- package/dist/icons/index.js.map +1 -0
- package/dist/imageEdit/index.d.ts +216 -0
- package/dist/imageEdit/index.js +54 -0
- package/dist/imageEdit/index.js.map +1 -0
- package/dist/index.d.ts +21 -9
- package/dist/index.js +461 -8
- package/dist/index.js.map +1 -1
- package/dist/jsonForm/index.d.ts +191 -0
- package/dist/jsonForm/index.js +27 -0
- package/dist/jsonForm/index.js.map +1 -0
- package/dist/markdown/index.d.ts +265 -19
- package/dist/markdown/index.js +39 -33
- package/dist/markdown/index.js.map +1 -1
- package/dist/random/index.d.ts +51 -2
- package/dist/random/index.js +9 -1
- package/dist/random/index.js.map +1 -1
- package/dist/recommend/index.d.ts +42 -0
- package/dist/recommend/index.js +10 -0
- package/dist/recommend/index.js.map +1 -0
- package/dist/schemas/index.d.ts +188 -10
- package/dist/schemas/index.js +120 -7
- package/dist/schemas/index.js.map +1 -1
- package/dist/spatial/index.d.ts +111 -3
- package/dist/spatial/index.js +24 -2
- package/dist/spatial/index.js.map +1 -1
- package/dist/storage/index.d.ts +159 -9
- package/dist/storage/index.js +20 -5
- package/dist/storage/index.js.map +1 -1
- package/dist/story/index.d.ts +53 -13
- package/dist/story/index.js +12 -4
- package/dist/themeLibrary-BHjJ0Qx6.d.ts +40 -0
- package/dist/timing/index.d.ts +109 -4
- package/dist/timing/index.js +23 -2
- package/dist/timing/index.js.map +1 -1
- package/dist/transform/index.d.ts +175 -6
- package/dist/transform/index.js +490 -3
- package/dist/transform/index.js.map +1 -1
- package/dist/{types-CkNttVSF.d.ts → types-BdZkdJ3z.d.ts} +17 -2
- package/dist/types-D98li1gV.d.ts +93 -0
- package/dist/versions/index.d.ts +128 -0
- package/dist/versions/index.js +35 -0
- package/dist/versions/index.js.map +1 -0
- package/package.json +28 -1
- package/src/__tests__/colorUtils.test.ts +112 -0
- package/src/__tests__/getLayers.test.ts +32 -7
- package/src/__tests__/iconResolve.test.ts +122 -0
- package/src/__tests__/imageEditState.test.ts +74 -0
- package/src/__tests__/imageEditVersions.test.ts +169 -0
- package/src/__tests__/inferDocumentTitle.test.ts +50 -0
- package/src/__tests__/jsonForm.chooseControl.test.ts +86 -0
- package/src/__tests__/jsonForm.evaluateWhen.test.ts +59 -0
- package/src/__tests__/jsonForm.inferSchema.test.ts +25 -0
- package/src/__tests__/jsonForm.pointer.test.ts +103 -0
- package/src/__tests__/markdownIcons.test.ts +104 -0
- package/src/__tests__/markdownToDoc.test.ts +19 -4
- package/src/__tests__/recommendTemplates.test.ts +183 -0
- package/src/__tests__/scopedContentContainer.test.ts +89 -0
- package/src/__tests__/slideshowGenerator.test.ts +1 -1
- package/src/__tests__/templateAnnotationParse.test.ts +42 -0
- package/src/__tests__/templates.test.ts +93 -9
- package/src/__tests__/themeCompile.test.ts +156 -0
- package/src/__tests__/themeValidator.test.ts +125 -0
- package/src/__tests__/versions.test.ts +415 -0
- package/src/doc/audioMapping.ts +55 -11
- package/src/doc/getLayers.ts +32 -21
- package/src/doc/markdownToDoc.ts +45 -7
- package/src/doc/templates/featureBlock.ts +202 -0
- package/src/doc/templates/fullBleedQuote.ts +13 -0
- package/src/doc/templates/index.ts +50 -16
- package/src/doc/templates/listBlock.ts +24 -5
- package/src/doc/templates/statHighlight.ts +13 -0
- package/src/doc/templates/titleBlock.ts +9 -0
- package/src/doc/utils/themeUtils.ts +8 -7
- package/src/generate/slideshowGenerator.ts +1 -1
- package/src/generate/templateMapper.ts +2 -2
- package/src/icons/iconData.ts +31 -0
- package/src/icons/index.ts +18 -0
- package/src/icons/resolve.ts +139 -0
- package/src/imageEdit/ImageEditVersionManager.ts +73 -0
- package/src/imageEdit/export.ts +326 -0
- package/src/imageEdit/index.ts +71 -0
- package/src/imageEdit/persistence.ts +62 -0
- package/src/imageEdit/state.ts +112 -0
- package/src/imageEdit/versionPaths.ts +57 -0
- package/src/imageEdit/versions.ts +231 -0
- package/src/index.ts +4 -0
- package/src/jsonForm/chooseControl.ts +88 -0
- package/src/jsonForm/evaluateWhen.ts +59 -0
- package/src/jsonForm/index.ts +22 -0
- package/src/jsonForm/inferSchema.ts +25 -0
- package/src/jsonForm/pointer.ts +109 -0
- package/src/jsonForm/types.ts +167 -0
- package/src/markdown/convert.ts +145 -5
- package/src/markdown/index.ts +3 -0
- package/src/markdown/types.ts +19 -2
- package/src/markdown/utils.ts +159 -0
- package/src/recommend/index.ts +16 -0
- package/src/recommend/templates.ts +273 -0
- package/src/schemas/BlockTemplates.ts +50 -7
- package/src/schemas/ImageEditDoc.ts +95 -0
- package/src/schemas/Theme.ts +98 -31
- package/src/schemas/colorUtils.ts +186 -0
- package/src/schemas/fontStacks.ts +257 -0
- package/src/schemas/index.ts +12 -0
- package/src/schemas/themeCompile.ts +203 -0
- package/src/schemas/themeLibrary.ts +18 -597
- package/src/schemas/themeValidator.ts +388 -0
- package/src/schemas/themes/bold.json +48 -0
- package/src/schemas/themes/cinematic.json +54 -0
- package/src/schemas/themes/documentary.json +49 -0
- package/src/schemas/themes/gezellig.json +44 -0
- package/src/schemas/themes/index.ts +53 -0
- package/src/schemas/themes/magazine.json +44 -0
- package/src/schemas/themes/minimalist.json +45 -0
- package/src/schemas/themes/morning-light.json +42 -0
- package/src/schemas/themes/standard-dark.json +50 -0
- package/src/schemas/themes/standard.json +42 -0
- package/src/schemas/themes/tech-dark.json +48 -0
- package/src/schemas/themes/warm-earth.json +50 -0
- package/src/storage/ScopedContentContainer.ts +113 -0
- package/src/storage/index.ts +1 -0
- package/src/versions/DocumentVersionManager.ts +70 -0
- package/src/versions/index.ts +54 -0
- package/src/versions/operations.ts +230 -0
- package/src/versions/paths.ts +66 -0
- package/src/versions/timestamp.ts +48 -0
- package/src/versions/types.ts +91 -0
- package/dist/__tests__/animationUtils.test.d.ts +0 -2
- package/dist/__tests__/animationUtils.test.d.ts.map +0 -1
- package/dist/__tests__/animationUtils.test.js +0 -60
- package/dist/__tests__/animationUtils.test.js.map +0 -1
- package/dist/__tests__/contentContainer.test.d.ts +0 -2
- package/dist/__tests__/contentContainer.test.d.ts.map +0 -1
- package/dist/__tests__/contentContainer.test.js +0 -234
- package/dist/__tests__/contentContainer.test.js.map +0 -1
- package/dist/__tests__/contentExtractor.test.d.ts +0 -2
- package/dist/__tests__/contentExtractor.test.d.ts.map +0 -1
- package/dist/__tests__/contentExtractor.test.js +0 -151
- package/dist/__tests__/contentExtractor.test.js.map +0 -1
- package/dist/__tests__/coverBlock.test.d.ts +0 -2
- package/dist/__tests__/coverBlock.test.d.ts.map +0 -1
- package/dist/__tests__/coverBlock.test.js +0 -197
- package/dist/__tests__/coverBlock.test.js.map +0 -1
- package/dist/__tests__/dataTable.test.d.ts +0 -2
- package/dist/__tests__/dataTable.test.d.ts.map +0 -1
- package/dist/__tests__/dataTable.test.js +0 -110
- package/dist/__tests__/dataTable.test.js.map +0 -1
- package/dist/__tests__/geohash.test.d.ts +0 -2
- package/dist/__tests__/geohash.test.d.ts.map +0 -1
- package/dist/__tests__/geohash.test.js +0 -87
- package/dist/__tests__/geohash.test.js.map +0 -1
- package/dist/__tests__/getLayers.test.d.ts +0 -2
- package/dist/__tests__/getLayers.test.d.ts.map +0 -1
- package/dist/__tests__/getLayers.test.js +0 -167
- package/dist/__tests__/getLayers.test.js.map +0 -1
- package/dist/__tests__/haversine.test.d.ts +0 -2
- package/dist/__tests__/haversine.test.d.ts.map +0 -1
- package/dist/__tests__/haversine.test.js +0 -56
- package/dist/__tests__/haversine.test.js.map +0 -1
- package/dist/__tests__/localForageAdapter.test.d.ts +0 -8
- package/dist/__tests__/localForageAdapter.test.d.ts.map +0 -1
- package/dist/__tests__/localForageAdapter.test.js +0 -130
- package/dist/__tests__/localForageAdapter.test.js.map +0 -1
- package/dist/__tests__/markdown.test.d.ts +0 -2
- package/dist/__tests__/markdown.test.d.ts.map +0 -1
- package/dist/__tests__/markdown.test.js +0 -715
- package/dist/__tests__/markdown.test.js.map +0 -1
- package/dist/__tests__/markdownToDoc.test.d.ts +0 -2
- package/dist/__tests__/markdownToDoc.test.d.ts.map +0 -1
- package/dist/__tests__/markdownToDoc.test.js +0 -447
- package/dist/__tests__/markdownToDoc.test.js.map +0 -1
- package/dist/__tests__/seededRandom.test.d.ts +0 -2
- package/dist/__tests__/seededRandom.test.d.ts.map +0 -1
- package/dist/__tests__/seededRandom.test.js +0 -125
- package/dist/__tests__/seededRandom.test.js.map +0 -1
- package/dist/__tests__/slideshowGenerator.test.d.ts +0 -2
- package/dist/__tests__/slideshowGenerator.test.d.ts.map +0 -1
- package/dist/__tests__/slideshowGenerator.test.js +0 -114
- package/dist/__tests__/slideshowGenerator.test.js.map +0 -1
- package/dist/__tests__/storage.test.d.ts +0 -2
- package/dist/__tests__/storage.test.d.ts.map +0 -1
- package/dist/__tests__/storage.test.js +0 -51
- package/dist/__tests__/storage.test.js.map +0 -1
- package/dist/__tests__/surface.test.d.ts +0 -2
- package/dist/__tests__/surface.test.d.ts.map +0 -1
- package/dist/__tests__/surface.test.js +0 -57
- package/dist/__tests__/surface.test.js.map +0 -1
- package/dist/__tests__/templates.test.d.ts +0 -2
- package/dist/__tests__/templates.test.d.ts.map +0 -1
- package/dist/__tests__/templates.test.js +0 -217
- package/dist/__tests__/templates.test.js.map +0 -1
- package/dist/__tests__/timing.test.d.ts +0 -2
- package/dist/__tests__/timing.test.d.ts.map +0 -1
- package/dist/__tests__/timing.test.js +0 -131
- package/dist/__tests__/timing.test.js.map +0 -1
- package/dist/chunk-6BAVXFYC.js.map +0 -1
- package/dist/chunk-7GF6RDED.js +0 -82
- package/dist/chunk-7GF6RDED.js.map +0 -1
- package/dist/chunk-CYQHG27J.js.map +0 -1
- package/dist/chunk-F22AE6BY.js +0 -121
- package/dist/chunk-F22AE6BY.js.map +0 -1
- package/dist/chunk-FIQR4AJG.js +0 -517
- package/dist/chunk-FIQR4AJG.js.map +0 -1
- package/dist/chunk-OASJKWO5.js.map +0 -1
- package/dist/chunk-QM5PKNPW.js.map +0 -1
- package/dist/chunk-TSG2OSE6.js.map +0 -1
- package/dist/doc/audioMapping.d.ts +0 -36
- package/dist/doc/audioMapping.d.ts.map +0 -1
- package/dist/doc/audioMapping.js +0 -315
- package/dist/doc/audioMapping.js.map +0 -1
- package/dist/doc/docToMarkdown.d.ts +0 -41
- package/dist/doc/docToMarkdown.d.ts.map +0 -1
- package/dist/doc/docToMarkdown.js +0 -88
- package/dist/doc/docToMarkdown.js.map +0 -1
- package/dist/doc/getLayers.d.ts +0 -65
- package/dist/doc/getLayers.d.ts.map +0 -1
- package/dist/doc/getLayers.js +0 -103
- package/dist/doc/getLayers.js.map +0 -1
- package/dist/doc/index.d.ts.map +0 -1
- package/dist/doc/markdownToDoc.d.ts +0 -75
- package/dist/doc/markdownToDoc.d.ts.map +0 -1
- package/dist/doc/markdownToDoc.js +0 -398
- package/dist/doc/markdownToDoc.js.map +0 -1
- package/dist/doc/templates/accentImage.d.ts +0 -59
- package/dist/doc/templates/accentImage.d.ts.map +0 -1
- package/dist/doc/templates/accentImage.js +0 -275
- package/dist/doc/templates/accentImage.js.map +0 -1
- package/dist/doc/templates/captionUtils.d.ts +0 -20
- package/dist/doc/templates/captionUtils.d.ts.map +0 -1
- package/dist/doc/templates/captionUtils.js +0 -29
- package/dist/doc/templates/captionUtils.js.map +0 -1
- package/dist/doc/templates/comparisonBar.d.ts +0 -13
- package/dist/doc/templates/comparisonBar.d.ts.map +0 -1
- package/dist/doc/templates/comparisonBar.js +0 -145
- package/dist/doc/templates/comparisonBar.js.map +0 -1
- package/dist/doc/templates/coverBlock.d.ts +0 -51
- package/dist/doc/templates/coverBlock.d.ts.map +0 -1
- package/dist/doc/templates/coverBlock.js +0 -153
- package/dist/doc/templates/coverBlock.js.map +0 -1
- package/dist/doc/templates/dataTable.d.ts +0 -14
- package/dist/doc/templates/dataTable.d.ts.map +0 -1
- package/dist/doc/templates/dataTable.js +0 -73
- package/dist/doc/templates/dataTable.js.map +0 -1
- package/dist/doc/templates/dateEvent.d.ts +0 -15
- package/dist/doc/templates/dateEvent.d.ts.map +0 -1
- package/dist/doc/templates/dateEvent.js +0 -114
- package/dist/doc/templates/dateEvent.js.map +0 -1
- package/dist/doc/templates/definitionCard.d.ts +0 -13
- package/dist/doc/templates/definitionCard.d.ts.map +0 -1
- package/dist/doc/templates/definitionCard.js +0 -117
- package/dist/doc/templates/definitionCard.js.map +0 -1
- package/dist/doc/templates/factCard.d.ts +0 -15
- package/dist/doc/templates/factCard.d.ts.map +0 -1
- package/dist/doc/templates/factCard.js +0 -104
- package/dist/doc/templates/factCard.js.map +0 -1
- package/dist/doc/templates/fullBleedQuote.d.ts +0 -13
- package/dist/doc/templates/fullBleedQuote.d.ts.map +0 -1
- package/dist/doc/templates/fullBleedQuote.js +0 -55
- package/dist/doc/templates/fullBleedQuote.js.map +0 -1
- package/dist/doc/templates/imageWithCaption.d.ts +0 -13
- package/dist/doc/templates/imageWithCaption.d.ts.map +0 -1
- package/dist/doc/templates/imageWithCaption.js +0 -146
- package/dist/doc/templates/imageWithCaption.js.map +0 -1
- package/dist/doc/templates/index.d.ts +0 -105
- package/dist/doc/templates/index.d.ts.map +0 -1
- package/dist/doc/templates/index.js +0 -445
- package/dist/doc/templates/index.js.map +0 -1
- package/dist/doc/templates/listBlock.d.ts +0 -13
- package/dist/doc/templates/listBlock.d.ts.map +0 -1
- package/dist/doc/templates/listBlock.js +0 -92
- package/dist/doc/templates/listBlock.js.map +0 -1
- package/dist/doc/templates/mapBlock.d.ts +0 -17
- package/dist/doc/templates/mapBlock.d.ts.map +0 -1
- package/dist/doc/templates/mapBlock.js +0 -115
- package/dist/doc/templates/mapBlock.js.map +0 -1
- package/dist/doc/templates/persistentLayers.d.ts +0 -39
- package/dist/doc/templates/persistentLayers.d.ts.map +0 -1
- package/dist/doc/templates/persistentLayers.js +0 -447
- package/dist/doc/templates/persistentLayers.js.map +0 -1
- package/dist/doc/templates/photoGrid.d.ts +0 -20
- package/dist/doc/templates/photoGrid.d.ts.map +0 -1
- package/dist/doc/templates/photoGrid.js +0 -175
- package/dist/doc/templates/photoGrid.js.map +0 -1
- package/dist/doc/templates/pullQuote.d.ts +0 -13
- package/dist/doc/templates/pullQuote.d.ts.map +0 -1
- package/dist/doc/templates/pullQuote.js +0 -113
- package/dist/doc/templates/pullQuote.js.map +0 -1
- package/dist/doc/templates/quoteBlock.d.ts +0 -15
- package/dist/doc/templates/quoteBlock.d.ts.map +0 -1
- package/dist/doc/templates/quoteBlock.js +0 -101
- package/dist/doc/templates/quoteBlock.js.map +0 -1
- package/dist/doc/templates/sectionHeader.d.ts +0 -14
- package/dist/doc/templates/sectionHeader.d.ts.map +0 -1
- package/dist/doc/templates/sectionHeader.js +0 -97
- package/dist/doc/templates/sectionHeader.js.map +0 -1
- package/dist/doc/templates/statHighlight.d.ts +0 -15
- package/dist/doc/templates/statHighlight.d.ts.map +0 -1
- package/dist/doc/templates/statHighlight.js +0 -102
- package/dist/doc/templates/statHighlight.js.map +0 -1
- package/dist/doc/templates/titleBlock.d.ts +0 -13
- package/dist/doc/templates/titleBlock.d.ts.map +0 -1
- package/dist/doc/templates/titleBlock.js +0 -95
- package/dist/doc/templates/titleBlock.js.map +0 -1
- package/dist/doc/templates/twoColumn.d.ts +0 -15
- package/dist/doc/templates/twoColumn.d.ts.map +0 -1
- package/dist/doc/templates/twoColumn.js +0 -225
- package/dist/doc/templates/twoColumn.js.map +0 -1
- package/dist/doc/templates/videoPullQuote.d.ts +0 -20
- package/dist/doc/templates/videoPullQuote.d.ts.map +0 -1
- package/dist/doc/templates/videoPullQuote.js +0 -122
- package/dist/doc/templates/videoPullQuote.js.map +0 -1
- package/dist/doc/templates/videoWithCaption.d.ts +0 -20
- package/dist/doc/templates/videoWithCaption.d.ts.map +0 -1
- package/dist/doc/templates/videoWithCaption.js +0 -107
- package/dist/doc/templates/videoWithCaption.js.map +0 -1
- package/dist/doc/utils/animationUtils.d.ts +0 -34
- package/dist/doc/utils/animationUtils.d.ts.map +0 -1
- package/dist/doc/utils/animationUtils.js +0 -110
- package/dist/doc/utils/animationUtils.js.map +0 -1
- package/dist/doc/utils/themeUtils.d.ts +0 -64
- package/dist/doc/utils/themeUtils.d.ts.map +0 -1
- package/dist/doc/utils/themeUtils.js +0 -135
- package/dist/doc/utils/themeUtils.js.map +0 -1
- package/dist/generate/contentExtractor.d.ts +0 -112
- package/dist/generate/contentExtractor.d.ts.map +0 -1
- package/dist/generate/contentExtractor.js +0 -638
- package/dist/generate/contentExtractor.js.map +0 -1
- package/dist/generate/index.d.ts.map +0 -1
- package/dist/generate/slideshowGenerator.d.ts +0 -72
- package/dist/generate/slideshowGenerator.d.ts.map +0 -1
- package/dist/generate/slideshowGenerator.js +0 -159
- package/dist/generate/slideshowGenerator.js.map +0 -1
- package/dist/generate/templateMapper.d.ts +0 -23
- package/dist/generate/templateMapper.d.ts.map +0 -1
- package/dist/generate/templateMapper.js +0 -114
- package/dist/generate/templateMapper.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/markdown/convert.d.ts +0 -71
- package/dist/markdown/convert.d.ts.map +0 -1
- package/dist/markdown/convert.js +0 -845
- package/dist/markdown/convert.js.map +0 -1
- package/dist/markdown/htmlParse.d.ts +0 -26
- package/dist/markdown/htmlParse.d.ts.map +0 -1
- package/dist/markdown/htmlParse.js +0 -195
- package/dist/markdown/htmlParse.js.map +0 -1
- package/dist/markdown/index.d.ts.map +0 -1
- package/dist/markdown/parse.d.ts +0 -27
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js +0 -90
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/stringify.d.ts +0 -32
- package/dist/markdown/stringify.d.ts.map +0 -1
- package/dist/markdown/stringify.js +0 -108
- package/dist/markdown/stringify.js.map +0 -1
- package/dist/markdown/types.d.ts +0 -516
- package/dist/markdown/types.d.ts.map +0 -1
- package/dist/markdown/types.js +0 -17
- package/dist/markdown/types.js.map +0 -1
- package/dist/markdown/utils.d.ts +0 -78
- package/dist/markdown/utils.d.ts.map +0 -1
- package/dist/markdown/utils.js +0 -153
- package/dist/markdown/utils.js.map +0 -1
- package/dist/random/SeededRandom.d.ts +0 -50
- package/dist/random/SeededRandom.d.ts.map +0 -1
- package/dist/random/SeededRandom.js +0 -115
- package/dist/random/SeededRandom.js.map +0 -1
- package/dist/random/index.d.ts.map +0 -1
- package/dist/schemas/BlockTemplates.d.ts +0 -600
- package/dist/schemas/BlockTemplates.d.ts.map +0 -1
- package/dist/schemas/BlockTemplates.js +0 -72
- package/dist/schemas/BlockTemplates.js.map +0 -1
- package/dist/schemas/Doc.d.ts +0 -503
- package/dist/schemas/Doc.d.ts.map +0 -1
- package/dist/schemas/Doc.js +0 -90
- package/dist/schemas/Doc.js.map +0 -1
- package/dist/schemas/LayoutStrategy.d.ts +0 -84
- package/dist/schemas/LayoutStrategy.d.ts.map +0 -1
- package/dist/schemas/LayoutStrategy.js +0 -123
- package/dist/schemas/LayoutStrategy.js.map +0 -1
- package/dist/schemas/MediaProvider.d.ts +0 -65
- package/dist/schemas/MediaProvider.d.ts.map +0 -1
- package/dist/schemas/MediaProvider.js +0 -13
- package/dist/schemas/MediaProvider.js.map +0 -1
- package/dist/schemas/Theme.d.ts +0 -194
- package/dist/schemas/Theme.d.ts.map +0 -1
- package/dist/schemas/Theme.js +0 -97
- package/dist/schemas/Theme.js.map +0 -1
- package/dist/schemas/Types.d.ts +0 -14
- package/dist/schemas/Types.d.ts.map +0 -1
- package/dist/schemas/Types.js +0 -5
- package/dist/schemas/Types.js.map +0 -1
- package/dist/schemas/Viewport.d.ts +0 -74
- package/dist/schemas/Viewport.d.ts.map +0 -1
- package/dist/schemas/Viewport.js +0 -63
- package/dist/schemas/Viewport.js.map +0 -1
- package/dist/schemas/index.d.ts.map +0 -1
- package/dist/schemas/themeLibrary.d.ts +0 -41
- package/dist/schemas/themeLibrary.d.ts.map +0 -1
- package/dist/schemas/themeLibrary.js +0 -597
- package/dist/schemas/themeLibrary.js.map +0 -1
- package/dist/spatial/Geohash.d.ts +0 -85
- package/dist/spatial/Geohash.d.ts.map +0 -1
- package/dist/spatial/Geohash.js +0 -142
- package/dist/spatial/Geohash.js.map +0 -1
- package/dist/spatial/Haversine.d.ts +0 -24
- package/dist/spatial/Haversine.d.ts.map +0 -1
- package/dist/spatial/Haversine.js +0 -48
- package/dist/spatial/Haversine.js.map +0 -1
- package/dist/spatial/index.d.ts.map +0 -1
- package/dist/storage/ContentContainer.d.ts +0 -87
- package/dist/storage/ContentContainer.d.ts.map +0 -1
- package/dist/storage/ContentContainer.js +0 -131
- package/dist/storage/ContentContainer.js.map +0 -1
- package/dist/storage/LocalForageAdapter.d.ts +0 -36
- package/dist/storage/LocalForageAdapter.d.ts.map +0 -1
- package/dist/storage/LocalForageAdapter.js +0 -82
- package/dist/storage/LocalForageAdapter.js.map +0 -1
- package/dist/storage/LocalStorageAdapter.d.ts +0 -16
- package/dist/storage/LocalStorageAdapter.d.ts.map +0 -1
- package/dist/storage/LocalStorageAdapter.js +0 -50
- package/dist/storage/LocalStorageAdapter.js.map +0 -1
- package/dist/storage/MediaProviderFromContainer.d.ts +0 -21
- package/dist/storage/MediaProviderFromContainer.d.ts.map +0 -1
- package/dist/storage/MediaProviderFromContainer.js +0 -79
- package/dist/storage/MediaProviderFromContainer.js.map +0 -1
- package/dist/storage/MemoryStorageAdapter.d.ts +0 -15
- package/dist/storage/MemoryStorageAdapter.d.ts.map +0 -1
- package/dist/storage/MemoryStorageAdapter.js +0 -34
- package/dist/storage/MemoryStorageAdapter.js.map +0 -1
- package/dist/storage/Storage.d.ts +0 -23
- package/dist/storage/Storage.d.ts.map +0 -1
- package/dist/storage/Storage.js +0 -9
- package/dist/storage/Storage.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/themeLibrary-CEo2D4b6.d.ts +0 -44
- package/dist/timing/index.d.ts.map +0 -1
- package/dist/timing/narrationTiming.d.ts +0 -54
- package/dist/timing/narrationTiming.d.ts.map +0 -1
- package/dist/timing/narrationTiming.js +0 -190
- package/dist/timing/narrationTiming.js.map +0 -1
- package/dist/timing/readingTime.d.ts +0 -54
- package/dist/timing/readingTime.d.ts.map +0 -1
- package/dist/timing/readingTime.js +0 -49
- package/dist/timing/readingTime.js.map +0 -1
- package/dist/transform/applyTransform.d.ts +0 -22
- package/dist/transform/applyTransform.d.ts.map +0 -1
- package/dist/transform/applyTransform.js +0 -68
- package/dist/transform/applyTransform.js.map +0 -1
- package/dist/transform/blockAnalyzer.d.ts +0 -48
- package/dist/transform/blockAnalyzer.d.ts.map +0 -1
- package/dist/transform/blockAnalyzer.js +0 -135
- package/dist/transform/blockAnalyzer.js.map +0 -1
- package/dist/transform/index.d.ts.map +0 -1
- package/dist/transform/registry.d.ts +0 -19
- package/dist/transform/registry.d.ts.map +0 -1
- package/dist/transform/registry.js +0 -41
- package/dist/transform/registry.js.map +0 -1
- package/dist/transform/styles/dataDriven.d.ts +0 -4
- package/dist/transform/styles/dataDriven.d.ts.map +0 -1
- package/dist/transform/styles/dataDriven.js +0 -15
- package/dist/transform/styles/dataDriven.js.map +0 -1
- package/dist/transform/styles/documentary.d.ts +0 -4
- package/dist/transform/styles/documentary.d.ts.map +0 -1
- package/dist/transform/styles/documentary.js +0 -15
- package/dist/transform/styles/documentary.js.map +0 -1
- package/dist/transform/styles/index.d.ts +0 -6
- package/dist/transform/styles/index.d.ts.map +0 -1
- package/dist/transform/styles/index.js +0 -6
- package/dist/transform/styles/index.js.map +0 -1
- package/dist/transform/styles/magazine.d.ts +0 -4
- package/dist/transform/styles/magazine.d.ts.map +0 -1
- package/dist/transform/styles/magazine.js +0 -15
- package/dist/transform/styles/magazine.js.map +0 -1
- package/dist/transform/styles/minimal.d.ts +0 -4
- package/dist/transform/styles/minimal.d.ts.map +0 -1
- package/dist/transform/styles/minimal.js +0 -15
- package/dist/transform/styles/minimal.js.map +0 -1
- package/dist/transform/styles/narrative.d.ts +0 -4
- package/dist/transform/styles/narrative.d.ts.map +0 -1
- package/dist/transform/styles/narrative.js +0 -15
- package/dist/transform/styles/narrative.js.map +0 -1
- package/dist/transform/templateSelector.d.ts +0 -30
- package/dist/transform/templateSelector.d.ts.map +0 -1
- package/dist/transform/templateSelector.js +0 -218
- package/dist/transform/templateSelector.js.map +0 -1
- package/dist/transform/timingAllocator.d.ts +0 -21
- package/dist/transform/timingAllocator.d.ts.map +0 -1
- package/dist/transform/timingAllocator.js +0 -111
- package/dist/transform/timingAllocator.js.map +0 -1
- package/dist/transform/types.d.ts +0 -86
- package/dist/transform/types.d.ts.map +0 -1
- package/dist/transform/types.js +0 -9
- package/dist/transform/types.js.map +0 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveIcon
|
|
3
|
+
} from "./chunk-DBS6RIFL.js";
|
|
1
4
|
import {
|
|
2
5
|
parseFrontmatter
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-H3AXU4MJ.js";
|
|
4
7
|
|
|
5
8
|
// src/markdown/parse.ts
|
|
6
9
|
import { unified } from "unified";
|
|
@@ -171,7 +174,7 @@ function extractText(node) {
|
|
|
171
174
|
}
|
|
172
175
|
return "";
|
|
173
176
|
}
|
|
174
|
-
var TEMPLATE_ANNOTATION_RE = /\s*\{\[([^\]]+)\]\}\s*$/;
|
|
177
|
+
var TEMPLATE_ANNOTATION_RE = /\s*\{\[([^\]]+)\]\}[\s\]}]*$/;
|
|
175
178
|
function extractTemplateAnnotation(children) {
|
|
176
179
|
for (let i = children.length - 1; i >= 0; i--) {
|
|
177
180
|
const child = children[i];
|
|
@@ -243,13 +246,88 @@ function convertBlockChildren(children, parseHtml) {
|
|
|
243
246
|
}
|
|
244
247
|
return result;
|
|
245
248
|
}
|
|
246
|
-
function convertInlineChildren(children, parseHtml) {
|
|
249
|
+
function convertInlineChildren(children, parseHtml, applyIconSplit = true) {
|
|
247
250
|
const result = [];
|
|
248
251
|
for (const child of children) {
|
|
249
252
|
const node = convertInlineNode(child, parseHtml);
|
|
250
253
|
if (node) result.push(node);
|
|
251
254
|
}
|
|
252
|
-
|
|
255
|
+
const mentioned = coalesceMentions(result);
|
|
256
|
+
return applyIconSplit ? splitInlineIcons(mentioned) : mentioned;
|
|
257
|
+
}
|
|
258
|
+
function splitInlineIcons(nodes) {
|
|
259
|
+
const recombined = recombineDirectiveTokens(nodes);
|
|
260
|
+
const out = [];
|
|
261
|
+
for (const node of recombined) {
|
|
262
|
+
if (node.type !== "text") {
|
|
263
|
+
out.push(node);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
out.push(...splitTextOnIcons(node.value, node.position));
|
|
267
|
+
}
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
function recombineDirectiveTokens(nodes) {
|
|
271
|
+
const out = [];
|
|
272
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
273
|
+
const cur = nodes[i];
|
|
274
|
+
const next = nodes[i + 1];
|
|
275
|
+
const after = nodes[i + 2];
|
|
276
|
+
if (cur?.type === "text" && next?.type === "textDirective" && after?.type === "text" && /\{\[[a-zA-Z0-9_-]+$/.test(cur.value) && /^\]\}/.test(after.value)) {
|
|
277
|
+
const directive = next;
|
|
278
|
+
const directiveName = directive.name ?? "";
|
|
279
|
+
const merged = {
|
|
280
|
+
type: "text",
|
|
281
|
+
value: cur.value + ":" + directiveName + after.value,
|
|
282
|
+
...cur.position ? { position: cur.position } : {}
|
|
283
|
+
};
|
|
284
|
+
out.push(merged);
|
|
285
|
+
i += 2;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
out.push(cur);
|
|
289
|
+
}
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
var ICON_TOKEN_RE = /\{\[([a-zA-Z0-9_:-]+)\]\}/g;
|
|
293
|
+
function splitTextOnIcons(value, position) {
|
|
294
|
+
if (!value) return [];
|
|
295
|
+
ICON_TOKEN_RE.lastIndex = 0;
|
|
296
|
+
let lastIndex = 0;
|
|
297
|
+
let match;
|
|
298
|
+
const out = [];
|
|
299
|
+
while ((match = ICON_TOKEN_RE.exec(value)) !== null) {
|
|
300
|
+
const token = match[1];
|
|
301
|
+
const icon = resolveIcon(token);
|
|
302
|
+
if (!icon) continue;
|
|
303
|
+
if (match.index > lastIndex) {
|
|
304
|
+
out.push({
|
|
305
|
+
type: "text",
|
|
306
|
+
value: value.slice(lastIndex, match.index),
|
|
307
|
+
...position ? { position } : {}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const iconNode = {
|
|
311
|
+
type: "inlineIcon",
|
|
312
|
+
token,
|
|
313
|
+
family: icon.family,
|
|
314
|
+
name: icon.name,
|
|
315
|
+
...position ? { position } : {}
|
|
316
|
+
};
|
|
317
|
+
out.push(iconNode);
|
|
318
|
+
lastIndex = match.index + match[0].length;
|
|
319
|
+
}
|
|
320
|
+
if (lastIndex === 0) {
|
|
321
|
+
return [{ type: "text", value, ...position ? { position } : {} }];
|
|
322
|
+
}
|
|
323
|
+
if (lastIndex < value.length) {
|
|
324
|
+
out.push({
|
|
325
|
+
type: "text",
|
|
326
|
+
value: value.slice(lastIndex),
|
|
327
|
+
...position ? { position } : {}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
253
331
|
}
|
|
254
332
|
function coalesceMentions(inlines) {
|
|
255
333
|
const out = [];
|
|
@@ -294,12 +372,13 @@ function convertBlockNode(node, parseHtml) {
|
|
|
294
372
|
const pos = convertPosition(node.position);
|
|
295
373
|
switch (node.type) {
|
|
296
374
|
case "heading": {
|
|
297
|
-
const headingChildren = convertInlineChildren(node.children ?? [], parseHtml);
|
|
375
|
+
const headingChildren = convertInlineChildren(node.children ?? [], parseHtml, false);
|
|
298
376
|
const annotation = extractTemplateAnnotation(headingChildren);
|
|
377
|
+
const finalChildren = splitInlineIcons(headingChildren);
|
|
299
378
|
const result = {
|
|
300
379
|
type: "heading",
|
|
301
380
|
depth: node.depth ?? 1,
|
|
302
|
-
children:
|
|
381
|
+
children: finalChildren,
|
|
303
382
|
...posField(pos)
|
|
304
383
|
};
|
|
305
384
|
if (annotation) {
|
|
@@ -826,6 +905,12 @@ function inlineToMdast(node) {
|
|
|
826
905
|
children: [{ type: "text", value: node.displayName }],
|
|
827
906
|
...mdastPosField(node.position)
|
|
828
907
|
};
|
|
908
|
+
case "inlineIcon":
|
|
909
|
+
return {
|
|
910
|
+
type: "text",
|
|
911
|
+
value: `{[${node.token}]}`,
|
|
912
|
+
...mdastPosField(node.position)
|
|
913
|
+
};
|
|
829
914
|
default:
|
|
830
915
|
return { type: "text", value: "" };
|
|
831
916
|
}
|
|
@@ -955,4 +1040,4 @@ export {
|
|
|
955
1040
|
parseMarkdown,
|
|
956
1041
|
stringifyMarkdown
|
|
957
1042
|
};
|
|
958
|
-
//# sourceMappingURL=chunk-
|
|
1043
|
+
//# sourceMappingURL=chunk-3K5OG6KI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/markdown/parse.ts","../src/markdown/htmlParse.ts","../src/markdown/convert.ts","../src/markdown/stringify.ts"],"sourcesContent":["/**\n * Markdown → JSON Parser\n *\n * Parses a markdown string into a MarkdownDocument JSON structure\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport type { MarkdownDocument, ParseOptions } from './types.js';\nimport { fromMdast } from './convert.js';\nimport { parseFrontmatter } from './utils.js';\n\n// Cache the default processor (all extensions enabled) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n/**\n * Parse a markdown string into a MarkdownDocument.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to disable specific extensions.\n *\n * @param markdown - The markdown source text\n * @param options - Parser options (extensions, HTML parsing)\n * @returns A MarkdownDocument representing the parsed markdown\n *\n * @example\n * ```ts\n * const doc = parseMarkdown('# Hello\\n\\nWorld **bold** and *italic*');\n * // doc.type === 'document'\n * // doc.children[0].type === 'heading'\n * // doc.children[1].type === 'paragraph'\n * ```\n */\nexport function parseMarkdown(markdown: string, options?: ParseOptions): MarkdownDocument {\n // Use cached default processor when all extensions are enabled (the common case).\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n options.frontmatter !== false);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkFrontmatter, ['yaml']);\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested extensions.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified().use(remarkParse);\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n if (options?.frontmatter !== false) {\n processor = processor.use(remarkFrontmatter, ['yaml']);\n }\n }\n\n // Parse markdown → mdast tree (result is an mdast Root node)\n const mdastTree = processor.parse(markdown) as {\n type: string;\n children?: Array<{ type: string; value?: string }>;\n };\n\n // Convert mdast → MarkdownDocument\n const doc = fromMdast(mdastTree as Parameters<typeof fromMdast>[0], {\n parseHtml: options?.parseHtml,\n });\n\n // Extract YAML frontmatter if present\n if (options?.frontmatter !== false) {\n const yamlNode = mdastTree.children?.find((n) => n.type === 'yaml');\n if (yamlNode?.value) {\n const fm = parseFrontmatter(yamlNode.value);\n if (fm && Object.keys(fm).length > 0) {\n doc.frontmatter = fm;\n }\n }\n }\n\n return doc;\n}\n","/**\n * HTML ↔ HtmlNode Conversion\n *\n * Parses raw HTML strings into the squisq HtmlNode tree representation\n * using hast-util-from-html (parse5-backed), and serializes HtmlNode trees\n * back to HTML strings with a lightweight custom serializer.\n */\n\nimport { fromHtml } from 'hast-util-from-html';\nimport type { HtmlNode } from './types.js';\n\n// ============================================\n// Hast → HtmlNode conversion\n// ============================================\n\n/**\n * Mapping from hast DOM-style property names to HTML attribute names.\n * Only includes names where the property and attribute differ.\n */\nconst HAST_PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n httpEquiv: 'http-equiv',\n acceptCharset: 'accept-charset',\n};\n\n/** HTML void elements that are self-closing. */\nconst VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\n/**\n * Convert a hast property name to an HTML attribute name.\n */\nfunction propertyToAttribute(prop: string): string {\n if (HAST_PROP_TO_ATTR[prop]) return HAST_PROP_TO_ATTR[prop];\n\n // data-* attributes: dataFooBar → data-foo-bar\n if (prop.length > 4 && prop.startsWith('data') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'data-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // aria-* attributes: ariaLabel → aria-label\n if (prop.length > 4 && prop.startsWith('aria') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'aria-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // Most HTML attributes are lowercase (tabIndex → tabindex, readOnly → readonly, etc.)\n return prop.toLowerCase();\n}\n\n/**\n * Convert hast element properties to a flat HTML attribute map.\n */\nfunction convertHastProperties(properties: Record<string, unknown>): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (value === false || value === undefined || value === null) continue;\n\n const attrName = propertyToAttribute(key);\n\n if (value === true) {\n attrs[attrName] = '';\n } else if (Array.isArray(value)) {\n attrs[attrName] = value.join(' ');\n } else {\n attrs[attrName] = String(value);\n }\n }\n return attrs;\n}\n\n/**\n * Recursively convert hast child nodes to HtmlNode[].\n */\nfunction hastChildrenToNodes(children: unknown[]): HtmlNode[] {\n const result: HtmlNode[] = [];\n for (const child of children) {\n const node = hastNodeToHtmlNode(child as Record<string, unknown>);\n if (node) result.push(node);\n }\n return result;\n}\n\n/**\n * Convert a single hast node to an HtmlNode.\n */\nfunction hastNodeToHtmlNode(node: Record<string, unknown>): HtmlNode | null {\n switch (node.type) {\n case 'element': {\n const tagName = node.tagName as string;\n const properties = (node.properties ?? {}) as Record<string, unknown>;\n const children = (node.children ?? []) as unknown[];\n return {\n type: 'htmlElement',\n tagName,\n attributes: convertHastProperties(properties),\n children: hastChildrenToNodes(children),\n selfClosing: VOID_ELEMENTS.has(tagName) && children.length === 0,\n };\n }\n case 'text':\n return {\n type: 'htmlText',\n value: node.value as string,\n };\n case 'comment':\n return {\n type: 'htmlComment',\n value: node.value as string,\n };\n case 'doctype':\n // Doctype nodes are rare in markdown HTML; skip them\n return null;\n default:\n return null;\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Parse a raw HTML string into an HtmlNode[] tree.\n *\n * Uses the HTML5 parsing algorithm (via parse5) for correct handling of\n * self-closing tags, optional end tags, entity decoding, etc.\n *\n * @param html - Raw HTML string to parse\n * @returns Array of top-level HtmlNode elements\n */\nexport function parseHtmlToNodes(html: string): HtmlNode[] {\n if (!html || !html.trim()) return [];\n\n try {\n const tree = fromHtml(html, { fragment: true });\n return hastChildrenToNodes(tree.children as unknown[]);\n } catch {\n // If parsing fails, return the raw text as a single text node\n return [{ type: 'htmlText', value: html }];\n }\n}\n\n/**\n * Escape special characters in HTML attribute values.\n */\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\n/**\n * Serialize an HtmlNode back to an HTML string.\n */\nfunction stringifyNode(node: HtmlNode): string {\n switch (node.type) {\n case 'htmlElement': {\n const attrs = Object.entries(node.attributes)\n .map(([k, v]) => (v === '' ? k : `${k}=\"${escapeHtmlAttr(v)}\"`))\n .join(' ');\n const open = attrs ? `<${node.tagName} ${attrs}>` : `<${node.tagName}>`;\n\n if (node.selfClosing) return open;\n\n const inner = node.children.map(stringifyNode).join('');\n return `${open}${inner}</${node.tagName}>`;\n }\n case 'htmlText':\n return node.value;\n case 'htmlComment':\n return `<!--${node.value}-->`;\n }\n}\n\n/**\n * Serialize an HtmlNode[] tree back to an HTML string.\n *\n * @param nodes - Array of HtmlNode elements to serialize\n * @returns HTML string\n */\nexport function stringifyHtmlNodes(nodes: HtmlNode[]): string {\n return nodes.map(stringifyNode).join('');\n}\n","/**\n * mdast ↔ Squisq Markdown Node Conversion\n *\n * Converts between the remark/mdast AST format and the squisq\n * MarkdownDocument JSON format. This is the bridge layer that allows\n * us to use the battle-tested unified/remark ecosystem for parsing\n * and serialization while exposing our own clean, well-typed interfaces.\n *\n * The conversion is designed for perfect round-tripping:\n * toMdast(fromMdast(tree)) should produce an equivalent mdast tree.\n */\n\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownListItem,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownSourcePosition,\n HeadingTemplateAnnotation,\n MarkdownHeading,\n MarkdownInlineIcon,\n} from './types.js';\nimport { parseHtmlToNodes } from './htmlParse.js';\nimport { resolveIcon } from '../icons/resolve.js';\n\n// ============================================\n// Generic mdast node shape\n// ============================================\n\n/**\n * Minimal interface for mdast/unist nodes.\n * Using a generic shape avoids hard dependency on @types/mdast\n * and handles extension nodes (GFM, math, directive) uniformly.\n */\ninterface MdastPosition {\n start: { line: number; column: number; offset?: number };\n end: { line: number; column: number; offset?: number };\n}\n\ninterface MdastNode {\n type: string;\n position?: MdastPosition;\n children?: MdastNode[];\n value?: string;\n depth?: number;\n ordered?: boolean | null;\n start?: number | null;\n spread?: boolean | null;\n checked?: boolean | null;\n lang?: string | null;\n meta?: string | null;\n url?: string;\n title?: string | null;\n alt?: string;\n identifier?: string;\n label?: string;\n referenceType?: string;\n align?: (string | null)[];\n name?: string;\n attributes?: Record<string, string>;\n data?: Record<string, unknown>;\n}\n\n// ============================================\n// Position conversion\n// ============================================\n\nfunction convertPosition(pos?: MdastPosition): MarkdownSourcePosition | undefined {\n if (!pos) return undefined;\n return {\n start: {\n line: pos.start.line,\n column: pos.start.column,\n ...(pos.start.offset != null ? { offset: pos.start.offset } : {}),\n },\n end: {\n line: pos.end.line,\n column: pos.end.column,\n ...(pos.end.offset != null ? { offset: pos.end.offset } : {}),\n },\n };\n}\n\nfunction toMdastPosition(pos: MarkdownSourcePosition): MdastPosition {\n return {\n start: {\n line: pos.start.line,\n column: pos.start.column,\n ...(pos.start.offset != null ? { offset: pos.start.offset } : {}),\n },\n end: {\n line: pos.end.line,\n column: pos.end.column,\n ...(pos.end.offset != null ? { offset: pos.end.offset } : {}),\n },\n };\n}\n\n/** Conditionally include position in node. */\nfunction posField(\n pos?: MarkdownSourcePosition,\n): { position: MarkdownSourcePosition } | Record<string, never> {\n return pos ? { position: pos } : {};\n}\n\nfunction mdastPosField(\n pos?: MarkdownSourcePosition,\n): { position: MdastPosition } | Record<string, never> {\n return pos ? { position: toMdastPosition(pos) } : {};\n}\n\n// ============================================\n// mdast → Squisq (fromMdast)\n// ============================================\n\n/**\n * Extract plain text content from an mdast node tree.\n * Used for directive labels.\n */\nfunction extractText(node: MdastNode): string {\n if (node.value != null) return node.value;\n if (node.children) {\n return node.children.map(extractText).join('');\n }\n return '';\n}\n\n// ============================================\n// Template annotation helpers\n// ============================================\n\n/**\n * Regex matching a trailing `{[templateName key=value …]}` annotation.\n * Captures the content between `{[` and `]}`.\n *\n * The trailing `[\\s\\]\\}]*` tolerates accidental doubled `]}` that users\n * sometimes type when learning the syntax (e.g. `{[foo]}]}`). It does\n * not allow trailing word characters, so a non-trailing annotation like\n * `## The {[chart]} section` still doesn't match.\n */\nconst TEMPLATE_ANNOTATION_RE = /\\s*\\{\\[([^\\]]+)\\]\\}[\\s\\]}]*$/;\n\n/**\n * Extract a `{[templateName key=value …]}` annotation from a heading's\n * inline children. Mutates the children array in-place: strips the\n * annotation text from the last text node (or removes the node entirely).\n *\n * @returns The parsed annotation, or null if none found.\n */\nfunction extractTemplateAnnotation(\n children: MarkdownInlineNode[],\n): HeadingTemplateAnnotation | null {\n // Walk backwards to find the last text node\n for (let i = children.length - 1; i >= 0; i--) {\n const child = children[i];\n if (child.type === 'text') {\n const match = child.value.match(TEMPLATE_ANNOTATION_RE);\n if (match) {\n const inner = match[1].trim();\n const annotation = parseAnnotationTokens(inner);\n\n // Strip the matched portion from the text\n const stripped = child.value.slice(0, match.index!).replace(/\\s+$/, '');\n if (stripped) {\n (child as { value: string }).value = stripped;\n } else {\n // Remove the now-empty text node\n children.splice(i, 1);\n }\n return annotation;\n }\n // Only check the last text node (trailing position)\n break;\n }\n // If the last child isn't a text node, there's no annotation\n break;\n }\n return null;\n}\n\n/**\n * Parse the inner content of a `{[…]}` annotation into template + params.\n *\n * Input: `\"chart colorScheme=blue size=large\"`\n * Output: `{ template: 'chart', params: { colorScheme: 'blue', size: 'large' } }`\n */\nfunction parseAnnotationTokens(inner: string): HeadingTemplateAnnotation {\n const tokens = inner.split(/\\s+/);\n const params: Record<string, string> = {};\n\n // If the first token contains '=', there's no template name —\n // the annotation is purely key-value (e.g., `{[audio=intro.mp3]}`).\n const firstIsParam = tokens[0].indexOf('=') > 0;\n const template = firstIsParam ? undefined : tokens[0];\n const startIdx = firstIsParam ? 0 : 1;\n\n for (let i = startIdx; i < tokens.length; i++) {\n const eqIdx = tokens[i].indexOf('=');\n if (eqIdx > 0) {\n params[tokens[i].slice(0, eqIdx)] = tokens[i].slice(eqIdx + 1);\n }\n }\n\n const result: HeadingTemplateAnnotation = {};\n if (template) {\n result.template = template;\n }\n if (Object.keys(params).length > 0) {\n result.params = params;\n }\n return result;\n}\n\n/**\n * Serialize a HeadingTemplateAnnotation back to `{[templateName key=value …]}` text.\n */\nfunction serializeTemplateAnnotation(annotation: HeadingTemplateAnnotation): string {\n const parts: string[] = [];\n if (annotation.template) {\n parts.push(annotation.template);\n }\n if (annotation.params) {\n for (const [key, value] of Object.entries(annotation.params)) {\n parts.push(`${key}=${value}`);\n }\n }\n return `{[${parts.join(' ')}]}`;\n}\n\n/**\n * Convert an mdast Root node to a MarkdownDocument.\n *\n * @param root - The mdast root node (from remark-parse)\n * @param options - Conversion options\n * @returns A MarkdownDocument\n */\nexport function fromMdast(root: MdastNode, options?: { parseHtml?: boolean }): MarkdownDocument {\n const doParseHtml = options?.parseHtml !== false;\n return {\n type: 'document',\n children: convertBlockChildren(root.children ?? [], doParseHtml),\n ...posField(convertPosition(root.position)),\n };\n}\n\nfunction convertBlockChildren(children: MdastNode[], parseHtml: boolean): MarkdownBlockNode[] {\n const result: MarkdownBlockNode[] = [];\n for (const child of children) {\n const node = convertBlockNode(child, parseHtml);\n if (node) result.push(node);\n }\n return result;\n}\n\nfunction convertInlineChildren(\n children: MdastNode[],\n parseHtml: boolean,\n /** When false, skip the inline-icon split. Headings opt out so the\n * `extractTemplateAnnotation` pass still sees the trailing\n * `{[…]}` in raw text form; the caller runs `splitInlineIcons`\n * itself afterward on the remaining children. */\n applyIconSplit: boolean = true,\n): MarkdownInlineNode[] {\n const result: MarkdownInlineNode[] = [];\n for (const child of children) {\n const node = convertInlineNode(child, parseHtml);\n if (node) result.push(node);\n }\n const mentioned = coalesceMentions(result);\n return applyIconSplit ? splitInlineIcons(mentioned) : mentioned;\n}\n\n/**\n * Walk an inline-children list and split each text node on `{[token]}`\n * occurrences whose tokens resolve to known FontAwesome icons. Returns\n * a new array — unresolved tokens are left as literal text so authors\n * can write `{[notathing]}` verbatim if they really mean to.\n *\n * Exposed for the heading code path, which first strips the trailing\n * template annotation and then runs this on what remains.\n */\nexport function splitInlineIcons(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {\n // First pass: reassemble `text + textDirective + text` patterns\n // formed when a qualified token like `{[fa-solid:user]}` was split\n // by remark-directive's colon recognition. We collapse those triples\n // back into a single text node containing the original token so the\n // regex below can match.\n const recombined = recombineDirectiveTokens(nodes);\n\n const out: MarkdownInlineNode[] = [];\n for (const node of recombined) {\n if (node.type !== 'text') {\n out.push(node);\n continue;\n }\n out.push(...splitTextOnIcons(node.value, node.position));\n }\n return out;\n}\n\n/**\n * When the user types `{[fa-solid:user]}`, remark's directive plugin\n * sees `:user` and emits a `textDirective` node. The result is a\n * three-node sequence `text(\"…{[fa-solid\") + textDirective(\"user\") +\n * text(\"]}…\")`. This helper detects that shape and stitches the three\n * back into one text node so downstream icon detection works on the\n * authored token.\n */\nfunction recombineDirectiveTokens(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {\n const out: MarkdownInlineNode[] = [];\n for (let i = 0; i < nodes.length; i++) {\n const cur = nodes[i];\n const next = nodes[i + 1];\n const after = nodes[i + 2];\n if (\n cur?.type === 'text' &&\n next?.type === 'textDirective' &&\n after?.type === 'text' &&\n /\\{\\[[a-zA-Z0-9_-]+$/.test(cur.value) &&\n /^\\]\\}/.test(after.value)\n ) {\n const directive = next as unknown as { name?: string };\n const directiveName = directive.name ?? '';\n const merged: MarkdownInlineNode = {\n type: 'text',\n value: cur.value + ':' + directiveName + after.value,\n ...(cur.position ? { position: cur.position } : {}),\n };\n out.push(merged);\n i += 2;\n continue;\n }\n out.push(cur);\n }\n return out;\n}\n\nconst ICON_TOKEN_RE = /\\{\\[([a-zA-Z0-9_:-]+)\\]\\}/g;\n\nfunction splitTextOnIcons(value: string, position?: MarkdownSourcePosition): MarkdownInlineNode[] {\n if (!value) return [];\n ICON_TOKEN_RE.lastIndex = 0;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n const out: MarkdownInlineNode[] = [];\n while ((match = ICON_TOKEN_RE.exec(value)) !== null) {\n const token = match[1];\n const icon = resolveIcon(token);\n if (!icon) continue; // leave the literal `{[…]}` in place\n if (match.index > lastIndex) {\n out.push({\n type: 'text',\n value: value.slice(lastIndex, match.index),\n ...(position ? { position } : {}),\n });\n }\n const iconNode: MarkdownInlineIcon = {\n type: 'inlineIcon',\n token,\n family: icon.family,\n name: icon.name,\n ...(position ? { position } : {}),\n };\n out.push(iconNode);\n lastIndex = match.index + match[0].length;\n }\n if (lastIndex === 0) {\n // No matches — return the original text node unchanged.\n return [{ type: 'text', value, ...(position ? { position } : {}) }];\n }\n if (lastIndex < value.length) {\n out.push({\n type: 'text',\n value: value.slice(lastIndex),\n ...(position ? { position } : {}),\n });\n }\n return out;\n}\n\n/**\n * Post-process an inline-children list to collapse `text('@') + link(scheme:id)`\n * pairs into a single `mention` node. The wire format `@[Display](scheme:id)`\n * comes back from remark as two adjacent nodes — we merge them so consumers\n * see a single atomic mention. A plain link whose URL doesn't have `scheme:id`\n * shape, or that isn't immediately preceded by `@`, is left alone so ordinary\n * links keep working.\n */\nfunction coalesceMentions(inlines: MarkdownInlineNode[]): MarkdownInlineNode[] {\n const out: MarkdownInlineNode[] = [];\n for (const cur of inlines) {\n const prev = out[out.length - 1];\n if (\n cur.type === 'link' &&\n typeof cur.url === 'string' &&\n prev &&\n prev.type === 'text' &&\n prev.value.endsWith('@')\n ) {\n const schemeMatch = cur.url.match(/^([a-z][a-z0-9+.-]*):(.+)$/i);\n if (schemeMatch && schemeMatch[1] && schemeMatch[2]) {\n const targetKind = schemeMatch[1];\n const targetId = schemeMatch[2];\n const displayName = extractInlineText(cur.children);\n if (prev.value === '@') {\n out.pop();\n } else {\n prev.value = prev.value.slice(0, -1);\n }\n out.push({\n type: 'mention',\n targetKind,\n targetId,\n displayName,\n ...(cur.position ? { position: cur.position } : {}),\n });\n continue;\n }\n }\n out.push(cur);\n }\n return out;\n}\n\n/** Plain-text of an inline children list — used to pull the display name\n * out of a mention link's children. */\nfunction extractInlineText(nodes: MarkdownInlineNode[]): string {\n let out = '';\n for (const n of nodes) {\n if (n.type === 'text' || n.type === 'inlineCode') out += n.value;\n else if ('children' in n && Array.isArray(n.children)) {\n out += extractInlineText(n.children as MarkdownInlineNode[]);\n }\n }\n return out;\n}\n\nfunction convertBlockNode(node: MdastNode, parseHtml: boolean): MarkdownBlockNode | null {\n const pos = convertPosition(node.position);\n\n switch (node.type) {\n case 'heading': {\n // Defer icon splitting so `extractTemplateAnnotation` sees the\n // trailing `{[name]}` as plain text and can decide whether it's\n // a template annotation. Anything that's NOT the template still\n // needs to become an icon node, so we run `splitInlineIcons` on\n // what's left after the template extraction.\n const headingChildren = convertInlineChildren(node.children ?? [], parseHtml, false);\n const annotation = extractTemplateAnnotation(headingChildren);\n const finalChildren = splitInlineIcons(headingChildren);\n const result: MarkdownHeading = {\n type: 'heading',\n depth: (node.depth ?? 1) as 1 | 2 | 3 | 4 | 5 | 6,\n children: finalChildren,\n ...posField(pos),\n };\n if (annotation) {\n result.templateAnnotation = annotation;\n }\n return result;\n }\n\n case 'paragraph':\n return {\n type: 'paragraph',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'blockquote':\n return {\n type: 'blockquote',\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'list':\n return {\n type: 'list',\n ordered: node.ordered ?? false,\n ...(node.start != null ? { start: node.start } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: (node.children ?? []).map((item) => convertListItem(item, parseHtml)),\n ...posField(pos),\n };\n\n case 'code':\n return {\n type: 'code',\n ...(node.lang != null ? { lang: node.lang } : {}),\n ...(node.meta != null ? { meta: node.meta } : {}),\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'thematicBreak':\n return {\n type: 'thematicBreak',\n ...posField(pos),\n };\n\n case 'table':\n return {\n type: 'table',\n ...(node.align\n ? { align: node.align.map((a) => a as 'left' | 'right' | 'center' | null) }\n : {}),\n children: (node.children ?? []).map((row, i) => convertTableRow(row, i === 0, parseHtml)),\n ...posField(pos),\n };\n\n case 'html':\n return {\n type: 'htmlBlock',\n rawHtml: node.value ?? '',\n htmlChildren: parseHtml ? parseHtmlToNodes(node.value ?? '') : [],\n ...posField(pos),\n };\n\n case 'math':\n return {\n type: 'math',\n value: node.value ?? '',\n ...(node.meta != null ? { meta: node.meta } : {}),\n ...posField(pos),\n };\n\n case 'definition':\n return {\n type: 'definition',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n url: node.url ?? '',\n ...(node.title != null ? { title: node.title } : {}),\n ...posField(pos),\n };\n\n case 'footnoteDefinition':\n return {\n type: 'footnoteDefinition',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'containerDirective': {\n const allChildren = node.children ?? [];\n let label: string | undefined;\n let contentChildren = allChildren;\n\n // Extract directive label (first paragraph with directiveLabel flag)\n if (\n allChildren.length > 0 &&\n allChildren[0].type === 'paragraph' &&\n allChildren[0].data?.directiveLabel === true\n ) {\n label = extractText(allChildren[0]);\n contentChildren = allChildren.slice(1);\n }\n\n return {\n type: 'containerDirective',\n name: node.name ?? '',\n ...(label ? { label } : {}),\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertBlockChildren(contentChildren, parseHtml),\n ...posField(pos),\n };\n }\n\n case 'leafDirective':\n return {\n type: 'leafDirective',\n name: node.name ?? '',\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'yaml':\n // YAML frontmatter nodes are handled separately in parse.ts — skip silently\n return null;\n\n default:\n // Unknown block node — skip with a warning\n if (typeof console !== 'undefined') {\n console.warn(`[squisq/markdown] Unknown mdast block node type: \"${node.type}\"`);\n }\n return null;\n }\n}\n\nfunction convertListItem(node: MdastNode, parseHtml: boolean): MarkdownListItem {\n const pos = convertPosition(node.position);\n return {\n type: 'listItem',\n ...(node.checked != null ? { checked: node.checked } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: convertBlockChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n}\n\nfunction convertTableRow(node: MdastNode, isHeader: boolean, parseHtml: boolean): MarkdownTableRow {\n const pos = convertPosition(node.position);\n return {\n type: 'tableRow',\n children: (node.children ?? []).map((cell) => convertTableCell(cell, isHeader, parseHtml)),\n ...posField(pos),\n };\n}\n\nfunction convertTableCell(\n node: MdastNode,\n isHeader: boolean,\n parseHtml: boolean,\n): MarkdownTableCell {\n const pos = convertPosition(node.position);\n return {\n type: 'tableCell',\n ...(isHeader ? { isHeader: true } : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n}\n\nfunction convertInlineNode(node: MdastNode, parseHtml: boolean): MarkdownInlineNode | null {\n const pos = convertPosition(node.position);\n\n switch (node.type) {\n case 'text':\n return {\n type: 'text',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'emphasis':\n return {\n type: 'emphasis',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'strong':\n return {\n type: 'strong',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'delete':\n return {\n type: 'delete',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'inlineCode':\n return {\n type: 'inlineCode',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'link':\n return {\n type: 'link',\n url: node.url ?? '',\n ...(node.title != null ? { title: node.title } : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'image':\n return {\n type: 'image',\n url: node.url ?? '',\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...(node.title != null ? { title: node.title } : {}),\n ...posField(pos),\n };\n\n case 'break':\n return {\n type: 'break',\n ...posField(pos),\n };\n\n case 'html':\n return {\n type: 'htmlInline',\n rawHtml: node.value ?? '',\n htmlChildren: parseHtml ? parseHtmlToNodes(node.value ?? '') : [],\n ...posField(pos),\n };\n\n case 'inlineMath':\n return {\n type: 'inlineMath',\n value: node.value ?? '',\n ...posField(pos),\n };\n\n case 'footnoteReference':\n return {\n type: 'footnoteReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n ...posField(pos),\n };\n\n case 'linkReference':\n return {\n type: 'linkReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: (node.referenceType ?? 'full') as 'full' | 'collapsed' | 'shortcut',\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n case 'imageReference':\n return {\n type: 'imageReference',\n identifier: node.identifier ?? '',\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: (node.referenceType ?? 'full') as 'full' | 'collapsed' | 'shortcut',\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...posField(pos),\n };\n\n case 'textDirective':\n return {\n type: 'textDirective',\n name: node.name ?? '',\n ...(node.attributes && Object.keys(node.attributes).length > 0\n ? { attributes: node.attributes }\n : {}),\n children: convertInlineChildren(node.children ?? [], parseHtml),\n ...posField(pos),\n };\n\n default:\n if (typeof console !== 'undefined') {\n console.warn(`[squisq/markdown] Unknown mdast inline node type: \"${node.type}\"`);\n }\n return null;\n }\n}\n\n// ============================================\n// Squisq → mdast (toMdast)\n// ============================================\n\n/**\n * Convert a MarkdownDocument back to an mdast Root node.\n *\n * @param doc - A MarkdownDocument\n * @returns An mdast Root node suitable for remark-stringify\n */\nexport function toMdast(doc: MarkdownDocument): MdastNode {\n return {\n type: 'root',\n children: doc.children.map((n) => blockToMdast(n)),\n ...mdastPosField(doc.position),\n };\n}\n\nfunction blockToMdast(node: MarkdownBlockNode): MdastNode {\n switch (node.type) {\n case 'heading': {\n const mdastChildren = inlineChildrenToMdast(node.children);\n if (node.templateAnnotation) {\n const suffix = serializeTemplateAnnotation(node.templateAnnotation);\n // Append to last text node, or create a new one\n const lastChild = mdastChildren[mdastChildren.length - 1];\n if (lastChild && lastChild.type === 'text') {\n lastChild.value = (lastChild.value ?? '') + ' ' + suffix;\n } else {\n mdastChildren.push({ type: 'text', value: ' ' + suffix });\n }\n }\n return {\n type: 'heading',\n depth: node.depth,\n children: mdastChildren,\n ...mdastPosField(node.position),\n };\n }\n\n case 'paragraph':\n return {\n type: 'paragraph',\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'blockquote':\n return {\n type: 'blockquote',\n children: node.children.map(blockToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'list':\n return {\n type: 'list',\n ordered: node.ordered,\n ...(node.start != null ? { start: node.start } : {}),\n ...(node.spread != null ? { spread: node.spread } : {}),\n children: node.children.map(listItemToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'code':\n return {\n type: 'code',\n ...(node.lang != null ? { lang: node.lang } : {}),\n ...(node.meta != null ? { meta: node.meta } : {}),\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'thematicBreak':\n return {\n type: 'thematicBreak',\n ...mdastPosField(node.position),\n };\n\n case 'table':\n return {\n type: 'table',\n ...(node.align ? { align: node.align } : {}),\n children: node.children.map(tableRowToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'htmlBlock':\n return {\n type: 'html',\n value: node.rawHtml,\n ...mdastPosField(node.position),\n };\n\n case 'math':\n return {\n type: 'math',\n value: node.value,\n ...(node.meta != null ? { meta: node.meta } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'definition':\n return {\n type: 'definition',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n url: node.url,\n ...(node.title != null ? { title: node.title } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'footnoteDefinition':\n return {\n type: 'footnoteDefinition',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n children: node.children.map(blockToMdast),\n ...mdastPosField(node.position),\n };\n\n case 'containerDirective': {\n const children: MdastNode[] = [];\n\n // Reinject label as first paragraph with directiveLabel flag\n if (node.label) {\n children.push({\n type: 'paragraph',\n data: { directiveLabel: true },\n children: [{ type: 'text', value: node.label }],\n });\n }\n\n children.push(...node.children.map(blockToMdast));\n\n return {\n type: 'containerDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children,\n ...mdastPosField(node.position),\n };\n }\n\n case 'leafDirective':\n return {\n type: 'leafDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'definitionList':\n // Definition lists don't map to standard mdast.\n // Convert to HTML as fallback.\n return {\n type: 'html',\n value: definitionListToHtml(node),\n ...mdastPosField(node.position),\n };\n\n default:\n return { type: 'html', value: '' };\n }\n}\n\nfunction listItemToMdast(item: MarkdownListItem): MdastNode {\n return {\n type: 'listItem',\n ...(item.checked != null ? { checked: item.checked } : {}),\n ...(item.spread != null ? { spread: item.spread } : {}),\n children: item.children.map(blockToMdast),\n ...mdastPosField(item.position),\n };\n}\n\nfunction tableRowToMdast(row: MarkdownTableRow): MdastNode {\n return {\n type: 'tableRow',\n children: row.children.map(tableCellToMdast),\n ...mdastPosField(row.position),\n };\n}\n\nfunction tableCellToMdast(cell: MarkdownTableCell): MdastNode {\n return {\n type: 'tableCell',\n children: inlineChildrenToMdast(cell.children),\n ...mdastPosField(cell.position),\n };\n}\n\n/**\n * Serialize an array of inline nodes to mdast, flat-mapping the result so\n * a single `mention` expands to two mdast nodes (`text('@')` + `link`).\n * Use in place of `.map(inlineToMdast)` wherever we walk a children array.\n */\nfunction inlineChildrenToMdast(nodes: MarkdownInlineNode[]): MdastNode[] {\n const out: MdastNode[] = [];\n for (const n of nodes) {\n if (n.type === 'mention') {\n out.push({ type: 'text', value: '@' });\n out.push({\n type: 'link',\n url: `${n.targetKind}:${n.targetId}`,\n children: [{ type: 'text', value: n.displayName }],\n ...mdastPosField(n.position),\n });\n } else {\n out.push(inlineToMdast(n));\n }\n }\n return out;\n}\n\nfunction inlineToMdast(node: MarkdownInlineNode): MdastNode {\n switch (node.type) {\n case 'text':\n return {\n type: 'text',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'emphasis':\n return {\n type: 'emphasis',\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'strong':\n return {\n type: 'strong',\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'delete':\n return {\n type: 'delete',\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'inlineCode':\n return {\n type: 'inlineCode',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'link':\n return {\n type: 'link',\n url: node.url,\n ...(node.title != null ? { title: node.title } : {}),\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'image':\n return {\n type: 'image',\n url: node.url,\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...(node.title != null ? { title: node.title } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'break':\n return {\n type: 'break',\n ...mdastPosField(node.position),\n };\n\n case 'htmlInline':\n return {\n type: 'html',\n value: node.rawHtml,\n ...mdastPosField(node.position),\n };\n\n case 'inlineMath':\n return {\n type: 'inlineMath',\n value: node.value,\n ...mdastPosField(node.position),\n };\n\n case 'footnoteReference':\n return {\n type: 'footnoteReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'linkReference':\n return {\n type: 'linkReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: node.referenceType,\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'imageReference':\n return {\n type: 'imageReference',\n identifier: node.identifier,\n ...(node.label != null ? { label: node.label } : {}),\n referenceType: node.referenceType,\n ...(node.alt != null ? { alt: node.alt } : {}),\n ...mdastPosField(node.position),\n };\n\n case 'textDirective':\n return {\n type: 'textDirective',\n name: node.name,\n ...(node.attributes ? { attributes: node.attributes } : {}),\n children: inlineChildrenToMdast(node.children),\n ...mdastPosField(node.position),\n };\n\n case 'mention':\n // Fallback: when `inlineToMdast` is called directly (not via\n // `inlineChildrenToMdast`), collapse the mention to just the link\n // form. The `@` prefix is lost, but the id+name still round-trip.\n // Normal call sites go through `inlineChildrenToMdast` and get\n // the proper text('@') + link pair.\n return {\n type: 'link',\n url: `${node.targetKind}:${node.targetId}`,\n children: [{ type: 'text', value: node.displayName }],\n ...mdastPosField(node.position),\n };\n\n case 'inlineIcon':\n // Serialize back to the authored token so source round-trips\n // byte-stable. The token already carries the qualified form when\n // required (the parser preserved it verbatim).\n return {\n type: 'text',\n value: `{[${node.token}]}`,\n ...mdastPosField(node.position),\n };\n\n default:\n return { type: 'text', value: '' };\n }\n}\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Convert a MarkdownDefinitionList to an HTML <dl> string.\n * Fallback for serialization since mdast doesn't support definition lists.\n */\nfunction definitionListToHtml(list: {\n children: Array<{ type: string; children?: unknown[] }>;\n}): string {\n let html = '<dl>\\n';\n for (const child of list.children) {\n if (child.type === 'definitionTerm') {\n // Extract plain text from inline children\n const text = (child.children as Array<{ value?: string; children?: unknown[] }>)\n .map((c) => c.value ?? '')\n .join('');\n html += ` <dt>${text}</dt>\\n`;\n } else if (child.type === 'definitionDescription') {\n html += ` <dd>...</dd>\\n`;\n }\n }\n html += '</dl>';\n return html;\n}\n","/**\n * JSON → Markdown Serializer\n *\n * Converts a MarkdownDocument JSON structure back to a markdown string\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkStringify from 'remark-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport type { MarkdownDocument, StringifyOptions } from './types.js';\nimport { toMdast } from './convert.js';\n\n// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n/**\n * Serialize a MarkdownDocument back to a markdown string.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to control formatting and disable extensions.\n *\n * @param doc - The MarkdownDocument to serialize\n * @param options - Serialization options (formatting, extensions)\n * @returns A markdown string\n *\n * @example\n * ```ts\n * const doc: MarkdownDocument = {\n * type: 'document',\n * children: [\n * { type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] },\n * { type: 'paragraph', children: [{ type: 'text', value: 'World' }] },\n * ],\n * };\n * const md = stringifyMarkdown(doc);\n * // '# Hello\\n\\nWorld\\n'\n * ```\n */\nexport function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOptions): string {\n // Convert MarkdownDocument → mdast tree\n const mdastTree = toMdast(doc);\n\n // Use cached default processor when all extensions and default formatting are used.\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n !options.bullet &&\n !options.bulletOrdered &&\n !options.emphasis &&\n !options.strong &&\n !options.rule &&\n !options.fence &&\n options.setext == null);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkStringify, {\n bullet: '-',\n bulletOrdered: '.',\n emphasis: '*',\n strong: '*',\n rule: '-',\n fence: '`',\n setext: false,\n });\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested options.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified();\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n\n processor = processor.use(remarkStringify, {\n bullet: options?.bullet ?? '-',\n bulletOrdered: options?.bulletOrdered ?? '.',\n emphasis: options?.emphasis ?? '*',\n strong: options?.strong ?? '*',\n rule: options?.rule ?? '-',\n fence: options?.fence ?? '`',\n setext: options?.setext ?? false,\n });\n }\n\n // Stringify mdast → markdown string\n const result = processor.stringify(mdastTree) as string;\n\n // remark-stringify escapes `[` in text nodes (to prevent link syntax),\n // which turns `{[template]}` into `{\\[template]}`. Unescape our annotation syntax.\n const cleaned = result.replace(/\\{\\\\\\[([^\\]]+)\\]\\}/g, '{[$1]}');\n\n // Prepend YAML frontmatter if present\n if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {\n const yamlLines = Object.entries(doc.frontmatter).map(\n ([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`,\n );\n return `---\\n${yamlLines.join('\\n')}\\n---\\n\\n${cleaned}`;\n }\n\n return cleaned;\n}\n"],"mappings":"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","names":["unified","remarkGfm","remarkMath","remarkDirective","defaultProcessor","unified","remarkGfm","remarkMath","remarkDirective"]}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
THEME_SCHEMA_VERSION,
|
|
3
|
+
assertTheme,
|
|
4
|
+
createTheme,
|
|
5
|
+
oklchDarken,
|
|
6
|
+
oklchLighten,
|
|
7
|
+
oklchSetChroma,
|
|
8
|
+
pickContrastingText,
|
|
9
|
+
relativeLuminance
|
|
10
|
+
} from "./chunk-B4EEON3N.js";
|
|
11
|
+
|
|
12
|
+
// src/schemas/Doc.ts
|
|
13
|
+
function calculateDuration(audio) {
|
|
14
|
+
return audio.segments.reduce((sum, seg) => sum + seg.duration, 0);
|
|
15
|
+
}
|
|
16
|
+
function getSegmentAtTime(audio, time) {
|
|
17
|
+
let elapsed = 0;
|
|
18
|
+
for (let i = 0; i < audio.segments.length; i++) {
|
|
19
|
+
elapsed += audio.segments[i].duration;
|
|
20
|
+
if (time < elapsed) return i;
|
|
21
|
+
}
|
|
22
|
+
return audio.segments.length - 1;
|
|
23
|
+
}
|
|
24
|
+
function getBlockAtTime(blocks, time) {
|
|
25
|
+
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
26
|
+
const block = blocks[i];
|
|
27
|
+
if (time >= block.startTime && time < block.startTime + block.duration) {
|
|
28
|
+
return block;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return blocks[0] || null;
|
|
32
|
+
}
|
|
33
|
+
function getCaptionAtTime(captions, time) {
|
|
34
|
+
if (!captions || !captions.phrases.length) return null;
|
|
35
|
+
for (const phrase of captions.phrases) {
|
|
36
|
+
if (time >= phrase.startTime && time < phrase.endTime) {
|
|
37
|
+
return phrase;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function validateDoc(script) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
if (!script.articleId) {
|
|
45
|
+
errors.push("Missing articleId");
|
|
46
|
+
}
|
|
47
|
+
if (!script.blocks || script.blocks.length === 0) {
|
|
48
|
+
errors.push("No blocks defined");
|
|
49
|
+
}
|
|
50
|
+
if (!script.audio || !script.audio.segments || script.audio.segments.length === 0) {
|
|
51
|
+
errors.push("No audio segments defined");
|
|
52
|
+
}
|
|
53
|
+
const totalDuration = script.duration;
|
|
54
|
+
let covered = 0;
|
|
55
|
+
for (const block of script.blocks) {
|
|
56
|
+
if (block.startTime > covered + 0.1) {
|
|
57
|
+
errors.push(`Gap in blocks: ${covered}s to ${block.startTime}s`);
|
|
58
|
+
}
|
|
59
|
+
covered = Math.max(covered, block.startTime + block.duration);
|
|
60
|
+
}
|
|
61
|
+
if (covered < totalDuration - 0.1) {
|
|
62
|
+
errors.push(`Blocks end at ${covered}s but audio is ${totalDuration}s`);
|
|
63
|
+
}
|
|
64
|
+
return errors;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/schemas/themeCompile.ts
|
|
68
|
+
var STARTER_BODY_FONT = { stackId: "system-sans" };
|
|
69
|
+
var STARTER_TITLE_FONT = { stackId: "system-serif" };
|
|
70
|
+
var STARTER_MONO_FONT = { stackId: "system-mono" };
|
|
71
|
+
var STARTER_COLOR_SCHEMES = {
|
|
72
|
+
blue: { bg: "#1a365d", text: "#63b3ed", accent: "#90cdf4" },
|
|
73
|
+
green: { bg: "#22543d", text: "#9ae6b4", accent: "#68d391" },
|
|
74
|
+
purple: { bg: "#44337a", text: "#d6bcfa", accent: "#b794f4" },
|
|
75
|
+
red: { bg: "#742a2a", text: "#fc8181", accent: "#feb2b2" },
|
|
76
|
+
orange: { bg: "#744210", text: "#fbd38d", accent: "#f6ad55" },
|
|
77
|
+
teal: { bg: "#234e52", text: "#81e6d9", accent: "#4fd1c5" }
|
|
78
|
+
};
|
|
79
|
+
var STARTER_THEME = {
|
|
80
|
+
schemaVersion: THEME_SCHEMA_VERSION,
|
|
81
|
+
id: "custom",
|
|
82
|
+
name: "Custom Theme",
|
|
83
|
+
description: "Customizer starter \u2014 gets overridden by user choices.",
|
|
84
|
+
colors: {
|
|
85
|
+
primary: "#3182ce",
|
|
86
|
+
secondary: "#4a5568",
|
|
87
|
+
background: "#1a202c",
|
|
88
|
+
backgroundLight: "#2d3748",
|
|
89
|
+
text: "#f7fafc",
|
|
90
|
+
textMuted: "#a0aec0",
|
|
91
|
+
highlight: "#4299e1",
|
|
92
|
+
warning: "#fc8181"
|
|
93
|
+
},
|
|
94
|
+
typography: {
|
|
95
|
+
bodyFont: STARTER_BODY_FONT,
|
|
96
|
+
titleFont: STARTER_TITLE_FONT,
|
|
97
|
+
monoFont: STARTER_MONO_FONT,
|
|
98
|
+
titleWeight: "bold"
|
|
99
|
+
},
|
|
100
|
+
style: {
|
|
101
|
+
textShadow: true,
|
|
102
|
+
overlayOpacity: 0.45,
|
|
103
|
+
animationSpeed: 1,
|
|
104
|
+
borderRadius: 6
|
|
105
|
+
},
|
|
106
|
+
renderStyle: {
|
|
107
|
+
name: "standard",
|
|
108
|
+
defaultTextAnimation: "fadeIn",
|
|
109
|
+
defaultImageAnimation: "slowZoom",
|
|
110
|
+
ambientMotion: true,
|
|
111
|
+
defaultTransition: { type: "fade", duration: 0.7 }
|
|
112
|
+
},
|
|
113
|
+
colorSchemes: STARTER_COLOR_SCHEMES
|
|
114
|
+
};
|
|
115
|
+
function deriveColorPalette(seeds, partialColors = {}, opts = {}) {
|
|
116
|
+
const spread = opts.contrast === "high" ? 0.22 : opts.contrast === "subtle" ? 0.08 : 0.15;
|
|
117
|
+
const primary = seeds.primary;
|
|
118
|
+
const secondary = seeds.secondary ?? oklchSetChroma(oklchLighten(primary, 0.05), 0.5);
|
|
119
|
+
const accent = seeds.accent ?? oklchLighten(primary, spread);
|
|
120
|
+
const bgSeed = partialColors.background ?? seeds.background;
|
|
121
|
+
let background;
|
|
122
|
+
if (bgSeed) {
|
|
123
|
+
background = bgSeed;
|
|
124
|
+
} else {
|
|
125
|
+
background = relativeLuminance(primary) > 0.5 ? "#0a0a0a" : "#1a202c";
|
|
126
|
+
}
|
|
127
|
+
const isLightSurface = relativeLuminance(background) > 0.5;
|
|
128
|
+
const backgroundLight = partialColors.backgroundLight ?? (isLightSurface ? oklchDarken(background, 0.04) : oklchLighten(background, 0.04));
|
|
129
|
+
const text = partialColors.text ?? seeds.text ?? pickContrastingText(background, "#f7fafc", "#1a202c");
|
|
130
|
+
const textMuted = partialColors.textMuted ?? (isLightSurface ? oklchLighten(text, 0.25) : oklchDarken(text, 0.25));
|
|
131
|
+
const highlight = partialColors.highlight ?? accent;
|
|
132
|
+
const warning = partialColors.warning ?? "#fc8181";
|
|
133
|
+
return {
|
|
134
|
+
primary: partialColors.primary ?? primary,
|
|
135
|
+
secondary: partialColors.secondary ?? secondary,
|
|
136
|
+
background,
|
|
137
|
+
backgroundLight,
|
|
138
|
+
text,
|
|
139
|
+
textMuted,
|
|
140
|
+
highlight,
|
|
141
|
+
warning
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function compileTheme(partial, opts = {}) {
|
|
145
|
+
const merged = createTheme(STARTER_THEME, partial);
|
|
146
|
+
merged.schemaVersion = THEME_SCHEMA_VERSION;
|
|
147
|
+
const partialTypography = partial.typography;
|
|
148
|
+
if (partialTypography) {
|
|
149
|
+
if (partialTypography.titleFont !== void 0) {
|
|
150
|
+
merged.typography.titleFont = partialTypography.titleFont;
|
|
151
|
+
}
|
|
152
|
+
if (partialTypography.bodyFont !== void 0) {
|
|
153
|
+
merged.typography.bodyFont = partialTypography.bodyFont;
|
|
154
|
+
}
|
|
155
|
+
if (partialTypography.monoFont !== void 0) {
|
|
156
|
+
merged.typography.monoFont = partialTypography.monoFont;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (merged.seedColors) {
|
|
160
|
+
const partialColors = partial.colors ?? {};
|
|
161
|
+
merged.colors = deriveColorPalette(merged.seedColors, partialColors, {
|
|
162
|
+
contrast: opts.contrast
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return assertTheme(merged, `compiled theme "${merged.id}"`);
|
|
166
|
+
}
|
|
167
|
+
function parseTheme(json) {
|
|
168
|
+
let parsed;
|
|
169
|
+
try {
|
|
170
|
+
parsed = JSON.parse(json);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
173
|
+
throw new Error(`Invalid theme JSON: ${msg}`);
|
|
174
|
+
}
|
|
175
|
+
return assertTheme(parsed, "parsed theme");
|
|
176
|
+
}
|
|
177
|
+
function serializeTheme(theme) {
|
|
178
|
+
return JSON.stringify(theme, null, 2);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export {
|
|
182
|
+
calculateDuration,
|
|
183
|
+
getSegmentAtTime,
|
|
184
|
+
getBlockAtTime,
|
|
185
|
+
getCaptionAtTime,
|
|
186
|
+
validateDoc,
|
|
187
|
+
STARTER_THEME,
|
|
188
|
+
deriveColorPalette,
|
|
189
|
+
compileTheme,
|
|
190
|
+
parseTheme,
|
|
191
|
+
serializeTheme
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=chunk-57CVQCFX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schemas/Doc.ts","../src/schemas/themeCompile.ts"],"sourcesContent":["/**\n * Doc Schema\n *\n * Defines the JSON format for AI-scriptable visual docs that accompany\n * audio narration. A Doc describes a sequence of animated blocks\n * with text overlays, images, and transitions synchronized to audio.\n *\n * The format is designed to be:\n * 1. AI-generatable - Claude can create scripts from article content\n * 2. Browser-renderable - SVG + CSS animations (ES2015 compatible)\n * 3. Video-exportable - Frame-by-frame capture via Playwright\n *\n * Timing: Blocks are timed to match narration audio segments. Each article\n * has 3-6 MP3 files (intro + sections), and blocks are grouped by segment.\n */\n\n// ============================================\n// Core Types\n// ============================================\n\n/**\n * Configuration for the Start/resting block shown before playback begins.\n */\nexport interface StartBlockConfig {\n /** Hero image path (relative to article media). When omitted, a theme-driven background is used. */\n heroSrc?: string;\n /** Alt text for the hero image */\n heroAlt?: string;\n /** Title to display over the hero */\n title: string;\n /** Optional subtitle */\n subtitle?: string;\n /** Ambient motion for the hero image */\n ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';\n /** Photo credit / artist name for the hero image */\n heroCredit?: string;\n /** License identifier for the hero image */\n heroLicense?: string;\n}\n\n/**\n * A complete visual doc for an article.\n */\nexport interface Doc {\n /** Article ID this doc belongs to */\n articleId: string;\n\n /** Total duration in seconds (sum of all audio segments) */\n duration: number;\n\n /** Ordered list of blocks */\n blocks: Block[];\n\n /** Audio track configuration */\n audio: AudioTrack;\n\n /** Caption track for closed captions (generated by postprocess-doc) */\n captions?: CaptionTrack;\n\n /**\n * Start/resting block shown before playback begins.\n * Displays hero image with title - acts as a \"poster\" for the doc.\n * The player shows this until the user starts playback.\n */\n startBlock?: StartBlockConfig;\n\n /**\n * Persistent layers rendered on every block (behind and/or on top of content).\n * Used for consistent branding overlays, background gradients, etc.\n */\n persistentLayers?: import('./BlockTemplates.js').PersistentLayerConfig;\n\n /**\n * Optional theme identifier. Resolved at render time via `resolveTheme()`.\n * When omitted, the renderer uses the default theme ('documentary').\n */\n themeId?: string;\n\n /** Optional metadata */\n meta?: {\n generatedAt?: string;\n generatedBy?: string;\n version?: number;\n };\n\n /**\n * YAML frontmatter from the source markdown.\n * Carries rendering hints like `document-render-as` and custom metadata.\n */\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * A single block in the doc.\n *\n * Blocks can be flat (audio-synced timeline) or nested (markdown-driven hierarchy).\n * When derived from markdown, a block corresponds to a heading section:\n * - `sourceHeading` references the original MarkdownHeading node\n * - `contents` holds the body markdown between this heading and the next\n * - `children` holds sub-heading blocks (e.g., H2s under an H1)\n */\nexport interface Block {\n /** Unique identifier for this block */\n id: string;\n\n /** When this block appears (seconds from start) */\n startTime: number;\n\n /** How long this block is visible (seconds) */\n duration: number;\n\n /** Which audio segment this block belongs to (0-indexed) */\n audioSegment: number;\n\n /**\n * Pre-computed visual layers, rendered back-to-front.\n *\n * Optional — template-derived blocks typically omit this and compute\n * layers on demand via `getLayers()` from `@bendyline/squisq/doc`.\n * Raw blocks (e.g., hand-crafted by AI) may provide layers directly.\n */\n layers?: Layer[];\n\n /** Entry transition from previous block */\n transition?: Transition;\n\n /** Template name that generated this block (for debugging) */\n template?: string;\n\n /**\n * Display title for template rendering.\n * Extracted from the sourceHeading when the block is created by markdownToDoc().\n * Templates like sectionHeader and titleBlock read this for their title layer.\n */\n title?: string;\n\n // ── Markdown-driven hierarchy (optional) ──\n\n /**\n * Nested sub-blocks (heading hierarchy).\n * When a doc is derived from markdown, sub-headings within this\n * heading's section become child blocks. For example, H2 blocks\n * under an H1 block appear here.\n */\n children?: Block[];\n\n /**\n * Markdown body content for this block's section.\n * Contains the block-level markdown nodes (paragraphs, lists, code,\n * blockquotes, etc.) that appear between this heading and the next\n * heading or sub-heading. Empty for pure structural blocks.\n */\n contents?: import('../markdown/types.js').MarkdownBlockNode[];\n\n /**\n * Reference to the MarkdownHeading node that created this block.\n * Present when the block was derived from markdown via markdownToDoc().\n * Enables round-tripping back to markdown and provides heading depth.\n * Absent for the preamble block (content before the first heading).\n */\n sourceHeading?: import('../markdown/types.js').MarkdownHeading;\n\n /**\n * Template overrides extracted from markdown directives.\n * For example, `### Data Section {template=chart colorScheme=blue}`\n * would produce `{ template: 'chart', colorScheme: 'blue' }`.\n * Allows per-block customization of template selection and parameters.\n */\n templateOverrides?: Record<string, string>;\n}\n\n// ============================================\n// Layer Types\n// ============================================\n\n/**\n * A visual element within a block.\n * Layers are composited back-to-front (first layer is background).\n */\nexport type Layer = ImageLayer | TextLayer | ShapeLayer | MapLayer | VideoLayer | TableLayer;\n\ninterface BaseLayer {\n /** Unique identifier for this layer */\n id: string;\n\n /** Position and size */\n position: Position;\n\n /** Animation to apply */\n animation?: Animation;\n}\n\n/**\n * Image layer - displays an image with optional Ken Burns effect.\n */\nexport interface ImageLayer extends BaseLayer {\n type: 'image';\n content: {\n /** Path to image file (relative to article media dir) */\n src: string;\n /** Alt text for accessibility */\n alt: string;\n /** How to fit image in bounds */\n fit?: 'cover' | 'contain' | 'fill';\n /** Photo credit / artist name */\n credit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n license?: string;\n };\n}\n\n/**\n * Text layer - displays text with styling.\n */\nexport interface TextLayer extends BaseLayer {\n type: 'text';\n content: {\n /** Text to display (supports \\n for line breaks) */\n text: string;\n /** Text styling */\n style: TextStyle;\n };\n}\n\n/**\n * Shape layer - simple geometric shapes for visual accents.\n */\nexport interface ShapeLayer extends BaseLayer {\n type: 'shape';\n content: {\n /** Shape type */\n shape: 'rect' | 'circle' | 'line';\n /** Fill color (CSS color or 'none') */\n fill?: string;\n /** Stroke color */\n stroke?: string;\n /** Stroke width in pixels */\n strokeWidth?: number;\n /** Corner radius for rect */\n borderRadius?: number;\n };\n}\n\n/**\n * Map layer - displays a geographic map with optional markers.\n *\n * Maps are rendered as composite tile images from free tile providers.\n * Supported styles use open-source/free tiles requiring attribution.\n */\nexport interface MapLayer extends BaseLayer {\n type: 'map';\n content: {\n /** Center coordinates for the map */\n center: {\n lat: number;\n lng: number;\n };\n /** Zoom level (0-18, typically 8-14 for doc blocks) */\n zoom: number;\n /** Map tile style */\n style: MapTileStyle;\n /** Optional markers to display on the map */\n markers?: MapMarker[];\n /** Pre-rendered static image path (for video export reliability) */\n staticSrc?: string;\n /** Show attribution text (default: true) */\n showAttribution?: boolean;\n };\n}\n\n/**\n * Video layer - displays a short video clip, always muted (narration provides audio).\n *\n * Rendered via an HTML5 <video> element inside <foreignObject>, similar to how\n * ImageLayer uses <img> for Ken Burns effects. clipStart/clipEnd define the window\n * within the source video to play. During Playwright frame capture (seekTo mode),\n * the video is seeked programmatically rather than playing in real time.\n */\nexport interface VideoLayer extends BaseLayer {\n type: 'video';\n content: {\n /** Path to video file (relative to article media dir) */\n src: string;\n /** Path to poster frame (shown before video loads / during seek) */\n posterSrc?: string;\n /** Alt text for accessibility */\n alt: string;\n /** How to fit video in bounds */\n fit?: 'cover' | 'contain' | 'fill';\n /** Start time within the source video (seconds) */\n clipStart: number;\n /** End time within the source video (seconds) */\n clipEnd: number;\n /** Total source video duration (for validation) */\n sourceDuration?: number;\n /** Video credit / artist name */\n credit?: string;\n /** License identifier (e.g., 'CC BY-SA 4.0') */\n license?: string;\n };\n}\n\n/**\n * Table layer - renders a data table with themed styling.\n * Uses foreignObject inside SVG for HTML-based table layout.\n */\nexport interface TableLayer extends BaseLayer {\n type: 'table';\n content: {\n /** Header cell values */\n headers: string[];\n /** Data rows (array of cell value arrays) */\n rows: string[][];\n /** Per-column alignment */\n align?: (('left' | 'right' | 'center') | null)[];\n /** Visual styling */\n style: TableLayerStyle;\n };\n}\n\n/**\n * Styling options for a TableLayer.\n */\nexport interface TableLayerStyle {\n /** Header row background color */\n headerBackground: string;\n /** Header row text color */\n headerColor: string;\n /** Body cell background color */\n cellBackground: string;\n /** Body cell text color */\n cellColor: string;\n /** Border/divider color */\n borderColor: string;\n /** Font size in pixels */\n fontSize: number;\n /** Font family */\n fontFamily?: string;\n /** Header font family (falls back to fontFamily) */\n headerFontFamily?: string;\n /** Corner radius for the table container */\n borderRadius?: number;\n}\n\n/**\n * Available map tile styles from free/open-source providers.\n */\nexport type MapTileStyle =\n | 'terrain' // OpenTopoMap - topographic/terrain (default)\n | 'satellite' // ESRI World Imagery\n | 'road' // OpenStreetMap standard\n | 'toner' // Stadia Toner (high contrast B&W)\n | 'watercolor'; // Stadia Watercolor (artistic)\n\n/**\n * A marker to display on the map.\n */\nexport interface MapMarker {\n /** Marker latitude */\n lat: number;\n /** Marker longitude */\n lng: number;\n /** Optional label text */\n label?: string;\n /** Marker color (CSS color, default: #ef4444) */\n color?: string;\n /** Marker icon type */\n icon?: 'pin' | 'circle' | 'star';\n}\n\n// ============================================\n// Position & Styling\n// ============================================\n\n/**\n * Position and dimensions for a layer.\n * Values can be pixels (number) or percentages (string like \"50%\").\n */\nexport interface Position {\n /** X position (pixels or percentage) */\n x: number | string;\n /** Y position (pixels or percentage) */\n y: number | string;\n /** Width (pixels or percentage) */\n width?: number | string;\n /** Height (pixels or percentage) */\n height?: number | string;\n /** Anchor point for positioning */\n anchor?: 'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/**\n * Text styling options.\n */\nexport interface TextStyle {\n /** Font size in pixels */\n fontSize: number;\n /** Font family (defaults to system sans-serif) */\n fontFamily?: string;\n /** Font weight */\n fontWeight?: 'normal' | 'bold';\n /** Text color (CSS color) */\n color: string;\n /** Text alignment */\n textAlign?: 'left' | 'center' | 'right';\n /** Line height multiplier */\n lineHeight?: number;\n /** Add drop shadow for readability over images */\n shadow?: boolean;\n /** Background color for text box */\n background?: string;\n /** Padding around text (pixels) */\n padding?: number;\n /** Maximum number of lines before truncation (adds \"...\" to last line) */\n maxLines?: number;\n}\n\n// ============================================\n// Animation & Transitions\n// ============================================\n\n/**\n * Animation to apply to a layer.\n */\nexport interface Animation {\n /** Animation type */\n type: AnimationType;\n /** Duration in seconds (defaults to layer's block duration) */\n duration?: number;\n /** Delay before animation starts (seconds) */\n delay?: number;\n /** CSS easing function */\n easing?: string;\n /** For Ken Burns: zoom direction */\n direction?: 'in' | 'out';\n /** For pan animations: pan direction */\n panDirection?: 'left' | 'right' | 'up' | 'down';\n}\n\n/**\n * Available animation types.\n */\nexport type AnimationType =\n | 'none'\n | 'fadeIn'\n | 'fadeOut'\n | 'slowZoom' // Slow zoom with optional pan\n | 'zoomIn'\n | 'zoomOut'\n | 'panLeft'\n | 'panRight'\n | 'typewriter'; // Text appears letter by letter\n\n/**\n * Transition between blocks.\n */\nexport interface Transition {\n /** Transition type */\n type: TransitionType;\n /** Duration in seconds */\n duration: number;\n}\n\nexport type TransitionType =\n | 'cut' // Instant switch\n | 'fade' // Cross-fade\n | 'dissolve' // Soft dissolve\n | 'slideLeft' // New block enters from right\n | 'slideRight'; // New block enters from left\n\n// ============================================\n// Audio Configuration\n// ============================================\n\n/**\n * Audio track configuration.\n * Articles have multiple MP3 segments (intro + sections).\n */\nexport interface AudioTrack {\n /** Audio segments in playback order */\n segments: AudioSegment[];\n}\n\n// ============================================\n// Captions\n// ============================================\n\n/**\n * A single word with precise timing, used for word-level highlighting\n * in social-style captions. Populated from TTS timing data when available.\n */\nexport interface CaptionWord {\n /** The word text */\n text: string;\n /** Start time in seconds (relative to doc start) */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n}\n\n/**\n * A single caption phrase to display during playback.\n */\nexport interface CaptionPhrase {\n /** The text to display */\n text: string;\n /** Start time in seconds (relative to doc start) */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n /** Which audio segment this caption belongs to (0-indexed) */\n audioSegment: number;\n /**\n * Optional per-word timing from TTS timing data.\n * When present, enables precise word-level highlighting in social\n * caption style. When absent, word timing is interpolated evenly\n * across the phrase duration.\n */\n words?: CaptionWord[];\n}\n\n/**\n * Caption track for a doc.\n */\nexport interface CaptionTrack {\n /** Caption phrases in chronological order */\n phrases: CaptionPhrase[];\n /** When the captions were generated */\n generatedAt: string;\n /** Algorithm version for regeneration detection */\n version: number;\n}\n\n/**\n * A single audio segment (one MP3 file).\n */\nexport interface AudioSegment {\n /** Path to MP3 file (relative to article media dir) */\n src: string;\n /** Segment name (e.g., \"intro\", \"history\") */\n name: string;\n /** Duration in seconds */\n duration: number;\n /** Start time in overall timeline (calculated) */\n startTime: number;\n}\n\n// ============================================\n// Audio Timing Data (from TTS)\n// ============================================\n\n/**\n * A single word-level timing bookmark from TTS synthesis.\n * Used for precise audio-to-content mapping and word-level caption sync.\n */\nexport interface AudioBookmark {\n /** Unique identifier (e.g., \"word-0\", \"word-1\") */\n id: string;\n /** Timestamp in seconds from audio start */\n time: number;\n /** Character offset in the source text */\n charOffset: number;\n /** The word text at this position */\n textFragment?: string;\n}\n\n/**\n * Timing data for an audio segment, typically from a `.timing.json` file\n * generated alongside TTS audio. Contains the source text and per-word\n * timing bookmarks for precise content matching and caption sync.\n */\nexport interface AudioTimingData {\n /** The normalized plain text that was synthesized */\n sourceText: string;\n /** Word-level timing bookmarks */\n bookmarks: AudioBookmark[];\n /** Total audio duration in seconds */\n duration: number;\n}\n\n// ============================================\n// Helper Functions\n// ============================================\n\n/**\n * Calculate total duration from audio segments.\n */\nexport function calculateDuration(audio: AudioTrack): number {\n return audio.segments.reduce((sum, seg) => sum + seg.duration, 0);\n}\n\n/**\n * Find which audio segment is playing at a given time.\n */\nexport function getSegmentAtTime(audio: AudioTrack, time: number): number {\n let elapsed = 0;\n for (let i = 0; i < audio.segments.length; i++) {\n elapsed += audio.segments[i].duration;\n if (time < elapsed) return i;\n }\n return audio.segments.length - 1;\n}\n\n/**\n * Find which block should be visible at a given time.\n */\nexport function getBlockAtTime(blocks: Block[], time: number): Block | null {\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i];\n if (time >= block.startTime && time < block.startTime + block.duration) {\n return block;\n }\n }\n return blocks[0] || null;\n}\n\n/**\n * Find the caption phrase that should be displayed at a given time.\n */\nexport function getCaptionAtTime(\n captions: CaptionTrack | undefined,\n time: number,\n): CaptionPhrase | null {\n if (!captions || !captions.phrases.length) return null;\n\n for (const phrase of captions.phrases) {\n if (time >= phrase.startTime && time < phrase.endTime) {\n return phrase;\n }\n }\n return null;\n}\n\n/**\n * Validate a Doc for completeness.\n */\nexport function validateDoc(script: Doc): string[] {\n const errors: string[] = [];\n\n if (!script.articleId) {\n errors.push('Missing articleId');\n }\n\n if (!script.blocks || script.blocks.length === 0) {\n errors.push('No blocks defined');\n }\n\n if (!script.audio || !script.audio.segments || script.audio.segments.length === 0) {\n errors.push('No audio segments defined');\n }\n\n // Check for gaps in block coverage\n const totalDuration = script.duration;\n let covered = 0;\n for (const block of script.blocks) {\n if (block.startTime > covered + 0.1) {\n errors.push(`Gap in blocks: ${covered}s to ${block.startTime}s`);\n }\n covered = Math.max(covered, block.startTime + block.duration);\n }\n\n if (covered < totalDuration - 0.1) {\n errors.push(`Blocks end at ${covered}s but audio is ${totalDuration}s`);\n }\n\n return errors;\n}\n","/**\n * Theme Compilation\n *\n * Turns a partial Theme (typically authored by the customizer panel from\n * a few seed colors and preset choices) into a complete, validated Theme\n * by:\n *\n * 1. Filling unspecified fields from a hardcoded `STARTER_THEME`.\n * 2. Deriving missing color slots from `seedColors` via OKLCh math.\n * 3. Validating the result.\n *\n * Built-in themes ship as fully-specified Theme JSON and skip this step.\n */\n\nimport type {\n Theme,\n DeepPartial,\n ThemeColorPalette,\n ThemeColorScheme,\n ThemeSeedColors,\n FontFamily,\n} from './Theme.js';\nimport { THEME_SCHEMA_VERSION, createTheme } from './Theme.js';\nimport { assertTheme } from './themeValidator.js';\nimport {\n oklchLighten,\n oklchDarken,\n oklchSetChroma,\n pickContrastingText,\n relativeLuminance,\n} from './colorUtils.js';\n\n/** Internal default theme used to fill in fields the customizer doesn't expose. */\nconst STARTER_BODY_FONT: FontFamily = { stackId: 'system-sans' };\nconst STARTER_TITLE_FONT: FontFamily = { stackId: 'system-serif' };\nconst STARTER_MONO_FONT: FontFamily = { stackId: 'system-mono' };\n\nconst STARTER_COLOR_SCHEMES: Record<string, ThemeColorScheme> = {\n blue: { bg: '#1a365d', text: '#63b3ed', accent: '#90cdf4' },\n green: { bg: '#22543d', text: '#9ae6b4', accent: '#68d391' },\n purple: { bg: '#44337a', text: '#d6bcfa', accent: '#b794f4' },\n red: { bg: '#742a2a', text: '#fc8181', accent: '#feb2b2' },\n orange: { bg: '#744210', text: '#fbd38d', accent: '#f6ad55' },\n teal: { bg: '#234e52', text: '#81e6d9', accent: '#4fd1c5' },\n};\n\nexport const STARTER_THEME: Theme = {\n schemaVersion: THEME_SCHEMA_VERSION,\n id: 'custom',\n name: 'Custom Theme',\n description: 'Customizer starter — gets overridden by user choices.',\n colors: {\n primary: '#3182ce',\n secondary: '#4a5568',\n background: '#1a202c',\n backgroundLight: '#2d3748',\n text: '#f7fafc',\n textMuted: '#a0aec0',\n highlight: '#4299e1',\n warning: '#fc8181',\n },\n typography: {\n bodyFont: STARTER_BODY_FONT,\n titleFont: STARTER_TITLE_FONT,\n monoFont: STARTER_MONO_FONT,\n titleWeight: 'bold',\n },\n style: {\n textShadow: true,\n overlayOpacity: 0.45,\n animationSpeed: 1.0,\n borderRadius: 6,\n },\n renderStyle: {\n name: 'standard',\n defaultTextAnimation: 'fadeIn',\n defaultImageAnimation: 'slowZoom',\n ambientMotion: true,\n defaultTransition: { type: 'fade', duration: 0.7 },\n },\n colorSchemes: STARTER_COLOR_SCHEMES,\n};\n\n/**\n * Derive a full `ThemeColorPalette` from `seedColors`, with any explicit\n * `partialColors` taking precedence over derived values. Background and\n * text are guessed from luminance when the user gave only a primary.\n */\nexport function deriveColorPalette(\n seeds: ThemeSeedColors,\n partialColors: Partial<ThemeColorPalette> = {},\n opts: { contrast?: 'subtle' | 'balanced' | 'high' } = {},\n): ThemeColorPalette {\n const spread = opts.contrast === 'high' ? 0.22 : opts.contrast === 'subtle' ? 0.08 : 0.15;\n\n const primary = seeds.primary;\n const secondary = seeds.secondary ?? oklchSetChroma(oklchLighten(primary, 0.05), 0.5);\n const accent = seeds.accent ?? oklchLighten(primary, spread);\n\n // Decide light vs dark surface from the explicit background, the seed background,\n // or default to dark.\n const bgSeed = partialColors.background ?? seeds.background;\n let background: string;\n if (bgSeed) {\n background = bgSeed;\n } else {\n // Pick a dark or light background that contrasts with primary\n background = relativeLuminance(primary) > 0.5 ? '#0a0a0a' : '#1a202c';\n }\n const isLightSurface = relativeLuminance(background) > 0.5;\n\n const backgroundLight =\n partialColors.backgroundLight ??\n (isLightSurface ? oklchDarken(background, 0.04) : oklchLighten(background, 0.04));\n\n const text =\n partialColors.text ?? seeds.text ?? pickContrastingText(background, '#f7fafc', '#1a202c');\n const textMuted =\n partialColors.textMuted ??\n (isLightSurface ? oklchLighten(text, 0.25) : oklchDarken(text, 0.25));\n\n const highlight = partialColors.highlight ?? accent;\n const warning = partialColors.warning ?? '#fc8181';\n\n return {\n primary: partialColors.primary ?? primary,\n secondary: partialColors.secondary ?? secondary,\n background,\n backgroundLight,\n text,\n textMuted,\n highlight,\n warning,\n };\n}\n\n/** Map a contrast preset to numeric spread used by `deriveColorPalette`. */\nexport type ContrastPreset = 'subtle' | 'balanced' | 'high';\n\nexport interface CompileOptions {\n /** Contrast level for OKLCh derivation (default 'balanced'). */\n contrast?: ContrastPreset;\n}\n\n/**\n * Compile a partial Theme into a complete one. Fills missing fields from\n * `STARTER_THEME`, derives missing color slots from `seedColors` (when\n * present), and validates the result.\n */\nexport function compileTheme(partial: DeepPartial<Theme>, opts: CompileOptions = {}): Theme {\n // Step 1: deep-merge over the starter\n const merged = createTheme(STARTER_THEME, partial);\n merged.schemaVersion = THEME_SCHEMA_VERSION;\n\n // Step 1b: typography fonts are discriminated unions ({stackId} | {custom}),\n // so deep-merge would leave stale keys from the starter when the partial\n // switches form. Replace each font wholesale when explicitly provided.\n const partialTypography = partial.typography;\n if (partialTypography) {\n if (partialTypography.titleFont !== undefined) {\n merged.typography.titleFont = partialTypography.titleFont as FontFamily;\n }\n if (partialTypography.bodyFont !== undefined) {\n merged.typography.bodyFont = partialTypography.bodyFont as FontFamily;\n }\n if (partialTypography.monoFont !== undefined) {\n merged.typography.monoFont = partialTypography.monoFont as FontFamily;\n }\n }\n\n // Step 2: derive missing color slots from seeds, if present\n if (merged.seedColors) {\n const partialColors = (partial.colors ?? {}) as Partial<ThemeColorPalette>;\n merged.colors = deriveColorPalette(merged.seedColors, partialColors, {\n contrast: opts.contrast,\n });\n }\n\n // Step 3: validate\n return assertTheme(merged, `compiled theme \"${merged.id}\"`);\n}\n\n/**\n * Parse a JSON string into a validated Theme. Throws on invalid input.\n */\nexport function parseTheme(json: string): Theme {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Invalid theme JSON: ${msg}`);\n }\n return assertTheme(parsed, 'parsed theme');\n}\n\n/**\n * Serialize a Theme to a stable, pretty-printed JSON string. Round-trips\n * cleanly through `parseTheme`.\n */\nexport function serializeTheme(theme: Theme): string {\n return JSON.stringify(theme, null, 2);\n}\n"],"mappings":"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","names":[]}
|