@bendyline/squisq 1.2.2 → 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-BfBhiCNr.d.ts → Doc-BeSeH2iX.d.ts} +141 -33
- 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__/markdown.test.js +40 -0
- package/dist/__tests__/markdown.test.js.map +1 -1
- 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__/surface.test.d.ts +2 -0
- package/dist/__tests__/surface.test.d.ts.map +1 -0
- package/dist/__tests__/surface.test.js +57 -0
- package/dist/__tests__/surface.test.js.map +1 -0
- 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-LAZELBON.js → chunk-3K5OG6KI.js} +165 -17
- 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-GAQLMORN.js → chunk-KSWJR66U.js} +248 -44
- 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 +80 -9
- 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 +235 -15
- package/dist/markdown/convert.js.map +1 -1
- package/dist/markdown/index.d.ts +2 -2
- 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 +34 -1
- 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 +93 -23
- package/dist/schemas/Theme.d.ts.map +1 -1
- package/dist/schemas/Theme.js +80 -13
- 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 -499
- 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-DYAzisMG.d.ts → types-BdZkdJ3z.d.ts} +35 -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__/markdown.test.ts +44 -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__/surface.test.ts +68 -0
- 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 +95 -13
- 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 +248 -15
- package/src/markdown/index.ts +4 -0
- package/src/markdown/types.ts +38 -1
- 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 +152 -23
- 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 -524
- 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-53TZ2UTL.js +0 -470
- package/dist/chunk-53TZ2UTL.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-GAQLMORN.js.map +0 -1
- package/dist/chunk-LAZELBON.js.map +0 -1
- package/dist/chunk-QM5PKNPW.js.map +0 -1
- package/dist/chunk-TSG2OSE6.js.map +0 -1
- package/dist/chunk-XM4WSOHB.js +0 -92
- package/dist/chunk-XM4WSOHB.js.map +0 -1
- package/dist/themeLibrary-IP7EdyRq.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
|
+
});
|
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
MarkdownLinkReference,
|
|
34
34
|
MarkdownImageReference,
|
|
35
35
|
MarkdownText,
|
|
36
|
+
MarkdownMention,
|
|
36
37
|
} from '../markdown/index';
|
|
37
38
|
|
|
38
39
|
// ============================================
|
|
@@ -151,6 +152,49 @@ describe('parseMarkdown / stringifyMarkdown', () => {
|
|
|
151
152
|
expect(link.children[0]).toEqual({ type: 'text', value: 'Click here' });
|
|
152
153
|
});
|
|
153
154
|
|
|
155
|
+
it('parses mentions from @[Name](scheme:id)', () => {
|
|
156
|
+
const doc = parse('Hey @[Leo](gezel:leo), take a look.');
|
|
157
|
+
const p = doc.children[0] as MarkdownParagraph;
|
|
158
|
+
const text0 = p.children[0] as MarkdownText;
|
|
159
|
+
expect(text0).toEqual({ type: 'text', value: 'Hey ' });
|
|
160
|
+
const mention = p.children[1] as MarkdownMention;
|
|
161
|
+
expect(mention).toEqual({
|
|
162
|
+
type: 'mention',
|
|
163
|
+
targetKind: 'gezel',
|
|
164
|
+
targetId: 'leo',
|
|
165
|
+
displayName: 'Leo',
|
|
166
|
+
});
|
|
167
|
+
const text2 = p.children[2] as MarkdownText;
|
|
168
|
+
expect(text2.value).toBe(', take a look.');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('round-trips a mention back to markdown', () => {
|
|
172
|
+
// remark-stringify escapes `:` in link URLs that follow an `@` (to
|
|
173
|
+
// disambiguate from autolink syntax). The escape is idempotent —
|
|
174
|
+
// re-parsing `\:` produces the unescaped url, so the round-trip
|
|
175
|
+
// preserves the mention semantically even if the raw text changes
|
|
176
|
+
// on the first pass.
|
|
177
|
+
const input = 'Hey @[Leo](gezel:leo), ping @[Tess](gezel:tess) too.\n';
|
|
178
|
+
const once = stringifyMarkdown(parseMarkdown(input));
|
|
179
|
+
const twice = stringifyMarkdown(parseMarkdown(once));
|
|
180
|
+
expect(twice).toBe(once);
|
|
181
|
+
// Semantics are preserved: re-parsing yields mentions.
|
|
182
|
+
const doc = parseMarkdown(once) as MarkdownDocument;
|
|
183
|
+
const p = doc.children[0] as MarkdownParagraph;
|
|
184
|
+
const m1 = p.children.find((c) => c.type === 'mention') as MarkdownMention;
|
|
185
|
+
expect(m1.targetKind).toBe('gezel');
|
|
186
|
+
expect(m1.targetId).toBe('leo');
|
|
187
|
+
expect(m1.displayName).toBe('Leo');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('does not collapse @ + link when the url has no scheme', () => {
|
|
191
|
+
const doc = parse('Cost @[100]($100)');
|
|
192
|
+
const p = doc.children[0] as MarkdownParagraph;
|
|
193
|
+
// "$100" doesn't match scheme:id because $ isn't a scheme character
|
|
194
|
+
// → stays as text("Cost @") + link
|
|
195
|
+
expect((p.children[1] as MarkdownLink).type).toBe('link');
|
|
196
|
+
});
|
|
197
|
+
|
|
154
198
|
it('parses images', () => {
|
|
155
199
|
const doc = parse('');
|
|
156
200
|
const p = doc.children[0] as MarkdownParagraph;
|
|
@@ -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,68 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
applySurface,
|
|
4
|
+
DARK_SURFACE,
|
|
5
|
+
DEFAULT_THEME,
|
|
6
|
+
LIGHT_SURFACE,
|
|
7
|
+
type SurfaceScheme,
|
|
8
|
+
} from '../schemas/index.js';
|
|
9
|
+
|
|
10
|
+
describe('SurfaceScheme', () => {
|
|
11
|
+
it('LIGHT_SURFACE and DARK_SURFACE expose the full field set', () => {
|
|
12
|
+
for (const s of [LIGHT_SURFACE, DARK_SURFACE]) {
|
|
13
|
+
expect(typeof s.id).toBe('string');
|
|
14
|
+
expect(typeof s.background).toBe('string');
|
|
15
|
+
expect(typeof s.backgroundLight).toBe('string');
|
|
16
|
+
expect(typeof s.text).toBe('string');
|
|
17
|
+
expect(typeof s.textMuted).toBe('string');
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('applySurface', () => {
|
|
23
|
+
it('overlays surface fields onto a theme', () => {
|
|
24
|
+
const out = applySurface(DEFAULT_THEME, LIGHT_SURFACE);
|
|
25
|
+
expect(out.colors.background).toBe(LIGHT_SURFACE.background);
|
|
26
|
+
expect(out.colors.backgroundLight).toBe(LIGHT_SURFACE.backgroundLight);
|
|
27
|
+
expect(out.colors.text).toBe(LIGHT_SURFACE.text);
|
|
28
|
+
expect(out.colors.textMuted).toBe(LIGHT_SURFACE.textMuted);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('leaves editorial colors (primary, highlight, warning) untouched', () => {
|
|
32
|
+
const out = applySurface(DEFAULT_THEME, DARK_SURFACE);
|
|
33
|
+
expect(out.colors.primary).toBe(DEFAULT_THEME.colors.primary);
|
|
34
|
+
expect(out.colors.secondary).toBe(DEFAULT_THEME.colors.secondary);
|
|
35
|
+
expect(out.colors.highlight).toBe(DEFAULT_THEME.colors.highlight);
|
|
36
|
+
expect(out.colors.warning).toBe(DEFAULT_THEME.colors.warning);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('leaves typography, style, renderStyle, colorSchemes untouched', () => {
|
|
40
|
+
const out = applySurface(DEFAULT_THEME, LIGHT_SURFACE);
|
|
41
|
+
expect(out.typography).toBe(DEFAULT_THEME.typography);
|
|
42
|
+
expect(out.style).toBe(DEFAULT_THEME.style);
|
|
43
|
+
expect(out.renderStyle).toBe(DEFAULT_THEME.renderStyle);
|
|
44
|
+
expect(out.colorSchemes).toBe(DEFAULT_THEME.colorSchemes);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns a new theme (does not mutate the base)', () => {
|
|
48
|
+
const frozenTheme = DEFAULT_THEME;
|
|
49
|
+
const originalBg = frozenTheme.colors.background;
|
|
50
|
+
const out = applySurface(frozenTheme, DARK_SURFACE);
|
|
51
|
+
expect(out).not.toBe(frozenTheme);
|
|
52
|
+
expect(out.colors).not.toBe(frozenTheme.colors);
|
|
53
|
+
expect(frozenTheme.colors.background).toBe(originalBg);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('accepts custom SurfaceScheme objects', () => {
|
|
57
|
+
const custom: SurfaceScheme = {
|
|
58
|
+
id: 'parchment',
|
|
59
|
+
background: '#f4ecd8',
|
|
60
|
+
backgroundLight: '#e8dfc4',
|
|
61
|
+
text: '#2b2b2b',
|
|
62
|
+
textMuted: '#6b6b6b',
|
|
63
|
+
};
|
|
64
|
+
const out = applySurface(DEFAULT_THEME, custom);
|
|
65
|
+
expect(out.colors.background).toBe('#f4ecd8');
|
|
66
|
+
expect(out.colors.textMuted).toBe('#6b6b6b');
|
|
67
|
+
});
|
|
68
|
+
});
|