@bendyline/squisq 2.0.1 → 2.2.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/LICENSE +21 -0
- package/NOTICE.md +24 -0
- package/dist/{ContentContainer-BXUlIf18.d.ts → ContentContainer-B2w9sUoL.d.ts} +15 -0
- package/dist/{Doc-ylqWKDc_.d.ts → Doc-BubIpWsp.d.ts} +160 -3
- package/dist/{ImageEditDoc-D828IXJ1.d.ts → ImageEditDoc-CGxk5CHc.d.ts} +1 -1
- package/dist/{annotationCoercion-Dr_DY9Hx.d.ts → annotationCoercion-PtRQhk5V.d.ts} +1 -1
- package/dist/chunk-2L55443L.js +0 -0
- package/dist/chunk-2ZWIXGAC.js +214 -0
- package/dist/{chunk-2PWOO4ZE.js → chunk-32R2RZAO.js} +72 -3
- package/dist/chunk-3IGPAPQ7.js +645 -0
- package/dist/{chunk-QE7TWGVT.js → chunk-4VOD55SX.js} +0 -1
- package/dist/{chunk-3E5F2XMR.js → chunk-67L6WRJN.js} +0 -1
- package/dist/{chunk-5DKBFXPY.js → chunk-6ECMNUIP.js} +26 -18
- package/dist/{chunk-X6DUONGT.js → chunk-7ARJZYQY.js} +280 -128
- package/dist/{chunk-DBS6RIFL.js → chunk-7N4G32LG.js} +0 -1
- package/dist/{chunk-JOQYPRDZ.js → chunk-7YEZFHLS.js} +10 -8
- package/dist/{chunk-2UQOHYE2.js → chunk-7ZAAICW4.js} +0 -1
- package/dist/{chunk-66QO7XFB.js → chunk-ACZOVWX3.js} +117 -7
- package/dist/{chunk-5J4HKRJF.js → chunk-BAOV476U.js} +0 -1
- package/dist/chunk-BCCXTMN5.js +699 -0
- package/dist/{chunk-W4O2TX5I.js → chunk-C33GTPUZ.js} +579 -4
- package/dist/{chunk-MMPBOUK7.js → chunk-CB45YGAM.js} +7 -2
- package/dist/{chunk-CY2R2VPR.js → chunk-DYPMOPIW.js} +18 -2
- package/dist/{chunk-VR7UPJ7C.js → chunk-FCG3BIZN.js} +12 -1
- package/dist/{chunk-5UYI3RJB.js → chunk-FDYDYL77.js} +54 -40
- package/dist/{chunk-TFJVXUHH.js → chunk-I2O6W7DX.js} +1513 -79
- package/dist/{chunk-Q7LROV2H.js → chunk-NKNMGIIN.js} +0 -1
- package/dist/chunk-OPO5K4DZ.js +241 -0
- package/dist/{chunk-TUWMK62B.js → chunk-PUS54YU6.js} +45 -2
- package/dist/chunk-QKPXHDLR.js +0 -0
- package/dist/{chunk-GZY66SDN.js → chunk-R3KTTOMS.js} +0 -1
- package/dist/{chunk-PXLVXTLW.js → chunk-RS5AP3J4.js} +72 -15
- package/dist/chunk-WV5ARNDA.js +0 -0
- package/dist/{chunk-4X3JQXNM.js → chunk-XOFT65EX.js} +1 -2
- package/dist/{chunk-FVTQSNR7.js → chunk-XRW6P7IF.js} +13 -5
- package/dist/chunk-XWNNA3KE.js +0 -0
- package/dist/chunk-YZNGMV3G.js +0 -0
- package/dist/{chunk-R2YZTCLO.js → chunk-ZQKZSJAX.js} +1 -2
- package/dist/doc/index.d.ts +166 -12
- package/dist/doc/index.js +36 -17
- package/dist/generate/index.d.ts +2 -2
- package/dist/generate/index.js +2 -3
- package/dist/icons/index.js +3 -4
- package/dist/icons/inlineIconMarker.js +1 -2
- package/dist/imageEdit/index.d.ts +48 -17
- package/dist/imageEdit/index.js +3 -4
- package/dist/index.d.ts +13 -12
- package/dist/index.js +118 -33
- package/dist/jsonForm/index.d.ts +10 -3
- package/dist/jsonForm/index.js +8 -7
- package/dist/markdown/index.d.ts +75 -11
- package/dist/markdown/index.js +32 -7
- package/dist/materializePageSection-wSLA2A1g.d.ts +223 -0
- package/dist/narration/index.d.ts +16 -3
- package/dist/narration/index.js +13 -14
- package/dist/random/index.d.ts +11 -2
- package/dist/random/index.js +2 -3
- package/dist/recommend/index.d.ts +1 -1
- package/dist/recommend/index.js +3 -4
- package/dist/schemas/index.d.ts +31 -6
- package/dist/schemas/index.js +43 -6
- package/dist/spatial/index.d.ts +2 -1
- package/dist/spatial/index.js +1 -2
- package/dist/storage/index.d.ts +4 -2
- package/dist/storage/index.js +1 -2
- package/dist/{themeLibrary-RjVgLpUi.d.ts → themeLibrary-DU9mUmVl.d.ts} +1 -1
- package/dist/timing/index.js +3 -4
- package/dist/transform/index.d.ts +9 -10
- package/dist/transform/index.js +6 -7
- package/dist/{types-C4kiRn_k.d.ts → types-8QjefM9J.d.ts} +38 -2
- package/dist/{types-BwMPl2CB.d.ts → types-CUNc9biN.d.ts} +24 -1
- package/dist/versions/index.d.ts +14 -11
- package/dist/versions/index.js +2 -3
- package/package.json +4 -2
- package/dist/chunk-2PWOO4ZE.js.map +0 -1
- package/dist/chunk-2UQOHYE2.js.map +0 -1
- package/dist/chunk-3E5F2XMR.js.map +0 -1
- package/dist/chunk-4X3JQXNM.js.map +0 -1
- package/dist/chunk-5DKBFXPY.js.map +0 -1
- package/dist/chunk-5J4HKRJF.js.map +0 -1
- package/dist/chunk-5UYI3RJB.js.map +0 -1
- package/dist/chunk-66QO7XFB.js.map +0 -1
- package/dist/chunk-AMIR72JP.js +0 -380
- package/dist/chunk-AMIR72JP.js.map +0 -1
- package/dist/chunk-CY2R2VPR.js.map +0 -1
- package/dist/chunk-DBS6RIFL.js.map +0 -1
- package/dist/chunk-DNJKBAI7.js +0 -1
- package/dist/chunk-DNJKBAI7.js.map +0 -1
- package/dist/chunk-ET53IIEP.js +0 -1
- package/dist/chunk-ET53IIEP.js.map +0 -1
- package/dist/chunk-FVJUUTEM.js +0 -74
- package/dist/chunk-FVJUUTEM.js.map +0 -1
- package/dist/chunk-FVTQSNR7.js.map +0 -1
- package/dist/chunk-GZY66SDN.js.map +0 -1
- package/dist/chunk-JOQYPRDZ.js.map +0 -1
- package/dist/chunk-MMPBOUK7.js.map +0 -1
- package/dist/chunk-PGGGKYWG.js +0 -1
- package/dist/chunk-PGGGKYWG.js.map +0 -1
- package/dist/chunk-PXLVXTLW.js.map +0 -1
- package/dist/chunk-Q7LROV2H.js.map +0 -1
- package/dist/chunk-QE7TWGVT.js.map +0 -1
- package/dist/chunk-R2GC6RLD.js +0 -344
- package/dist/chunk-R2GC6RLD.js.map +0 -1
- package/dist/chunk-R2YZTCLO.js.map +0 -1
- package/dist/chunk-S7A5D7ZJ.js +0 -174
- package/dist/chunk-S7A5D7ZJ.js.map +0 -1
- package/dist/chunk-TFJVXUHH.js.map +0 -1
- package/dist/chunk-TUWMK62B.js.map +0 -1
- package/dist/chunk-V543AI2F.js +0 -1
- package/dist/chunk-V543AI2F.js.map +0 -1
- package/dist/chunk-VR7UPJ7C.js.map +0 -1
- package/dist/chunk-W4O2TX5I.js.map +0 -1
- package/dist/chunk-X6DUONGT.js.map +0 -1
- package/dist/chunk-ZLQKNOZQ.js +0 -1
- package/dist/chunk-ZLQKNOZQ.js.map +0 -1
- package/dist/doc/index.js.map +0 -1
- package/dist/generate/index.js.map +0 -1
- package/dist/icons/index.js.map +0 -1
- package/dist/icons/inlineIconMarker.js.map +0 -1
- package/dist/imageEdit/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jsonForm/index.js.map +0 -1
- package/dist/markdown/index.js.map +0 -1
- package/dist/narration/index.js.map +0 -1
- package/dist/random/index.js.map +0 -1
- package/dist/recommend/index.js.map +0 -1
- package/dist/schemas/index.js.map +0 -1
- package/dist/spatial/index.js.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/timing/index.js.map +0 -1
- package/dist/transform/index.js.map +0 -1
- package/dist/versions/index.js.map +0 -1
- package/src/__tests__/animationUtils.test.ts +0 -117
- package/src/__tests__/annotationCoercion.test.ts +0 -243
- package/src/__tests__/applyNarrationTiming.test.ts +0 -190
- package/src/__tests__/applyRenderStyle.test.ts +0 -146
- package/src/__tests__/asciiDiagramBattle.test.ts +0 -246
- package/src/__tests__/asciiDiagramDetect.test.ts +0 -162
- package/src/__tests__/asciiDiagramMapping.test.ts +0 -38
- package/src/__tests__/asciiDiagramParse.test.ts +0 -233
- package/src/__tests__/asciiDiagramPipeline.test.ts +0 -273
- package/src/__tests__/asciiDiagramRender.test.ts +0 -162
- package/src/__tests__/asciiDiagramRepair.test.ts +0 -84
- package/src/__tests__/asciiTimelineDetect.test.ts +0 -157
- package/src/__tests__/asciiTimelineParse.test.ts +0 -205
- package/src/__tests__/asciiTimelinePipeline.test.ts +0 -349
- package/src/__tests__/asciiTimelineRender.test.ts +0 -376
- package/src/__tests__/atmosphereLayers.test.ts +0 -108
- package/src/__tests__/attrTokens.test.ts +0 -167
- package/src/__tests__/colorUtils.test.ts +0 -112
- package/src/__tests__/contentContainer.test.ts +0 -325
- package/src/__tests__/contentExtractor.test.ts +0 -243
- package/src/__tests__/coverBlock.test.ts +0 -310
- package/src/__tests__/customTemplateSample.test.ts +0 -144
- package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -221
- package/src/__tests__/customThemesFrontmatter.test.ts +0 -146
- package/src/__tests__/dataTable.test.ts +0 -145
- package/src/__tests__/diagramBlock.test.ts +0 -122
- package/src/__tests__/diagramLayout.test.ts +0 -134
- package/src/__tests__/diagramText.test.ts +0 -26
- package/src/__tests__/docToMarkdownMedia.test.ts +0 -83
- package/src/__tests__/drawing.test.ts +0 -109
- package/src/__tests__/drawingBlock.test.ts +0 -124
- package/src/__tests__/drawingLayout.test.ts +0 -171
- package/src/__tests__/fixtures/asciiDiagramBattle.ts +0 -512
- package/src/__tests__/fixtures/asciiDiagrams.ts +0 -287
- package/src/__tests__/fixtures/asciiTimelines.ts +0 -35
- package/src/__tests__/fixtures/treeviewFixtures.ts +0 -243
- package/src/__tests__/geohash.test.ts +0 -110
- package/src/__tests__/getLayers.test.ts +0 -243
- package/src/__tests__/getLayersFallback.test.ts +0 -103
- package/src/__tests__/haversine.test.ts +0 -63
- package/src/__tests__/iconResolve.test.ts +0 -122
- package/src/__tests__/imageEditExportSvg.test.ts +0 -70
- package/src/__tests__/imageEditState.test.ts +0 -74
- package/src/__tests__/imageEditVersions.test.ts +0 -243
- package/src/__tests__/inferDocumentTitle.test.ts +0 -50
- package/src/__tests__/inlineIconMarker.test.ts +0 -36
- package/src/__tests__/inputDescriptors.test.ts +0 -162
- package/src/__tests__/jsonForm.chooseControl.test.ts +0 -86
- package/src/__tests__/jsonForm.evaluateWhen.test.ts +0 -59
- package/src/__tests__/jsonForm.inferSchema.test.ts +0 -25
- package/src/__tests__/jsonForm.pointer.test.ts +0 -149
- package/src/__tests__/jsonForm.tokens.test.ts +0 -84
- package/src/__tests__/layoutBlock.test.ts +0 -83
- package/src/__tests__/layoutLayout.test.ts +0 -169
- package/src/__tests__/localForageAdapter.test.ts +0 -155
- package/src/__tests__/majorApiTypes.test.ts +0 -18
- package/src/__tests__/markdown.test.ts +0 -1009
- package/src/__tests__/markdownIcons.test.ts +0 -104
- package/src/__tests__/markdownSanitize.test.ts +0 -81
- package/src/__tests__/markdownToDoc.test.ts +0 -957
- package/src/__tests__/matchFontFamily.test.ts +0 -114
- package/src/__tests__/materializeBlockLayers.test.ts +0 -135
- package/src/__tests__/materializeTestUtils.ts +0 -17
- package/src/__tests__/mediaAnnotations.test.ts +0 -91
- package/src/__tests__/mediaSchedule.test.ts +0 -118
- package/src/__tests__/narrationAlign.test.ts +0 -150
- package/src/__tests__/narrationFeatures.test.ts +0 -71
- package/src/__tests__/narrationNuclei.test.ts +0 -45
- package/src/__tests__/narrationPacing.test.ts +0 -189
- package/src/__tests__/narrationScript.test.ts +0 -149
- package/src/__tests__/narrationSessionRepro.test.ts +0 -62
- package/src/__tests__/narrationSidecar.test.ts +0 -88
- package/src/__tests__/narrationSyllables.test.ts +0 -65
- package/src/__tests__/narrationTestSignals.ts +0 -167
- package/src/__tests__/narrationVad.test.ts +0 -66
- package/src/__tests__/pandocAttrParse.test.ts +0 -187
- package/src/__tests__/recommendTemplates.test.ts +0 -183
- package/src/__tests__/resolveDocTheme.test.ts +0 -69
- package/src/__tests__/rootExports.test.ts +0 -61
- package/src/__tests__/scopedContentContainer.test.ts +0 -109
- package/src/__tests__/seededRandom.test.ts +0 -142
- package/src/__tests__/shapeGeometry.test.ts +0 -138
- package/src/__tests__/shapeVocabulary.test.ts +0 -133
- package/src/__tests__/storage.test.ts +0 -60
- package/src/__tests__/structuredData.test.ts +0 -249
- package/src/__tests__/surface.test.ts +0 -68
- package/src/__tests__/templateAnnotationParse.test.ts +0 -160
- package/src/__tests__/templateInputs.test.ts +0 -69
- package/src/__tests__/templateMetadata.test.ts +0 -32
- package/src/__tests__/templates.test.ts +0 -710
- package/src/__tests__/themeCompile.test.ts +0 -225
- package/src/__tests__/themeValidator.test.ts +0 -131
- package/src/__tests__/timing.test.ts +0 -167
- package/src/__tests__/transformProvenance.test.ts +0 -160
- package/src/__tests__/transformV2.test.ts +0 -163
- package/src/__tests__/transitionNormalize.test.ts +0 -61
- package/src/__tests__/treeviewBattle.test.ts +0 -76
- package/src/__tests__/treeviewDetect.test.ts +0 -168
- package/src/__tests__/treeviewParse.test.ts +0 -118
- package/src/__tests__/treeviewPipeline.test.ts +0 -183
- package/src/__tests__/treeviewRender.test.ts +0 -91
- package/src/__tests__/validateDoc.test.ts +0 -260
- package/src/__tests__/versions.test.ts +0 -459
- package/src/doc/annotationBlocks.ts +0 -117
- package/src/doc/applyNarrationTiming.ts +0 -244
- package/src/doc/asciiDiagram/chars.ts +0 -210
- package/src/doc/asciiDiagram/detect.ts +0 -175
- package/src/doc/asciiDiagram/index.ts +0 -38
- package/src/doc/asciiDiagram/mapping.ts +0 -225
- package/src/doc/asciiDiagram/parse.ts +0 -1095
- package/src/doc/asciiDiagram/render.ts +0 -963
- package/src/doc/asciiDiagram/repair.ts +0 -400
- package/src/doc/asciiDiagram/types.ts +0 -65
- package/src/doc/asciiTimeline/detect.ts +0 -138
- package/src/doc/asciiTimeline/index.ts +0 -26
- package/src/doc/asciiTimeline/mapping.ts +0 -43
- package/src/doc/asciiTimeline/parse.ts +0 -870
- package/src/doc/asciiTimeline/render.ts +0 -427
- package/src/doc/asciiTimeline/types.ts +0 -73
- package/src/doc/asciiTimeline/wrappedFlow.ts +0 -427
- package/src/doc/audioMapping.ts +0 -442
- package/src/doc/customTemplatesFrontmatter.ts +0 -273
- package/src/doc/customThemesFrontmatter.ts +0 -95
- package/src/doc/docToMarkdown.ts +0 -284
- package/src/doc/index.ts +0 -127
- package/src/doc/markdownToDoc.ts +0 -1054
- package/src/doc/materializeBlockLayers.ts +0 -304
- package/src/doc/mediaAnnotations.ts +0 -120
- package/src/doc/resolveDocTheme.ts +0 -56
- package/src/doc/standaloneAnnotation.ts +0 -58
- package/src/doc/structuredData.ts +0 -301
- package/src/doc/templateInputs.ts +0 -423
- package/src/doc/templates/__tests__/customTemplate.test.ts +0 -170
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +0 -124
- package/src/doc/templates/__tests__/listBlock.test.ts +0 -64
- package/src/doc/templates/accentImage.ts +0 -362
- package/src/doc/templates/captionUtils.ts +0 -56
- package/src/doc/templates/comparisonBar.ts +0 -173
- package/src/doc/templates/coverBlock.ts +0 -255
- package/src/doc/templates/customTemplate.ts +0 -51
- package/src/doc/templates/dataTable.ts +0 -102
- package/src/doc/templates/dateEvent.ts +0 -147
- package/src/doc/templates/definitionCard.ts +0 -140
- package/src/doc/templates/diagramBlock.ts +0 -403
- package/src/doc/templates/diagramLayout.ts +0 -159
- package/src/doc/templates/drawingBlock.ts +0 -325
- package/src/doc/templates/drawingLayout.ts +0 -539
- package/src/doc/templates/factCard.ts +0 -146
- package/src/doc/templates/fallbackBlock.ts +0 -123
- package/src/doc/templates/featureBlock.ts +0 -209
- package/src/doc/templates/fullBleedQuote.ts +0 -83
- package/src/doc/templates/imageWithCaption.ts +0 -186
- package/src/doc/templates/index.ts +0 -642
- package/src/doc/templates/inputDescriptors.ts +0 -451
- package/src/doc/templates/layoutBlock.ts +0 -43
- package/src/doc/templates/layoutLayout.ts +0 -243
- package/src/doc/templates/listBlock.ts +0 -207
- package/src/doc/templates/mapBlock.ts +0 -138
- package/src/doc/templates/metadata.ts +0 -130
- package/src/doc/templates/persistentLayers.ts +0 -526
- package/src/doc/templates/photoGrid.ts +0 -190
- package/src/doc/templates/pullQuote.ts +0 -141
- package/src/doc/templates/quoteBlock.ts +0 -141
- package/src/doc/templates/registry.ts +0 -88
- package/src/doc/templates/sectionHeader.ts +0 -120
- package/src/doc/templates/statHighlight.ts +0 -146
- package/src/doc/templates/templateNames.ts +0 -22
- package/src/doc/templates/timelineBlock.ts +0 -613
- package/src/doc/templates/titleBlock.ts +0 -150
- package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +0 -487
- package/src/doc/templates/tokens/resolveTokens.ts +0 -453
- package/src/doc/templates/treeBlock.ts +0 -131
- package/src/doc/templates/twoColumn.ts +0 -263
- package/src/doc/templates/videoPullQuote.ts +0 -142
- package/src/doc/templates/videoWithCaption.ts +0 -135
- package/src/doc/treeview/chars.ts +0 -72
- package/src/doc/treeview/detect.ts +0 -145
- package/src/doc/treeview/index.ts +0 -30
- package/src/doc/treeview/mapping.ts +0 -95
- package/src/doc/treeview/parse.ts +0 -201
- package/src/doc/treeview/render.ts +0 -41
- package/src/doc/treeview/types.ts +0 -43
- package/src/doc/utils/animationUtils.ts +0 -399
- package/src/doc/utils/applyRenderStyle.ts +0 -106
- package/src/doc/utils/diagramText.ts +0 -78
- package/src/doc/utils/imageTreatment.ts +0 -70
- package/src/doc/utils/nearest.ts +0 -59
- package/src/doc/utils/shapeGeometry.ts +0 -614
- package/src/doc/utils/themeUtils.ts +0 -243
- package/src/doc/validate.ts +0 -575
- package/src/generate/contentExtractor.ts +0 -921
- package/src/generate/index.ts +0 -18
- package/src/generate/templateMapper.ts +0 -177
- package/src/icons/iconData.ts +0 -31
- package/src/icons/index.ts +0 -26
- package/src/icons/inlineIconMarker.ts +0 -73
- package/src/icons/resolve.ts +0 -139
- package/src/imageEdit/ImageEditVersionManager.ts +0 -73
- package/src/imageEdit/export.ts +0 -410
- package/src/imageEdit/index.ts +0 -72
- package/src/imageEdit/persistence.ts +0 -113
- package/src/imageEdit/state.ts +0 -112
- package/src/imageEdit/versionPaths.ts +0 -44
- package/src/imageEdit/versions.ts +0 -152
- package/src/index.ts +0 -15
- package/src/internal/immutable.ts +0 -58
- package/src/jsonForm/chooseControl.ts +0 -88
- package/src/jsonForm/evaluateWhen.ts +0 -59
- package/src/jsonForm/index.ts +0 -32
- package/src/jsonForm/inferSchema.ts +0 -25
- package/src/jsonForm/pointer.ts +0 -154
- package/src/jsonForm/tokens.ts +0 -78
- package/src/jsonForm/types.ts +0 -167
- package/src/markdown/annotationCoercion.ts +0 -295
- package/src/markdown/attrTokens.ts +0 -243
- package/src/markdown/convert.ts +0 -1353
- package/src/markdown/htmlParse.ts +0 -212
- package/src/markdown/index.ts +0 -152
- package/src/markdown/parse.ts +0 -103
- package/src/markdown/sanitize.ts +0 -318
- package/src/markdown/stringify.ts +0 -207
- package/src/markdown/types.ts +0 -710
- package/src/markdown/utils.ts +0 -459
- package/src/narration/align.ts +0 -514
- package/src/narration/features.ts +0 -231
- package/src/narration/index.ts +0 -86
- package/src/narration/nuclei.ts +0 -118
- package/src/narration/pacing.ts +0 -211
- package/src/narration/script.ts +0 -239
- package/src/narration/session.ts +0 -100
- package/src/narration/sidecar.ts +0 -187
- package/src/narration/syllables.ts +0 -85
- package/src/narration/trace.ts +0 -50
- package/src/narration/types.ts +0 -299
- package/src/narration/vad.ts +0 -131
- package/src/ngeohash.d.ts +0 -11
- package/src/random/SeededRandom.ts +0 -127
- package/src/random/index.ts +0 -1
- package/src/recommend/index.ts +0 -16
- package/src/recommend/numberHighlight.ts +0 -25
- package/src/recommend/templates.ts +0 -393
- package/src/schemas/BlockTemplates.ts +0 -1046
- package/src/schemas/CustomTemplates.ts +0 -126
- package/src/schemas/Doc.ts +0 -1052
- package/src/schemas/ImageEditDoc.ts +0 -102
- package/src/schemas/LayoutStrategy.ts +0 -178
- package/src/schemas/Media.ts +0 -179
- package/src/schemas/MediaProvider.ts +0 -70
- package/src/schemas/Theme.ts +0 -406
- package/src/schemas/Transitions.ts +0 -224
- package/src/schemas/Types.ts +0 -15
- package/src/schemas/Viewport.ts +0 -91
- package/src/schemas/colorUtils.ts +0 -219
- package/src/schemas/fontStacks.ts +0 -339
- package/src/schemas/index.ts +0 -29
- package/src/schemas/themeCompile.ts +0 -237
- package/src/schemas/themeConstants.ts +0 -2
- package/src/schemas/themeLibrary.ts +0 -60
- package/src/schemas/themeValidator.ts +0 -426
- package/src/schemas/themes/bold.json +0 -48
- package/src/schemas/themes/cinematic.json +0 -111
- package/src/schemas/themes/documentary.json +0 -96
- package/src/schemas/themes/gezellig.json +0 -93
- package/src/schemas/themes/index.ts +0 -53
- package/src/schemas/themes/magazine.json +0 -89
- package/src/schemas/themes/minimalist.json +0 -45
- package/src/schemas/themes/morning-light.json +0 -85
- package/src/schemas/themes/standard-dark.json +0 -85
- package/src/schemas/themes/standard.json +0 -74
- package/src/schemas/themes/tech-dark.json +0 -101
- package/src/schemas/themes/warm-earth.json +0 -50
- package/src/spatial/Geohash.ts +0 -196
- package/src/spatial/Haversine.ts +0 -59
- package/src/spatial/index.ts +0 -2
- package/src/storage/ContentContainer.ts +0 -220
- package/src/storage/LocalForageAdapter.ts +0 -103
- package/src/storage/LocalStorageAdapter.ts +0 -57
- package/src/storage/MediaProviderFromContainer.ts +0 -92
- package/src/storage/MemoryStorageAdapter.ts +0 -37
- package/src/storage/ScopedContentContainer.ts +0 -143
- package/src/storage/Storage.ts +0 -28
- package/src/storage/index.ts +0 -9
- package/src/timing/index.ts +0 -17
- package/src/timing/narrationTiming.ts +0 -220
- package/src/timing/readingTime.ts +0 -94
- package/src/transform/applyTransform.ts +0 -122
- package/src/transform/blockAnalyzer.ts +0 -181
- package/src/transform/index.ts +0 -23
- package/src/transform/registry.ts +0 -198
- package/src/transform/styles/dataDriven.ts +0 -17
- package/src/transform/styles/documentary.ts +0 -17
- package/src/transform/styles/index.ts +0 -5
- package/src/transform/styles/magazine.ts +0 -18
- package/src/transform/styles/minimal.ts +0 -17
- package/src/transform/styles/narrative.ts +0 -18
- package/src/transform/templateSelector.ts +0 -389
- package/src/transform/timingAllocator.ts +0 -257
- package/src/transform/types.ts +0 -151
- package/src/versions/DocumentVersionManager.ts +0 -70
- package/src/versions/gitignore.ts +0 -34
- package/src/versions/index.ts +0 -56
- package/src/versions/operations.ts +0 -144
- package/src/versions/paths.ts +0 -51
- package/src/versions/snapshotStore.ts +0 -202
- package/src/versions/timestamp.ts +0 -48
- package/src/versions/types.ts +0 -93
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Synthetic PCM builders for narration-engine tests (test-only, not
|
|
3
|
-
* exported from the package). Everything is seeded via SeededRandom —
|
|
4
|
-
* the same arguments always produce the same samples.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { SeededRandom } from '../random/SeededRandom.js';
|
|
8
|
-
import { parseMarkdown } from '../markdown/index.js';
|
|
9
|
-
import { markdownToDoc } from '../doc/markdownToDoc.js';
|
|
10
|
-
import { buildNarrationScript } from '../narration/script.js';
|
|
11
|
-
import type { NarrationScript } from '../narration/types.js';
|
|
12
|
-
|
|
13
|
-
export function silence(sec: number, sampleRate: number): Float32Array {
|
|
14
|
-
return new Float32Array(Math.max(0, Math.round(sec * sampleRate)));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Seeded white noise at a target RMS (uniform noise: rms = amp/√3). */
|
|
18
|
-
export function noise(sec: number, sampleRate: number, rms: number, seed: number): Float32Array {
|
|
19
|
-
const rng = new SeededRandom(seed);
|
|
20
|
-
const out = new Float32Array(Math.max(0, Math.round(sec * sampleRate)));
|
|
21
|
-
const amp = rms * Math.sqrt(3);
|
|
22
|
-
for (let i = 0; i < out.length; i++) {
|
|
23
|
-
out[i] = (rng.next() * 2 - 1) * amp;
|
|
24
|
-
}
|
|
25
|
-
return out;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Pure tone (for band-response tests). */
|
|
29
|
-
export function tone(freqHz: number, sec: number, sampleRate: number, amp = 0.5): Float32Array {
|
|
30
|
-
const out = new Float32Array(Math.round(sec * sampleRate));
|
|
31
|
-
const w = (2 * Math.PI * freqHz) / sampleRate;
|
|
32
|
-
for (let i = 0; i < out.length; i++) {
|
|
33
|
-
out[i] = amp * Math.sin(w * i);
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* A voiced burst: harmonics at 220/700/1400/2200 Hz under a Hann
|
|
40
|
-
* envelope plus 10% seeded noise — enough speech-band energy to trip
|
|
41
|
-
* the VAD and produce one envelope peak (one syllable nucleus).
|
|
42
|
-
*/
|
|
43
|
-
export function speechBurst(
|
|
44
|
-
sec: number,
|
|
45
|
-
sampleRate: number,
|
|
46
|
-
seed: number,
|
|
47
|
-
amp = 0.35,
|
|
48
|
-
): Float32Array {
|
|
49
|
-
const rng = new SeededRandom(seed);
|
|
50
|
-
const n = Math.round(sec * sampleRate);
|
|
51
|
-
const out = new Float32Array(n);
|
|
52
|
-
const freqs = [220, 700, 1400, 2200];
|
|
53
|
-
const gains = [0.5, 1, 0.6, 0.3];
|
|
54
|
-
for (let i = 0; i < n; i++) {
|
|
55
|
-
const env = 0.5 * (1 - Math.cos((2 * Math.PI * i) / Math.max(1, n - 1)));
|
|
56
|
-
let s = 0;
|
|
57
|
-
for (let f = 0; f < freqs.length; f++) {
|
|
58
|
-
s += gains[f] * Math.sin((2 * Math.PI * freqs[f] * i) / sampleRate);
|
|
59
|
-
}
|
|
60
|
-
out[i] = amp * env * (s / 2.4 + 0.1 * (rng.next() * 2 - 1));
|
|
61
|
-
}
|
|
62
|
-
return out;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Overlay `piece` onto `target` starting at `atSec` (adds samples). */
|
|
66
|
-
export function overlay(
|
|
67
|
-
target: Float32Array,
|
|
68
|
-
piece: Float32Array,
|
|
69
|
-
atSec: number,
|
|
70
|
-
sampleRate: number,
|
|
71
|
-
): void {
|
|
72
|
-
const start = Math.round(atSec * sampleRate);
|
|
73
|
-
for (let i = 0; i < piece.length && start + i < target.length; i++) {
|
|
74
|
-
target[start + i] += piece[i];
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface SyllableTrainOptions {
|
|
79
|
-
burstSec?: number;
|
|
80
|
-
noiseRms?: number;
|
|
81
|
-
totalSec?: number;
|
|
82
|
-
seed?: number;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Bursts at each onset time over a quiet noise floor (≈ −50 dB). */
|
|
86
|
-
export function syllableTrain(
|
|
87
|
-
onsetsSec: number[],
|
|
88
|
-
sampleRate: number,
|
|
89
|
-
options?: SyllableTrainOptions,
|
|
90
|
-
): Float32Array {
|
|
91
|
-
const burstSec = options?.burstSec ?? 0.16;
|
|
92
|
-
const noiseRms = options?.noiseRms ?? 0.003;
|
|
93
|
-
const seed = options?.seed ?? 1;
|
|
94
|
-
const last = onsetsSec.length > 0 ? Math.max(...onsetsSec) : 0;
|
|
95
|
-
const totalSec = options?.totalSec ?? last + burstSec + 0.5;
|
|
96
|
-
const pcm = noise(totalSec, sampleRate, noiseRms, seed);
|
|
97
|
-
onsetsSec.forEach((t, i) => {
|
|
98
|
-
overlay(pcm, speechBurst(burstSec, sampleRate, seed + 100 + i), t, sampleRate);
|
|
99
|
-
});
|
|
100
|
-
return pcm;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Build a real NarrationScript through the production pipeline. */
|
|
104
|
-
export function scriptFromMarkdown(markdown: string): NarrationScript {
|
|
105
|
-
return buildNarrationScript(markdownToDoc(parseMarkdown(markdown)));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface SyntheticTake {
|
|
109
|
-
pcm: Float32Array;
|
|
110
|
-
/** Burst start time of each token's first syllable. */
|
|
111
|
-
trueWordTimes: number[];
|
|
112
|
-
/** True block start times (first word of each script block). */
|
|
113
|
-
trueBlockStarts: number[];
|
|
114
|
-
durationSec: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Synthesize a spoken take of a script: one burst per token syllable at
|
|
119
|
-
* a steady rate, with pauses matching the token pause classes.
|
|
120
|
-
*/
|
|
121
|
-
export function takeFromScript(
|
|
122
|
-
script: NarrationScript,
|
|
123
|
-
wpm: number,
|
|
124
|
-
sampleRate: number,
|
|
125
|
-
seed: number,
|
|
126
|
-
opts?: { leadInSec?: number; extraOnsets?: number[]; dropTokenSyllable?: number[] },
|
|
127
|
-
): SyntheticTake {
|
|
128
|
-
const meanSylPerWord = Math.max(1, script.totalSyllables / Math.max(1, script.tokens.length));
|
|
129
|
-
const sylPeriod = 60 / wpm / meanSylPerWord;
|
|
130
|
-
const burstSec = Math.min(0.16, sylPeriod * 0.7);
|
|
131
|
-
const leadIn = opts?.leadInSec ?? 0.6;
|
|
132
|
-
const dropSet = new Set(opts?.dropTokenSyllable ?? []);
|
|
133
|
-
|
|
134
|
-
const onsets: number[] = [];
|
|
135
|
-
const trueWordTimes: number[] = [];
|
|
136
|
-
let cursor = leadIn;
|
|
137
|
-
for (let k = 0; k < script.tokens.length; k++) {
|
|
138
|
-
const token = script.tokens[k];
|
|
139
|
-
trueWordTimes.push(cursor);
|
|
140
|
-
for (let s = 0; s < token.syllables; s++) {
|
|
141
|
-
if (!dropSet.has(k * 1000 + s)) onsets.push(cursor);
|
|
142
|
-
cursor += sylPeriod;
|
|
143
|
-
}
|
|
144
|
-
if (token.pauseAfter === 1) cursor += 0.35;
|
|
145
|
-
else if (token.pauseAfter === 2) cursor += 0.9;
|
|
146
|
-
else if (token.pauseAfter === 3) cursor += 1.2;
|
|
147
|
-
}
|
|
148
|
-
for (const extra of opts?.extraOnsets ?? []) onsets.push(extra);
|
|
149
|
-
onsets.sort((a, b) => a - b);
|
|
150
|
-
|
|
151
|
-
const durationSec = cursor + 0.6;
|
|
152
|
-
const pcm = syllableTrain(onsets, sampleRate, { burstSec, totalSec: durationSec, seed });
|
|
153
|
-
const trueBlockStarts = script.blocks.map((b) => trueWordTimes[b.tokenStart]);
|
|
154
|
-
return { pcm, trueWordTimes, trueBlockStarts, durationSec };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function concat(...segments: Float32Array[]): Float32Array {
|
|
158
|
-
let total = 0;
|
|
159
|
-
for (const s of segments) total += s.length;
|
|
160
|
-
const out = new Float32Array(total);
|
|
161
|
-
let offset = 0;
|
|
162
|
-
for (const s of segments) {
|
|
163
|
-
out.set(s, offset);
|
|
164
|
-
offset += s.length;
|
|
165
|
-
}
|
|
166
|
-
return out;
|
|
167
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { extractFrameFeatures } from '../narration/features';
|
|
3
|
-
import { createVadState, vadStep } from '../narration/vad';
|
|
4
|
-
import { concat, noise, speechBurst } from './narrationTestSignals';
|
|
5
|
-
|
|
6
|
-
const SR = 48000;
|
|
7
|
-
|
|
8
|
-
function runVad(pcm: Float32Array) {
|
|
9
|
-
const frames = extractFrameFeatures(pcm, SR);
|
|
10
|
-
let state = createVadState();
|
|
11
|
-
const flags: boolean[] = [];
|
|
12
|
-
for (const frame of frames) {
|
|
13
|
-
state = vadStep(state, frame);
|
|
14
|
-
flags.push(state.speaking);
|
|
15
|
-
}
|
|
16
|
-
return { frames, flags };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('vadStep', () => {
|
|
20
|
-
it('enters within 100 ms of speech onset and exits within hangover + 100 ms', () => {
|
|
21
|
-
const quiet = 0.004;
|
|
22
|
-
const pcm = concat(noise(1.0, SR, quiet, 1), speechBurst(0.8, SR, 2), noise(1.2, SR, quiet, 3));
|
|
23
|
-
const { frames, flags } = runVad(pcm);
|
|
24
|
-
|
|
25
|
-
const burstStart = 1.0;
|
|
26
|
-
const burstEnd = 1.8;
|
|
27
|
-
const enterIdx = flags.findIndex((f) => f);
|
|
28
|
-
expect(enterIdx).toBeGreaterThan(-1);
|
|
29
|
-
// Hann envelope reaches useful energy ~15% in, so allow 100 ms + ramp.
|
|
30
|
-
expect(frames[enterIdx].tSec).toBeGreaterThan(burstStart);
|
|
31
|
-
expect(frames[enterIdx].tSec).toBeLessThan(burstStart + 0.25);
|
|
32
|
-
|
|
33
|
-
let lastSpeaking = -1;
|
|
34
|
-
for (let i = 0; i < flags.length; i++) if (flags[i]) lastSpeaking = i;
|
|
35
|
-
expect(lastSpeaking).toBeGreaterThan(-1);
|
|
36
|
-
// Envelope tails off before burstEnd; exit must happen within
|
|
37
|
-
// (ramp-down ~0.15) + hangover 0.3 + margin 0.1 of the burst end.
|
|
38
|
-
expect(frames[lastSpeaking].tSec).toBeLessThan(burstEnd + 0.45);
|
|
39
|
-
expect(frames[lastSpeaking].tSec).toBeGreaterThan(burstEnd - 0.45);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('detects no speech on steady noise', () => {
|
|
43
|
-
const { flags } = runVad(noise(3, SR, 0.01, 9));
|
|
44
|
-
expect(flags.some(Boolean)).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('adapts the floor to a noise step without flapping', () => {
|
|
48
|
-
// −40 dB → −30 dB noise step: the step transient may read as speech,
|
|
49
|
-
// but the dip-starved rebaseline (1.5 s of zero dips) must adopt the
|
|
50
|
-
// new floor and settle back to silence — permanently.
|
|
51
|
-
const pcm = concat(noise(2, SR, 0.01, 4), noise(4, SR, 0.0316, 5));
|
|
52
|
-
const { frames, flags } = runVad(pcm);
|
|
53
|
-
const tail = flags.filter((_, i) => frames[i].tSec > 3.8);
|
|
54
|
-
expect(tail.length).toBeGreaterThan(0);
|
|
55
|
-
expect(tail.some(Boolean)).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('still detects speech over the louder floor after adapting', () => {
|
|
59
|
-
const pcm = concat(noise(2, SR, 0.0316, 6), speechBurst(0.6, SR, 7), noise(0.8, SR, 0.0316, 8));
|
|
60
|
-
const { frames, flags } = runVad(pcm);
|
|
61
|
-
const speakingTimes = frames.filter((_, i) => flags[i]).map((f) => f.tSec);
|
|
62
|
-
expect(speakingTimes.length).toBeGreaterThan(0);
|
|
63
|
-
expect(Math.min(...speakingTimes)).toBeGreaterThan(1.9);
|
|
64
|
-
expect(Math.min(...speakingTimes)).toBeLessThan(2.3);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { parseMarkdown, stringifyMarkdown } from '../markdown/index.js';
|
|
3
|
-
|
|
4
|
-
function getHeading(md: string) {
|
|
5
|
-
const doc = parseMarkdown(md);
|
|
6
|
-
const heading = doc.children[0];
|
|
7
|
-
if (heading.type !== 'heading') throw new Error('expected heading');
|
|
8
|
-
return heading;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe('Pandoc heading attribute parse', () => {
|
|
12
|
-
it('extracts an id-only block', () => {
|
|
13
|
-
const h = getHeading('## Section title {#intro}');
|
|
14
|
-
expect(h.attributes).toEqual({ id: 'intro' });
|
|
15
|
-
expect(h.templateAnnotation).toBeUndefined();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('extracts classes', () => {
|
|
19
|
-
const h = getHeading('## X {.alpha .beta}');
|
|
20
|
-
expect(h.attributes?.classes).toEqual(['alpha', 'beta']);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('extracts id + classes + params', () => {
|
|
24
|
-
const h = getHeading('## X {#abc .v1 .v2 x=400 y=200}');
|
|
25
|
-
expect(h.attributes?.id).toBe('abc');
|
|
26
|
-
expect(h.attributes?.classes).toEqual(['v1', 'v2']);
|
|
27
|
-
expect(h.attributes?.params).toEqual({ x: '400', y: '200' });
|
|
28
|
-
expect(h.attributes?.blockMeta).toEqual({ x: 400, y: 200 });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('coerces connectsTo to a typed list', () => {
|
|
32
|
-
const h = getHeading('## X {connectsTo=foo:veryImportant,bar:requires,baz:requires}');
|
|
33
|
-
expect(h.attributes?.blockMeta?.connectsTo).toEqual([
|
|
34
|
-
{ target: 'foo', type: 'veryImportant' },
|
|
35
|
-
{ target: 'bar', type: 'requires' },
|
|
36
|
-
{ target: 'baz', type: 'requires' },
|
|
37
|
-
]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('coerces startTime/duration to seconds', () => {
|
|
41
|
-
const h = getHeading('## X {startTime=01:30 duration=00:45}');
|
|
42
|
-
expect(h.attributes?.blockMeta?.startTime).toBe(90);
|
|
43
|
-
expect(h.attributes?.blockMeta?.duration).toBe(45);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('routes unknown keys to metadata', () => {
|
|
47
|
-
const h = getHeading('## X {#a priority=high level=info}');
|
|
48
|
-
expect(h.attributes?.metadata).toEqual({ priority: 'high', level: 'info' });
|
|
49
|
-
expect(h.attributes?.blockMeta).toBeUndefined();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('handles quoted values with spaces', () => {
|
|
53
|
-
const h = getHeading('## X {caption="hello world"}');
|
|
54
|
-
expect(h.attributes?.params).toEqual({ caption: 'hello world' });
|
|
55
|
-
expect(h.attributes?.metadata).toEqual({ caption: 'hello world' });
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('preserves commas inside quoted values', () => {
|
|
59
|
-
const h = getHeading('## X {caption="hello, friend"}');
|
|
60
|
-
expect(h.attributes?.metadata).toEqual({ caption: 'hello, friend' });
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('escaped quotes inside quoted values', () => {
|
|
64
|
-
const h = getHeading('## X {label="he said \\"hi\\""}');
|
|
65
|
-
expect(h.attributes?.metadata?.label).toBe('he said "hi"');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('handles single-quoted values with spaces', () => {
|
|
69
|
-
const h = getHeading("## X {caption='hello world'}");
|
|
70
|
-
expect(h.attributes?.params).toEqual({ caption: 'hello world' });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('allows } inside a quoted value', () => {
|
|
74
|
-
const h = getHeading('## X {caption="a } b"}');
|
|
75
|
-
expect(h.attributes?.params).toEqual({ caption: 'a } b' });
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('treats an apostrophe inside an unquoted value as literal', () => {
|
|
79
|
-
const h = getHeading("## X {name=O'Brien x=4}");
|
|
80
|
-
expect(h.attributes?.metadata).toEqual({ name: "O'Brien" });
|
|
81
|
-
expect(h.attributes?.blockMeta?.x).toBe(4);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('duplicate id: last wins', () => {
|
|
85
|
-
const h = getHeading('## X {#a #b}');
|
|
86
|
-
expect(h.attributes?.id).toBe('b');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('duplicate key: last wins', () => {
|
|
90
|
-
const h = getHeading('## X {x=1 x=2}');
|
|
91
|
-
expect(h.attributes?.blockMeta?.x).toBe(2);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('does NOT match a non-trailing brace block', () => {
|
|
95
|
-
const h = getHeading('## The {#wrong} section');
|
|
96
|
-
expect(h.attributes).toBeUndefined();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('does NOT collide with {[…]} template annotation', () => {
|
|
100
|
-
const h = getHeading('## X {[chart]}');
|
|
101
|
-
expect(h.templateAnnotation).toEqual({ template: 'chart' });
|
|
102
|
-
expect(h.attributes).toBeUndefined();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('Coexistence of {#…} and {[…]}', () => {
|
|
107
|
-
it('Pandoc first, then template', () => {
|
|
108
|
-
const h = getHeading('## X {#abc} {[chart]}');
|
|
109
|
-
expect(h.attributes?.id).toBe('abc');
|
|
110
|
-
expect(h.templateAnnotation?.template).toBe('chart');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('Template first, then Pandoc (order-agnostic)', () => {
|
|
114
|
-
const h = getHeading('## X {[chart]} {#abc}');
|
|
115
|
-
expect(h.attributes?.id).toBe('abc');
|
|
116
|
-
expect(h.templateAnnotation?.template).toBe('chart');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('both with params on each side', () => {
|
|
120
|
-
const h = getHeading('## X {#a x=400 y=200} {[chart colorScheme=blue]}');
|
|
121
|
-
expect(h.attributes?.id).toBe('a');
|
|
122
|
-
expect(h.attributes?.blockMeta?.x).toBe(400);
|
|
123
|
-
expect(h.attributes?.blockMeta?.y).toBe(200);
|
|
124
|
-
expect(h.templateAnnotation?.template).toBe('chart');
|
|
125
|
-
expect(h.templateAnnotation?.params).toEqual({ colorScheme: 'blue' });
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('Pandoc block-meta does not leak into templateOverrides', () => {
|
|
129
|
-
const h = getHeading('## X {#a x=400} {[chart colorScheme=blue]}');
|
|
130
|
-
expect(h.templateAnnotation?.params).toEqual({ colorScheme: 'blue' });
|
|
131
|
-
expect(h.attributes?.params).toEqual({ x: '400' });
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('Markdown round-trip with attributes', () => {
|
|
136
|
-
it('Pandoc-only id round-trips', () => {
|
|
137
|
-
const src = '## Section title {#intro}\n';
|
|
138
|
-
const md = parseMarkdown(src);
|
|
139
|
-
expect(stringifyMarkdown(md).trim()).toBe(src.trim());
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('canonicalizes order: Pandoc first, then template', () => {
|
|
143
|
-
const input = '## X {[chart]} {#abc}\n';
|
|
144
|
-
const md = parseMarkdown(input);
|
|
145
|
-
const out = stringifyMarkdown(md).trim();
|
|
146
|
-
expect(out).toBe('## X {#abc} {[chart]}');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('full attribute set round-trips', () => {
|
|
150
|
-
const src =
|
|
151
|
-
'## Architecture {#arch x=600 y=300 connectsTo=foo:veryImportant,bar:requires,baz:requires} {[diagramNode]}\n';
|
|
152
|
-
const md = parseMarkdown(src);
|
|
153
|
-
const out = stringifyMarkdown(md).trim();
|
|
154
|
-
expect(out).toBe(src.trim());
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('quoted value round-trips with quoting preserved', () => {
|
|
158
|
-
const src = '## X {caption="hello world"}\n';
|
|
159
|
-
const md = parseMarkdown(src);
|
|
160
|
-
const out = stringifyMarkdown(md).trim();
|
|
161
|
-
expect(out).toBe(src.trim());
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('existing {[…]}-only docs are unchanged', () => {
|
|
165
|
-
const src = '## Getting Started {[chart colorScheme=blue]}\n';
|
|
166
|
-
const md = parseMarkdown(src);
|
|
167
|
-
const out = stringifyMarkdown(md).trim();
|
|
168
|
-
expect(out).toBe(src.trim());
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('single-quoted values canonicalize to double quotes', () => {
|
|
172
|
-
const md = parseMarkdown("## X {caption='hello world'}");
|
|
173
|
-
expect(stringifyMarkdown(md).trim()).toBe('## X {caption="hello world"}');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('a value containing a double quote round-trips via single quotes', () => {
|
|
177
|
-
const src = '## X {label=\'say "hi"\'}';
|
|
178
|
-
const md = parseMarkdown(src);
|
|
179
|
-
expect(stringifyMarkdown(md).trim()).toBe(src);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('a quoted } round-trips', () => {
|
|
183
|
-
const src = '## X {caption="a } b"}';
|
|
184
|
-
const md = parseMarkdown(src);
|
|
185
|
-
expect(stringifyMarkdown(md).trim()).toBe(src);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
@@ -1,183 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* resolveThemeForDoc — pure, doc-scoped theme resolution.
|
|
3
|
-
*
|
|
4
|
-
* The theme analog of buildRegistry: an inline custom theme resolves from the
|
|
5
|
-
* doc's own `customThemes` list. Explicit caller registries and built-ins are
|
|
6
|
-
* fallback sources for non-inline ids.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect } from 'vitest';
|
|
10
|
-
import { resolveThemeForDoc } from '../doc/resolveDocTheme';
|
|
11
|
-
import { compileTheme } from '../schemas/themeCompile';
|
|
12
|
-
import { createThemeRegistry } from '../schemas/Theme';
|
|
13
|
-
import { DEFAULT_THEME } from '../schemas/themeLibrary';
|
|
14
|
-
import type { Doc } from '../schemas/Doc.js';
|
|
15
|
-
|
|
16
|
-
const brand = compileTheme({
|
|
17
|
-
id: 'my-brand',
|
|
18
|
-
name: 'My Brand',
|
|
19
|
-
seedColors: { primary: '#3182ce' },
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function docWith(partial: Partial<Doc>): Doc {
|
|
23
|
-
return {
|
|
24
|
-
articleId: 'd',
|
|
25
|
-
duration: 0,
|
|
26
|
-
blocks: [],
|
|
27
|
-
audio: { segments: [] },
|
|
28
|
-
...partial,
|
|
29
|
-
} as Doc;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('resolveThemeForDoc', () => {
|
|
33
|
-
it('resolves an inline custom theme doc-scoped (no global registration)', () => {
|
|
34
|
-
const doc = docWith({ customThemes: [brand], frontmatter: { 'squisq-theme': 'my-brand' } });
|
|
35
|
-
expect(resolveThemeForDoc(doc)).toEqual(brand);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('honors an explicit id over the frontmatter selection', () => {
|
|
39
|
-
const doc = docWith({ customThemes: [brand], frontmatter: { 'squisq-theme': 'cinematic' } });
|
|
40
|
-
expect(resolveThemeForDoc(doc, 'my-brand')).toEqual(brand);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('falls back to a built-in when the id is not an inline theme', () => {
|
|
44
|
-
const doc = docWith({ customThemes: [brand], frontmatter: { 'squisq-theme': 'cinematic' } });
|
|
45
|
-
expect(resolveThemeForDoc(doc).id).toBe('cinematic');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('uses Doc.themeId when there is no explicit or frontmatter id', () => {
|
|
49
|
-
const doc = docWith({ customThemes: [brand], themeId: 'my-brand' });
|
|
50
|
-
expect(resolveThemeForDoc(doc)).toEqual(brand);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('returns the default theme for a null doc', () => {
|
|
54
|
-
expect(resolveThemeForDoc(null).id).toBe(DEFAULT_THEME.id);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('uses an explicit registry only after document-scoped definitions', () => {
|
|
58
|
-
const external = compileTheme({
|
|
59
|
-
id: 'external-brand',
|
|
60
|
-
name: 'External Brand',
|
|
61
|
-
seedColors: { primary: '#8844aa' },
|
|
62
|
-
});
|
|
63
|
-
const registry = createThemeRegistry([external]);
|
|
64
|
-
const doc = docWith({ themeId: 'external-brand' });
|
|
65
|
-
|
|
66
|
-
expect(resolveThemeForDoc(doc)).toBe(DEFAULT_THEME);
|
|
67
|
-
expect(resolveThemeForDoc(doc, undefined, registry)).toEqual(external);
|
|
68
|
-
});
|
|
69
|
-
});
|