@bendyline/squisq 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Doc-Ch4tc3vv.d.ts → Doc-BeSeH2iX.d.ts} +118 -41
- package/dist/ImageEditDoc-BTIvw0oq.d.ts +84 -0
- package/dist/__tests__/colorUtils.test.d.ts +2 -0
- package/dist/__tests__/colorUtils.test.d.ts.map +1 -0
- package/dist/__tests__/colorUtils.test.js +94 -0
- package/dist/__tests__/colorUtils.test.js.map +1 -0
- package/dist/__tests__/getLayers.test.js +31 -7
- package/dist/__tests__/getLayers.test.js.map +1 -1
- package/dist/__tests__/iconResolve.test.d.ts +2 -0
- package/dist/__tests__/iconResolve.test.d.ts.map +1 -0
- package/dist/__tests__/iconResolve.test.js +99 -0
- package/dist/__tests__/iconResolve.test.js.map +1 -0
- package/dist/__tests__/imageEditState.test.d.ts +2 -0
- package/dist/__tests__/imageEditState.test.d.ts.map +1 -0
- package/dist/__tests__/imageEditState.test.js +61 -0
- package/dist/__tests__/imageEditState.test.js.map +1 -0
- package/dist/__tests__/imageEditVersions.test.d.ts +2 -0
- package/dist/__tests__/imageEditVersions.test.d.ts.map +1 -0
- package/dist/__tests__/imageEditVersions.test.js +141 -0
- package/dist/__tests__/imageEditVersions.test.js.map +1 -0
- package/dist/__tests__/inferDocumentTitle.test.d.ts +2 -0
- package/dist/__tests__/inferDocumentTitle.test.d.ts.map +1 -0
- package/dist/__tests__/inferDocumentTitle.test.js +42 -0
- package/dist/__tests__/inferDocumentTitle.test.js.map +1 -0
- package/dist/__tests__/jsonForm.chooseControl.test.d.ts +2 -0
- package/dist/__tests__/jsonForm.chooseControl.test.d.ts.map +1 -0
- package/dist/__tests__/jsonForm.chooseControl.test.js +74 -0
- package/dist/__tests__/jsonForm.chooseControl.test.js.map +1 -0
- package/dist/__tests__/jsonForm.evaluateWhen.test.d.ts +2 -0
- package/dist/__tests__/jsonForm.evaluateWhen.test.d.ts.map +1 -0
- package/dist/__tests__/jsonForm.evaluateWhen.test.js +51 -0
- package/dist/__tests__/jsonForm.evaluateWhen.test.js.map +1 -0
- package/dist/__tests__/jsonForm.inferSchema.test.d.ts +2 -0
- package/dist/__tests__/jsonForm.inferSchema.test.d.ts.map +1 -0
- package/dist/__tests__/jsonForm.inferSchema.test.js +23 -0
- package/dist/__tests__/jsonForm.inferSchema.test.js.map +1 -0
- package/dist/__tests__/jsonForm.pointer.test.d.ts +2 -0
- package/dist/__tests__/jsonForm.pointer.test.d.ts.map +1 -0
- package/dist/__tests__/jsonForm.pointer.test.js +94 -0
- package/dist/__tests__/jsonForm.pointer.test.js.map +1 -0
- package/dist/__tests__/markdownIcons.test.d.ts +2 -0
- package/dist/__tests__/markdownIcons.test.d.ts.map +1 -0
- package/dist/__tests__/markdownIcons.test.js +93 -0
- package/dist/__tests__/markdownIcons.test.js.map +1 -0
- package/dist/__tests__/markdownToDoc.test.js +15 -4
- package/dist/__tests__/markdownToDoc.test.js.map +1 -1
- package/dist/__tests__/recommendTemplates.test.d.ts +2 -0
- package/dist/__tests__/recommendTemplates.test.d.ts.map +1 -0
- package/dist/__tests__/recommendTemplates.test.js +158 -0
- package/dist/__tests__/recommendTemplates.test.js.map +1 -0
- package/dist/__tests__/scopedContentContainer.test.d.ts +2 -0
- package/dist/__tests__/scopedContentContainer.test.d.ts.map +1 -0
- package/dist/__tests__/scopedContentContainer.test.js +74 -0
- package/dist/__tests__/scopedContentContainer.test.js.map +1 -0
- package/dist/__tests__/slideshowGenerator.test.js +1 -1
- package/dist/__tests__/slideshowGenerator.test.js.map +1 -1
- package/dist/__tests__/templateAnnotationParse.test.d.ts +2 -0
- package/dist/__tests__/templateAnnotationParse.test.d.ts.map +1 -0
- package/dist/__tests__/templateAnnotationParse.test.js +42 -0
- package/dist/__tests__/templateAnnotationParse.test.js.map +1 -0
- package/dist/__tests__/templates.test.js +90 -9
- package/dist/__tests__/templates.test.js.map +1 -1
- package/dist/__tests__/themeCompile.test.d.ts +2 -0
- package/dist/__tests__/themeCompile.test.d.ts.map +1 -0
- package/dist/__tests__/themeCompile.test.js +134 -0
- package/dist/__tests__/themeCompile.test.js.map +1 -0
- package/dist/__tests__/themeValidator.test.d.ts +2 -0
- package/dist/__tests__/themeValidator.test.d.ts.map +1 -0
- package/dist/__tests__/themeValidator.test.js +107 -0
- package/dist/__tests__/themeValidator.test.js.map +1 -0
- package/dist/__tests__/versions.test.d.ts +2 -0
- package/dist/__tests__/versions.test.d.ts.map +1 -0
- package/dist/__tests__/versions.test.js +339 -0
- package/dist/__tests__/versions.test.js.map +1 -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/audioMapping.d.ts.map +1 -1
- package/dist/doc/audioMapping.js +42 -7
- package/dist/doc/audioMapping.js.map +1 -1
- package/dist/doc/getLayers.d.ts.map +1 -1
- package/dist/doc/getLayers.js +29 -18
- package/dist/doc/getLayers.js.map +1 -1
- package/dist/doc/markdownToDoc.d.ts.map +1 -1
- package/dist/doc/markdownToDoc.js +40 -6
- package/dist/doc/markdownToDoc.js.map +1 -1
- package/dist/doc/templates/featureBlock.d.ts +18 -0
- package/dist/doc/templates/featureBlock.d.ts.map +1 -0
- package/dist/doc/templates/featureBlock.js +180 -0
- package/dist/doc/templates/featureBlock.js.map +1 -0
- package/dist/doc/templates/fullBleedQuote.d.ts +12 -0
- package/dist/doc/templates/fullBleedQuote.d.ts.map +1 -1
- package/dist/doc/templates/fullBleedQuote.js +12 -0
- package/dist/doc/templates/fullBleedQuote.js.map +1 -1
- package/dist/doc/templates/index.d.ts +28 -4
- package/dist/doc/templates/index.d.ts.map +1 -1
- package/dist/doc/templates/index.js +45 -12
- package/dist/doc/templates/index.js.map +1 -1
- package/dist/doc/templates/listBlock.d.ts.map +1 -1
- package/dist/doc/templates/listBlock.js +22 -5
- package/dist/doc/templates/listBlock.js.map +1 -1
- package/dist/doc/templates/statHighlight.d.ts +12 -0
- package/dist/doc/templates/statHighlight.d.ts.map +1 -1
- package/dist/doc/templates/statHighlight.js +12 -0
- package/dist/doc/templates/statHighlight.js.map +1 -1
- package/dist/doc/templates/titleBlock.d.ts +11 -0
- package/dist/doc/templates/titleBlock.d.ts.map +1 -1
- package/dist/doc/templates/titleBlock.js +8 -0
- package/dist/doc/templates/titleBlock.js.map +1 -1
- package/dist/doc/utils/themeUtils.d.ts +4 -4
- package/dist/doc/utils/themeUtils.d.ts.map +1 -1
- package/dist/doc/utils/themeUtils.js +8 -7
- package/dist/doc/utils/themeUtils.js.map +1 -1
- package/dist/generate/slideshowGenerator.js +1 -1
- package/dist/generate/slideshowGenerator.js.map +1 -1
- package/dist/generate/templateMapper.js +2 -2
- package/dist/generate/templateMapper.js.map +1 -1
- package/dist/icons/iconData.d.ts +26 -0
- package/dist/icons/iconData.d.ts.map +1 -0
- package/dist/icons/iconData.js +12 -0
- package/dist/icons/iconData.js.map +1 -0
- package/dist/icons/index.d.ts +12 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/icons/index.js +10 -0
- package/dist/icons/index.js.map +1 -0
- package/dist/icons/resolve.d.ts +61 -0
- package/dist/icons/resolve.d.ts.map +1 -0
- package/dist/icons/resolve.js +130 -0
- package/dist/icons/resolve.js.map +1 -0
- package/dist/imageEdit/ImageEditVersionManager.d.ts +31 -0
- package/dist/imageEdit/ImageEditVersionManager.d.ts.map +1 -0
- package/dist/imageEdit/ImageEditVersionManager.js +39 -0
- package/dist/imageEdit/ImageEditVersionManager.js.map +1 -0
- package/dist/imageEdit/export.d.ts +33 -0
- package/dist/imageEdit/export.d.ts.map +1 -0
- package/dist/imageEdit/export.js +253 -0
- package/dist/imageEdit/export.js.map +1 -0
- package/dist/imageEdit/index.d.ts +14 -0
- package/dist/imageEdit/index.d.ts.map +1 -0
- package/dist/imageEdit/index.js +22 -0
- package/dist/imageEdit/index.js.map +1 -0
- package/dist/imageEdit/persistence.d.ts +15 -0
- package/dist/imageEdit/persistence.d.ts.map +1 -0
- package/dist/imageEdit/persistence.js +49 -0
- package/dist/imageEdit/persistence.js.map +1 -0
- package/dist/imageEdit/state.d.ts +42 -0
- package/dist/imageEdit/state.d.ts.map +1 -0
- package/dist/imageEdit/state.js +98 -0
- package/dist/imageEdit/state.js.map +1 -0
- package/dist/imageEdit/versionPaths.d.ts +27 -0
- package/dist/imageEdit/versionPaths.d.ts.map +1 -0
- package/dist/imageEdit/versionPaths.js +57 -0
- package/dist/imageEdit/versionPaths.js.map +1 -0
- package/dist/imageEdit/versions.d.ts +67 -0
- package/dist/imageEdit/versions.d.ts.map +1 -0
- package/dist/imageEdit/versions.js +161 -0
- package/dist/imageEdit/versions.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/jsonForm/chooseControl.d.ts +20 -0
- package/dist/jsonForm/chooseControl.d.ts.map +1 -0
- package/dist/jsonForm/chooseControl.js +82 -0
- package/dist/jsonForm/chooseControl.js.map +1 -0
- package/dist/jsonForm/evaluateWhen.d.ts +12 -0
- package/dist/jsonForm/evaluateWhen.d.ts.map +1 -0
- package/dist/jsonForm/evaluateWhen.js +65 -0
- package/dist/jsonForm/evaluateWhen.js.map +1 -0
- package/dist/jsonForm/index.d.ts +14 -0
- package/dist/jsonForm/index.d.ts.map +1 -0
- package/dist/jsonForm/index.js +12 -0
- package/dist/jsonForm/index.js.map +1 -0
- package/dist/jsonForm/inferSchema.d.ts +12 -0
- package/dist/jsonForm/inferSchema.d.ts.map +1 -0
- package/dist/jsonForm/inferSchema.js +15 -0
- package/dist/jsonForm/inferSchema.js.map +1 -0
- package/dist/jsonForm/pointer.d.ts +26 -0
- package/dist/jsonForm/pointer.d.ts.map +1 -0
- package/dist/jsonForm/pointer.js +102 -0
- package/dist/jsonForm/pointer.js.map +1 -0
- package/dist/jsonForm/types.d.ts +120 -0
- package/dist/jsonForm/types.d.ts.map +1 -0
- package/dist/jsonForm/types.js +10 -0
- package/dist/jsonForm/types.js.map +1 -0
- package/dist/markdown/convert.d.ts +11 -1
- package/dist/markdown/convert.d.ts.map +1 -1
- package/dist/markdown/convert.js +135 -5
- package/dist/markdown/convert.js.map +1 -1
- package/dist/markdown/index.d.ts +1 -1
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +1 -1
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/types.d.ts +17 -2
- package/dist/markdown/types.d.ts.map +1 -1
- package/dist/markdown/utils.d.ts +40 -0
- package/dist/markdown/utils.d.ts.map +1 -1
- package/dist/markdown/utils.js +155 -0
- package/dist/markdown/utils.js.map +1 -1
- package/dist/recommend/index.d.ts +16 -0
- package/dist/recommend/index.d.ts.map +1 -0
- package/dist/recommend/index.js +15 -0
- package/dist/recommend/index.js.map +1 -0
- package/dist/recommend/templates.d.ts +39 -0
- package/dist/recommend/templates.d.ts.map +1 -0
- package/dist/recommend/templates.js +235 -0
- package/dist/recommend/templates.js.map +1 -0
- package/dist/schemas/BlockTemplates.d.ts +46 -8
- package/dist/schemas/BlockTemplates.d.ts.map +1 -1
- package/dist/schemas/BlockTemplates.js +3 -3
- package/dist/schemas/BlockTemplates.js.map +1 -1
- package/dist/schemas/ImageEditDoc.d.ts +81 -0
- package/dist/schemas/ImageEditDoc.d.ts.map +1 -0
- package/dist/schemas/ImageEditDoc.js +20 -0
- package/dist/schemas/ImageEditDoc.js.map +1 -0
- package/dist/schemas/Theme.d.ts +70 -31
- package/dist/schemas/Theme.d.ts.map +1 -1
- package/dist/schemas/Theme.js +48 -16
- package/dist/schemas/Theme.js.map +1 -1
- package/dist/schemas/colorUtils.d.ts +51 -0
- package/dist/schemas/colorUtils.d.ts.map +1 -0
- package/dist/schemas/colorUtils.js +154 -0
- package/dist/schemas/colorUtils.js.map +1 -0
- package/dist/schemas/fontStacks.d.ts +57 -0
- package/dist/schemas/fontStacks.d.ts.map +1 -0
- package/dist/schemas/fontStacks.js +227 -0
- package/dist/schemas/fontStacks.js.map +1 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +4 -0
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/themeCompile.d.ts +45 -0
- package/dist/schemas/themeCompile.d.ts.map +1 -0
- package/dist/schemas/themeCompile.js +160 -0
- package/dist/schemas/themeCompile.js.map +1 -0
- package/dist/schemas/themeLibrary.d.ts +11 -15
- package/dist/schemas/themeLibrary.d.ts.map +1 -1
- package/dist/schemas/themeLibrary.js +18 -566
- package/dist/schemas/themeLibrary.js.map +1 -1
- package/dist/schemas/themeValidator.d.ts +26 -0
- package/dist/schemas/themeValidator.d.ts.map +1 -0
- package/dist/schemas/themeValidator.js +352 -0
- package/dist/schemas/themeValidator.js.map +1 -0
- package/dist/schemas/themes/bold.json +48 -0
- package/dist/schemas/themes/cinematic.json +54 -0
- package/dist/schemas/themes/documentary.json +49 -0
- package/dist/schemas/themes/gezellig.json +44 -0
- package/dist/schemas/themes/index.d.ts +26 -0
- package/dist/schemas/themes/index.d.ts.map +1 -0
- package/dist/schemas/themes/index.js +49 -0
- package/dist/schemas/themes/index.js.map +1 -0
- package/dist/schemas/themes/magazine.json +44 -0
- package/dist/schemas/themes/minimalist.json +45 -0
- package/dist/schemas/themes/morning-light.json +42 -0
- package/dist/schemas/themes/standard-dark.json +50 -0
- package/dist/schemas/themes/standard.json +42 -0
- package/dist/schemas/themes/tech-dark.json +48 -0
- package/dist/schemas/themes/warm-earth.json +50 -0
- package/dist/storage/ScopedContentContainer.d.ts +44 -0
- package/dist/storage/ScopedContentContainer.d.ts.map +1 -0
- package/dist/storage/ScopedContentContainer.js +94 -0
- package/dist/storage/ScopedContentContainer.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- 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/{types-CkNttVSF.d.ts → types-BdZkdJ3z.d.ts} +17 -2
- package/dist/types-D98li1gV.d.ts +93 -0
- package/dist/versions/DocumentVersionManager.d.ts +31 -0
- package/dist/versions/DocumentVersionManager.d.ts.map +1 -0
- package/dist/versions/DocumentVersionManager.js +33 -0
- package/dist/versions/DocumentVersionManager.js.map +1 -0
- package/dist/versions/index.d.ts +29 -0
- package/dist/versions/index.d.ts.map +1 -0
- package/dist/versions/index.js +28 -0
- package/dist/versions/index.js.map +1 -0
- package/dist/versions/operations.d.ts +44 -0
- package/dist/versions/operations.d.ts.map +1 -0
- package/dist/versions/operations.js +190 -0
- package/dist/versions/operations.js.map +1 -0
- package/dist/versions/paths.d.ts +29 -0
- package/dist/versions/paths.d.ts.map +1 -0
- package/dist/versions/paths.js +66 -0
- package/dist/versions/paths.js.map +1 -0
- package/dist/versions/timestamp.d.ts +22 -0
- package/dist/versions/timestamp.d.ts.map +1 -0
- package/dist/versions/timestamp.js +44 -0
- package/dist/versions/timestamp.js.map +1 -0
- package/dist/versions/types.d.ts +92 -0
- package/dist/versions/types.d.ts.map +1 -0
- package/dist/versions/types.js +11 -0
- package/dist/versions/types.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/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/themeLibrary-CEo2D4b6.d.ts +0 -44
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
toPointer,
|
|
4
|
+
pointerSegments,
|
|
5
|
+
getByPointer,
|
|
6
|
+
setByPointer,
|
|
7
|
+
resolveRef,
|
|
8
|
+
} from '../jsonForm/index.js';
|
|
9
|
+
|
|
10
|
+
describe('toPointer', () => {
|
|
11
|
+
it('keeps pointer-form paths intact', () => {
|
|
12
|
+
expect(toPointer('/a/b')).toBe('/a/b');
|
|
13
|
+
expect(toPointer('/0/1')).toBe('/0/1');
|
|
14
|
+
});
|
|
15
|
+
it('converts dotted-form to pointer-form', () => {
|
|
16
|
+
expect(toPointer('user.role')).toBe('/user/role');
|
|
17
|
+
expect(toPointer('a')).toBe('/a');
|
|
18
|
+
});
|
|
19
|
+
it('returns empty for root', () => {
|
|
20
|
+
expect(toPointer('')).toBe('');
|
|
21
|
+
expect(toPointer('/')).toBe('');
|
|
22
|
+
});
|
|
23
|
+
it('escapes ~ and / per RFC 6901', () => {
|
|
24
|
+
// dotted form does not allow / inside a segment; we test pointer-form decoding instead.
|
|
25
|
+
expect(pointerSegments('/a~1b')).toEqual(['a/b']);
|
|
26
|
+
expect(pointerSegments('/a~0b')).toEqual(['a~b']);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('getByPointer', () => {
|
|
31
|
+
const data = { user: { role: 'admin', tags: ['a', 'b'] } };
|
|
32
|
+
it('resolves nested object paths', () => {
|
|
33
|
+
expect(getByPointer(data, '/user/role')).toBe('admin');
|
|
34
|
+
expect(getByPointer(data, 'user.role')).toBe('admin');
|
|
35
|
+
});
|
|
36
|
+
it('resolves array indices', () => {
|
|
37
|
+
expect(getByPointer(data, '/user/tags/1')).toBe('b');
|
|
38
|
+
});
|
|
39
|
+
it('returns undefined for missing paths', () => {
|
|
40
|
+
expect(getByPointer(data, '/user/missing')).toBeUndefined();
|
|
41
|
+
expect(getByPointer(data, '/user/tags/9')).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
it('returns the data itself for empty pointer', () => {
|
|
44
|
+
expect(getByPointer(data, '')).toBe(data);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('setByPointer', () => {
|
|
49
|
+
it('writes a leaf value immutably', () => {
|
|
50
|
+
const before = { a: 1, b: 2 };
|
|
51
|
+
const after = setByPointer(before, '/a', 99);
|
|
52
|
+
expect(after).toEqual({ a: 99, b: 2 });
|
|
53
|
+
expect(before).toEqual({ a: 1, b: 2 });
|
|
54
|
+
});
|
|
55
|
+
it('clones intermediate objects, shares siblings', () => {
|
|
56
|
+
const before = { x: { y: 1 }, z: { w: 2 } };
|
|
57
|
+
const after = setByPointer(before, '/x/y', 7);
|
|
58
|
+
expect(after).toEqual({ x: { y: 7 }, z: { w: 2 } });
|
|
59
|
+
expect(after.z).toBe(before.z);
|
|
60
|
+
expect(after.x).not.toBe(before.x);
|
|
61
|
+
});
|
|
62
|
+
it('writes into array indices', () => {
|
|
63
|
+
const before = { items: ['a', 'b', 'c'] };
|
|
64
|
+
const after = setByPointer(before, '/items/1', 'B');
|
|
65
|
+
expect(after.items).toEqual(['a', 'B', 'c']);
|
|
66
|
+
expect(after.items).not.toBe(before.items);
|
|
67
|
+
});
|
|
68
|
+
it('creates intermediate containers when missing', () => {
|
|
69
|
+
const after = setByPointer({}, '/a/b/0/c', 'leaf');
|
|
70
|
+
expect(after).toEqual({ a: { b: [{ c: 'leaf' }] } });
|
|
71
|
+
});
|
|
72
|
+
it('round-trips with getByPointer', () => {
|
|
73
|
+
const before = { a: { b: [{ c: 1 }, { c: 2 }] } };
|
|
74
|
+
const after = setByPointer(before, '/a/b/1/c', 42);
|
|
75
|
+
expect(getByPointer(after, '/a/b/1/c')).toBe(42);
|
|
76
|
+
expect(getByPointer(after, '/a/b/0/c')).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('resolveRef', () => {
|
|
81
|
+
const root = {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
pet: { $ref: '#/$defs/Pet' },
|
|
85
|
+
},
|
|
86
|
+
$defs: {
|
|
87
|
+
Pet: { type: 'object', properties: { name: { type: 'string' } } },
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
it('returns referenced subschema for a local $ref', () => {
|
|
91
|
+
const ref = { $ref: '#/$defs/Pet' };
|
|
92
|
+
const resolved = resolveRef(ref, root);
|
|
93
|
+
expect(resolved?.type).toBe('object');
|
|
94
|
+
expect(resolved?.properties?.name?.type).toBe('string');
|
|
95
|
+
});
|
|
96
|
+
it('returns the schema unchanged when no $ref present', () => {
|
|
97
|
+
const node = { type: 'string' };
|
|
98
|
+
expect(resolveRef(node, root)).toBe(node);
|
|
99
|
+
});
|
|
100
|
+
it('returns undefined for unknown refs', () => {
|
|
101
|
+
expect(resolveRef({ $ref: '#/$defs/Nope' }, root)).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseMarkdown, stringifyMarkdown } from '../markdown/index.js';
|
|
3
|
+
|
|
4
|
+
describe('inline icon parsing', () => {
|
|
5
|
+
it('detects a unique bare {[name]} as an inlineIcon node', () => {
|
|
6
|
+
const doc = parseMarkdown('Hello {[github]} world');
|
|
7
|
+
const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
|
|
8
|
+
expect(para.children.length).toBe(3);
|
|
9
|
+
expect(para.children[0]).toMatchObject({ type: 'text', value: 'Hello ' });
|
|
10
|
+
expect(para.children[1]).toMatchObject({
|
|
11
|
+
type: 'inlineIcon',
|
|
12
|
+
token: 'github',
|
|
13
|
+
family: 'brands',
|
|
14
|
+
name: 'github',
|
|
15
|
+
});
|
|
16
|
+
expect(para.children[2]).toMatchObject({ type: 'text', value: ' world' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('resolves qualified tokens', () => {
|
|
20
|
+
const doc = parseMarkdown('Q: {[fa-solid:user]}');
|
|
21
|
+
const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
|
|
22
|
+
const icon = para.children.find((c) => c.type === 'inlineIcon');
|
|
23
|
+
expect(icon).toMatchObject({
|
|
24
|
+
type: 'inlineIcon',
|
|
25
|
+
token: 'fa-solid:user',
|
|
26
|
+
family: 'solid',
|
|
27
|
+
name: 'user',
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('leaves unknown / ambiguous tokens as literal text', () => {
|
|
32
|
+
// `user` is ambiguous (multiple families), `notathing` is unknown —
|
|
33
|
+
// both should round-trip as plain text so authors can write them.
|
|
34
|
+
const doc = parseMarkdown('a {[user]} b {[notathing]} c');
|
|
35
|
+
const para = doc.children[0] as unknown as { children: Array<Record<string, unknown>> };
|
|
36
|
+
expect(para.children).toHaveLength(1);
|
|
37
|
+
expect(para.children[0]).toMatchObject({
|
|
38
|
+
type: 'text',
|
|
39
|
+
value: 'a {[user]} b {[notathing]} c',
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('round-trips bare icons byte-stable through stringify', () => {
|
|
44
|
+
// Bare names contain no colon, so the serializer can re-emit them
|
|
45
|
+
// verbatim. (Qualified tokens like `fa-solid:user` get the colon
|
|
46
|
+
// escaped as `\:` for safety against remark-directive — that form
|
|
47
|
+
// re-parses to the same token, just isn't byte-stable.)
|
|
48
|
+
const src = 'Built with {[github]} and {[face-smile]}.';
|
|
49
|
+
expect(stringifyMarkdown(parseMarkdown(src)).trim()).toBe(src);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('round-trips qualified icons semantically through stringify', () => {
|
|
53
|
+
// `{[fa-solid:user]}` may serialize as `{[fa-solid\:user]}` — both
|
|
54
|
+
// forms parse back to the same inlineIcon node, so we verify
|
|
55
|
+
// semantic equivalence rather than byte equality.
|
|
56
|
+
const src = 'Q: {[fa-solid:user]} end';
|
|
57
|
+
const round1 = stringifyMarkdown(parseMarkdown(src));
|
|
58
|
+
const reparsed = parseMarkdown(round1);
|
|
59
|
+
const para = reparsed.children[0] as unknown as { children: Array<Record<string, unknown>> };
|
|
60
|
+
const icon = para.children.find((c) => c.type === 'inlineIcon');
|
|
61
|
+
expect(icon).toMatchObject({
|
|
62
|
+
type: 'inlineIcon',
|
|
63
|
+
token: 'fa-solid:user',
|
|
64
|
+
family: 'solid',
|
|
65
|
+
name: 'user',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('inline icons inside headings', () => {
|
|
71
|
+
it('parses the trailing template annotation as the template, body icon as inlineIcon', () => {
|
|
72
|
+
const doc = parseMarkdown('# Repo {[github]} status {[sectionHeader]}\n');
|
|
73
|
+
const heading = doc.children[0] as unknown as {
|
|
74
|
+
type: string;
|
|
75
|
+
templateAnnotation?: { template?: string };
|
|
76
|
+
children: Array<Record<string, unknown>>;
|
|
77
|
+
};
|
|
78
|
+
expect(heading.type).toBe('heading');
|
|
79
|
+
expect(heading.templateAnnotation?.template).toBe('sectionHeader');
|
|
80
|
+
// Children should be: text('Repo '), inlineIcon(github), text(' status')
|
|
81
|
+
const icon = heading.children.find((c) => c.type === 'inlineIcon');
|
|
82
|
+
expect(icon).toMatchObject({ type: 'inlineIcon', name: 'github' });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('round-trips heading template + inline icon', () => {
|
|
86
|
+
const src = '# Repo {[github]} status {[sectionHeader]}\n';
|
|
87
|
+
expect(stringifyMarkdown(parseMarkdown(src))).toBe(src);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('round-trips a heading where the whole trailing annotation is an icon (not a template)', () => {
|
|
91
|
+
// `# Foo {[github]}` — `github` is a valid icon (not a known template),
|
|
92
|
+
// so the bare token should NOT be treated as a template annotation
|
|
93
|
+
// unless it also happens to be in the template registry. The parser
|
|
94
|
+
// is liberal here — `extractTemplateAnnotation` runs first and would
|
|
95
|
+
// capture `{[github]}` as a template annotation; we accept that as
|
|
96
|
+
// the documented precedence.
|
|
97
|
+
const src = '# Foo {[github]}\n';
|
|
98
|
+
const out = stringifyMarkdown(parseMarkdown(src));
|
|
99
|
+
// The annotation extraction happens first, so the round-trip is
|
|
100
|
+
// either `# Foo {[github]}` (when serialized back via template
|
|
101
|
+
// suffix) — verify the output still matches the source.
|
|
102
|
+
expect(out).toBe(src);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -142,12 +142,12 @@ describe('markdownToDoc', () => {
|
|
|
142
142
|
const md = parseMarkdown('# Test');
|
|
143
143
|
const doc = markdownToDoc(md, {
|
|
144
144
|
articleId: 'custom-article',
|
|
145
|
-
defaultTemplate: '
|
|
145
|
+
defaultTemplate: 'title',
|
|
146
146
|
defaultDuration: 10,
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
expect(doc.articleId).toBe('custom-article');
|
|
150
|
-
expect(doc.blocks[0].template).toBe('
|
|
150
|
+
expect(doc.blocks[0].template).toBe('title');
|
|
151
151
|
expect(doc.blocks[0].duration).toBe(10);
|
|
152
152
|
});
|
|
153
153
|
|
|
@@ -363,11 +363,11 @@ describe('template annotation in markdownToDoc', () => {
|
|
|
363
363
|
});
|
|
364
364
|
|
|
365
365
|
it('nested headings preserve their own annotations', () => {
|
|
366
|
-
const input = '# Chapter {[
|
|
366
|
+
const input = '# Chapter {[title]}\n\nIntro\n\n## Section {[chart]}\n\nData';
|
|
367
367
|
const md = parseMarkdown(input);
|
|
368
368
|
const doc = markdownToDoc(md);
|
|
369
369
|
|
|
370
|
-
expect(doc.blocks[0].template).toBe('
|
|
370
|
+
expect(doc.blocks[0].template).toBe('title');
|
|
371
371
|
expect(doc.blocks[0].children).toHaveLength(1);
|
|
372
372
|
expect(doc.blocks[0].children![0].template).toBe('chart');
|
|
373
373
|
});
|
|
@@ -509,6 +509,21 @@ describe('auto cover block generation', () => {
|
|
|
509
509
|
expect(doc.startBlock!.heroSrc).toBe('deep.png');
|
|
510
510
|
});
|
|
511
511
|
|
|
512
|
+
it('picks raw HTML <img> as heroSrc (WYSIWYG resize case)', () => {
|
|
513
|
+
// The WYSIWYG editor serializes resized images as raw `<img src width>`
|
|
514
|
+
// because markdown shorthand has no width syntax. The cover scanner
|
|
515
|
+
// must see through that, or every resized image silently drops out
|
|
516
|
+
// of exports.
|
|
517
|
+
const md = parseMarkdown(
|
|
518
|
+
'# Title\n\n<img alt="resized" src="resized.jpg" width="194">\n\nText.',
|
|
519
|
+
);
|
|
520
|
+
const doc = markdownToDoc(md);
|
|
521
|
+
|
|
522
|
+
expect(doc.startBlock).toBeDefined();
|
|
523
|
+
expect(doc.startBlock!.heroSrc).toBe('resized.jpg');
|
|
524
|
+
expect(doc.startBlock!.heroAlt).toBe('resized');
|
|
525
|
+
});
|
|
526
|
+
|
|
512
527
|
it('works without images (no heroSrc)', () => {
|
|
513
528
|
const md = parseMarkdown('# Title\n\nPlain text, no images.');
|
|
514
529
|
const doc = markdownToDoc(md);
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseMarkdown } from '../markdown/parse.js';
|
|
3
|
+
import { profileBlockContents, recommendTemplatesForBlock } from '../recommend/templates.js';
|
|
4
|
+
|
|
5
|
+
const ALL_TEMPLATES = [
|
|
6
|
+
'title',
|
|
7
|
+
'sectionHeader',
|
|
8
|
+
'statHighlight',
|
|
9
|
+
'quote',
|
|
10
|
+
'factCard',
|
|
11
|
+
'twoColumn',
|
|
12
|
+
'dateEvent',
|
|
13
|
+
'imageWithCaption',
|
|
14
|
+
'leftFeature',
|
|
15
|
+
'rightFeature',
|
|
16
|
+
'map',
|
|
17
|
+
'fullBleedQuote',
|
|
18
|
+
'list',
|
|
19
|
+
'photoGrid',
|
|
20
|
+
'definitionCard',
|
|
21
|
+
'comparisonBar',
|
|
22
|
+
'pullQuote',
|
|
23
|
+
'videoWithCaption',
|
|
24
|
+
'videoPullQuote',
|
|
25
|
+
'dataTable',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function profileOf(source: string) {
|
|
29
|
+
const doc = parseMarkdown(source);
|
|
30
|
+
return profileBlockContents(doc.children);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('profileBlockContents', () => {
|
|
34
|
+
it('returns an empty profile for no nodes', () => {
|
|
35
|
+
const p = profileBlockContents([]);
|
|
36
|
+
expect(p.wordCount).toBe(0);
|
|
37
|
+
expect(p.hasImage).toBe(false);
|
|
38
|
+
expect(p.hasVideo).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('detects a single markdown image', () => {
|
|
42
|
+
const p = profileOf('');
|
|
43
|
+
expect(p.imageCount).toBe(1);
|
|
44
|
+
expect(p.hasImage).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('detects an HTML <img> tag', () => {
|
|
48
|
+
const p = profileOf('<img src="a.png" alt="x" />');
|
|
49
|
+
expect(p.imageCount).toBe(1);
|
|
50
|
+
expect(p.hasImage).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('detects a blockquote', () => {
|
|
54
|
+
const p = profileOf('> The mountain was calling me.');
|
|
55
|
+
expect(p.hasBlockquote).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('detects a bullet list', () => {
|
|
59
|
+
const p = profileOf('- one\n- two\n- three');
|
|
60
|
+
expect(p.hasList).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('detects a GFM table', () => {
|
|
64
|
+
const p = profileOf('| a | b |\n| --- | --- |\n| 1 | 2 |');
|
|
65
|
+
expect(p.hasTable).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('detects a date string in body text', () => {
|
|
69
|
+
const p = profileOf('Construction began in July 1899.');
|
|
70
|
+
expect(p.hasDate).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('detects an ISO date', () => {
|
|
74
|
+
const p = profileOf('Launched on 2024-03-15.');
|
|
75
|
+
expect(p.hasDate).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('detects a prominent number paragraph', () => {
|
|
79
|
+
const p = profileOf('$2.3M raised');
|
|
80
|
+
expect(p.hasNumberHighlight).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('detects a YouTube embed via link', () => {
|
|
84
|
+
const p = profileOf('[Watch](https://www.youtube.com/watch?v=abc)');
|
|
85
|
+
expect(p.hasVideo).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('detects a <video> tag', () => {
|
|
89
|
+
const p = profileOf('<video src="x.mp4"></video>');
|
|
90
|
+
expect(p.hasVideo).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('counts multiple images', () => {
|
|
94
|
+
const p = profileOf('\n\n\n\n');
|
|
95
|
+
expect(p.imageCount).toBe(3);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('recommendTemplatesForBlock', () => {
|
|
100
|
+
it('falls back to universal defaults when nothing is detected', () => {
|
|
101
|
+
const profile = profileBlockContents([]);
|
|
102
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
103
|
+
expect(recommended).toEqual(['title', 'sectionHeader', 'factCard', 'twoColumn']);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('keeps recommended ordering aligned with the input list', () => {
|
|
107
|
+
const profile = profileOf('');
|
|
108
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
109
|
+
// imageWithCaption comes before leftFeature/rightFeature in ALL_TEMPLATES
|
|
110
|
+
expect(recommended.indexOf('imageWithCaption')).toBeLessThan(
|
|
111
|
+
recommended.indexOf('leftFeature'),
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('recommends image templates for blocks with one image (no photoGrid)', () => {
|
|
116
|
+
const profile = profileOf('');
|
|
117
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
118
|
+
expect(recommended).toContain('imageWithCaption');
|
|
119
|
+
expect(recommended).toContain('leftFeature');
|
|
120
|
+
expect(recommended).toContain('rightFeature');
|
|
121
|
+
expect(recommended).not.toContain('photoGrid');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('adds photoGrid when 2+ images present', () => {
|
|
125
|
+
const profile = profileOf('\n\n');
|
|
126
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
127
|
+
expect(recommended).toContain('photoGrid');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('recommends quote templates for blocks with blockquotes', () => {
|
|
131
|
+
const profile = profileOf('> something memorable');
|
|
132
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
133
|
+
expect(recommended).toContain('quote');
|
|
134
|
+
expect(recommended).toContain('pullQuote');
|
|
135
|
+
expect(recommended).toContain('fullBleedQuote');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('recommends dateEvent for blocks with date strings', () => {
|
|
139
|
+
const profile = profileOf('On March 5, 2024, the ribbon was cut.');
|
|
140
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
141
|
+
expect(recommended).toContain('dateEvent');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('recommends statHighlight for blocks with prominent numbers', () => {
|
|
145
|
+
const profile = profileOf('$2.3M raised');
|
|
146
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
147
|
+
expect(recommended).toContain('statHighlight');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('recommends dataTable + comparisonBar for blocks with tables', () => {
|
|
151
|
+
const profile = profileOf('| a | b |\n| --- | --- |\n| 1 | 2 |');
|
|
152
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
153
|
+
expect(recommended).toContain('dataTable');
|
|
154
|
+
expect(recommended).toContain('comparisonBar');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('omits universal generics (factCard, twoColumn) when specific signal fires', () => {
|
|
158
|
+
const profile = profileOf('> something memorable');
|
|
159
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
160
|
+
// title + sectionHeader still come along, but factCard/twoColumn do not
|
|
161
|
+
expect(recommended).toContain('title');
|
|
162
|
+
expect(recommended).toContain('sectionHeader');
|
|
163
|
+
expect(recommended).not.toContain('factCard');
|
|
164
|
+
expect(recommended).not.toContain('twoColumn');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('combines multiple signals additively', () => {
|
|
168
|
+
const profile = profileOf('\n\n> a great line\n\n- alpha\n- beta');
|
|
169
|
+
const { recommended } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
170
|
+
expect(recommended).toContain('imageWithCaption');
|
|
171
|
+
expect(recommended).toContain('quote');
|
|
172
|
+
expect(recommended).toContain('list');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('places unrecommended templates in the rest bucket', () => {
|
|
176
|
+
const profile = profileOf('');
|
|
177
|
+
const { recommended, rest } = recommendTemplatesForBlock(profile, ALL_TEMPLATES);
|
|
178
|
+
expect(rest).toContain('map');
|
|
179
|
+
expect(rest).toContain('definitionCard');
|
|
180
|
+
// No template appears in both buckets
|
|
181
|
+
for (const name of recommended) expect(rest).not.toContain(name);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { MemoryContentContainer } from '../storage/ContentContainer';
|
|
3
|
+
import { ScopedContentContainer, scopeContainer } from '../storage/ScopedContentContainer';
|
|
4
|
+
|
|
5
|
+
const enc = (s: string) => new TextEncoder().encode(s);
|
|
6
|
+
const dec = (b: ArrayBuffer | null) => (b ? new TextDecoder().decode(b) : null);
|
|
7
|
+
|
|
8
|
+
describe('ScopedContentContainer', () => {
|
|
9
|
+
let parent: MemoryContentContainer;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
parent = new MemoryContentContainer();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('writeFile + readFile route through the prefix', async () => {
|
|
16
|
+
const scoped = scopeContainer(parent, 'image_files');
|
|
17
|
+
await scoped.writeFile('state.json', enc('{}'), 'application/json');
|
|
18
|
+
|
|
19
|
+
expect(dec(await parent.readFile('image_files/state.json'))).toBe('{}');
|
|
20
|
+
expect(dec(await scoped.readFile('state.json'))).toBe('{}');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('exists + removeFile honor the prefix', async () => {
|
|
24
|
+
const scoped = scopeContainer(parent, 'image_files');
|
|
25
|
+
await scoped.writeFile('a.txt', enc('a'));
|
|
26
|
+
expect(await scoped.exists('a.txt')).toBe(true);
|
|
27
|
+
expect(await parent.exists('image_files/a.txt')).toBe(true);
|
|
28
|
+
|
|
29
|
+
await scoped.removeFile('a.txt');
|
|
30
|
+
expect(await scoped.exists('a.txt')).toBe(false);
|
|
31
|
+
expect(await parent.exists('image_files/a.txt')).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('listFiles returns prefix-stripped paths and ignores siblings', async () => {
|
|
35
|
+
const scoped = scopeContainer(parent, 'image_files');
|
|
36
|
+
await parent.writeFile('outside.txt', enc('x'));
|
|
37
|
+
await parent.writeFile('image_files_sibling/peek.txt', enc('y')); // not in scope
|
|
38
|
+
await scoped.writeFile('a.txt', enc('1'));
|
|
39
|
+
await scoped.writeFile('sub/b.txt', enc('2'));
|
|
40
|
+
|
|
41
|
+
const list = await scoped.listFiles();
|
|
42
|
+
const paths = list.map((e) => e.path).sort();
|
|
43
|
+
expect(paths).toEqual(['a.txt', 'sub/b.txt']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('listFiles with prefix joins the scope prefix', async () => {
|
|
47
|
+
const scoped = scopeContainer(parent, 'sidecar');
|
|
48
|
+
await scoped.writeFile('assets/a.png', enc('a'));
|
|
49
|
+
await scoped.writeFile('assets/b.png', enc('b'));
|
|
50
|
+
await scoped.writeFile('state.json', enc('{}'));
|
|
51
|
+
|
|
52
|
+
const list = await scoped.listFiles('assets/');
|
|
53
|
+
expect(list.map((e) => e.path).sort()).toEqual(['assets/a.png', 'assets/b.png']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('nested scopes compose', async () => {
|
|
57
|
+
const a = scopeContainer(parent, 'a');
|
|
58
|
+
const ab = scopeContainer(a, 'b');
|
|
59
|
+
await ab.writeFile('c.txt', enc('hi'));
|
|
60
|
+
expect(dec(await parent.readFile('a/b/c.txt'))).toBe('hi');
|
|
61
|
+
expect(dec(await ab.readFile('c.txt'))).toBe('hi');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('normalizes leading/trailing slashes in prefix and paths', async () => {
|
|
65
|
+
const scoped = scopeContainer(parent, '/sidecar/');
|
|
66
|
+
expect(scoped.prefix).toBe('sidecar');
|
|
67
|
+
await scoped.writeFile('/x.txt', enc('x'));
|
|
68
|
+
expect(dec(await parent.readFile('sidecar/x.txt'))).toBe('x');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('rejects empty / dotted prefixes', () => {
|
|
72
|
+
expect(() => scopeContainer(parent, '')).toThrow();
|
|
73
|
+
expect(() => scopeContainer(parent, '/')).toThrow();
|
|
74
|
+
expect(() => scopeContainer(parent, 'a/../b')).toThrow();
|
|
75
|
+
expect(() => scopeContainer(parent, './a')).toThrow();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('getDocumentPath / readDocument always return null', async () => {
|
|
79
|
+
const scoped = scopeContainer(parent, 'sidecar');
|
|
80
|
+
await scoped.writeFile('index.md', enc('# hi'));
|
|
81
|
+
expect(await scoped.getDocumentPath()).toBeNull();
|
|
82
|
+
expect(await scoped.readDocument()).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('writeDocument throws', async () => {
|
|
86
|
+
const scoped = new ScopedContentContainer(parent, 'sidecar');
|
|
87
|
+
await expect(scoped.writeDocument()).rejects.toThrow();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseMarkdown } from '../markdown/parse.js';
|
|
3
|
+
import { markdownToDoc } from '../doc/markdownToDoc.js';
|
|
4
|
+
|
|
5
|
+
describe('Template annotation in Heading parse path', () => {
|
|
6
|
+
it('parseMarkdown extracts {[template]} from a heading', () => {
|
|
7
|
+
const md = '## Getting Started {[comparisonBar]}';
|
|
8
|
+
const doc = parseMarkdown(md);
|
|
9
|
+
const heading = doc.children[0];
|
|
10
|
+
expect(heading.type).toBe('heading');
|
|
11
|
+
if (heading.type !== 'heading') return;
|
|
12
|
+
expect(heading.templateAnnotation).toEqual({ template: 'comparisonBar' });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('markdownToDoc preserves templateAnnotation on the resulting block', () => {
|
|
16
|
+
const md = '# Title\n\n## Getting Started {[comparisonBar]}\n\nBody.';
|
|
17
|
+
const parsed = parseMarkdown(md);
|
|
18
|
+
const doc = markdownToDoc(parsed, { articleId: 'test' });
|
|
19
|
+
// The h1 is the root; first child is the h2 with annotation
|
|
20
|
+
const h1 = doc.blocks[0];
|
|
21
|
+
expect(h1.sourceHeading?.depth).toBe(1);
|
|
22
|
+
const h2 = h1.children?.[0];
|
|
23
|
+
expect(h2?.sourceHeading?.depth).toBe(2);
|
|
24
|
+
expect(h2?.sourceHeading?.templateAnnotation).toEqual({ template: 'comparisonBar' });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('tolerates accidentally-doubled trailing `]}` (user typo)', () => {
|
|
28
|
+
const md = '## Getting Started {[comparisonBar]}]}';
|
|
29
|
+
const doc = parseMarkdown(md);
|
|
30
|
+
const heading = doc.children[0];
|
|
31
|
+
if (heading.type !== 'heading') throw new Error('expected heading');
|
|
32
|
+
expect(heading.templateAnnotation).toEqual({ template: 'comparisonBar' });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('still rejects non-trailing annotations (regex stays anchored)', () => {
|
|
36
|
+
const md = '## The {[chart]} section'; // word continues after — not an annotation
|
|
37
|
+
const doc = parseMarkdown(md);
|
|
38
|
+
const heading = doc.children[0];
|
|
39
|
+
if (heading.type !== 'heading') throw new Error('expected heading');
|
|
40
|
+
expect(heading.templateAnnotation).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
});
|