@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,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Pointer helpers (RFC 6901) plus immutable get/set, plus a tiny
|
|
3
|
+
* dotted-path normalizer so hosts can write `user.role` instead of
|
|
4
|
+
* `/user/role`. `$ref` resolution is local-only (`#/$defs/Foo`).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { SquisqAnnotatedSchema } from './types.js';
|
|
8
|
+
|
|
9
|
+
/** Normalize a path to a JSON Pointer string. Accepts both forms. */
|
|
10
|
+
export function toPointer(path: string): string {
|
|
11
|
+
if (path === '' || path === '/') return '';
|
|
12
|
+
if (path.startsWith('/')) return path;
|
|
13
|
+
// Dotted form: split on `.` and escape per RFC 6901.
|
|
14
|
+
return (
|
|
15
|
+
'/' +
|
|
16
|
+
path
|
|
17
|
+
.split('.')
|
|
18
|
+
.map((seg) => seg.replace(/~/g, '~0').replace(/\//g, '~1'))
|
|
19
|
+
.join('/')
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Split a JSON Pointer into decoded segments. Empty string → []. */
|
|
24
|
+
export function pointerSegments(pointer: string): string[] {
|
|
25
|
+
if (pointer === '') return [];
|
|
26
|
+
return pointer
|
|
27
|
+
.slice(1)
|
|
28
|
+
.split('/')
|
|
29
|
+
.map((seg) => seg.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Read a value from data by path (dotted or pointer). Returns undefined if missing. */
|
|
33
|
+
export function getByPointer(data: unknown, path: string): unknown {
|
|
34
|
+
const segments = pointerSegments(toPointer(path));
|
|
35
|
+
let cur: unknown = data;
|
|
36
|
+
for (const seg of segments) {
|
|
37
|
+
if (cur === null || cur === undefined) return undefined;
|
|
38
|
+
if (Array.isArray(cur)) {
|
|
39
|
+
const idx = Number(seg);
|
|
40
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= cur.length) return undefined;
|
|
41
|
+
cur = cur[idx];
|
|
42
|
+
} else if (typeof cur === 'object') {
|
|
43
|
+
cur = (cur as Record<string, unknown>)[seg];
|
|
44
|
+
} else {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return cur;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Return a structurally-shared copy of `data` with the value at `path`
|
|
53
|
+
* replaced. Intermediate objects/arrays are cloned along the path; the
|
|
54
|
+
* rest is shared by reference. Creates intermediate objects if missing.
|
|
55
|
+
*/
|
|
56
|
+
export function setByPointer<T>(data: T, path: string, value: unknown): T {
|
|
57
|
+
const segments = pointerSegments(toPointer(path));
|
|
58
|
+
if (segments.length === 0) return value as T;
|
|
59
|
+
return setRec(data, segments, 0, value) as T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setRec(node: unknown, segments: string[], i: number, value: unknown): unknown {
|
|
63
|
+
const seg = segments[i];
|
|
64
|
+
const isLast = i === segments.length - 1;
|
|
65
|
+
|
|
66
|
+
// Decide whether the slot we're about to write into is an array index.
|
|
67
|
+
const nextSeg = isLast ? undefined : segments[i + 1];
|
|
68
|
+
const writingArrayChild = nextSeg !== undefined && /^\d+$/.test(nextSeg);
|
|
69
|
+
|
|
70
|
+
if (Array.isArray(node)) {
|
|
71
|
+
const idx = Number(seg);
|
|
72
|
+
const next = node.slice();
|
|
73
|
+
next[idx] = isLast
|
|
74
|
+
? value
|
|
75
|
+
: setRec(node[idx] ?? (writingArrayChild ? [] : {}), segments, i + 1, value);
|
|
76
|
+
return next;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const obj = (node && typeof node === 'object' ? { ...(node as object) } : {}) as Record<
|
|
80
|
+
string,
|
|
81
|
+
unknown
|
|
82
|
+
>;
|
|
83
|
+
obj[seg] = isLast
|
|
84
|
+
? value
|
|
85
|
+
: setRec(obj[seg] ?? (writingArrayChild ? [] : {}), segments, i + 1, value);
|
|
86
|
+
return obj;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve a local `$ref` (e.g. `#/$defs/Foo`) against the root schema.
|
|
91
|
+
* Returns the original schema if no `$ref` is present, or `undefined`
|
|
92
|
+
* if the reference cannot be resolved. Cross-document refs are not
|
|
93
|
+
* supported.
|
|
94
|
+
*/
|
|
95
|
+
export function resolveRef(
|
|
96
|
+
schema: SquisqAnnotatedSchema,
|
|
97
|
+
root: SquisqAnnotatedSchema,
|
|
98
|
+
): SquisqAnnotatedSchema | undefined {
|
|
99
|
+
if (!schema.$ref) return schema;
|
|
100
|
+
const ref = schema.$ref;
|
|
101
|
+
if (!ref.startsWith('#/')) return undefined;
|
|
102
|
+
const segments = pointerSegments(ref.slice(1));
|
|
103
|
+
let cur: unknown = root;
|
|
104
|
+
for (const seg of segments) {
|
|
105
|
+
if (cur === null || cur === undefined || typeof cur !== 'object') return undefined;
|
|
106
|
+
cur = (cur as Record<string, unknown>)[seg];
|
|
107
|
+
}
|
|
108
|
+
return (cur as SquisqAnnotatedSchema | undefined) ?? undefined;
|
|
109
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Form types — Squisq's hint vocabulary layered on JSON Schema.
|
|
3
|
+
*
|
|
4
|
+
* Hints live inline on a JSON Schema under a single `squisq` key. The
|
|
5
|
+
* schema remains a valid JSON Schema; the extra key is ignored by
|
|
6
|
+
* standard validators. Renderers consult the hints to pick a control
|
|
7
|
+
* and tune its presentation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Every concrete control rendered by `<JsonView>` and `<JsonEditor>`.
|
|
12
|
+
*
|
|
13
|
+
* Auto-defaults pick one based on schema shape; hosts override via
|
|
14
|
+
* `squisq.control` when they want something specific (e.g. radio over
|
|
15
|
+
* segmented for a long, descriptive enum).
|
|
16
|
+
*/
|
|
17
|
+
export type ControlKind =
|
|
18
|
+
| 'text'
|
|
19
|
+
| 'multiline'
|
|
20
|
+
| 'richtext'
|
|
21
|
+
| 'color'
|
|
22
|
+
| 'date'
|
|
23
|
+
| 'time'
|
|
24
|
+
| 'datetime'
|
|
25
|
+
| 'slider'
|
|
26
|
+
| 'stepper'
|
|
27
|
+
| 'segmented'
|
|
28
|
+
| 'radio'
|
|
29
|
+
| 'combobox'
|
|
30
|
+
| 'toggle'
|
|
31
|
+
| 'checkbox'
|
|
32
|
+
| 'card'
|
|
33
|
+
| 'card-stack'
|
|
34
|
+
| 'chip-bin'
|
|
35
|
+
| 'tabs'
|
|
36
|
+
| 'group';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Conditional rule: looked up by field path against the root data and
|
|
40
|
+
* compared with one of the operators. The first defined operator wins
|
|
41
|
+
* (in declaration order: equals, oneOf, matches, truthy).
|
|
42
|
+
*/
|
|
43
|
+
export interface SquisqWhen {
|
|
44
|
+
/**
|
|
45
|
+
* Path to the controlling field, relative to the root data.
|
|
46
|
+
* Accepts dotted-path (`user.role`) or JSON Pointer (`/user/role`).
|
|
47
|
+
*/
|
|
48
|
+
field: string;
|
|
49
|
+
equals?: unknown;
|
|
50
|
+
oneOf?: readonly unknown[];
|
|
51
|
+
/** ECMAScript regex source; matched against the string value. */
|
|
52
|
+
matches?: string;
|
|
53
|
+
/** When true, condition holds if value is truthy; when false, if falsy. */
|
|
54
|
+
truthy?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hints for a single schema node. Attach via `squisq` on any JSON
|
|
59
|
+
* Schema object — properties, items, the root, or sub-schemas inside
|
|
60
|
+
* `oneOf`/`anyOf`.
|
|
61
|
+
*/
|
|
62
|
+
export interface SquisqHints {
|
|
63
|
+
/** Force a specific control. If omitted, picked from type/format/enum cardinality. */
|
|
64
|
+
control?: ControlKind;
|
|
65
|
+
|
|
66
|
+
/** Override schema `title` for display. */
|
|
67
|
+
label?: string;
|
|
68
|
+
/** Override schema `description` for the inline helper text. */
|
|
69
|
+
help?: string;
|
|
70
|
+
/** Placeholder text for empty fields. */
|
|
71
|
+
placeholder?: string;
|
|
72
|
+
|
|
73
|
+
/** Suggested layout sizing in a parent grid. Renderers may ignore on narrow viewports. */
|
|
74
|
+
width?: 'full' | 'half' | 'third' | 'auto';
|
|
75
|
+
|
|
76
|
+
/** Hide the field — literal, or a `SquisqWhen` evaluated against root data. */
|
|
77
|
+
hidden?: boolean | SquisqWhen;
|
|
78
|
+
/** Disable the field — literal, or a `SquisqWhen` evaluated against root data. */
|
|
79
|
+
disabled?: boolean | SquisqWhen;
|
|
80
|
+
|
|
81
|
+
/** Override schema's required-ness for the UI only. */
|
|
82
|
+
required?: boolean;
|
|
83
|
+
|
|
84
|
+
/** For arrays of objects: how to label each card in a `card-stack`. */
|
|
85
|
+
itemLabel?: string | { fromField: string };
|
|
86
|
+
/** Label for the "+ Add" button on arrays. */
|
|
87
|
+
addLabel?: string;
|
|
88
|
+
/** Label for the remove button on arrays. */
|
|
89
|
+
removeLabel?: string;
|
|
90
|
+
|
|
91
|
+
/** Slider/stepper visual step. Falls back to schema `multipleOf`. */
|
|
92
|
+
step?: number;
|
|
93
|
+
|
|
94
|
+
/** Optional human-readable labels for enum values. */
|
|
95
|
+
enumLabels?: Record<string, string>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The subset of JSON Schema we understand, with an optional `squisq`
|
|
100
|
+
* key. We deliberately keep this as a structural type so any JSON
|
|
101
|
+
* Schema authored elsewhere can be passed in without conversion.
|
|
102
|
+
*/
|
|
103
|
+
export interface SquisqAnnotatedSchema {
|
|
104
|
+
type?: string | readonly string[];
|
|
105
|
+
format?: string;
|
|
106
|
+
title?: string;
|
|
107
|
+
description?: string;
|
|
108
|
+
default?: unknown;
|
|
109
|
+
examples?: readonly unknown[];
|
|
110
|
+
|
|
111
|
+
enum?: readonly unknown[];
|
|
112
|
+
const?: unknown;
|
|
113
|
+
|
|
114
|
+
// Object
|
|
115
|
+
properties?: Record<string, SquisqAnnotatedSchema>;
|
|
116
|
+
required?: readonly string[];
|
|
117
|
+
additionalProperties?: boolean | SquisqAnnotatedSchema;
|
|
118
|
+
|
|
119
|
+
// Array
|
|
120
|
+
items?: SquisqAnnotatedSchema | readonly SquisqAnnotatedSchema[];
|
|
121
|
+
minItems?: number;
|
|
122
|
+
maxItems?: number;
|
|
123
|
+
|
|
124
|
+
// Number
|
|
125
|
+
minimum?: number;
|
|
126
|
+
maximum?: number;
|
|
127
|
+
exclusiveMinimum?: number;
|
|
128
|
+
exclusiveMaximum?: number;
|
|
129
|
+
multipleOf?: number;
|
|
130
|
+
|
|
131
|
+
// String
|
|
132
|
+
minLength?: number;
|
|
133
|
+
maxLength?: number;
|
|
134
|
+
pattern?: string;
|
|
135
|
+
|
|
136
|
+
// Combinators
|
|
137
|
+
oneOf?: readonly SquisqAnnotatedSchema[];
|
|
138
|
+
anyOf?: readonly SquisqAnnotatedSchema[];
|
|
139
|
+
allOf?: readonly SquisqAnnotatedSchema[];
|
|
140
|
+
|
|
141
|
+
// References
|
|
142
|
+
$ref?: string;
|
|
143
|
+
$defs?: Record<string, SquisqAnnotatedSchema>;
|
|
144
|
+
definitions?: Record<string, SquisqAnnotatedSchema>;
|
|
145
|
+
|
|
146
|
+
// Squisq-native UI hints
|
|
147
|
+
squisq?: SquisqHints;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A validation error reported by the consumer-supplied `validate` prop.
|
|
152
|
+
* Matches the shape of common validators (ajv, etc.) without depending
|
|
153
|
+
* on any specific one.
|
|
154
|
+
*/
|
|
155
|
+
export interface JsonFormValidationError {
|
|
156
|
+
/** JSON Pointer to the failing value, e.g. `/sections/0/heading`. */
|
|
157
|
+
path: string;
|
|
158
|
+
/** Human-readable message. */
|
|
159
|
+
message: string;
|
|
160
|
+
/** Optional schema keyword that failed (e.g. "required", "minimum"). */
|
|
161
|
+
keyword?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type JsonFormValidator = (
|
|
165
|
+
value: unknown,
|
|
166
|
+
schema: SquisqAnnotatedSchema,
|
|
167
|
+
) => readonly JsonFormValidationError[];
|
package/src/markdown/convert.ts
CHANGED
|
@@ -20,8 +20,10 @@ import type {
|
|
|
20
20
|
MarkdownSourcePosition,
|
|
21
21
|
HeadingTemplateAnnotation,
|
|
22
22
|
MarkdownHeading,
|
|
23
|
+
MarkdownInlineIcon,
|
|
23
24
|
} from './types.js';
|
|
24
25
|
import { parseHtmlToNodes } from './htmlParse.js';
|
|
26
|
+
import { resolveIcon } from '../icons/resolve.js';
|
|
25
27
|
|
|
26
28
|
// ============================================
|
|
27
29
|
// Generic mdast node shape
|
|
@@ -132,8 +134,13 @@ function extractText(node: MdastNode): string {
|
|
|
132
134
|
/**
|
|
133
135
|
* Regex matching a trailing `{[templateName key=value …]}` annotation.
|
|
134
136
|
* Captures the content between `{[` and `]}`.
|
|
137
|
+
*
|
|
138
|
+
* The trailing `[\s\]\}]*` tolerates accidental doubled `]}` that users
|
|
139
|
+
* sometimes type when learning the syntax (e.g. `{[foo]}]}`). It does
|
|
140
|
+
* not allow trailing word characters, so a non-trailing annotation like
|
|
141
|
+
* `## The {[chart]} section` still doesn't match.
|
|
135
142
|
*/
|
|
136
|
-
const TEMPLATE_ANNOTATION_RE = /\s*\{\[([^\]]+)\]\}\s*$/;
|
|
143
|
+
const TEMPLATE_ANNOTATION_RE = /\s*\{\[([^\]]+)\]\}[\s\]}]*$/;
|
|
137
144
|
|
|
138
145
|
/**
|
|
139
146
|
* Extract a `{[templateName key=value …]}` annotation from a heading's
|
|
@@ -247,13 +254,130 @@ function convertBlockChildren(children: MdastNode[], parseHtml: boolean): Markdo
|
|
|
247
254
|
return result;
|
|
248
255
|
}
|
|
249
256
|
|
|
250
|
-
function convertInlineChildren(
|
|
257
|
+
function convertInlineChildren(
|
|
258
|
+
children: MdastNode[],
|
|
259
|
+
parseHtml: boolean,
|
|
260
|
+
/** When false, skip the inline-icon split. Headings opt out so the
|
|
261
|
+
* `extractTemplateAnnotation` pass still sees the trailing
|
|
262
|
+
* `{[…]}` in raw text form; the caller runs `splitInlineIcons`
|
|
263
|
+
* itself afterward on the remaining children. */
|
|
264
|
+
applyIconSplit: boolean = true,
|
|
265
|
+
): MarkdownInlineNode[] {
|
|
251
266
|
const result: MarkdownInlineNode[] = [];
|
|
252
267
|
for (const child of children) {
|
|
253
268
|
const node = convertInlineNode(child, parseHtml);
|
|
254
269
|
if (node) result.push(node);
|
|
255
270
|
}
|
|
256
|
-
|
|
271
|
+
const mentioned = coalesceMentions(result);
|
|
272
|
+
return applyIconSplit ? splitInlineIcons(mentioned) : mentioned;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Walk an inline-children list and split each text node on `{[token]}`
|
|
277
|
+
* occurrences whose tokens resolve to known FontAwesome icons. Returns
|
|
278
|
+
* a new array — unresolved tokens are left as literal text so authors
|
|
279
|
+
* can write `{[notathing]}` verbatim if they really mean to.
|
|
280
|
+
*
|
|
281
|
+
* Exposed for the heading code path, which first strips the trailing
|
|
282
|
+
* template annotation and then runs this on what remains.
|
|
283
|
+
*/
|
|
284
|
+
export function splitInlineIcons(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {
|
|
285
|
+
// First pass: reassemble `text + textDirective + text` patterns
|
|
286
|
+
// formed when a qualified token like `{[fa-solid:user]}` was split
|
|
287
|
+
// by remark-directive's colon recognition. We collapse those triples
|
|
288
|
+
// back into a single text node containing the original token so the
|
|
289
|
+
// regex below can match.
|
|
290
|
+
const recombined = recombineDirectiveTokens(nodes);
|
|
291
|
+
|
|
292
|
+
const out: MarkdownInlineNode[] = [];
|
|
293
|
+
for (const node of recombined) {
|
|
294
|
+
if (node.type !== 'text') {
|
|
295
|
+
out.push(node);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
out.push(...splitTextOnIcons(node.value, node.position));
|
|
299
|
+
}
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* When the user types `{[fa-solid:user]}`, remark's directive plugin
|
|
305
|
+
* sees `:user` and emits a `textDirective` node. The result is a
|
|
306
|
+
* three-node sequence `text("…{[fa-solid") + textDirective("user") +
|
|
307
|
+
* text("]}…")`. This helper detects that shape and stitches the three
|
|
308
|
+
* back into one text node so downstream icon detection works on the
|
|
309
|
+
* authored token.
|
|
310
|
+
*/
|
|
311
|
+
function recombineDirectiveTokens(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {
|
|
312
|
+
const out: MarkdownInlineNode[] = [];
|
|
313
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
314
|
+
const cur = nodes[i];
|
|
315
|
+
const next = nodes[i + 1];
|
|
316
|
+
const after = nodes[i + 2];
|
|
317
|
+
if (
|
|
318
|
+
cur?.type === 'text' &&
|
|
319
|
+
next?.type === 'textDirective' &&
|
|
320
|
+
after?.type === 'text' &&
|
|
321
|
+
/\{\[[a-zA-Z0-9_-]+$/.test(cur.value) &&
|
|
322
|
+
/^\]\}/.test(after.value)
|
|
323
|
+
) {
|
|
324
|
+
const directive = next as unknown as { name?: string };
|
|
325
|
+
const directiveName = directive.name ?? '';
|
|
326
|
+
const merged: MarkdownInlineNode = {
|
|
327
|
+
type: 'text',
|
|
328
|
+
value: cur.value + ':' + directiveName + after.value,
|
|
329
|
+
...(cur.position ? { position: cur.position } : {}),
|
|
330
|
+
};
|
|
331
|
+
out.push(merged);
|
|
332
|
+
i += 2;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
out.push(cur);
|
|
336
|
+
}
|
|
337
|
+
return out;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const ICON_TOKEN_RE = /\{\[([a-zA-Z0-9_:-]+)\]\}/g;
|
|
341
|
+
|
|
342
|
+
function splitTextOnIcons(value: string, position?: MarkdownSourcePosition): MarkdownInlineNode[] {
|
|
343
|
+
if (!value) return [];
|
|
344
|
+
ICON_TOKEN_RE.lastIndex = 0;
|
|
345
|
+
let lastIndex = 0;
|
|
346
|
+
let match: RegExpExecArray | null;
|
|
347
|
+
const out: MarkdownInlineNode[] = [];
|
|
348
|
+
while ((match = ICON_TOKEN_RE.exec(value)) !== null) {
|
|
349
|
+
const token = match[1];
|
|
350
|
+
const icon = resolveIcon(token);
|
|
351
|
+
if (!icon) continue; // leave the literal `{[…]}` in place
|
|
352
|
+
if (match.index > lastIndex) {
|
|
353
|
+
out.push({
|
|
354
|
+
type: 'text',
|
|
355
|
+
value: value.slice(lastIndex, match.index),
|
|
356
|
+
...(position ? { position } : {}),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const iconNode: MarkdownInlineIcon = {
|
|
360
|
+
type: 'inlineIcon',
|
|
361
|
+
token,
|
|
362
|
+
family: icon.family,
|
|
363
|
+
name: icon.name,
|
|
364
|
+
...(position ? { position } : {}),
|
|
365
|
+
};
|
|
366
|
+
out.push(iconNode);
|
|
367
|
+
lastIndex = match.index + match[0].length;
|
|
368
|
+
}
|
|
369
|
+
if (lastIndex === 0) {
|
|
370
|
+
// No matches — return the original text node unchanged.
|
|
371
|
+
return [{ type: 'text', value, ...(position ? { position } : {}) }];
|
|
372
|
+
}
|
|
373
|
+
if (lastIndex < value.length) {
|
|
374
|
+
out.push({
|
|
375
|
+
type: 'text',
|
|
376
|
+
value: value.slice(lastIndex),
|
|
377
|
+
...(position ? { position } : {}),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
257
381
|
}
|
|
258
382
|
|
|
259
383
|
/**
|
|
@@ -318,12 +442,18 @@ function convertBlockNode(node: MdastNode, parseHtml: boolean): MarkdownBlockNod
|
|
|
318
442
|
|
|
319
443
|
switch (node.type) {
|
|
320
444
|
case 'heading': {
|
|
321
|
-
|
|
445
|
+
// Defer icon splitting so `extractTemplateAnnotation` sees the
|
|
446
|
+
// trailing `{[name]}` as plain text and can decide whether it's
|
|
447
|
+
// a template annotation. Anything that's NOT the template still
|
|
448
|
+
// needs to become an icon node, so we run `splitInlineIcons` on
|
|
449
|
+
// what's left after the template extraction.
|
|
450
|
+
const headingChildren = convertInlineChildren(node.children ?? [], parseHtml, false);
|
|
322
451
|
const annotation = extractTemplateAnnotation(headingChildren);
|
|
452
|
+
const finalChildren = splitInlineIcons(headingChildren);
|
|
323
453
|
const result: MarkdownHeading = {
|
|
324
454
|
type: 'heading',
|
|
325
455
|
depth: (node.depth ?? 1) as 1 | 2 | 3 | 4 | 5 | 6,
|
|
326
|
-
children:
|
|
456
|
+
children: finalChildren,
|
|
327
457
|
...posField(pos),
|
|
328
458
|
};
|
|
329
459
|
if (annotation) {
|
|
@@ -967,6 +1097,16 @@ function inlineToMdast(node: MarkdownInlineNode): MdastNode {
|
|
|
967
1097
|
...mdastPosField(node.position),
|
|
968
1098
|
};
|
|
969
1099
|
|
|
1100
|
+
case 'inlineIcon':
|
|
1101
|
+
// Serialize back to the authored token so source round-trips
|
|
1102
|
+
// byte-stable. The token already carries the qualified form when
|
|
1103
|
+
// required (the parser preserved it verbatim).
|
|
1104
|
+
return {
|
|
1105
|
+
type: 'text',
|
|
1106
|
+
value: `{[${node.token}]}`,
|
|
1107
|
+
...mdastPosField(node.position),
|
|
1108
|
+
};
|
|
1109
|
+
|
|
970
1110
|
default:
|
|
971
1111
|
return { type: 'text', value: '' };
|
|
972
1112
|
}
|
package/src/markdown/index.ts
CHANGED
package/src/markdown/types.ts
CHANGED
|
@@ -453,6 +453,22 @@ export interface MarkdownInlineMath extends MarkdownNodeBase {
|
|
|
453
453
|
value: string;
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Inline FontAwesome icon. Authored as `{[name]}` (bare) or
|
|
458
|
+
* `{[fa-<family>:name]}` (qualified) in markdown source. The parser
|
|
459
|
+
* resolves the token against the FA Free catalog and emits this node;
|
|
460
|
+
* unknown / ambiguous tokens stay as literal text.
|
|
461
|
+
*/
|
|
462
|
+
export interface MarkdownInlineIcon extends MarkdownNodeBase {
|
|
463
|
+
type: 'inlineIcon';
|
|
464
|
+
/** Token exactly as authored — e.g. `github` or `fa-solid:user`. */
|
|
465
|
+
token: string;
|
|
466
|
+
/** Resolved icon family. */
|
|
467
|
+
family: 'brands' | 'solid' | 'regular';
|
|
468
|
+
/** Resolved icon name (without the `fa-` prefix). */
|
|
469
|
+
name: string;
|
|
470
|
+
}
|
|
471
|
+
|
|
456
472
|
/**
|
|
457
473
|
* A footnote reference [^id]. GFM extension.
|
|
458
474
|
*/
|
|
@@ -518,7 +534,7 @@ export interface MarkdownTextDirective extends MarkdownNodeBase {
|
|
|
518
534
|
*/
|
|
519
535
|
export interface MarkdownMention extends MarkdownNodeBase {
|
|
520
536
|
type: 'mention';
|
|
521
|
-
/** Namespace for the mentioned entity (e.g. `"
|
|
537
|
+
/** Namespace for the mentioned entity (e.g. `"user"`). */
|
|
522
538
|
targetKind: string;
|
|
523
539
|
/** Stable identifier within the namespace. */
|
|
524
540
|
targetId: string;
|
|
@@ -563,7 +579,8 @@ export type MarkdownInlineNode =
|
|
|
563
579
|
| MarkdownLinkReference
|
|
564
580
|
| MarkdownImageReference
|
|
565
581
|
| MarkdownTextDirective
|
|
566
|
-
| MarkdownMention
|
|
582
|
+
| MarkdownMention
|
|
583
|
+
| MarkdownInlineIcon;
|
|
567
584
|
|
|
568
585
|
/**
|
|
569
586
|
* Any node in the markdown tree. Includes structural nodes (listItem,
|
package/src/markdown/utils.ts
CHANGED
|
@@ -157,6 +157,109 @@ export function parseFrontmatter(yaml: string): Record<string, unknown> | null {
|
|
|
157
157
|
return Object.keys(result).length > 0 ? result : null;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
const FRONTMATTER_BLOCK_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
|
|
161
|
+
|
|
162
|
+
/** Quote a frontmatter scalar so it round-trips cleanly through `parseFrontmatter`. */
|
|
163
|
+
function formatFrontmatterValue(value: string | number | boolean): string {
|
|
164
|
+
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
|
|
165
|
+
// Quote when needed: leading/trailing whitespace, leading punctuation that
|
|
166
|
+
// could trigger YAML modes, or values that look like reserved literals.
|
|
167
|
+
const needsQuote =
|
|
168
|
+
/^\s|\s$/.test(value) ||
|
|
169
|
+
/^[!&*?|>%@`]/.test(value) ||
|
|
170
|
+
/[:#]/.test(value) ||
|
|
171
|
+
value === '' ||
|
|
172
|
+
value === 'true' ||
|
|
173
|
+
value === 'false' ||
|
|
174
|
+
/^-?\d+(\.\d+)?$/.test(value);
|
|
175
|
+
if (!needsQuote) return value;
|
|
176
|
+
// Prefer double quotes; escape any embedded double-quote / backslash.
|
|
177
|
+
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
178
|
+
return `"${escaped}"`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Update a markdown source string's YAML frontmatter block, applying the
|
|
183
|
+
* given key/value updates. A `null` or `undefined` value removes the key.
|
|
184
|
+
* If no frontmatter block exists and any non-null update is supplied, a
|
|
185
|
+
* new block is prepended; if every update is a removal and no block
|
|
186
|
+
* exists, the source is returned unchanged.
|
|
187
|
+
*
|
|
188
|
+
* Existing key order is preserved; new keys are appended in the order
|
|
189
|
+
* they appear in `updates`. The simple parser used here handles plain
|
|
190
|
+
* `key: value` pairs only — comments, multi-line scalars, and nested
|
|
191
|
+
* structures are left untouched (we only rewrite lines whose key matches
|
|
192
|
+
* one in `updates`).
|
|
193
|
+
*
|
|
194
|
+
* @param source - The markdown source string.
|
|
195
|
+
* @param updates - Map of key → new value (or null/undefined to remove).
|
|
196
|
+
* @returns The updated markdown source string.
|
|
197
|
+
*/
|
|
198
|
+
export function setFrontmatterValues(
|
|
199
|
+
source: string,
|
|
200
|
+
updates: Record<string, string | number | boolean | null | undefined>,
|
|
201
|
+
): string {
|
|
202
|
+
const updateKeys = Object.keys(updates);
|
|
203
|
+
if (updateKeys.length === 0) return source;
|
|
204
|
+
|
|
205
|
+
const match = source.match(FRONTMATTER_BLOCK_RE);
|
|
206
|
+
const handled = new Set<string>();
|
|
207
|
+
|
|
208
|
+
// Existing frontmatter block — rewrite line-by-line, preserving order.
|
|
209
|
+
if (match) {
|
|
210
|
+
const inner = match[1];
|
|
211
|
+
const lines = inner.split(/\r?\n/);
|
|
212
|
+
const newLines: string[] = [];
|
|
213
|
+
|
|
214
|
+
for (const line of lines) {
|
|
215
|
+
const trimmed = line.trim();
|
|
216
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
217
|
+
newLines.push(line);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const colonIdx = trimmed.indexOf(':');
|
|
221
|
+
if (colonIdx < 1) {
|
|
222
|
+
newLines.push(line);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
226
|
+
if (!Object.prototype.hasOwnProperty.call(updates, key)) {
|
|
227
|
+
newLines.push(line);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
handled.add(key);
|
|
231
|
+
const next = updates[key];
|
|
232
|
+
if (next === null || next === undefined) continue; // remove
|
|
233
|
+
newLines.push(`${key}: ${formatFrontmatterValue(next)}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Append any keys that weren't already present.
|
|
237
|
+
for (const key of updateKeys) {
|
|
238
|
+
if (handled.has(key)) continue;
|
|
239
|
+
const next = updates[key];
|
|
240
|
+
if (next === null || next === undefined) continue;
|
|
241
|
+
newLines.push(`${key}: ${formatFrontmatterValue(next)}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// If every line got removed, drop the block entirely.
|
|
245
|
+
const nonEmpty = newLines.filter((l) => l.trim().length > 0);
|
|
246
|
+
const rest = source.slice(match[0].length);
|
|
247
|
+
if (nonEmpty.length === 0) return rest;
|
|
248
|
+
return `---\n${newLines.join('\n')}\n---\n${rest}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// No existing block — only create one if there's at least one non-null
|
|
252
|
+
// value to write.
|
|
253
|
+
const fresh: string[] = [];
|
|
254
|
+
for (const key of updateKeys) {
|
|
255
|
+
const next = updates[key];
|
|
256
|
+
if (next === null || next === undefined) continue;
|
|
257
|
+
fresh.push(`${key}: ${formatFrontmatterValue(next)}`);
|
|
258
|
+
}
|
|
259
|
+
if (fresh.length === 0) return source;
|
|
260
|
+
return `---\n${fresh.join('\n')}\n---\n${source}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
160
263
|
/**
|
|
161
264
|
* Create a minimal MarkdownDocument from a list of block nodes.
|
|
162
265
|
* Convenience function for programmatic document construction.
|
|
@@ -167,3 +270,59 @@ export function createDocument(...children: MarkdownDocument['children']): Markd
|
|
|
167
270
|
children,
|
|
168
271
|
};
|
|
169
272
|
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Infer a human-readable title for a markdown document.
|
|
276
|
+
*
|
|
277
|
+
* Resolution order:
|
|
278
|
+
* 1. `title:` value in YAML frontmatter (when present and non-empty)
|
|
279
|
+
* 2. Text of the first heading found, walking top-level depth (H1, then
|
|
280
|
+
* H2, etc.) — the shallowest heading wins regardless of source order
|
|
281
|
+
*
|
|
282
|
+
* Returns `undefined` when no usable title is found.
|
|
283
|
+
*/
|
|
284
|
+
export function inferDocumentTitle(doc: MarkdownDocument): string | undefined {
|
|
285
|
+
const fmTitle = doc.frontmatter?.title;
|
|
286
|
+
if (typeof fmTitle === 'string') {
|
|
287
|
+
const trimmed = fmTitle.trim();
|
|
288
|
+
if (trimmed) return trimmed;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let best: { depth: number; text: string } | null = null;
|
|
292
|
+
for (const node of doc.children) {
|
|
293
|
+
if (node.type !== 'heading') continue;
|
|
294
|
+
const text = extractPlainText(node).trim();
|
|
295
|
+
if (!text) continue;
|
|
296
|
+
if (best === null || node.depth < best.depth) {
|
|
297
|
+
best = { depth: node.depth, text };
|
|
298
|
+
if (best.depth === 1) break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return best?.text;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Read a theme id from a markdown document's frontmatter.
|
|
307
|
+
*
|
|
308
|
+
* Checks the editor's canonical `squisq-theme` key first, then the
|
|
309
|
+
* shorter legacy aliases (`themeId`, `theme`). Returns `undefined`
|
|
310
|
+
* when none of them carry a non-empty string. Centralizing the lookup
|
|
311
|
+
* keeps the export pipelines (DOCX, PPTX, HTML, plain-HTML bundle) in
|
|
312
|
+
* sync with whatever the editor writes — adding a new frontmatter
|
|
313
|
+
* spelling later is a one-file change.
|
|
314
|
+
*/
|
|
315
|
+
export function readFrontmatterThemeId(
|
|
316
|
+
frontmatter: Record<string, unknown> | undefined,
|
|
317
|
+
): string | undefined {
|
|
318
|
+
if (!frontmatter) return undefined;
|
|
319
|
+
const keys = ['squisq-theme', 'themeId', 'theme'] as const;
|
|
320
|
+
for (const key of keys) {
|
|
321
|
+
const raw = frontmatter[key];
|
|
322
|
+
if (typeof raw === 'string') {
|
|
323
|
+
const trimmed = raw.trim();
|
|
324
|
+
if (trimmed) return trimmed;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|