@bendyline/squisq 2.1.0 → 2.3.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/README.md +8 -8
- package/dist/{ContentContainer-BXUlIf18.d.ts → ContentContainer-B2w9sUoL.d.ts} +15 -0
- package/dist/{Doc-DynJleo-.d.ts → Doc-COe-rPQB.d.ts} +7 -5
- package/dist/{ImageEditDoc-Ca4Tr9uN.d.ts → ImageEditDoc-dtKahVyD.d.ts} +1 -1
- package/dist/{annotationCoercion-Dr_DY9Hx.d.ts → annotationCoercion-PtRQhk5V.d.ts} +1 -1
- package/dist/{chunk-VWCJ2WXC.js → chunk-2ZWIXGAC.js} +0 -1
- package/dist/{chunk-VYFRI7R6.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-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-JHZWYRSW.js → chunk-C33GTPUZ.js} +11 -4
- package/dist/{chunk-XNLB57UD.js → chunk-EK446BUE.js} +18 -2
- package/dist/{chunk-VR7UPJ7C.js → chunk-FCG3BIZN.js} +12 -1
- package/dist/{chunk-JI77MGAK.js → chunk-GIIOY6XR.js} +2178 -1291
- 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-OTKVFVL6.js → chunk-QNTQIEIV.js} +130 -47
- package/dist/{chunk-5UYI3RJB.js → chunk-QWCFK5FN.js} +55 -40
- package/dist/{chunk-GZY66SDN.js → chunk-R3KTTOMS.js} +0 -1
- package/dist/{chunk-ILWDXLUK.js → chunk-RS5AP3J4.js} +49 -10
- package/dist/{chunk-QUUPYOSP.js → chunk-SPTY4C6F.js} +136 -5
- package/dist/{chunk-4X3JQXNM.js → chunk-XOFT65EX.js} +1 -2
- package/dist/{chunk-FVTQSNR7.js → chunk-XRW6P7IF.js} +13 -5
- package/dist/{chunk-R2YZTCLO.js → chunk-ZQKZSJAX.js} +1 -2
- package/dist/doc/index.d.ts +311 -13
- package/dist/doc/index.js +25 -17
- package/dist/generate/index.d.ts +2 -2
- package/dist/generate/index.js +1 -3
- package/dist/icons/index.js +2 -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 -13
- package/dist/index.js +67 -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-DDzubQXo.d.ts → materializePageSection-BSMOocve.d.ts} +2 -2
- package/dist/narration/index.d.ts +16 -3
- package/dist/narration/index.js +12 -14
- package/dist/random/index.d.ts +11 -2
- package/dist/random/index.js +1 -3
- package/dist/recommend/index.d.ts +1 -1
- package/dist/recommend/index.js +2 -4
- package/dist/schemas/index.d.ts +18 -6
- package/dist/schemas/index.js +7 -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-B72m0W1y.d.ts → themeLibrary-BUTWjufL.d.ts} +1 -1
- package/dist/timing/index.js +2 -4
- package/dist/transform/index.d.ts +3 -3
- 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 +8 -3
- 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-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-FVTQSNR7.js.map +0 -1
- package/dist/chunk-GZY66SDN.js.map +0 -1
- package/dist/chunk-ILWDXLUK.js.map +0 -1
- package/dist/chunk-JHZWYRSW.js.map +0 -1
- package/dist/chunk-JI77MGAK.js.map +0 -1
- package/dist/chunk-JOQYPRDZ.js.map +0 -1
- package/dist/chunk-OTKVFVL6.js.map +0 -1
- package/dist/chunk-PGGGKYWG.js +0 -1
- package/dist/chunk-PGGGKYWG.js.map +0 -1
- package/dist/chunk-Q7LROV2H.js.map +0 -1
- package/dist/chunk-QE7TWGVT.js.map +0 -1
- package/dist/chunk-QUUPYOSP.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-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-VWCJ2WXC.js.map +0 -1
- package/dist/chunk-VYFRI7R6.js.map +0 -1
- package/dist/chunk-XNLB57UD.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 -167
- package/src/__tests__/materializePageSections.test.ts +0 -404
- 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__/pageStyle.test.ts +0 -258
- 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 -173
- 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 -161
- package/src/doc/markdownToDoc.ts +0 -1054
- package/src/doc/materializeBlockLayers.ts +0 -465
- package/src/doc/mediaAnnotations.ts +0 -120
- package/src/doc/page/PageSection.ts +0 -174
- package/src/doc/page/materializePageSection.ts +0 -616
- package/src/doc/page/resolvePageBlock.ts +0 -108
- package/src/doc/page/sectionExtractors.ts +0 -466
- package/src/doc/pageCss.ts +0 -628
- 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 -424
- 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 -1073
- 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/PageStyle.ts +0 -213
- package/src/schemas/Theme.ts +0 -413
- 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 -31
- package/src/schemas/pageStyleDefaults.ts +0 -204
- package/src/schemas/themeCompile.ts +0 -244
- package/src/schemas/themeConstants.ts +0 -2
- package/src/schemas/themeLibrary.ts +0 -60
- package/src/schemas/themeValidator.ts +0 -609
- package/src/schemas/themes/bold.json +0 -77
- package/src/schemas/themes/cinematic.json +0 -143
- package/src/schemas/themes/documentary.json +0 -125
- package/src/schemas/themes/gezellig.json +0 -123
- package/src/schemas/themes/index.ts +0 -53
- package/src/schemas/themes/magazine.json +0 -126
- package/src/schemas/themes/minimalist.json +0 -74
- package/src/schemas/themes/morning-light.json +0 -117
- package/src/schemas/themes/standard-dark.json +0 -109
- package/src/schemas/themes/standard.json +0 -98
- package/src/schemas/themes/tech-dark.json +0 -143
- package/src/schemas/themes/warm-earth.json +0 -74
- 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 -217
- package/src/transform/styles/dataDriven.ts +0 -18
- package/src/transform/styles/documentary.ts +0 -18
- package/src/transform/styles/index.ts +0 -5
- package/src/transform/styles/magazine.ts +0 -19
- package/src/transform/styles/minimal.ts +0 -18
- package/src/transform/styles/narrative.ts +0 -19
- package/src/transform/templateSelector.ts +0 -389
- package/src/transform/timingAllocator.ts +0 -257
- package/src/transform/types.ts +0 -158
- 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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/markdown/stringify.ts","../src/markdown/sanitize.ts"],"sourcesContent":["/**\n * JSON → Markdown Serializer\n *\n * Converts a MarkdownDocument JSON structure back to a markdown string\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkStringify from 'remark-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport type { MarkdownDocument, StringifyOptions } from './types.js';\nimport { toMdast } from './convert.js';\nimport { formatFrontmatterValue } from './utils.js';\n\n// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n// ============================================\n// Annotation-span unescaping\n// ============================================\n\n// Quoted runs as they appear in remark-stringify output. Backslashes inside\n// the span are doubled by remark (a literal `\\` before punctuation becomes\n// `\\\\`), so `\\\\.` covers both authored escapes and remark's own.\nconst DQ_RUN = `\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`;\nconst SQ_RUN = `'(?:[^'\\\\\\\\]|\\\\\\\\.)*'`;\n\n/**\n * A `{[…]}` template-annotation (or inline-icon) span as it appears in\n * remark output: `{\\[inner]}`, where `inner` may contain backslash escapes\n * and quoted runs (which may themselves contain `]`).\n */\nconst ESCAPED_TEMPLATE_SPAN_RE = new RegExp(\n `\\\\{\\\\\\\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^\\\\]\\\\\\\\])*)\\\\]\\\\}`,\n 'g',\n);\n\n/** A heading line (annotations are only emitted trailing on headings). */\nconst HEADING_LINE_RE = /^#{1,6} .*$/gm;\n\n/**\n * The trailing Pandoc `{…}` span on a heading line, possibly followed by an\n * already-unescaped `{[…]}` template span (canonical emit order).\n */\nconst TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(\n `\\\\{(?!\\\\\\\\?\\\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^}\\\\\\\\])*)\\\\}(?=\\\\s*(?:\\\\{\\\\[.*)?$)`,\n);\n\n/**\n * Backslash+punctuation pairs that are safe to unescape inside an\n * annotation span: the bare character re-parses as literal text in\n * trailing-heading position, so removing the backslash is purely\n * cosmetic and round-trips.\n *\n * Deliberately EXCLUDED (left escaped in the emitted markdown): emphasis\n * and other inline-construct openers (`*`, `_`, `` ` ``, `~`, `<`, `>`,\n * `&`, `!`, `|`, `^`). Unescaping those could make remark-parse split the\n * heading's text run into inline nodes on reparse, which would hide the\n * annotation from the extractor. remark-parse resolves the surviving\n * `\\X` escapes back to bare characters, so values still round-trip —\n * the escapes are a cosmetic cost only.\n *\n * Pairs are consumed left to right, so an authored `\\\"` that remark\n * doubled to `\\\\\"` correctly comes back as `\\\"`.\n */\nconst UNESCAPE_PUNCT_RE = /\\\\([\\\\[\\]:#.,+=/?;@%$(){}'\"-])/g;\n\nfunction unescapeMarkdownPunct(text: string): string {\n return text.replace(UNESCAPE_PUNCT_RE, '$1');\n}\n\n/**\n * Serialize a MarkdownDocument back to a markdown string.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to control formatting and disable extensions.\n *\n * @param doc - The MarkdownDocument to serialize\n * @param options - Serialization options (formatting, extensions)\n * @returns A markdown string\n *\n * @example\n * ```ts\n * const doc: MarkdownDocument = {\n * type: 'document',\n * children: [\n * { type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] },\n * { type: 'paragraph', children: [{ type: 'text', value: 'World' }] },\n * ],\n * };\n * const md = stringifyMarkdown(doc);\n * // '# Hello\\n\\nWorld\\n'\n * ```\n */\nexport function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOptions): string {\n // Convert MarkdownDocument → mdast tree\n const mdastTree = toMdast(doc);\n\n // Use cached default processor when all extensions and default formatting are used.\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n !options.bullet &&\n !options.bulletOrdered &&\n !options.emphasis &&\n !options.strong &&\n !options.rule &&\n !options.fence &&\n options.setext == null);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkStringify, {\n bullet: '-',\n bulletOrdered: '.',\n emphasis: '*',\n strong: '*',\n rule: '-',\n fence: '`',\n setext: false,\n });\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested options.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified();\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n\n processor = processor.use(remarkStringify, {\n bullet: options?.bullet ?? '-',\n bulletOrdered: options?.bulletOrdered ?? '.',\n emphasis: options?.emphasis ?? '*',\n strong: options?.strong ?? '*',\n rule: options?.rule ?? '-',\n fence: options?.fence ?? '`',\n setext: options?.setext ?? false,\n });\n }\n\n // Stringify mdast → markdown string\n const result = processor.stringify(mdastTree) as string;\n\n // remark-stringify backslash-escapes markdown-significant characters in\n // text nodes (`[` to prevent link syntax, `:` for directives, `*`/`_` for\n // emphasis, a literal `\\` before punctuation, …). Our annotations are\n // injected as plain text, so their interiors come back escaped. Undo that\n // inside `{[…]}` template-annotation spans: removing one backslash from\n // each backslash+punctuation pair (left to right) exactly inverts\n // remark's escaping — including restoring an authored `\\\"` that remark\n // doubled to `\\\\\"`. The span regex understands quoted values, so a\n // quoted `]` doesn't terminate the span.\n let cleaned = result.replace(\n ESCAPED_TEMPLATE_SPAN_RE,\n (_m, inner: string) => `{[${unescapeMarkdownPunct(inner)}]}`,\n );\n\n // Same treatment for the trailing Pandoc `{…}` attribute block on heading\n // lines (canonical emit order is Pandoc block then template annotation,\n // so the span sits at end-of-line or just before a `{[…]}` span).\n cleaned = cleaned.replace(HEADING_LINE_RE, (line) =>\n line.replace(\n TRAILING_ESCAPED_PANDOC_SPAN_RE,\n (_m, inner: string) => `{${unescapeMarkdownPunct(inner)}}`,\n ),\n );\n\n // remark-directive escapes `:` in text (since `:name` would round-trip as\n // a text directive). Inside any other Pandoc-style `{…}` brace span, `:`\n // is purely a value separator (e.g. `connectsTo=foo:flow`), so unescape\n // it to keep round-trips lossless. The negative lookahead skips `{[…]}`\n // template annotations — those are handled above.\n cleaned = cleaned.replace(/\\{(?!\\\\?\\[)[^}]*\\}/g, (match) => match.replace(/\\\\:/g, ':'));\n\n // Prepend YAML frontmatter if present\n if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {\n const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n return `${k}: ${formatFrontmatterValue(v)}`;\n }\n return `${k}: ${JSON.stringify(v)}`;\n });\n return `---\\n${yamlLines.join('\\n')}\\n---\\n\\n${cleaned}`;\n }\n\n return cleaned;\n}\n","import type { HtmlElement, HtmlNode } from './types.js';\n\nexport type HtmlPolicy = 'strip' | 'sanitize' | 'trusted';\n\nexport type UrlKind = 'link' | 'media';\n\nconst SAFE_LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst SAFE_MEDIA_SCHEMES = new Set(['http', 'https', 'blob']);\n\n/** Schemes that can execute script — never allowed, even via extraLinkSchemes. */\nconst NEVER_ALLOWED_SCHEMES = new Set(['javascript', 'vbscript', 'data']);\n\nexport interface SanitizeUrlOptions {\n /**\n * Extra lowercase schemes allowed for `kind: 'link'` — the host-app escape\n * hatch for custom link protocols it intercepts itself (e.g. an app-internal\n * navigation scheme). Executable schemes are refused regardless.\n */\n extraLinkSchemes?: readonly string[];\n}\n\nconst SAFE_DATA_MEDIA_RE =\n /^data:(?:image\\/(?!svg\\+xml)[a-z0-9.+-]+|audio\\/[a-z0-9.+-]+|video\\/[a-z0-9.+-]+);/i;\n\nconst SAFE_TAGS = new Set([\n 'a',\n 'abbr',\n 'b',\n 'blockquote',\n 'br',\n 'caption',\n 'cite',\n 'code',\n 'col',\n 'colgroup',\n 'data',\n 'dd',\n 'del',\n 'details',\n 'dfn',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'figcaption',\n 'figure',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'hr',\n 'i',\n 'img',\n 'kbd',\n 'li',\n 'mark',\n 'ol',\n 'p',\n 'pre',\n 'q',\n 's',\n 'samp',\n 'small',\n 'source',\n 'span',\n 'strong',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'track',\n 'u',\n 'ul',\n 'var',\n 'video',\n 'audio',\n]);\n\nconst DROP_WITH_CONTENT_TAGS = new Set([\n 'base',\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'link',\n 'meta',\n 'object',\n 'option',\n 'script',\n 'select',\n 'style',\n 'svg',\n 'template',\n 'textarea',\n]);\n\nconst GLOBAL_ATTRS = new Set(['class', 'id', 'title', 'role']);\n\nconst TAG_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']),\n audio: new Set(['src', 'controls', 'preload', 'muted', 'loop']),\n col: new Set(['span', 'width']),\n colgroup: new Set(['span']),\n data: new Set(['value']),\n img: new Set(['src', 'alt', 'width', 'height', 'loading', 'decoding']),\n li: new Set(['value']),\n ol: new Set(['start', 'type']),\n q: new Set(['cite']),\n source: new Set(['src', 'type', 'media']),\n td: new Set(['colspan', 'rowspan', 'align']),\n th: new Set(['colspan', 'rowspan', 'align', 'scope']),\n time: new Set(['datetime']),\n track: new Set(['src', 'kind', 'label', 'srclang', 'default']),\n video: new Set([\n 'src',\n 'poster',\n 'width',\n 'height',\n 'controls',\n 'preload',\n 'muted',\n 'loop',\n 'playsinline',\n ]),\n};\n\nconst SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;\nconst SAFE_OL_TYPES = new Set(['1', 'a', 'A', 'i', 'I']);\nconst SAFE_PRELOAD_VALUES = new Set(['none', 'metadata', 'auto']);\nconst SAFE_TRACK_KINDS = new Set(['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']);\n\n/**\n * Sanitize a URL for a rendered link or media attribute.\n *\n * Relative URLs are allowed. Absolute URLs are restricted to schemes that\n * cannot execute script in a document context. Media URLs additionally allow\n * `blob:` and image/audio/video `data:` URLs because Squisq uses both for\n * browser-local media previews and self-contained exports.\n */\nexport function sanitizeUrl(\n url: string | null | undefined,\n kind: UrlKind = 'link',\n options?: SanitizeUrlOptions,\n): string | null {\n if (typeof url !== 'string') return null;\n\n const trimmed = url.trim();\n if (!trimmed) return null;\n\n const compact = stripUrlSchemeNoise(trimmed);\n const colon = compact.indexOf(':');\n const firstPathChar = firstIndexOfAny(compact, ['/', '?', '#']);\n const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);\n\n if (!hasScheme) return trimmed;\n\n const scheme = compact.slice(0, colon).toLowerCase();\n if (kind === 'media') {\n if (scheme === 'data') return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;\n return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;\n }\n\n if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;\n if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {\n return trimmed;\n }\n return null;\n}\n\n/**\n * Return a sanitized copy of an HtmlNode tree. The original parsed tree is left\n * untouched so markdown stringify can still preserve source fidelity.\n */\nexport function sanitizeHtmlNodes(nodes: HtmlNode[]): HtmlNode[] {\n const out: HtmlNode[] = [];\n for (const node of nodes) {\n out.push(...sanitizeHtmlNode(node));\n }\n return out;\n}\n\nfunction sanitizeHtmlNode(node: HtmlNode): HtmlNode[] {\n switch (node.type) {\n case 'htmlText':\n return [node];\n case 'htmlComment':\n return [];\n case 'htmlElement':\n return sanitizeHtmlElement(node);\n }\n}\n\nfunction sanitizeHtmlElement(node: HtmlElement): HtmlNode[] {\n const tag = node.tagName.toLowerCase();\n\n if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];\n\n const children = sanitizeHtmlNodes(node.children);\n if (!SAFE_TAGS.has(tag)) return children;\n\n return [\n {\n type: 'htmlElement',\n tagName: tag,\n attributes: sanitizeAttrs(tag, node.attributes),\n children,\n selfClosing: node.selfClosing,\n },\n ];\n}\n\nfunction sanitizeAttrs(tag: string, attrs: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [rawName, rawValue] of Object.entries(attrs)) {\n const name = rawName.toLowerCase();\n if (!isAllowedAttr(tag, name)) continue;\n\n const value = String(rawValue);\n const safeValue = sanitizeAttrValue(tag, name, value);\n if (safeValue === null) continue;\n out[name] = safeValue;\n }\n if (tag === 'a' && out.target === '_blank') {\n out.rel = ensureRelTokens(out.rel, ['noopener', 'noreferrer']);\n }\n return out;\n}\n\nfunction isAllowedAttr(tag: string, name: string): boolean {\n if (!SAFE_ATTR_NAME_RE.test(name)) return false;\n if (name.startsWith('on')) return false;\n if (name.startsWith('aria-') || name.startsWith('data-')) return true;\n if (GLOBAL_ATTRS.has(name)) return true;\n return TAG_ATTRS[tag]?.has(name) ?? false;\n}\n\nfunction sanitizeAttrValue(tag: string, name: string, value: string): string | null {\n if (name === 'href') return sanitizeUrl(value, 'link');\n if (name === 'src' || name === 'poster') return sanitizeUrl(value, 'media');\n if (\n (name === 'width' ||\n name === 'height' ||\n name === 'span' ||\n name === 'colspan' ||\n name === 'rowspan' ||\n name === 'value') &&\n !isNonNegativeInteger(value)\n ) {\n return null;\n }\n if (name === 'start' && !isInteger(value)) return null;\n if (name === 'type' && tag === 'ol' && !SAFE_OL_TYPES.has(value)) return null;\n if (name === 'preload' && !SAFE_PRELOAD_VALUES.has(value)) return null;\n if (name === 'kind' && tag === 'track' && !SAFE_TRACK_KINDS.has(value)) return null;\n if (name === 'align' && value !== 'left' && value !== 'right' && value !== 'center') return null;\n if (name === 'target')\n return value === '_blank' || value === '_self' || value === '_parent' || value === '_top'\n ? value\n : null;\n if (name === 'rel') return sanitizeRel(value);\n return value;\n}\n\nfunction sanitizeRel(value: string): string {\n return relTokens(value).join(' ');\n}\n\nfunction ensureRelTokens(value: string | undefined, required: string[]): string {\n const tokens = new Set(value ? relTokens(value) : []);\n for (const token of required) tokens.add(token);\n return Array.from(tokens).join(' ');\n}\n\nfunction relTokens(value: string): string[] {\n const tokens = value\n .split(/\\s+/)\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean)\n .filter((token) => /^[a-z0-9_-]+$/.test(token));\n return Array.from(new Set(tokens));\n}\n\nfunction isInteger(value: string): boolean {\n return /^-?\\d+$/.test(value.trim());\n}\n\nfunction isNonNegativeInteger(value: string): boolean {\n return /^\\d+$/.test(value.trim());\n}\n\nfunction firstIndexOfAny(value: string, needles: string[]): number {\n let result = -1;\n for (const needle of needles) {\n const index = value.indexOf(needle);\n if (index >= 0 && (result < 0 || index < result)) result = index;\n }\n return result;\n}\n\nfunction stripUrlSchemeNoise(value: string): string {\n let out = '';\n for (const char of value) {\n const code = char.charCodeAt(0);\n if (code <= 0x20 || code === 0x7f || char.trim() === '') continue;\n out += char;\n }\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/markdown/annotationCoercion.ts","../src/markdown/attrTokens.ts"],"sourcesContent":["/**\n * Heading-attribute value coercion.\n *\n * The Pandoc-style heading attribute block `{#id .class key=value}` parses\n * to a flat `Record<string, string>` of `key → raw value`. This module\n * coerces a known subset of keys to typed values (numbers, time-in-seconds,\n * `BlockConnection[]`) so downstream consumers don't have to re-parse them.\n *\n * Unknown keys are returned in `metadata` as raw strings. Malformed known-key\n * values are dropped from the typed result and reported via `warnings` (the\n * raw string is preserved in `params` upstream for lossless round-tripping —\n * coercion failures do not corrupt the source).\n */\n\nimport type { BlockConnection } from './types.js';\nimport type { TransitionDirection, TransitionType } from '../schemas/Transitions.js';\nimport {\n normalizeTransitionDirection,\n normalizeTransitionType,\n TRANSITION_TYPES,\n TRANSITION_DIRECTIONS,\n} from '../schemas/Transitions.js';\n\n/**\n * Registry of known block-meta keys and their coercion strategies.\n *\n * Adding a new typed key: add an entry here, add the typed field to\n * `HeadingAttributes.blockMeta` in `types.ts`, and apply the coerced value\n * in `markdownToDoc.ts`.\n */\nexport const KNOWN_BLOCK_META_KEYS = {\n x: 'number',\n y: 'number',\n startTime: 'time',\n duration: 'time',\n connectsTo: 'connectionList',\n transition: 'transition',\n transitionDuration: 'time',\n transitionDirection: 'transitionDirection',\n} as const;\n\nexport type KnownBlockMetaKey = keyof typeof KNOWN_BLOCK_META_KEYS;\n\n/**\n * Editor-facing description of a known block-meta key: what it does, and\n * either its closed set of valid values or a free-form format hint. This is\n * the single source the markdown editor's `{[name key=value]}` attribute\n * autocomplete reads from, so the suggestion list stays in lockstep with\n * `KNOWN_BLOCK_META_KEYS` and the transition vocabulary rather than drifting\n * in a hand-maintained copy. `values`/`valueHint` are mutually exclusive: a\n * key has a closed enum (`values`) or a format hint (`valueHint`), not both.\n */\nexport interface BlockMetaKeyDescriptor {\n key: KnownBlockMetaKey;\n /** One-line description, shown as the completion item's detail. */\n description: string;\n /** Closed set of valid values, for keys backed by an enum. */\n values?: readonly string[];\n /** Format hint shown when the value is free-form (no closed set). */\n valueHint?: string;\n}\n\n/**\n * Ordered descriptors for every key in `KNOWN_BLOCK_META_KEYS`. Order is the\n * suggestion display order: the transition family first (the most common\n * authoring case), then timeline timing, then diagram geometry/links.\n */\nexport const BLOCK_META_KEY_DESCRIPTORS: readonly BlockMetaKeyDescriptor[] = [\n {\n key: 'transition',\n description: 'Block-to-block transition effect',\n values: TRANSITION_TYPES,\n },\n {\n key: 'transitionDuration',\n description: 'How long the transition lasts',\n valueHint: 'seconds — e.g. 0.7, 700ms',\n },\n {\n key: 'transitionDirection',\n description: 'Directional variant for the transition',\n values: TRANSITION_DIRECTIONS,\n },\n {\n key: 'startTime',\n description: 'Timeline start time of this block',\n valueHint: 'mm:ss or seconds — e.g. 01:30, 5, 1500ms',\n },\n {\n key: 'duration',\n description: 'How long this block lasts',\n valueHint: 'mm:ss or seconds — e.g. 45, 1500ms',\n },\n {\n key: 'x',\n description: 'Horizontal position on the diagram canvas',\n valueHint: 'number — e.g. 600',\n },\n {\n key: 'y',\n description: 'Vertical position on the diagram canvas',\n valueHint: 'number — e.g. 300',\n },\n {\n key: 'connectsTo',\n description: 'Diagram connections to other blocks',\n valueHint: 'comma-separated target or target:type — e.g. foo,bar:flow',\n },\n];\n\n/**\n * The shape of the typed values produced by coercion. Mirrors\n * `HeadingAttributes.blockMeta` in `types.ts`.\n */\nexport interface CoercedBlockMeta {\n x?: number;\n y?: number;\n startTime?: number;\n duration?: number;\n connectsTo?: BlockConnection[];\n transition?: {\n type: TransitionType;\n duration?: number;\n direction?: TransitionDirection;\n };\n}\n\nexport interface CoerceResult {\n /** Typed values for known keys whose coercion succeeded. */\n blockMeta: CoercedBlockMeta;\n /** Raw string values for keys not in the known-key registry. */\n metadata: Record<string, string>;\n /** Human-readable diagnostics — currently emitted for coercion failures. */\n warnings: string[];\n}\n\n/**\n * Coerce a flat `key → raw string` map into typed block-meta + an\n * untyped metadata bag, per the `KNOWN_BLOCK_META_KEYS` registry.\n */\nexport function coerceAnnotationValues(params: Record<string, string>): CoerceResult {\n const blockMeta: CoercedBlockMeta = {};\n const metadata: Record<string, string> = {};\n const warnings: string[] = [];\n let transitionType: TransitionType | null = null;\n let transitionDuration: number | undefined;\n let transitionDirection: TransitionDirection | undefined;\n\n for (const [key, raw] of Object.entries(params)) {\n const kind = (KNOWN_BLOCK_META_KEYS as Record<string, string>)[key];\n if (!kind) {\n metadata[key] = raw;\n continue;\n }\n if (kind === 'number') {\n const n = parseNumber(raw);\n if (n == null) {\n warnings.push(`Invalid number for \"${key}\": ${JSON.stringify(raw)}`);\n } else {\n (blockMeta as Record<string, unknown>)[key] = n;\n }\n } else if (kind === 'time') {\n const s = parseTimeSeconds(raw);\n if (s == null) {\n warnings.push(`Invalid time for \"${key}\": ${JSON.stringify(raw)}`);\n } else if (key === 'transitionDuration') {\n transitionDuration = s;\n } else {\n (blockMeta as Record<string, unknown>)[key] = s;\n }\n } else if (kind === 'connectionList') {\n const { list, warning } = parseConnectionList(raw);\n if (warning) warnings.push(`\"${key}\": ${warning}`);\n blockMeta.connectsTo = list;\n } else if (kind === 'transition') {\n const transition = normalizeTransitionType(raw);\n if (transition == null) {\n warnings.push(`Invalid transition for \"${key}\": ${JSON.stringify(raw)}`);\n } else {\n transitionType = transition;\n }\n } else if (kind === 'transitionDirection') {\n const direction = normalizeTransitionDirection(raw);\n if (direction == null) {\n warnings.push(`Invalid transition direction for \"${key}\": ${JSON.stringify(raw)}`);\n } else {\n transitionDirection = direction;\n }\n }\n }\n\n if (transitionType) {\n blockMeta.transition = {\n type: transitionType,\n ...(transitionDuration !== undefined ? { duration: transitionDuration } : {}),\n ...(transitionDirection !== undefined ? { direction: transitionDirection } : {}),\n };\n }\n\n return { blockMeta, metadata, warnings };\n}\n\n// ============================================\n// Coercers\n// ============================================\n\n/** Parse a finite number from a string. Returns null on failure. */\nexport function parseNumber(raw: string): number | null {\n if (raw.trim() === '') return null;\n const n = Number(raw);\n return Number.isFinite(n) ? n : null;\n}\n\nconst TIME_BARE_RE = /^\\d+(?:\\.\\d+)?$/;\nconst TIME_MS_RE = /^(\\d+(?:\\.\\d+)?)ms$/;\nconst TIME_MMSS_RE = /^(\\d+):(\\d{1,2})(?:\\.(\\d+))?$/;\n\n/**\n * Parse a time value to seconds. Accepts:\n * - bare number: `5` → 5, `5.5` → 5.5\n * - `mm:ss`: `01:30` → 90\n * - `mm:ss.ms`: `01:30.500` → 90.5\n * - `1500ms` → 1.5\n *\n * Returns null on malformed input.\n */\nexport function parseTimeSeconds(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n\n if (TIME_BARE_RE.test(trimmed)) {\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : null;\n }\n\n const msMatch = trimmed.match(TIME_MS_RE);\n if (msMatch) {\n const n = Number(msMatch[1]);\n return Number.isFinite(n) ? n / 1000 : null;\n }\n\n const mmssMatch = trimmed.match(TIME_MMSS_RE);\n if (mmssMatch) {\n const mins = Number(mmssMatch[1]);\n const secs = Number(mmssMatch[2]);\n const frac = mmssMatch[3] ? Number(`0.${mmssMatch[3]}`) : 0;\n if (!Number.isFinite(mins) || !Number.isFinite(secs) || !Number.isFinite(frac)) {\n return null;\n }\n if (secs >= 60) return null;\n return mins * 60 + secs + frac;\n }\n\n return null;\n}\n\n/**\n * Parse a comma-separated connection list. Each entry is `target` or\n * `target:type`. Empty entries (`a,,b`) are filtered with a warning; an\n * entirely empty input yields an empty list with no warning.\n */\nexport function parseConnectionList(raw: string): {\n list: BlockConnection[];\n warning: string | null;\n} {\n const trimmed = raw.trim();\n if (!trimmed) return { list: [], warning: null };\n\n const list: BlockConnection[] = [];\n let droppedEmpty = false;\n for (const part of trimmed.split(',')) {\n const entry = part.trim();\n if (!entry) {\n droppedEmpty = true;\n continue;\n }\n const colonIdx = entry.indexOf(':');\n if (colonIdx < 0) {\n list.push({ target: entry });\n } else {\n const target = entry.slice(0, colonIdx).trim();\n const type = entry.slice(colonIdx + 1).trim();\n if (!target) {\n droppedEmpty = true;\n continue;\n }\n list.push(type ? { target, type } : { target });\n }\n }\n\n return {\n list,\n warning: droppedEmpty ? 'dropped empty connection entries' : null,\n };\n}\n","/**\n * Shared key=value attribute machinery for the two heading annotation forms:\n * the squisq-native `{[templateName key=value …]}` template annotation and\n * the Pandoc-style `{#id .class key=value}` attribute block.\n *\n * Both forms share one interior grammar — whitespace-separated tokens whose\n * values may be quoted (double or single) to include spaces, brackets, or\n * quote characters. This module is the single implementation of that\n * grammar: one tokenizer, one value un/escaper, one quoting rule. The\n * trailing-annotation recognizers live here too so the editor bridge\n * (packages/editor-react/src/tiptapBridge.ts) stays in sync by importing\n * them rather than copying regexes.\n *\n * Value syntax:\n * - Unquoted: no whitespace, quotes, or `[ ] { }` (those would collide with\n * the surrounding annotation delimiters), e.g. `columns=3`, `name=O'Brien`\n * (a quote *inside* an unquoted value is literal).\n * - Quoted: `key=\"value with spaces\"` or `key='…'`. Inside a quoted run,\n * `\\` escapes the active quote character and itself; any other `\\` is\n * literal. Quoted values may contain `]` and `}`.\n * - Serialization emits double quotes, switching to single quotes when the\n * value contains a double quote (markdown character-escape resolution\n * makes backslash-escaped quotes fragile in `.md` source, so picking the\n * other quote character is preferred over escaping). A value containing\n * BOTH quote characters falls back to double quotes with `\\\"` escapes —\n * that survives the API path but is best avoided in markdown source.\n */\n\n// ============================================\n// Trailing-annotation recognizers\n// ============================================\n\n/** Regex source for a double-quoted run with `\\\"` / `\\\\` escapes. */\nconst DQ_RUN = `\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`;\n/** Regex source for a single-quoted run with `\\'` / `\\\\` escapes. */\nconst SQ_RUN = `'(?:[^'\\\\\\\\]|\\\\\\\\.)*'`;\n\n/**\n * Quote-aware match for a trailing `{[…]}` annotation: the interior is any\n * mix of quoted runs and characters other than `]` and quotes, so quoted\n * values may contain `]`. An empty interior is valid so `{[]}` can remain\n * lossless metadata instead of visible heading text. The trailing\n * `[\\s\\]}]*` tolerates an accidental doubled `]}` that users sometimes type\n * when learning the syntax.\n */\nconst TEMPLATE_STRICT_RE = new RegExp(\n `\\\\s*\\\\{\\\\[((?:[^\\\\]\"']|${DQ_RUN}|${SQ_RUN})*)\\\\]\\\\}[\\\\s\\\\]}]*$`,\n);\n\n/**\n * Legacy fallback matching the pre-quote-aware grammar (`[^\\]]*` interior).\n * Tried after the strict form so inputs with unbalanced quotes keep parsing\n * exactly as they did before quote support landed.\n */\nconst TEMPLATE_LEGACY_RE = /\\s*\\{\\[([^\\]]*)\\]\\}[\\s\\]}]*$/;\n\n/**\n * Quote-aware match for a trailing Pandoc `{…}` block. The negative\n * lookahead `(?!\\[)` keeps it from colliding with the `{[…]}` template\n * form. Quoted values may contain `}`.\n */\nconst PANDOC_STRICT_RE = new RegExp(`\\\\s*\\\\{(?!\\\\[)((?:[^}\"']|${DQ_RUN}|${SQ_RUN})*)\\\\}\\\\s*$`);\n\n/** Legacy fallback for the Pandoc block (`[^}]*` interior). */\nconst PANDOC_LEGACY_RE = /\\s*\\{(?!\\[)([^}]*)\\}\\s*$/;\n\n/** Result of matching a trailing annotation against a text run. */\nexport interface TrailingAnnotationMatch {\n /** Interior of the brace block (between `{[`/`{` and `]}`/`}`), untrimmed. */\n inner: string;\n /** Offset of the match start (including leading whitespace) in the input. */\n index: number;\n}\n\nfunction matchTrailing(\n text: string,\n strict: RegExp,\n legacy: RegExp,\n): TrailingAnnotationMatch | null {\n const m = strict.exec(text) ?? legacy.exec(text);\n return m ? { inner: m[1], index: m.index } : null;\n}\n\n/**\n * Match a trailing `{[templateName key=value …]}` annotation. Quote-aware\n * (values may contain `]` when quoted), with a legacy fallback so inputs\n * the old `[^\\]]+` grammar accepted still match.\n */\nexport function matchTrailingTemplateAnnotation(text: string): TrailingAnnotationMatch | null {\n return matchTrailing(text, TEMPLATE_STRICT_RE, TEMPLATE_LEGACY_RE);\n}\n\n/**\n * Match a trailing Pandoc `{#id .class key=value}` block. Quote-aware\n * (values may contain `}` when quoted), with a legacy fallback.\n */\nexport function matchTrailingPandocAttr(text: string): TrailingAnnotationMatch | null {\n return matchTrailing(text, PANDOC_STRICT_RE, PANDOC_LEGACY_RE);\n}\n\n// ============================================\n// Tokenizer\n// ============================================\n\nconst WHITESPACE_RE = /\\s/;\n\n/**\n * Quote-aware whitespace tokenizer for the interior of either annotation\n * form.\n *\n * A quote run starts only at the beginning of a token or immediately after\n * `=` — a quote elsewhere (e.g. `name=O'Brien`) is a literal character.\n * Inside a run, `\\` escapes the active quote character and itself; the\n * escape pair is preserved in the returned token (callers strip and\n * unescape via {@link splitKeyValueToken} / {@link unquoteAttrValue}).\n * An unbalanced opening quote is tolerated: the run extends to the end of\n * the input.\n */\nexport function tokenizeAttrTokens(input: string): string[] {\n const tokens: string[] = [];\n let i = 0;\n const n = input.length;\n while (i < n) {\n while (i < n && WHITESPACE_RE.test(input[i])) i++;\n if (i >= n) break;\n\n let token = '';\n while (i < n && !WHITESPACE_RE.test(input[i])) {\n const ch = input[i];\n const atValueStart = token === '' || token.endsWith('=');\n if ((ch === '\"' || ch === \"'\") && atValueStart) {\n token += ch;\n i++;\n while (i < n && input[i] !== ch) {\n if (input[i] === '\\\\' && i + 1 < n && (input[i + 1] === ch || input[i + 1] === '\\\\')) {\n token += input[i] + input[i + 1];\n i += 2;\n } else {\n token += input[i];\n i++;\n }\n }\n if (i < n && input[i] === ch) {\n token += ch;\n i++;\n }\n } else {\n token += ch;\n i++;\n }\n }\n if (token) tokens.push(token);\n }\n return tokens;\n}\n\n// ============================================\n// Value un/escaping\n// ============================================\n\nconst DQ_UNESCAPE_RE = /\\\\([\\\\\"])/g;\nconst SQ_UNESCAPE_RE = /\\\\([\\\\'])/g;\n\n/**\n * Strip surrounding quotes from a raw token value and unescape the active\n * quote character and backslash. Values not wrapped in matching quotes are\n * returned verbatim (including any interior quotes or backslashes).\n */\nexport function unquoteAttrValue(raw: string): string {\n if (raw.length >= 2) {\n const q = raw[0];\n if ((q === '\"' || q === \"'\") && raw.endsWith(q)) {\n const body = raw.slice(1, -1);\n return body.replace(q === '\"' ? DQ_UNESCAPE_RE : SQ_UNESCAPE_RE, '$1');\n }\n }\n return raw;\n}\n\n/**\n * Split a `key=value` token into its parts, unquoting/unescaping the value.\n * Returns null for tokens with no `=` past the first character (bare words,\n * `#id`, `.class`, or a leading `=`).\n */\nexport function splitKeyValueToken(token: string): { key: string; value: string } | null {\n const eqIdx = token.indexOf('=');\n if (eqIdx <= 0) return null;\n return { key: token.slice(0, eqIdx), value: unquoteAttrValue(token.slice(eqIdx + 1)) };\n}\n\n// ============================================\n// Serialization\n// ============================================\n\nconst NEEDS_QUOTING_RE = /[\\s\"'[\\]{}]/;\nconst DQ_ESCAPE_RE = /[\\\\\"]/g;\nconst SQ_ESCAPE_RE = /[\\\\']/g;\n\n/**\n * Whether a value must be quoted to survive tokenization and the outer\n * annotation regexes: any whitespace, quote character, bracket, or brace —\n * or an empty value, which would otherwise serialize as a bare `key=`.\n */\nexport function needsQuoting(value: string): boolean {\n return value === '' || NEEDS_QUOTING_RE.test(value);\n}\n\n/**\n * Serialize an attribute value, quoting when {@link needsQuoting} says the\n * bare form would not round-trip.\n *\n * Double quotes are canonical; a value containing a double quote (but no\n * single quote) is single-quoted instead, because markdown resolves\n * backslash escapes in text runs before our parser sees them — switching\n * the quote character round-trips through `.md` source where `\\\"` would\n * not. Backslashes and the active quote character are escaped.\n */\nexport function quoteAttrValue(value: string): string {\n if (!needsQuoting(value)) return value;\n if (value.includes('\"') && !value.includes(\"'\")) {\n return `'${value.replace(SQ_ESCAPE_RE, (ch) => `\\\\${ch}`)}'`;\n }\n return `\"${value.replace(DQ_ESCAPE_RE, (ch) => `\\\\${ch}`)}\"`;\n}\n\n/**\n * Serialize a `{[templateName key=value …]}` template annotation. The inverse\n * of {@link parseStandaloneAnnotation} / the tokenizer: emits the template\n * name (when present) followed by each `key=value` pair in insertion order,\n * quoting values via {@link quoteAttrValue}. Used to re-emit heading-less\n * standalone annotation blocks in `docToMarkdown`.\n */\nexport function serializeAnnotation(\n template: string | undefined,\n params?: Record<string, string>,\n): string {\n const parts: string[] = [];\n if (template) parts.push(template);\n for (const [key, value] of Object.entries(params ?? {})) {\n parts.push(`${key}=${quoteAttrValue(value)}`);\n }\n return `{[${parts.join(' ')}]}`;\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-S7A5D7ZJ.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
// src/versions/timestamp.ts
|
|
2
|
-
var TIMESTAMP_RE = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/;
|
|
3
|
-
function pad2(n) {
|
|
4
|
-
return n < 10 ? `0${n}` : String(n);
|
|
5
|
-
}
|
|
6
|
-
function formatVersionTimestamp(date) {
|
|
7
|
-
return String(date.getUTCFullYear()).padStart(4, "0") + pad2(date.getUTCMonth() + 1) + pad2(date.getUTCDate()) + "T" + pad2(date.getUTCHours()) + pad2(date.getUTCMinutes()) + pad2(date.getUTCSeconds()) + "Z";
|
|
8
|
-
}
|
|
9
|
-
function parseVersionTimestamp(stamp) {
|
|
10
|
-
const m = TIMESTAMP_RE.exec(stamp);
|
|
11
|
-
if (!m) return null;
|
|
12
|
-
const [, y, mo, d, h, mi, s] = m;
|
|
13
|
-
const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));
|
|
14
|
-
if (Number.isNaN(ms)) return null;
|
|
15
|
-
return new Date(ms);
|
|
16
|
-
}
|
|
17
|
-
var VERSION_TIMESTAMP_PATTERN = TIMESTAMP_RE;
|
|
18
|
-
|
|
19
|
-
// src/versions/gitignore.ts
|
|
20
|
-
var decoder = new TextDecoder();
|
|
21
|
-
var encoder = new TextEncoder();
|
|
22
|
-
var GITIGNORE_PATH = ".gitignore";
|
|
23
|
-
var VERSIONS_IGNORE_RULE = ".versions/";
|
|
24
|
-
var EQUIVALENT_VERSIONS_RULE = /^(!?)\/?\.versions\/?$/;
|
|
25
|
-
async function ensureVersionsGitIgnored(container) {
|
|
26
|
-
const data = await container.readFile(GITIGNORE_PATH);
|
|
27
|
-
const existing = data ? decoder.decode(data) : "";
|
|
28
|
-
let alreadyIgnored = false;
|
|
29
|
-
for (const line of existing.split(/\r?\n/)) {
|
|
30
|
-
const match = EQUIVALENT_VERSIONS_RULE.exec(line.trim());
|
|
31
|
-
if (match) alreadyIgnored = match[1] !== "!";
|
|
32
|
-
}
|
|
33
|
-
if (alreadyIgnored) return;
|
|
34
|
-
const newline = existing.includes("\r\n") ? "\r\n" : "\n";
|
|
35
|
-
const separator = existing.length > 0 && !existing.endsWith("\n") ? newline : "";
|
|
36
|
-
const next = `${existing}${separator}${VERSIONS_IGNORE_RULE}${newline}`;
|
|
37
|
-
await container.writeFile(GITIGNORE_PATH, encoder.encode(next), "text/plain");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/versions/snapshotStore.ts
|
|
41
|
-
var decoder2 = new TextDecoder();
|
|
42
|
-
var encoder2 = new TextEncoder();
|
|
43
|
-
function buildSnapshotPath(strategy, basename, date, collision = 0) {
|
|
44
|
-
const stamp = formatVersionTimestamp(date);
|
|
45
|
-
const suffix = collision > 0 ? `-${collision + 1}` : "";
|
|
46
|
-
return `${strategy.prefix}${basename}.${stamp}${suffix}.${strategy.extension}`;
|
|
47
|
-
}
|
|
48
|
-
function parseSnapshotPath(strategy, path) {
|
|
49
|
-
if (!path.startsWith(strategy.prefix)) return null;
|
|
50
|
-
const extension = `.${strategy.extension}`;
|
|
51
|
-
const rest = path.slice(strategy.prefix.length);
|
|
52
|
-
if (!rest.endsWith(extension)) return null;
|
|
53
|
-
const stem = rest.slice(0, -extension.length);
|
|
54
|
-
const lastDot = stem.lastIndexOf(".");
|
|
55
|
-
if (lastDot <= 0) return null;
|
|
56
|
-
const basename = stem.slice(0, lastDot);
|
|
57
|
-
const tail = stem.slice(lastDot + 1);
|
|
58
|
-
const dash = tail.indexOf("-");
|
|
59
|
-
let stamp = tail;
|
|
60
|
-
let collision = 0;
|
|
61
|
-
if (dash >= 0) {
|
|
62
|
-
stamp = tail.slice(0, dash);
|
|
63
|
-
const suffix = Number(tail.slice(dash + 1));
|
|
64
|
-
if (!Number.isInteger(suffix) || suffix < 2) return null;
|
|
65
|
-
collision = suffix - 1;
|
|
66
|
-
}
|
|
67
|
-
const timestamp = parseVersionTimestamp(stamp);
|
|
68
|
-
return timestamp ? { basename, timestamp, collision } : null;
|
|
69
|
-
}
|
|
70
|
-
async function listSnapshots(container, strategy, basename) {
|
|
71
|
-
const entries = await container.listFiles(strategy.prefix);
|
|
72
|
-
const versions = [];
|
|
73
|
-
for (const entry of entries) {
|
|
74
|
-
const parsed = parseSnapshotPath(strategy, entry.path);
|
|
75
|
-
if (!parsed || basename !== void 0 && parsed.basename !== basename) continue;
|
|
76
|
-
versions.push({
|
|
77
|
-
path: entry.path,
|
|
78
|
-
basename: parsed.basename,
|
|
79
|
-
timestamp: parsed.timestamp,
|
|
80
|
-
size: entry.size,
|
|
81
|
-
collision: parsed.collision
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
versions.sort((a, b) => {
|
|
85
|
-
const timestampDelta = b.timestamp.getTime() - a.timestamp.getTime();
|
|
86
|
-
return timestampDelta || b.collision - a.collision;
|
|
87
|
-
});
|
|
88
|
-
return versions;
|
|
89
|
-
}
|
|
90
|
-
async function readTextSnapshot(container, version) {
|
|
91
|
-
const path = typeof version === "string" ? version : version.path;
|
|
92
|
-
const data = await container.readFile(path);
|
|
93
|
-
return data ? decoder2.decode(data) : null;
|
|
94
|
-
}
|
|
95
|
-
async function saveTextSnapshot(container, strategy, options) {
|
|
96
|
-
const versions = await listSnapshots(container, strategy, options.basename);
|
|
97
|
-
if (!options.force && versions.length > 0) {
|
|
98
|
-
const existing = await readTextSnapshot(container, versions[0]);
|
|
99
|
-
if (existing === options.content) {
|
|
100
|
-
await ensureVersionsGitIgnored(container);
|
|
101
|
-
return { saved: false, version: null, reason: "unchanged" };
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
105
|
-
let collision = 0;
|
|
106
|
-
let path = buildSnapshotPath(strategy, options.basename, now, collision);
|
|
107
|
-
while (await container.exists(path)) {
|
|
108
|
-
collision += 1;
|
|
109
|
-
path = buildSnapshotPath(strategy, options.basename, now, collision);
|
|
110
|
-
}
|
|
111
|
-
const data = encoder2.encode(options.content);
|
|
112
|
-
await ensureVersionsGitIgnored(container);
|
|
113
|
-
await container.writeFile(path, data, options.mimeType);
|
|
114
|
-
return {
|
|
115
|
-
saved: true,
|
|
116
|
-
version: {
|
|
117
|
-
path,
|
|
118
|
-
basename: options.basename,
|
|
119
|
-
timestamp: now,
|
|
120
|
-
size: data.byteLength,
|
|
121
|
-
collision
|
|
122
|
-
},
|
|
123
|
-
reason: "saved"
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
async function pruneSnapshots(container, strategy, policy, basename) {
|
|
127
|
-
const versions = await listSnapshots(container, strategy, basename);
|
|
128
|
-
let toDelete;
|
|
129
|
-
if (policy.type === "keep-last-n") {
|
|
130
|
-
toDelete = versions.slice(Math.max(0, Math.floor(policy.n)));
|
|
131
|
-
} else if (policy.type === "older-than") {
|
|
132
|
-
const cutoff = policy.date.getTime();
|
|
133
|
-
toDelete = versions.filter((version) => version.timestamp.getTime() < cutoff);
|
|
134
|
-
} else {
|
|
135
|
-
toDelete = versions.filter((version) => !policy.keep(version, versions));
|
|
136
|
-
}
|
|
137
|
-
await removeSnapshots(container, toDelete);
|
|
138
|
-
return toDelete;
|
|
139
|
-
}
|
|
140
|
-
async function coalesceSnapshots(container, strategy, options = {}, basename) {
|
|
141
|
-
const windowMs = options.windowMs ?? 6e4;
|
|
142
|
-
const versions = await listSnapshots(container, strategy, basename);
|
|
143
|
-
const toDelete = [];
|
|
144
|
-
if (versions.length > 0) {
|
|
145
|
-
let anchor = versions[0];
|
|
146
|
-
for (let index = 1; index < versions.length; index++) {
|
|
147
|
-
const candidate = versions[index];
|
|
148
|
-
if (anchor.timestamp.getTime() - candidate.timestamp.getTime() <= windowMs) {
|
|
149
|
-
toDelete.push(candidate);
|
|
150
|
-
} else {
|
|
151
|
-
anchor = candidate;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
await removeSnapshots(container, toDelete);
|
|
156
|
-
return toDelete;
|
|
157
|
-
}
|
|
158
|
-
async function removeSnapshots(container, versions) {
|
|
159
|
-
for (const version of versions) await container.removeFile(version.path);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export {
|
|
163
|
-
formatVersionTimestamp,
|
|
164
|
-
parseVersionTimestamp,
|
|
165
|
-
VERSION_TIMESTAMP_PATTERN,
|
|
166
|
-
buildSnapshotPath,
|
|
167
|
-
parseSnapshotPath,
|
|
168
|
-
listSnapshots,
|
|
169
|
-
readTextSnapshot,
|
|
170
|
-
saveTextSnapshot,
|
|
171
|
-
pruneSnapshots,
|
|
172
|
-
coalesceSnapshots
|
|
173
|
-
};
|
|
174
|
-
//# sourceMappingURL=chunk-S7A5D7ZJ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/versions/timestamp.ts","../src/versions/gitignore.ts","../src/versions/snapshotStore.ts"],"sourcesContent":["/**\n * Sortable, human-readable timestamps for version filenames.\n *\n * Format: ISO 8601 basic, UTC, second precision, `Z` suffix —\n * e.g. `20260430T152030Z`. Lexicographic order matches chronological order,\n * so `listFiles` returns versions in time order without parsing.\n */\n\nconst TIMESTAMP_RE = /^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z$/;\n\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : String(n);\n}\n\n/**\n * Format a Date as a sortable, filename-safe UTC timestamp string.\n */\nexport function formatVersionTimestamp(date: Date): string {\n return (\n String(date.getUTCFullYear()).padStart(4, '0') +\n pad2(date.getUTCMonth() + 1) +\n pad2(date.getUTCDate()) +\n 'T' +\n pad2(date.getUTCHours()) +\n pad2(date.getUTCMinutes()) +\n pad2(date.getUTCSeconds()) +\n 'Z'\n );\n}\n\n/**\n * Parse a version timestamp string back to a Date. Returns null if the input\n * doesn't match the expected format.\n */\nexport function parseVersionTimestamp(stamp: string): Date | null {\n const m = TIMESTAMP_RE.exec(stamp);\n if (!m) return null;\n const [, y, mo, d, h, mi, s] = m;\n const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));\n if (Number.isNaN(ms)) return null;\n return new Date(ms);\n}\n\n/**\n * The pattern a valid version timestamp matches. Exported so other modules\n * can validate filenames without re-deriving the regex.\n */\nexport const VERSION_TIMESTAMP_PATTERN = TIMESTAMP_RE;\n","/**\n * Keep version snapshots out of Git when a ContentContainer is persisted\n * as a real folder (typically a `<basename>_files/` sidecar).\n */\n\nimport type { ContentContainer } from '../storage/ContentContainer.js';\n\nconst decoder = new TextDecoder();\nconst encoder = new TextEncoder();\n\nconst GITIGNORE_PATH = '.gitignore';\nconst VERSIONS_IGNORE_RULE = '.versions/';\nconst EQUIVALENT_VERSIONS_RULE = /^(!?)\\/?\\.versions\\/?$/;\n\n/**\n * Ensure the container-root `.gitignore` ignores its `.versions/` folder.\n * Existing rules and newline style are preserved.\n */\nexport async function ensureVersionsGitIgnored(container: ContentContainer): Promise<void> {\n const data = await container.readFile(GITIGNORE_PATH);\n const existing = data ? decoder.decode(data) : '';\n\n let alreadyIgnored = false;\n for (const line of existing.split(/\\r?\\n/)) {\n const match = EQUIVALENT_VERSIONS_RULE.exec(line.trim());\n if (match) alreadyIgnored = match[1] !== '!';\n }\n if (alreadyIgnored) return;\n\n const newline = existing.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const separator = existing.length > 0 && !existing.endsWith('\\n') ? newline : '';\n const next = `${existing}${separator}${VERSIONS_IGNORE_RULE}${newline}`;\n await container.writeFile(GITIGNORE_PATH, encoder.encode(next), 'text/plain');\n}\n","/**\n * Internal storage engine shared by document and image-editor histories.\n * Public APIs keep their domain-specific names and result reasons; this module\n * owns the mechanics that must behave identically across both histories.\n */\n\nimport type { ContentContainer } from '../storage/ContentContainer.js';\nimport { ensureVersionsGitIgnored } from './gitignore.js';\nimport { formatVersionTimestamp, parseVersionTimestamp } from './timestamp.js';\nimport type { CoalesceOptions, PrunePolicy, Version } from './types.js';\n\nconst decoder = new TextDecoder();\nconst encoder = new TextEncoder();\n\nexport interface SnapshotPathStrategy {\n readonly prefix: string;\n readonly extension: string;\n}\n\nexport interface SaveTextSnapshotOptions {\n content: string;\n basename: string;\n mimeType: string;\n now?: Date;\n force?: boolean;\n}\n\nexport interface SaveTextSnapshotResult {\n saved: boolean;\n version: Version | null;\n reason: 'saved' | 'unchanged';\n}\n\nexport function buildSnapshotPath(\n strategy: SnapshotPathStrategy,\n basename: string,\n date: Date,\n collision = 0,\n): string {\n const stamp = formatVersionTimestamp(date);\n const suffix = collision > 0 ? `-${collision + 1}` : '';\n return `${strategy.prefix}${basename}.${stamp}${suffix}.${strategy.extension}`;\n}\n\nexport function parseSnapshotPath(\n strategy: SnapshotPathStrategy,\n path: string,\n): { basename: string; timestamp: Date; collision: number } | null {\n if (!path.startsWith(strategy.prefix)) return null;\n const extension = `.${strategy.extension}`;\n const rest = path.slice(strategy.prefix.length);\n if (!rest.endsWith(extension)) return null;\n const stem = rest.slice(0, -extension.length);\n const lastDot = stem.lastIndexOf('.');\n if (lastDot <= 0) return null;\n\n const basename = stem.slice(0, lastDot);\n const tail = stem.slice(lastDot + 1);\n const dash = tail.indexOf('-');\n let stamp = tail;\n let collision = 0;\n if (dash >= 0) {\n stamp = tail.slice(0, dash);\n const suffix = Number(tail.slice(dash + 1));\n if (!Number.isInteger(suffix) || suffix < 2) return null;\n collision = suffix - 1;\n }\n\n const timestamp = parseVersionTimestamp(stamp);\n return timestamp ? { basename, timestamp, collision } : null;\n}\n\nexport async function listSnapshots(\n container: ContentContainer,\n strategy: SnapshotPathStrategy,\n basename?: string,\n): Promise<Version[]> {\n const entries = await container.listFiles(strategy.prefix);\n const versions: Version[] = [];\n for (const entry of entries) {\n const parsed = parseSnapshotPath(strategy, entry.path);\n if (!parsed || (basename !== undefined && parsed.basename !== basename)) continue;\n versions.push({\n path: entry.path,\n basename: parsed.basename,\n timestamp: parsed.timestamp,\n size: entry.size,\n collision: parsed.collision,\n });\n }\n\n versions.sort((a, b) => {\n const timestampDelta = b.timestamp.getTime() - a.timestamp.getTime();\n return timestampDelta || b.collision - a.collision;\n });\n return versions;\n}\n\nexport async function readTextSnapshot(\n container: ContentContainer,\n version: Version | string,\n): Promise<string | null> {\n const path = typeof version === 'string' ? version : version.path;\n const data = await container.readFile(path);\n return data ? decoder.decode(data) : null;\n}\n\nexport async function saveTextSnapshot(\n container: ContentContainer,\n strategy: SnapshotPathStrategy,\n options: SaveTextSnapshotOptions,\n): Promise<SaveTextSnapshotResult> {\n const versions = await listSnapshots(container, strategy, options.basename);\n if (!options.force && versions.length > 0) {\n const existing = await readTextSnapshot(container, versions[0]!);\n if (existing === options.content) {\n // Backfill containers written before history owned its ignore rule.\n await ensureVersionsGitIgnored(container);\n return { saved: false, version: null, reason: 'unchanged' };\n }\n }\n\n const now = options.now ?? new Date();\n let collision = 0;\n let path = buildSnapshotPath(strategy, options.basename, now, collision);\n while (await container.exists(path)) {\n collision += 1;\n path = buildSnapshotPath(strategy, options.basename, now, collision);\n }\n\n const data = encoder.encode(options.content);\n await ensureVersionsGitIgnored(container);\n await container.writeFile(path, data, options.mimeType);\n return {\n saved: true,\n version: {\n path,\n basename: options.basename,\n timestamp: now,\n size: data.byteLength,\n collision,\n },\n reason: 'saved',\n };\n}\n\nexport async function pruneSnapshots(\n container: ContentContainer,\n strategy: SnapshotPathStrategy,\n policy: PrunePolicy,\n basename?: string,\n): Promise<Version[]> {\n const versions = await listSnapshots(container, strategy, basename);\n let toDelete: Version[];\n\n if (policy.type === 'keep-last-n') {\n toDelete = versions.slice(Math.max(0, Math.floor(policy.n)));\n } else if (policy.type === 'older-than') {\n const cutoff = policy.date.getTime();\n toDelete = versions.filter((version) => version.timestamp.getTime() < cutoff);\n } else {\n toDelete = versions.filter((version) => !policy.keep(version, versions));\n }\n\n await removeSnapshots(container, toDelete);\n return toDelete;\n}\n\nexport async function coalesceSnapshots(\n container: ContentContainer,\n strategy: SnapshotPathStrategy,\n options: CoalesceOptions = {},\n basename?: string,\n): Promise<Version[]> {\n const windowMs = options.windowMs ?? 60_000;\n const versions = await listSnapshots(container, strategy, basename);\n const toDelete: Version[] = [];\n\n // Compare against the last kept snapshot. Pairwise comparison against a\n // deleted neighbor would chain through a dense run and over-prune it.\n if (versions.length > 0) {\n let anchor = versions[0]!;\n for (let index = 1; index < versions.length; index++) {\n const candidate = versions[index]!;\n if (anchor.timestamp.getTime() - candidate.timestamp.getTime() <= windowMs) {\n toDelete.push(candidate);\n } else {\n anchor = candidate;\n }\n }\n }\n\n await removeSnapshots(container, toDelete);\n return toDelete;\n}\n\nasync function removeSnapshots(\n container: ContentContainer,\n versions: readonly Version[],\n): Promise<void> {\n for (const version of versions) await container.removeFile(version.path);\n}\n"],"mappings":"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","names":["decoder","encoder"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/doc/utils/shapeGeometry.ts"],"sourcesContent":["/**\n * shapeGeometry — pure SVG geometry for the drawing/diagram shape vocabulary.\n *\n * Three concerns, all deterministic and dependency-free:\n * - `shapePath(kind, x, y, w, h)` → an SVG path `d` for a named shape inscribed\n * in a bounding box. Returns null for the natively-rendered primitives\n * (rect / circle / line / text / path) so the caller can keep using a\n * `ShapeLayer` for those; everything else becomes a computed `PathLayer`.\n * - `markerPath(style, dir)` → the `<marker>` path for a line end-style, in the\n * standard 0–10 marker viewBox (shared by the SSR `PathLayer` renderer and the\n * editor's edge renderer).\n * - `connectorPath(routing, start, end)` + `snapEndpoints(a, b)` → the line/edge\n * geometry between two shapes (straight / orthogonal / curved), lifted from the\n * diagram template so drawings and diagrams draw identical connectors. The older\n * `clipEndpoints(a, b)` helper remains for free-angle clipping, but rendered\n * connectors use snapped ports so authored lines land on stable attachment points.\n */\n\nimport type { MarkerStyle } from '../../schemas/Doc.js';\n\n// ============================================\n// Shapes\n// ============================================\n\n/** Connector routing styles. */\nexport type ConnectorRouting = 'straight' | 'orthogonal' | 'curved';\n\n/**\n * Shape kinds rendered as computed paths (everything except the primitives the\n * renderer draws natively). Used by templates to decide ShapeLayer vs PathLayer.\n */\nexport const PATH_SHAPE_KINDS = new Set([\n 'triangle',\n 'right-triangle',\n 'diamond',\n 'pentagon',\n 'hexagon',\n 'octagon',\n 'star',\n 'star4',\n 'star6',\n 'parallelogram',\n 'trapezoid',\n 'plus',\n 'chevron',\n 'arrow-right',\n 'arrow-left',\n 'arrow-up',\n 'arrow-down',\n 'double-arrow',\n 'callout',\n 'cylinder',\n 'cloud',\n 'heart',\n 'lightning',\n]);\n\n/** Round to 1 decimal for compact, stable path output. */\nfunction r(n: number): number {\n return Math.round(n * 10) / 10;\n}\n\n/** Close a list of points into an `M … L … Z` polygon path. */\nfunction poly(points: ReadonlyArray<readonly [number, number]>): string {\n if (points.length === 0) return '';\n const [first, ...rest] = points;\n const head = `M ${r(first[0])} ${r(first[1])}`;\n const tail = rest.map(([x, y]) => `L ${r(x)} ${r(y)}`).join(' ');\n return `${head} ${tail} Z`;\n}\n\n/** Regular polygon points inscribed in the box ellipse, `rotation` in radians. */\nfunction regular(\n cx: number,\n cy: number,\n rx: number,\n ry: number,\n sides: number,\n rotation: number,\n): Array<[number, number]> {\n const pts: Array<[number, number]> = [];\n for (let i = 0; i < sides; i++) {\n const a = rotation + (i * 2 * Math.PI) / sides;\n pts.push([cx + rx * Math.cos(a), cy + ry * Math.sin(a)]);\n }\n return pts;\n}\n\n/** Star points alternating outer/inner radius. */\nfunction star(\n cx: number,\n cy: number,\n rx: number,\n ry: number,\n spikes: number,\n innerRatio: number,\n): Array<[number, number]> {\n const pts: Array<[number, number]> = [];\n const rot = -Math.PI / 2; // first point up\n for (let i = 0; i < spikes * 2; i++) {\n const outer = i % 2 === 0;\n const a = rot + (i * Math.PI) / spikes;\n const fx = outer ? rx : rx * innerRatio;\n const fy = outer ? ry : ry * innerRatio;\n pts.push([cx + fx * Math.cos(a), cy + fy * Math.sin(a)]);\n }\n return pts;\n}\n\n/**\n * SVG path `d` for a named shape inscribed in `[x, y, w, h]`, or null when the\n * kind is rendered natively (rect / circle / line / text / path / arrow).\n */\nexport function shapePath(kind: string, x: number, y: number, w: number, h: number): string | null {\n const cx = x + w / 2;\n const cy = y + h / 2;\n const rx = w / 2;\n const ry = h / 2;\n const right = x + w;\n const bottom = y + h;\n\n switch (kind) {\n case 'triangle':\n return poly([\n [cx, y],\n [right, bottom],\n [x, bottom],\n ]);\n case 'right-triangle':\n return poly([\n [x, y],\n [x, bottom],\n [right, bottom],\n ]);\n case 'diamond':\n return poly([\n [cx, y],\n [right, cy],\n [cx, bottom],\n [x, cy],\n ]);\n case 'pentagon':\n return poly(regular(cx, cy, rx, ry, 5, -Math.PI / 2));\n case 'hexagon': {\n // Flat top/bottom hexagon: inset the left/right vertices.\n const inset = w * 0.25;\n return poly([\n [x + inset, y],\n [right - inset, y],\n [right, cy],\n [right - inset, bottom],\n [x + inset, bottom],\n [x, cy],\n ]);\n }\n case 'octagon': {\n const cut = Math.min(w, h) * 0.29;\n return poly([\n [x + cut, y],\n [right - cut, y],\n [right, y + cut],\n [right, bottom - cut],\n [right - cut, bottom],\n [x + cut, bottom],\n [x, bottom - cut],\n [x, y + cut],\n ]);\n }\n case 'star':\n return poly(star(cx, cy, rx, ry, 5, 0.4));\n case 'star4':\n return poly(star(cx, cy, rx, ry, 4, 0.4));\n case 'star6':\n return poly(star(cx, cy, rx, ry, 6, 0.5));\n case 'parallelogram': {\n const off = w * 0.25;\n return poly([\n [x + off, y],\n [right, y],\n [right - off, bottom],\n [x, bottom],\n ]);\n }\n case 'trapezoid': {\n const off = w * 0.25;\n return poly([\n [x + off, y],\n [right - off, y],\n [right, bottom],\n [x, bottom],\n ]);\n }\n case 'plus': {\n const t = Math.min(w, h) / 3; // arm thickness\n const lx = x + (w - t) / 2;\n const rxp = lx + t;\n const ty = y + (h - t) / 2;\n const byp = ty + t;\n return poly([\n [lx, y],\n [rxp, y],\n [rxp, ty],\n [right, ty],\n [right, byp],\n [rxp, byp],\n [rxp, bottom],\n [lx, bottom],\n [lx, byp],\n [x, byp],\n [x, ty],\n [lx, ty],\n ]);\n }\n case 'chevron': {\n const notch = w * 0.25;\n return poly([\n [x, y],\n [right - notch, y],\n [right, cy],\n [right - notch, bottom],\n [x, bottom],\n [x + notch, cy],\n ]);\n }\n case 'arrow-right':\n return blockArrow(x, y, w, h, 'right');\n case 'arrow-left':\n return blockArrow(x, y, w, h, 'left');\n case 'arrow-up':\n return blockArrow(x, y, w, h, 'up');\n case 'arrow-down':\n return blockArrow(x, y, w, h, 'down');\n case 'double-arrow': {\n const head = w * 0.3;\n const bodyT = h * 0.4;\n const ty = cy - bodyT / 2;\n const by = cy + bodyT / 2;\n return poly([\n [x, cy],\n [x + head, y],\n [x + head, ty],\n [right - head, ty],\n [right - head, y],\n [right, cy],\n [right - head, bottom],\n [right - head, by],\n [x + head, by],\n [x + head, bottom],\n ]);\n }\n case 'callout': {\n // Speech bubble: rounded-ish body (square corners for path simplicity)\n // with a tail off the lower-left.\n const bodyH = h * 0.78;\n const bb = y + bodyH;\n const tailX = x + w * 0.25;\n return `M ${r(x)} ${r(y)} L ${r(right)} ${r(y)} L ${r(right)} ${r(bb)} L ${r(tailX + w * 0.12)} ${r(bb)} L ${r(tailX)} ${r(bottom)} L ${r(tailX - w * 0.02)} ${r(bb)} L ${r(x)} ${r(bb)} Z`;\n }\n case 'cylinder': {\n const ell = h * 0.16; // ellipse half-height\n return (\n `M ${r(x)} ${r(y + ell)} ` +\n `A ${r(rx)} ${r(ell)} 0 0 1 ${r(right)} ${r(y + ell)} ` +\n `L ${r(right)} ${r(bottom - ell)} ` +\n `A ${r(rx)} ${r(ell)} 0 0 1 ${r(x)} ${r(bottom - ell)} ` +\n `Z ` +\n // top ellipse outline\n `M ${r(x)} ${r(y + ell)} ` +\n `A ${r(rx)} ${r(ell)} 0 0 0 ${r(right)} ${r(y + ell)}`\n );\n }\n case 'cloud': {\n // Four bumps along the top + a flat-ish bottom, via arcs.\n const b = h * 0.5;\n return (\n `M ${r(x + w * 0.2)} ${r(bottom)} ` +\n `A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.15)} ${r(cy)} ` +\n `A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.45)} ${r(y + h * 0.2)} ` +\n `A ${r(w * 0.2)} ${r(b)} 0 0 1 ${r(x + w * 0.8)} ${r(y + h * 0.25)} ` +\n `A ${r(w * 0.18)} ${r(b)} 0 0 1 ${r(x + w * 0.85)} ${r(bottom)} ` +\n `Z`\n );\n }\n case 'heart': {\n const topY = y + h * 0.3;\n return (\n `M ${r(cx)} ${r(bottom)} ` +\n `C ${r(x - w * 0.1)} ${r(cy)} ${r(x)} ${r(y)} ${r(cx)} ${r(topY)} ` +\n `C ${r(right)} ${r(y)} ${r(right + w * 0.1)} ${r(cy)} ${r(cx)} ${r(bottom)} ` +\n `Z`\n );\n }\n case 'lightning':\n return poly([\n [x + w * 0.55, y],\n [x + w * 0.2, y + h * 0.55],\n [x + w * 0.45, y + h * 0.55],\n [x + w * 0.3, bottom],\n [right, y + h * 0.4],\n [x + w * 0.55, y + h * 0.4],\n ]);\n default:\n return null;\n }\n}\n\n/** Block arrow (body rectangle + triangular head) pointing in `dir`. */\nfunction blockArrow(\n x: number,\n y: number,\n w: number,\n h: number,\n dir: 'right' | 'left' | 'up' | 'down',\n): string {\n const right = x + w;\n const bottom = y + h;\n const cx = x + w / 2;\n const cy = y + h / 2;\n if (dir === 'right' || dir === 'left') {\n const head = w * 0.4;\n const bodyT = h * 0.5;\n const ty = cy - bodyT / 2;\n const by = cy + bodyT / 2;\n if (dir === 'right') {\n return poly([\n [x, ty],\n [right - head, ty],\n [right - head, y],\n [right, cy],\n [right - head, bottom],\n [right - head, by],\n [x, by],\n ]);\n }\n return poly([\n [right, ty],\n [x + head, ty],\n [x + head, y],\n [x, cy],\n [x + head, bottom],\n [x + head, by],\n [right, by],\n ]);\n }\n const head = h * 0.4;\n const bodyT = w * 0.5;\n const lx = cx - bodyT / 2;\n const rxp = cx + bodyT / 2;\n if (dir === 'down') {\n return poly([\n [lx, y],\n [rxp, y],\n [rxp, bottom - head],\n [right, bottom - head],\n [cx, bottom],\n [x, bottom - head],\n [lx, bottom - head],\n ]);\n }\n return poly([\n [lx, bottom],\n [rxp, bottom],\n [rxp, y + head],\n [right, y + head],\n [cx, y],\n [x, y + head],\n [lx, y + head],\n ]);\n}\n\n// ============================================\n// End markers\n// ============================================\n\n/**\n * The `<marker>` path for a line end-style, in the 0–10 marker viewBox.\n * `dir` selects orientation (`end` points along +x; `start` is mirrored).\n * Returns null for `'none'`. `filled` shapes paint with the stroke color;\n * non-filled (`open`) draw as a stroked outline.\n */\nexport function markerPath(\n style: MarkerStyle,\n dir: 'start' | 'end',\n): { d: string; filled: boolean } | null {\n const mirror = dir === 'start';\n const mx = (n: number) => (mirror ? 10 - n : n);\n const tri = (closed: boolean) => `M ${mx(0)} 0 L ${mx(10)} 5 L ${mx(0)} 10${closed ? ' z' : ''}`;\n switch (style) {\n case 'arrow':\n return { d: tri(true), filled: true };\n case 'open':\n return { d: tri(false), filled: false };\n case 'diamond':\n return { d: 'M 0 5 L 5 0 L 10 5 L 5 10 z', filled: true };\n case 'square':\n return { d: 'M 1 1 L 9 1 L 9 9 L 1 9 z', filled: true };\n case 'circle':\n return { d: 'M 0 5 A 5 5 0 1 0 10 5 A 5 5 0 1 0 0 5 z', filled: true };\n case 'none':\n default:\n return null;\n }\n}\n\n// ============================================\n// Connector routing\n// ============================================\n\nexport interface ClipBox {\n cx: number;\n cy: number;\n rx: number;\n ry: number;\n}\n\nexport type ConnectorPort =\n | 'top'\n | 'right'\n | 'bottom'\n | 'left'\n | 'top-left'\n | 'top-right'\n | 'bottom-right'\n | 'bottom-left';\n\nexport interface ConnectorSnapPoint {\n port: ConnectorPort;\n x: number;\n y: number;\n}\n\nexport interface ConnectorAnchor {\n side: 'top' | 'right' | 'bottom' | 'left';\n offset: number;\n}\n\n/** Intersection of the center-to-center line with `from`'s bounding box. */\nexport function clipPoint(from: ClipBox, to: ClipBox): { x: number; y: number } {\n const dx = to.cx - from.cx;\n const dy = to.cy - from.cy;\n if (dx === 0 && dy === 0) return { x: from.cx, y: from.cy };\n const sx = Math.abs(dx) > 0 ? from.rx / Math.abs(dx) : Infinity;\n const sy = Math.abs(dy) > 0 ? from.ry / Math.abs(dy) : Infinity;\n const s = Math.min(sx, sy);\n return { x: from.cx + dx * s, y: from.cy + dy * s };\n}\n\n/** Clip both endpoints of an edge to the two shapes it joins. */\nexport function clipEndpoints(\n a: ClipBox,\n b: ClipBox,\n): { start: { x: number; y: number }; end: { x: number; y: number } } {\n return { start: clipPoint(a, b), end: clipPoint(b, a) };\n}\n\n/** The stable connector ports exposed around a shape/card box. */\nexport function snapPoints(box: ClipBox): ConnectorSnapPoint[] {\n const left = box.cx - box.rx;\n const right = box.cx + box.rx;\n const top = box.cy - box.ry;\n const bottom = box.cy + box.ry;\n return [\n { port: 'top', x: box.cx, y: top },\n { port: 'right', x: right, y: box.cy },\n { port: 'bottom', x: box.cx, y: bottom },\n { port: 'left', x: left, y: box.cy },\n { port: 'top-left', x: left, y: top },\n { port: 'top-right', x: right, y: top },\n { port: 'bottom-right', x: right, y: bottom },\n { port: 'bottom-left', x: left, y: bottom },\n ];\n}\n\n/** Resolve a normalized authored anchor to a concrete point on a box. */\nexport function anchorPoint(box: ClipBox, anchor: ConnectorAnchor): ConnectorSnapPoint {\n const offset = Math.max(0, Math.min(1, anchor.offset));\n const left = box.cx - box.rx;\n const right = box.cx + box.rx;\n const top = box.cy - box.ry;\n const bottom = box.cy + box.ry;\n switch (anchor.side) {\n case 'top':\n return { port: 'top', x: left + (right - left) * offset, y: top };\n case 'right':\n return { port: 'right', x: right, y: top + (bottom - top) * offset };\n case 'bottom':\n return { port: 'bottom', x: left + (right - left) * offset, y: bottom };\n case 'left':\n return { port: 'left', x: left, y: top + (bottom - top) * offset };\n }\n}\n\n/** Nearest stable connector port on `box` to an arbitrary point. */\nexport function nearestSnapPoint(\n box: ClipBox,\n point: { x: number; y: number },\n): ConnectorSnapPoint {\n let best = snapPoints(box)[0];\n let bestScore = Number.POSITIVE_INFINITY;\n for (const candidate of snapPoints(box)) {\n const score = distanceSq(candidate, point) + portTieBreak(candidate.port);\n if (score < bestScore) {\n best = candidate;\n bestScore = score;\n }\n }\n return best;\n}\n\n/**\n * Pick the pair of connector ports, one on each box, with the shortest\n * distance between them. This gives stable attachments to side/corner handles\n * instead of arbitrary center-ray intersections.\n */\nexport function snapEndpoints(\n a: ClipBox,\n b: ClipBox,\n): { start: ConnectorSnapPoint; end: ConnectorSnapPoint } {\n let bestStart = snapPoints(a)[0];\n let bestEnd = snapPoints(b)[0];\n let bestScore = Number.POSITIVE_INFINITY;\n for (const start of snapPoints(a)) {\n for (const end of snapPoints(b)) {\n const score = distanceSq(start, end) + portTieBreak(start.port) + portTieBreak(end.port);\n if (score < bestScore) {\n bestStart = start;\n bestEnd = end;\n bestScore = score;\n }\n }\n }\n return { start: bestStart, end: bestEnd };\n}\n\nfunction distanceSq(a: { x: number; y: number }, b: { x: number; y: number }): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\nfunction portTieBreak(port: ConnectorPort): number {\n return port.includes('-') ? 0.02 : 0;\n}\n\n/** Build the SVG `d` for a connector between two clipped endpoints. */\nexport function connectorPath(\n routing: ConnectorRouting,\n start: { x: number; y: number; port?: ConnectorPort },\n end: { x: number; y: number; port?: ConnectorPort },\n): string {\n if (routing === 'straight') {\n return `M ${r(start.x)} ${r(start.y)} L ${r(end.x)} ${r(end.y)}`;\n }\n if (routing === 'orthogonal') {\n const startAxis = portAxis(start.port);\n const endAxis = portAxis(end.port);\n if (startAxis === 'vertical' && endAxis === 'vertical') {\n const midY = (start.y + end.y) / 2;\n return `M ${r(start.x)} ${r(start.y)} L ${r(start.x)} ${r(midY)} L ${r(end.x)} ${r(midY)} L ${r(end.x)} ${r(end.y)}`;\n }\n if (startAxis === 'vertical' && endAxis === 'horizontal') {\n return `M ${r(start.x)} ${r(start.y)} L ${r(start.x)} ${r(end.y)} L ${r(end.x)} ${r(end.y)}`;\n }\n if (startAxis === 'horizontal' && endAxis === 'vertical') {\n return `M ${r(start.x)} ${r(start.y)} L ${r(end.x)} ${r(start.y)} L ${r(end.x)} ${r(end.y)}`;\n }\n const midX = (start.x + end.x) / 2;\n return `M ${r(start.x)} ${r(start.y)} L ${r(midX)} ${r(start.y)} L ${r(midX)} ${r(end.y)} L ${r(end.x)} ${r(end.y)}`;\n }\n // curved\n const distance = Math.hypot(end.x - start.x, end.y - start.y);\n const cp = Math.max(40, distance / 2);\n const startVector = portVector(start.port, { x: 1, y: 0 });\n const endVector = portVector(end.port, { x: -1, y: 0 });\n return `M ${r(start.x)} ${r(start.y)} C ${r(start.x + startVector.x * cp)} ${r(start.y + startVector.y * cp)}, ${r(end.x + endVector.x * cp)} ${r(end.y + endVector.y * cp)}, ${r(end.x)} ${r(end.y)}`;\n}\n\nfunction portAxis(port: ConnectorPort | undefined): 'horizontal' | 'vertical' | null {\n if (port === 'top' || port === 'bottom') return 'vertical';\n if (port === 'left' || port === 'right') return 'horizontal';\n return null;\n}\n\nfunction portVector(\n port: ConnectorPort | undefined,\n fallback: { x: number; y: number },\n): { x: number; y: number } {\n switch (port) {\n case 'top':\n return { x: 0, y: -1 };\n case 'right':\n return { x: 1, y: 0 };\n case 'bottom':\n return { x: 0, y: 1 };\n case 'left':\n return { x: -1, y: 0 };\n default:\n return fallback;\n }\n}\n\n/** Map a `lineStyle` (solid|dashed|dotted) to an SVG `stroke-dasharray`. */\nexport function lineStyleDasharray(style: string | undefined): string | undefined {\n switch (style?.toLowerCase()) {\n case 'dashed':\n case 'dash':\n return '8 6';\n case 'dotted':\n case 'dot':\n return '2 6';\n default:\n return undefined;\n }\n}\n"],"mappings":"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","names":["head","bodyT"]}
|
package/dist/chunk-V543AI2F.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-V543AI2F.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage/MemoryStorageAdapter.ts","../src/storage/LocalStorageAdapter.ts","../src/storage/LocalForageAdapter.ts","../src/storage/ContentContainer.ts","../src/storage/ScopedContentContainer.ts","../src/storage/MediaProviderFromContainer.ts"],"sourcesContent":["/**\n * In-memory storage adapter for testing and SSR environments.\n * Data does not persist across page reloads.\n */\n\nimport type { StorageAdapter } from './Storage.js';\n\nexport class MemoryStorageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private store = new Map<string, string>();\n\n async get<T>(key: string): Promise<T | null> {\n const val = this.store.get(key);\n if (val === undefined) return null;\n try {\n return JSON.parse(val) as T;\n } catch {\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n this.store.set(key, JSON.stringify(value));\n }\n\n async remove(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n async keys(): Promise<string[]> {\n return Array.from(this.store.keys());\n }\n}\n","/**\n * localStorage adapter for web browsers.\n * Supports a configurable key prefix to namespace stored data.\n */\n\nimport type { StorageAdapter } from './Storage.js';\n\nexport class LocalStorageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private prefix: string;\n\n constructor(prefix = '') {\n this.prefix = prefix;\n }\n\n async get<T>(key: string): Promise<T | null> {\n try {\n const raw = localStorage.getItem(this.prefix + key);\n if (raw === null) return null;\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n localStorage.setItem(this.prefix + key, JSON.stringify(value));\n }\n\n async remove(key: string): Promise<void> {\n localStorage.removeItem(this.prefix + key);\n }\n\n async clear(): Promise<void> {\n const keysToRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(this.prefix)) {\n keysToRemove.push(k);\n }\n }\n for (const k of keysToRemove) {\n localStorage.removeItem(k);\n }\n }\n\n async keys(): Promise<string[]> {\n const result: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(this.prefix)) {\n result.push(k.slice(this.prefix.length));\n }\n }\n return result;\n }\n}\n","/**\n * LocalForage adapter - IndexedDB storage with automatic fallbacks.\n *\n * Uses localforage which provides IndexedDB storage with automatic\n * fallbacks to WebSQL and localStorage. This allows storing much more\n * data than raw localStorage (which is limited to ~5MB).\n *\n * Unlike LocalStorageAdapter, this adapter can store binary data\n * (ArrayBuffer, Blob, Uint8Array) natively without JSON serialization.\n *\n * Ported from qualla-internal/shared/services/StorageAdapters.ts with\n * configurable store name and key prefix.\n */\n\nimport localforage from 'localforage';\nimport type { StorageAdapter } from './Storage.js';\n\nexport interface LocalForageAdapterOptions {\n /** IndexedDB database name (default: 'squisq') */\n name?: string;\n /** IndexedDB object store name (default: 'data') */\n storeName?: string;\n /** Key prefix for namespacing (default: '') */\n prefix?: string;\n /** Human-readable description (default: 'Squisq storage') */\n description?: string;\n}\n\nexport class LocalForageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private store: LocalForage;\n private prefix: string;\n\n constructor(options: LocalForageAdapterOptions = {}) {\n const {\n name = 'squisq',\n storeName = 'data',\n prefix = '',\n description = 'Squisq storage',\n } = options;\n\n this.prefix = prefix;\n this.store = localforage.createInstance({ name, storeName, description });\n }\n\n async get<T>(key: string): Promise<T | null> {\n try {\n const value = await this.store.getItem<T>(this.prefix + key);\n return value;\n } catch (e: unknown) {\n console.warn('[Storage] get (IndexedDB) failed:', key, e);\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n try {\n await this.store.setItem(this.prefix + key, value);\n } catch (e: unknown) {\n console.error('[Storage] set (IndexedDB) failed:', key, e);\n throw e;\n }\n }\n\n async remove(key: string): Promise<void> {\n try {\n await this.store.removeItem(this.prefix + key);\n } catch (e: unknown) {\n console.warn('[Storage] remove (IndexedDB) failed:', key, e);\n }\n }\n\n async clear(): Promise<void> {\n try {\n if (this.prefix) {\n // Only remove keys with our prefix\n const keys = await this.store.keys();\n const keysToRemove = keys.filter((k) => k.startsWith(this.prefix));\n await Promise.all(keysToRemove.map((k) => this.store.removeItem(k)));\n } else {\n // No prefix — clear the entire store\n await this.store.clear();\n }\n } catch (e: unknown) {\n console.error('[Storage] clear (IndexedDB) failed:', e);\n }\n }\n\n async keys(): Promise<string[]> {\n try {\n const allKeys = await this.store.keys();\n if (this.prefix) {\n return allKeys\n .filter((k) => k.startsWith(this.prefix))\n .map((k) => k.slice(this.prefix.length));\n }\n return allKeys;\n } catch (e: unknown) {\n console.warn('[Storage] keys (IndexedDB) failed:', e);\n return [];\n }\n }\n}\n","/**\n * ContentContainer — virtual file system for document containers.\n *\n * A Squisq document is more than just markdown: it's a container of files\n * including the primary markdown document, images, audio, timing data, and\n * other media. ContentContainer provides an abstract async file system\n * interface for reading, writing, and listing these files.\n *\n * Paths are forward-slash separated strings relative to the container root\n * (e.g., 'images/hero.jpg', 'index.md'). No leading slash.\n *\n * Implementations (in core):\n * - MemoryContentContainer — in-memory Map (for zip import, tests, transient use)\n * - ScopedContentContainer — a prefix-scoped view onto a parent container\n */\n\n/**\n * Metadata about a file in a ContentContainer.\n */\nexport interface ContentEntry {\n /** Relative path within the container (e.g., 'images/hero.jpg') */\n path: string;\n /** MIME type (e.g., 'image/jpeg') */\n mimeType: string;\n /** File size in bytes */\n size: number;\n}\n\n/**\n * Abstract async file system for document containers.\n *\n * All paths are forward-slash separated, relative to the container root,\n * with no leading slash. Example: 'images/hero.jpg', 'index.md'.\n */\nexport interface ContentContainer {\n /** Read a file's binary content. Returns null if the file does not exist. */\n readFile(path: string): Promise<ArrayBuffer | null>;\n\n /** Write a file. Creates or overwrites. */\n writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void>;\n\n /** Remove a file. No-op if the file does not exist. */\n removeFile(path: string): Promise<void>;\n\n /**\n * List files in the container.\n *\n * Returns file **entries** (`ContentEntry[]` with `.path`/`.mimeType`/`.size`),\n * NOT bare path strings — despite the name. Read `entry.path` for the path;\n * calling string methods (e.g. `.startsWith`) directly on an entry will throw.\n * @param prefix — Optional path prefix to filter by (e.g., 'images/')\n */\n listFiles(prefix?: string): Promise<ContentEntry[]>;\n\n /** Check whether a file exists. */\n exists(path: string): Promise<boolean>;\n\n /**\n * Find the primary markdown document path.\n *\n * Discovery order: index.md → doc.md → document.md → first *.md at root.\n * Returns null if no markdown file is found at the root level.\n */\n getDocumentPath(): Promise<string | null>;\n\n /**\n * Convenience: read the primary markdown document as a UTF-8 string.\n * Returns null if no markdown file is found.\n */\n readDocument(): Promise<string | null>;\n\n /**\n * Convenience: write a markdown document.\n * @param markdown — The markdown content\n * @param filename — Filename to use (defaults to 'index.md')\n */\n writeDocument(markdown: string, filename?: string): Promise<void>;\n}\n\n// ============================================\n// Well-known markdown filenames in priority order\n// ============================================\n\nconst MARKDOWN_PRIORITY = ['index.md', 'doc.md', 'document.md'];\n\n/**\n * Find the primary markdown path from a list of file entries.\n * Exported for reuse by other ContentContainer implementations.\n */\nexport function findDocumentPath(entries: ContentEntry[]): string | null {\n // Only consider root-level files (no '/' in path)\n const rootFiles = entries.filter((e) => !e.path.includes('/'));\n\n for (const name of MARKDOWN_PRIORITY) {\n const match = rootFiles.find((e) => e.path.toLowerCase() === name);\n if (match) return match.path;\n }\n\n // Fallback: first .md file at root\n const firstMd = rootFiles.find((e) => e.path.toLowerCase().endsWith('.md'));\n return firstMd?.path ?? null;\n}\n\n// ============================================\n// MemoryContentContainer\n// ============================================\n\ninterface MemoryFile {\n data: ArrayBuffer;\n mimeType: string;\n}\n\n/**\n * In-memory ContentContainer backed by a Map.\n *\n * Used for zip import (deserialize into memory), tests, and transient operations.\n */\nexport class MemoryContentContainer implements ContentContainer {\n private files = new Map<string, MemoryFile>();\n\n async readFile(path: string): Promise<ArrayBuffer | null> {\n const stored = this.files.get(path)?.data;\n // A container owns its bytes. Returning the backing buffer would let a\n // caller mutate stored state without going through writeFile().\n return stored ? stored.slice(0) : null;\n }\n\n async writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void> {\n // Copy to a standalone ArrayBuffer. Uint8Array/Buffer may share a larger\n // backing buffer (e.g., Node's buffer pool), so we must slice to the\n // exact byte range to avoid storing stale pool data.\n // ArrayBuffer.isView is cross-realm-safe (unlike `instanceof\n // ArrayBuffer`, which fails for values created in jsdom/iframes).\n let source: Uint8Array;\n if (ArrayBuffer.isView(data)) {\n source = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n } else {\n source = new Uint8Array(data);\n }\n // Always materialize in this realm, including for iframe ArrayBuffers and\n // views backed by SharedArrayBuffer.\n const owned = new Uint8Array(source.byteLength);\n owned.set(source);\n this.files.set(path, {\n data: owned.buffer,\n mimeType: mimeType ?? guessMimeType(path),\n });\n }\n\n async removeFile(path: string): Promise<void> {\n this.files.delete(path);\n }\n\n async listFiles(prefix?: string): Promise<ContentEntry[]> {\n const entries: ContentEntry[] = [];\n for (const [path, file] of this.files) {\n if (prefix && !path.startsWith(prefix)) continue;\n entries.push({\n path,\n mimeType: file.mimeType,\n size: file.data.byteLength,\n });\n }\n return entries;\n }\n\n async exists(path: string): Promise<boolean> {\n return this.files.has(path);\n }\n\n async getDocumentPath(): Promise<string | null> {\n return findDocumentPath(await this.listFiles());\n }\n\n async readDocument(): Promise<string | null> {\n const docPath = await this.getDocumentPath();\n if (!docPath) return null;\n const data = await this.readFile(docPath);\n if (!data) return null;\n return new TextDecoder().decode(data);\n }\n\n async writeDocument(markdown: string, filename?: string): Promise<void> {\n const name = filename ?? 'index.md';\n const data = new TextEncoder().encode(markdown);\n await this.writeFile(name, data, 'text/markdown');\n }\n}\n\n// ============================================\n// MIME type guessing\n// ============================================\n\nconst EXTENSION_MIME_MAP: Record<string, string> = {\n '.md': 'text/markdown',\n '.txt': 'text/plain',\n '.json': 'application/json',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.css': 'text/css',\n '.html': 'text/html',\n '.js': 'application/javascript',\n};\n\nfunction guessMimeType(path: string): string {\n const dot = path.lastIndexOf('.');\n if (dot === -1) return 'application/octet-stream';\n const ext = path.slice(dot).toLowerCase();\n return EXTENSION_MIME_MAP[ext] ?? 'application/octet-stream';\n}\n","/**\n * ScopedContentContainer\n *\n * Wraps a parent {@link ContentContainer} and prefixes every path with a\n * fixed subfolder, presenting that subfolder as the root. Useful for\n * sidecar folders (e.g. `<imagebasename>_files/`) where a component\n * should be able to read/write/list files relative to its own root\n * without knowing where it lives in the parent tree.\n *\n * Scopes nest: `scopeContainer(scopeContainer(parent, 'a'), 'b')` is\n * equivalent to `scopeContainer(parent, 'a/b')`.\n *\n * `getDocumentPath()` always returns `null` for scoped sidecars — they\n * are not document roots.\n */\n\nimport type { ContentContainer, ContentEntry } from './ContentContainer.js';\n\ninterface NormalizePathOptions {\n allowEmpty?: boolean;\n preserveTrailingSlash?: boolean;\n}\n\n/**\n * Normalize a container-relative path without ever resolving traversal.\n * Backslashes are rejected rather than translated because a filesystem-backed\n * parent may interpret them as separators even when an in-memory parent does\n * not. Dot segments are similarly rejected instead of collapsed so an invalid\n * caller path can never escape after a later platform-specific normalization.\n */\nfunction normalizeRelativePath(\n path: string,\n label: 'prefix' | 'path',\n options: NormalizePathOptions = {},\n): string {\n const original = path;\n let normalized = label === 'prefix' ? path.trim() : path;\n if (normalized.includes('\\\\')) {\n throw new Error(`ScopedContentContainer: ${label} must use forward slashes: ${original}`);\n }\n\n const hadTrailingSlash = normalized.endsWith('/');\n normalized = normalized.replace(/^\\/+|\\/+$/g, '');\n const segments = normalized === '' ? [] : normalized.split('/').filter(Boolean);\n if (segments.some((segment) => segment === '.' || segment === '..')) {\n throw new Error(\n `ScopedContentContainer: ${label} must not contain '.' or '..' segments: ${original}`,\n );\n }\n if (segments.length === 0 && !options.allowEmpty) {\n throw new Error(`ScopedContentContainer: ${label} must not be empty`);\n }\n\n const result = segments.join('/');\n return options.preserveTrailingSlash && hadTrailingSlash && result ? `${result}/` : result;\n}\n\n/** Normalize a prefix: strip leading/trailing slashes, ensure no `..` segments. */\nfunction normalizePrefix(prefix: string): string {\n return normalizeRelativePath(prefix, 'prefix');\n}\n\n/**\n * A ContentContainer view rooted at a subfolder of a parent container.\n * All paths passed to / returned from this container are relative to\n * the subfolder; the parent never sees an unprefixed path.\n */\nexport class ScopedContentContainer implements ContentContainer {\n private readonly parent: ContentContainer;\n /** Prefix without trailing slash. */\n readonly prefix: string;\n /** Prefix with trailing slash. */\n private readonly prefixSlash: string;\n\n constructor(parent: ContentContainer, prefix: string) {\n this.parent = parent;\n this.prefix = normalizePrefix(prefix);\n this.prefixSlash = `${this.prefix}/`;\n }\n\n private toParent(path: string, options: NormalizePathOptions = {}): string {\n const normalized = normalizeRelativePath(path, 'path', options);\n return this.prefixSlash + normalized;\n }\n\n /** Strip the prefix from a parent-space path. Returns null if the path is outside the scope. */\n private fromParent(parentPath: string): string | null {\n if (!parentPath.startsWith(this.prefixSlash)) return null;\n return parentPath.slice(this.prefixSlash.length);\n }\n\n readFile(path: string): Promise<ArrayBuffer | null> {\n return this.parent.readFile(this.toParent(path));\n }\n\n writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void> {\n return this.parent.writeFile(this.toParent(path), data, mimeType);\n }\n\n removeFile(path: string): Promise<void> {\n return this.parent.removeFile(this.toParent(path));\n }\n\n async listFiles(prefix?: string): Promise<ContentEntry[]> {\n const parentPrefix =\n prefix == null\n ? this.prefixSlash\n : this.toParent(prefix, { allowEmpty: true, preserveTrailingSlash: true });\n const entries = await this.parent.listFiles(parentPrefix);\n const out: ContentEntry[] = [];\n for (const e of entries) {\n const local = this.fromParent(e.path);\n if (local === null) continue;\n out.push({ path: local, mimeType: e.mimeType, size: e.size });\n }\n return out;\n }\n\n exists(path: string): Promise<boolean> {\n return this.parent.exists(this.toParent(path));\n }\n\n /** Sidecars are not document roots. */\n async getDocumentPath(): Promise<string | null> {\n return null;\n }\n\n async readDocument(): Promise<string | null> {\n return null;\n }\n\n async writeDocument(): Promise<void> {\n throw new Error(\n 'ScopedContentContainer is a sidecar view and does not support writeDocument(). ' +\n 'Write to a specific path with writeFile() instead.',\n );\n }\n}\n\n/** Factory: convenience wrapper for `new ScopedContentContainer(parent, prefix)`. */\nexport function scopeContainer(parent: ContentContainer, prefix: string): ScopedContentContainer {\n return new ScopedContentContainer(parent, prefix);\n}\n","/**\n * MediaProviderFromContainer — bridges ContentContainer to MediaProvider.\n *\n * Creates a MediaProvider that resolves relative paths by reading binary data\n * from a ContentContainer and generating blob URLs. Blob URLs are cached and\n * revoked on dispose().\n *\n * This allows any ContentContainer (memory, slot-backed, zip-loaded) to be\n * used with existing rendering components (DocPlayer, ImageLayer, VideoLayer)\n * that consume MediaProvider.\n */\n\nimport type { MediaProvider, MediaEntry } from '../schemas/MediaProvider.js';\nimport type { ContentContainer } from './ContentContainer.js';\n\n/**\n * Create a MediaProvider backed by a ContentContainer.\n *\n * @param container — The ContentContainer to read/write media from\n * @returns A MediaProvider that resolves paths to blob URLs\n */\nexport function createMediaProviderFromContainer(container: ContentContainer): MediaProvider {\n const blobUrlCache = new Map<string, string>();\n\n return {\n async resolveUrl(relativePath: string): Promise<string> {\n const cached = blobUrlCache.get(relativePath);\n if (cached) return cached;\n\n const data = await container.readFile(relativePath);\n if (!data) return relativePath;\n\n const entries = await container.listFiles();\n const entry = entries.find((e) => e.path === relativePath);\n const mimeType = entry?.mimeType ?? 'application/octet-stream';\n\n const blob = new Blob([data], { type: mimeType });\n const url = URL.createObjectURL(blob);\n blobUrlCache.set(relativePath, url);\n return url;\n },\n\n async listMedia(): Promise<MediaEntry[]> {\n const entries = await container.listFiles();\n return entries\n .filter((e) => !e.path.toLowerCase().endsWith('.md'))\n .map((e) => ({\n name: e.path,\n mimeType: e.mimeType,\n size: e.size,\n }));\n },\n\n async addMedia(\n name: string,\n data: ArrayBuffer | Blob | Uint8Array,\n mimeType: string,\n ): Promise<string> {\n // Invalidate any cached blob URL for this path before overwriting\n const cached = blobUrlCache.get(name);\n if (cached) {\n URL.revokeObjectURL(cached);\n blobUrlCache.delete(name);\n }\n\n let buffer: ArrayBuffer | Uint8Array;\n if (data instanceof Blob) {\n buffer = await data.arrayBuffer();\n } else {\n buffer = data;\n }\n await container.writeFile(name, buffer, mimeType);\n return name;\n },\n\n async removeMedia(relativePath: string): Promise<void> {\n const cached = blobUrlCache.get(relativePath);\n if (cached) {\n URL.revokeObjectURL(cached);\n blobUrlCache.delete(relativePath);\n }\n await container.removeFile(relativePath);\n },\n\n dispose(): void {\n for (const url of blobUrlCache.values()) {\n URL.revokeObjectURL(url);\n }\n blobUrlCache.clear();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schemas/Media.ts","../src/schemas/pageStyleDefaults.ts"],"sourcesContent":["/**\n * Media-clip timing model.\n *\n * An additive layer on top of the simple sequential `Doc.audio.segments[]`\n * narration track, which remains first-class. A {@link MediaClip} is a piece\n * of audio or video whose\n * timing is expressed *relative to its parent block* (or the whole document),\n * with an optional `startAt` offset and an optional `spillover` past the\n * block's end. {@link resolveMediaSchedule} flattens these into absolute,\n * doc-timeline `ScheduledClip`s that playback and export consume.\n *\n * Authoring: a body-level `{[audio …]}` / `{[video …]}` annotation inside a\n * block becomes a `block.media` clip; the same annotation in the preamble\n * (before the first heading) with `anchor=document` becomes a\n * `doc.documentMedia` clip that spans the whole timeline.\n */\n\nimport type { Block, Doc } from './Doc.js';\n\n/**\n * A timed piece of media attached to a block (or the document).\n */\nexport interface MediaClip {\n /** Stable id (for React keys and timeline selection). */\n id: string;\n /** Source path (mp3/mp4/…), relative to the article media dir. */\n src: string;\n kind: 'audio' | 'video';\n /**\n * Seconds from the parent block's start when this clip begins. For an\n * `anchor='document'` clip, seconds from the document start. Default 0.\n */\n startAt: number;\n /** Source in-point within the file (seconds). Default 0. */\n clipStart?: number;\n /** Source out-point within the file (seconds). Default: file/block end. */\n clipEnd?: number;\n /**\n * When false (default) the clip stops at its block's end. When true it\n * keeps playing past the block boundary until the clip itself finishes.\n * Ignored for `anchor='document'` clips (they already span the timeline).\n */\n spillover?: boolean;\n /**\n * `'block'` (default) — timed relative to the parent block. `'document'` —\n * timed relative to the document start and able to span every block.\n */\n anchor: 'block' | 'document';\n /**\n * 1-based source line of the authoring annotation, when derived from\n * markdown. Enables round-tripping edits (e.g. the timeline editor rewriting\n * `startAt`) back to the exact line. Absent for programmatically built clips.\n */\n sourceLine?: number;\n /**\n * Round-trip home of the authoring annotation: the block whose contents\n * held the paragraph, its position in that block's ORIGINAL contents\n * array (before extraction), and the paragraph's exact source text.\n * `docToMarkdown` re-inserts the annotation from this — `raw` verbatim\n * when present, a re-serialized annotation otherwise. Absent for\n * programmatically built clips (those emit at the document top).\n */\n origin?: { blockId: string; index: number; raw?: string };\n}\n\n/**\n * A {@link MediaClip} resolved to absolute document-timeline coordinates.\n * Produced by {@link resolveMediaSchedule}; consumed by the playback\n * scheduler and the MP4 export.\n */\nexport interface ScheduledClip {\n id: string;\n src: string;\n kind: 'audio' | 'video';\n /** Absolute doc-timeline second the clip starts. */\n absoluteStart: number;\n /** Absolute doc-timeline second the clip ends (exclusive). */\n absoluteEnd: number;\n /** Source in-point to seek to at `absoluteStart`. */\n sourceIn: number;\n anchor: 'block' | 'document';\n /** Owning block id for block-anchored clips (for video re-homing/export). */\n blockId?: string;\n /** 1-based source line of the authoring annotation, when known. */\n sourceLine?: number;\n}\n\n/** Depth-first flatten of the block tree (local copy to avoid a doc-layer dep). */\nfunction flatten(blocks: Block[], out: Block[] = []): Block[] {\n for (const b of blocks) {\n out.push(b);\n if (b.children && b.children.length > 0) flatten(b.children, out);\n }\n return out;\n}\n\n/** Played length of a clip when known from its in/out points, else null. */\nfunction clipLength(clip: MediaClip): number | null {\n if (clip.clipEnd == null) return null;\n return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));\n}\n\n/**\n * Base playback span ignoring media — the larger of the audio-segment sum\n * and the last block's end. Document clips that don't pin a length run to\n * this point.\n */\nfunction baseTimelineEnd(doc: Doc): number {\n const blockEnd = flatten(doc.blocks).reduce(\n (max, b) => Math.max(max, b.startTime + b.duration),\n 0,\n );\n return Math.max(doc.duration ?? 0, blockEnd);\n}\n\n/**\n * Flatten every block clip and document clip into absolute-timed\n * `ScheduledClip`s. Pure — depends only on `doc`.\n */\nexport function resolveMediaSchedule(doc: Doc): ScheduledClip[] {\n const out: ScheduledClip[] = [];\n const docEnd = baseTimelineEnd(doc);\n\n for (const block of flatten(doc.blocks)) {\n const blockEnd = block.startTime + block.duration;\n for (const clip of block.media ?? []) {\n const start = block.startTime + clip.startAt;\n const len = clipLength(clip);\n let end: number;\n if (clip.spillover) {\n // Continue past the block; fall back to the block end when the\n // played length is unknown (no clipEnd authored).\n end = len != null ? start + len : blockEnd;\n } else {\n end = len != null ? Math.min(start + len, blockEnd) : blockEnd;\n }\n out.push({\n id: clip.id,\n src: clip.src,\n kind: clip.kind,\n absoluteStart: start,\n absoluteEnd: Math.max(start, end),\n sourceIn: clip.clipStart ?? 0,\n anchor: 'block',\n blockId: block.id,\n ...(clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}),\n });\n }\n }\n\n for (const clip of doc.documentMedia ?? []) {\n const start = clip.startAt;\n const len = clipLength(clip);\n const end = len != null ? start + len : docEnd;\n out.push({\n id: clip.id,\n src: clip.src,\n kind: clip.kind,\n absoluteStart: start,\n absoluteEnd: Math.max(start, end),\n sourceIn: clip.clipStart ?? 0,\n anchor: 'document',\n ...(clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}),\n });\n }\n\n return out;\n}\n\n/**\n * Total playback duration including any media that spills past the last\n * block (or audio segment). Export uses this for the frame count and the\n * player for its effective timeline length.\n */\nexport function getDocPlaybackDuration(doc: Doc): number {\n const base = baseTimelineEnd(doc);\n const mediaEnd = resolveMediaSchedule(doc).reduce((max, c) => Math.max(max, c.absoluteEnd), 0);\n return Math.max(base, mediaEnd);\n}\n","/**\n * Page Style Defaults\n *\n * Derives a complete `ThemePageStyle` from a theme's existing fields so\n * legacy themes, customizer themes, and file-inferred themes get a\n * coherent page art direction without declaring one. Built-in themes\n * ship explicit `pageStyle` blocks in their JSON; this derivation is the\n * fallback used by `compileTheme` and by `resolvePageStyle` at read time.\n *\n * Derivation signals:\n * - `renderStyle.name` → design family (documentary → documentary,\n * magazine → editorial, bold → brutalist, tech-dark → terminal, …)\n * - `style.borderRadius` → cornerRadius + rounded vs flush image framing\n * - `style.imageTreatment` (mono/duotone) → letterboxed framing\n * - `style.textShadow` → shadow language\n * - `persistentLayers` patternBackground → page pattern\n */\n\nimport type { Theme } from './Theme.js';\nimport type {\n PageDesignFamily,\n PageTokens,\n ThemePageStyle,\n PageHeadingTreatment,\n} from './PageStyle.js';\n\n/** renderStyle.name → design family. Unknown names fall back to 'clean'. */\nconst FAMILY_BY_RENDER_STYLE: Record<string, PageDesignFamily> = {\n standard: 'clean',\n minimalist: 'clean',\n documentary: 'documentary',\n magazine: 'editorial',\n bold: 'brutalist',\n 'tech-dark': 'terminal',\n cinematic: 'cinematic',\n 'warm-earth': 'organic',\n 'morning-light': 'soft',\n gezellig: 'soft',\n};\n\ninterface FamilyDefaults {\n sectionSpacing: PageTokens['sectionSpacing'];\n divider: PageTokens['divider'];\n backgroundRhythm: PageTokens['backgroundRhythm'];\n heroStyle: PageTokens['heroStyle'];\n headingTreatment: PageHeadingTreatment;\n quoteMark: PageTokens['quoteMark'];\n numeralStyle: PageTokens['numeralStyle'];\n accentStrategy: ThemePageStyle['accentRotation']['strategy'];\n}\n\nconst FAMILY_DEFAULTS: Record<PageDesignFamily, FamilyDefaults> = {\n clean: {\n sectionSpacing: 'comfortable',\n divider: 'hairline',\n backgroundRhythm: 'alternate',\n heroStyle: 'stacked',\n headingTreatment: { eyebrow: 'kicker', scale: 'regular', underline: 'none' },\n quoteMark: 'accent-bar',\n numeralStyle: 'plain',\n accentStrategy: 'cycle',\n },\n editorial: {\n sectionSpacing: 'comfortable',\n divider: 'double-rule',\n backgroundRhythm: 'flat',\n heroStyle: 'split',\n headingTreatment: { eyebrow: 'kicker', scale: 'display', underline: 'none' },\n quoteMark: 'oversized-glyph',\n numeralStyle: 'oversized',\n accentStrategy: 'alternate-two',\n },\n brutalist: {\n sectionSpacing: 'compact',\n divider: 'thick-rule',\n backgroundRhythm: 'accent-bands',\n heroStyle: 'oversized-type',\n headingTreatment: { eyebrow: 'kicker', scale: 'oversized', case: 'uppercase' },\n quoteMark: 'none',\n numeralStyle: 'oversized',\n accentStrategy: 'cycle',\n },\n terminal: {\n sectionSpacing: 'compact',\n divider: 'hairline',\n backgroundRhythm: 'tinted-panels',\n heroStyle: 'stacked',\n headingTreatment: { eyebrow: 'mono-tag', scale: 'regular', underline: 'none' },\n quoteMark: 'none',\n numeralStyle: 'mono',\n accentStrategy: 'primary-only',\n },\n cinematic: {\n sectionSpacing: 'generous',\n divider: 'gap-only',\n backgroundRhythm: 'flat',\n heroStyle: 'full-bleed',\n headingTreatment: { eyebrow: 'kicker', scale: 'display', case: 'uppercase' },\n quoteMark: 'none',\n numeralStyle: 'oversized',\n accentStrategy: 'primary-only',\n },\n documentary: {\n sectionSpacing: 'generous',\n divider: 'thick-rule',\n backgroundRhythm: 'flat',\n heroStyle: 'letterbox',\n headingTreatment: { eyebrow: 'numbered', scale: 'display', underline: 'none' },\n quoteMark: 'accent-bar',\n numeralStyle: 'boxed',\n accentStrategy: 'primary-only',\n },\n organic: {\n sectionSpacing: 'comfortable',\n divider: 'thick-rule',\n backgroundRhythm: 'tinted-panels',\n heroStyle: 'stacked',\n headingTreatment: { eyebrow: 'kicker', scale: 'regular', underline: 'accent-bar' },\n quoteMark: 'oversized-glyph',\n numeralStyle: 'boxed',\n accentStrategy: 'alternate-two',\n },\n soft: {\n sectionSpacing: 'comfortable',\n divider: 'dotted',\n backgroundRhythm: 'tinted-panels',\n heroStyle: 'split',\n headingTreatment: { eyebrow: 'kicker', scale: 'regular', underline: 'none' },\n quoteMark: 'oversized-glyph',\n numeralStyle: 'plain',\n accentStrategy: 'alternate-two',\n },\n};\n\n/** Read the first patternBackground pattern from a theme's persistent layers. */\nfunction derivePattern(theme: Theme): PageTokens['pattern'] {\n const layers = [\n ...(theme.persistentLayers?.bottomLayers ?? []),\n ...(theme.persistentLayers?.topLayers ?? []),\n ];\n for (const layer of layers) {\n if (\n typeof layer === 'object' &&\n layer !== null &&\n 'template' in layer &&\n (layer as { template?: unknown }).template === 'patternBackground'\n ) {\n const config = (layer as { config?: { pattern?: unknown } }).config;\n const pattern = config?.pattern;\n if (\n pattern === 'dots' ||\n pattern === 'grid' ||\n pattern === 'diagonal' ||\n pattern === 'noise'\n ) {\n return pattern;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Derive a complete `ThemePageStyle` from a theme's existing fields.\n * Deterministic and read-only; explicit `theme.pageStyle` always wins\n * (callers check before deriving — see `resolvePageStyle`).\n */\nexport function defaultPageStyle(theme: Theme): ThemePageStyle {\n const family = FAMILY_BY_RENDER_STYLE[theme.renderStyle?.name ?? ''] ?? 'clean';\n const defaults = FAMILY_DEFAULTS[family];\n\n const cornerRadius = theme.style?.borderRadius ?? 6;\n const treatmentType = theme.style?.imageTreatment?.type;\n const imageFraming: PageTokens['imageFraming'] =\n treatmentType === 'mono' || treatmentType === 'duotone'\n ? 'letterboxed'\n : cornerRadius > 0\n ? 'rounded'\n : 'flush';\n\n const tokens: PageTokens = {\n contentMaxWidth: 760,\n wideMaxWidth: 1100,\n sectionSpacing: defaults.sectionSpacing,\n cornerRadius,\n divider: defaults.divider,\n backgroundRhythm: defaults.backgroundRhythm,\n heroStyle: defaults.heroStyle,\n headingTreatment: { ...defaults.headingTreatment },\n imageFraming,\n shadow: theme.style?.textShadow ? 'soft' : 'none',\n quoteMark: defaults.quoteMark,\n numeralStyle: defaults.numeralStyle,\n };\n\n const pattern = derivePattern(theme);\n if (pattern) tokens.pattern = pattern;\n\n return {\n family,\n tokens,\n accentRotation: { strategy: defaults.accentStrategy },\n };\n}\n"],"mappings":";AAwFA,SAAS,QAAQ,QAAiB,MAAe,CAAC,GAAY;AAC5D,aAAW,KAAK,QAAQ;AACtB,QAAI,KAAK,CAAC;AACV,QAAI,EAAE,YAAY,EAAE,SAAS,SAAS,EAAG,SAAQ,EAAE,UAAU,GAAG;AAAA,EAClE;AACA,SAAO;AACT;AAGA,SAAS,WAAW,MAAgC;AAClD,MAAI,KAAK,WAAW,KAAM,QAAO;AACjC,SAAO,KAAK,IAAI,GAAG,KAAK,WAAW,KAAK,aAAa,EAAE;AACzD;AAOA,SAAS,gBAAgB,KAAkB;AACzC,QAAM,WAAW,QAAQ,IAAI,MAAM,EAAE;AAAA,IACnC,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ;AAAA,IAClD;AAAA,EACF;AACA,SAAO,KAAK,IAAI,IAAI,YAAY,GAAG,QAAQ;AAC7C;AAMO,SAAS,qBAAqB,KAA2B;AAC9D,QAAM,MAAuB,CAAC;AAC9B,QAAM,SAAS,gBAAgB,GAAG;AAElC,aAAW,SAAS,QAAQ,IAAI,MAAM,GAAG;AACvC,UAAM,WAAW,MAAM,YAAY,MAAM;AACzC,eAAW,QAAQ,MAAM,SAAS,CAAC,GAAG;AACpC,YAAM,QAAQ,MAAM,YAAY,KAAK;AACrC,YAAM,MAAM,WAAW,IAAI;AAC3B,UAAI;AACJ,UAAI,KAAK,WAAW;AAGlB,cAAM,OAAO,OAAO,QAAQ,MAAM;AAAA,MACpC,OAAO;AACL,cAAM,OAAO,OAAO,KAAK,IAAI,QAAQ,KAAK,QAAQ,IAAI;AAAA,MACxD;AACA,UAAI,KAAK;AAAA,QACP,IAAI,KAAK;AAAA,QACT,KAAK,KAAK;AAAA,QACV,MAAM,KAAK;AAAA,QACX,eAAe;AAAA,QACf,aAAa,KAAK,IAAI,OAAO,GAAG;AAAA,QAChC,UAAU,KAAK,aAAa;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS,MAAM;AAAA,QACf,GAAI,KAAK,cAAc,OAAO,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,QAAQ,IAAI,iBAAiB,CAAC,GAAG;AAC1C,UAAM,QAAQ,KAAK;AACnB,UAAM,MAAM,WAAW,IAAI;AAC3B,UAAM,MAAM,OAAO,OAAO,QAAQ,MAAM;AACxC,QAAI,KAAK;AAAA,MACP,IAAI,KAAK;AAAA,MACT,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,eAAe;AAAA,MACf,aAAa,KAAK,IAAI,OAAO,GAAG;AAAA,MAChC,UAAU,KAAK,aAAa;AAAA,MAC5B,QAAQ;AAAA,MACR,GAAI,KAAK,cAAc,OAAO,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,IACnE,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAOO,SAAS,uBAAuB,KAAkB;AACvD,QAAM,OAAO,gBAAgB,GAAG;AAChC,QAAM,WAAW,qBAAqB,GAAG,EAAE,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,WAAW,GAAG,CAAC;AAC7F,SAAO,KAAK,IAAI,MAAM,QAAQ;AAChC;;;ACvJA,IAAM,yBAA2D;AAAA,EAC/D,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,UAAU;AACZ;AAaA,IAAM,kBAA4D;AAAA,EAChE,OAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,WAAW,WAAW,OAAO;AAAA,IAC3E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,WAAW;AAAA,IACT,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,WAAW,WAAW,OAAO;AAAA,IAC3E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,WAAW;AAAA,IACT,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,aAAa,MAAM,YAAY;AAAA,IAC7E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,UAAU;AAAA,IACR,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,YAAY,OAAO,WAAW,WAAW,OAAO;AAAA,IAC7E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,WAAW;AAAA,IACT,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,WAAW,MAAM,YAAY;AAAA,IAC3E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,aAAa;AAAA,IACX,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,YAAY,OAAO,WAAW,WAAW,OAAO;AAAA,IAC7E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,SAAS;AAAA,IACP,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,WAAW,WAAW,aAAa;AAAA,IACjF,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AAAA,EACA,MAAM;AAAA,IACJ,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,EAAE,SAAS,UAAU,OAAO,WAAW,WAAW,OAAO;AAAA,IAC3E,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EAClB;AACF;AAGA,SAAS,cAAc,OAAqC;AAC1D,QAAM,SAAS;AAAA,IACb,GAAI,MAAM,kBAAkB,gBAAgB,CAAC;AAAA,IAC7C,GAAI,MAAM,kBAAkB,aAAa,CAAC;AAAA,EAC5C;AACA,aAAW,SAAS,QAAQ;AAC1B,QACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACb,MAAiC,aAAa,qBAC/C;AACA,YAAM,SAAU,MAA6C;AAC7D,YAAM,UAAU,QAAQ;AACxB,UACE,YAAY,UACZ,YAAY,UACZ,YAAY,cACZ,YAAY,SACZ;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,iBAAiB,OAA8B;AAC7D,QAAM,SAAS,uBAAuB,MAAM,aAAa,QAAQ,EAAE,KAAK;AACxE,QAAM,WAAW,gBAAgB,MAAM;AAEvC,QAAM,eAAe,MAAM,OAAO,gBAAgB;AAClD,QAAM,gBAAgB,MAAM,OAAO,gBAAgB;AACnD,QAAM,eACJ,kBAAkB,UAAU,kBAAkB,YAC1C,gBACA,eAAe,IACb,YACA;AAER,QAAM,SAAqB;AAAA,IACzB,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,gBAAgB,SAAS;AAAA,IACzB;AAAA,IACA,SAAS,SAAS;AAAA,IAClB,kBAAkB,SAAS;AAAA,IAC3B,WAAW,SAAS;AAAA,IACpB,kBAAkB,EAAE,GAAG,SAAS,iBAAiB;AAAA,IACjD;AAAA,IACA,QAAQ,MAAM,OAAO,aAAa,SAAS;AAAA,IAC3C,WAAW,SAAS;AAAA,IACpB,cAAc,SAAS;AAAA,EACzB;AAEA,QAAM,UAAU,cAAc,KAAK;AACnC,MAAI,QAAS,QAAO,UAAU;AAE9B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB,EAAE,UAAU,SAAS,eAAe;AAAA,EACtD;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/jsonForm/chooseControl.ts","../src/jsonForm/pointer.ts","../src/jsonForm/evaluateWhen.ts","../src/jsonForm/inferSchema.ts","../src/jsonForm/tokens.ts"],"sourcesContent":["/**\n * Pick a `ControlKind` for a schema node. The single dispatcher used by\n * both `<JsonView>` and `<JsonEditor>` so view and edit modes always\n * agree on what each field _is_, even when their UIs differ.\n *\n * Order of precedence:\n * 1. Explicit `squisq.control` hint\n * 2. `format` keyword (color/date/time/datetime/markdown/textarea)\n * 3. Enum cardinality (segmented vs. combobox)\n * 4. Numeric range (slider when both min and max present)\n * 5. String length (multiline when maxLength > 200)\n * 6. Type-default (object/array/boolean/string/number)\n */\n\nimport type { ControlKind, SquisqAnnotatedSchema } from './types.js';\n\nconst ENUM_SEGMENTED_LIMIT = 4;\nconst MULTILINE_LENGTH_THRESHOLD = 200;\n\nexport function chooseControl(schema: SquisqAnnotatedSchema): ControlKind {\n const hint = schema.squisq?.control;\n if (hint) return hint;\n\n const type = primaryType(schema);\n\n if (schema.format && type === 'string') {\n switch (schema.format) {\n case 'color':\n return 'color';\n case 'date':\n return 'date';\n case 'time':\n return 'time';\n case 'date-time':\n return 'datetime';\n case 'markdown':\n return 'richtext';\n case 'textarea':\n return 'multiline';\n }\n }\n\n if (schema.enum && schema.enum.length > 0) {\n return schema.enum.length <= ENUM_SEGMENTED_LIMIT ? 'segmented' : 'combobox';\n }\n\n if (schema.oneOf || schema.anyOf) return 'tabs';\n\n switch (type) {\n case 'array':\n return arrayItemKind(schema) === 'object' ? 'card-stack' : 'chip-bin';\n case 'object':\n return 'group';\n case 'boolean':\n return 'toggle';\n case 'integer':\n case 'number':\n return hasFiniteRange(schema) ? 'slider' : 'stepper';\n case 'string':\n if (typeof schema.maxLength === 'number' && schema.maxLength > MULTILINE_LENGTH_THRESHOLD) {\n return 'multiline';\n }\n return 'text';\n default:\n return 'text';\n }\n}\n\n/** Return the first concrete type of a schema, ignoring `null` for nullable shorthand. */\nexport function primaryType(schema: SquisqAnnotatedSchema): string | undefined {\n const t = schema.type;\n if (typeof t === 'string') return t;\n if (Array.isArray(t)) return t.find((x) => x !== 'null');\n return undefined;\n}\n\n/** Inspect `items` to decide whether an array holds objects or primitives. */\nexport function arrayItemKind(schema: SquisqAnnotatedSchema): 'object' | 'primitive' {\n const items = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n if (!items) return 'primitive';\n return primaryType(items) === 'object' ? 'object' : 'primitive';\n}\n\nfunction hasFiniteRange(schema: SquisqAnnotatedSchema): boolean {\n const min = schema.minimum ?? schema.exclusiveMinimum;\n const max = schema.maximum ?? schema.exclusiveMaximum;\n return typeof min === 'number' && typeof max === 'number';\n}\n","/**\n * JSON Pointer helpers (RFC 6901) plus immutable get/set, plus a tiny\n * dotted-path normalizer so hosts can write `user.role` instead of\n * `/user/role`. `$ref` resolution is local-only (`#/$defs/Foo`).\n */\n\nimport type { SquisqAnnotatedSchema } from './types.js';\n\n/** Escape one object-member name for use as an RFC 6901 pointer segment. */\nexport function escapePointerSegment(segment: string): string {\n return segment.replace(/~/g, '~0').replace(/\\//g, '~1');\n}\n\n/** Normalize a path to a JSON Pointer string. Accepts both forms. */\nexport function toPointer(path: string): string {\n if (path === '') return '';\n if (path.startsWith('/')) return path;\n // Dotted form: split on `.` and escape per RFC 6901.\n return '/' + path.split('.').map(escapePointerSegment).join('/');\n}\n\n/** Append one member name to a dotted path or validated JSON Pointer. */\nexport function appendPointer(base: string, segment: string): string {\n const pointer = toPointer(base);\n pointerSegments(pointer); // validate explicit pointer syntax before composing\n return `${pointer}/${escapePointerSegment(segment)}`;\n}\n\n/** Split a JSON Pointer into decoded segments. Empty string → []. */\nexport function pointerSegments(pointer: string): string[] {\n if (pointer === '') return [];\n if (!pointer.startsWith('/')) {\n throw new TypeError(`Invalid JSON Pointer ${JSON.stringify(pointer)}: expected \"\" or \"/...\"`);\n }\n return pointer\n .slice(1)\n .split('/')\n .map((seg) => {\n if (/~(?:[^01]|$)/.test(seg)) {\n throw new TypeError(\n `Invalid JSON Pointer ${JSON.stringify(pointer)}: \"~\" must be escaped as \"~0\"`,\n );\n }\n return seg.replace(/~1/g, '/').replace(/~0/g, '~');\n });\n}\n\n/** Parse the canonical array-index spelling used by JSON Pointer consumers. */\nfunction arrayIndex(segment: string): number | undefined {\n if (!/^(?:0|[1-9]\\d*)$/.test(segment)) return undefined;\n const index = Number(segment);\n // 2^32 - 1 is the Array length sentinel, not an element index.\n return Number.isSafeInteger(index) && index <= 0xffff_fffe ? index : undefined;\n}\n\nfunction ownValue(object: object, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(object, key)\n ? (object as Record<string, unknown>)[key]\n : undefined;\n}\n\n/** Define a data member without invoking Object.prototype.__proto__'s setter. */\nfunction setOwn(object: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(object, key, {\n value,\n enumerable: true,\n configurable: true,\n writable: true,\n });\n}\n\n/** Read a value from data by path (dotted or pointer). Returns undefined if missing. */\nexport function getByPointer(data: unknown, path: string): unknown {\n const segments = pointerSegments(toPointer(path));\n let cur: unknown = data;\n for (const seg of segments) {\n if (cur === null || cur === undefined) return undefined;\n if (Array.isArray(cur)) {\n const idx = arrayIndex(seg);\n if (idx === undefined || idx >= cur.length) return undefined;\n cur = cur[idx];\n } else if (typeof cur === 'object') {\n cur = ownValue(cur, seg);\n } else {\n return undefined;\n }\n }\n return cur;\n}\n\n/**\n * Return a structurally-shared copy of `data` with the value at `path`\n * replaced. Intermediate objects/arrays are cloned along the path; the\n * rest is shared by reference. Creates intermediate objects if missing.\n */\nexport function setByPointer<T>(data: T, path: string, value: unknown): T {\n const segments = pointerSegments(toPointer(path));\n if (segments.length === 0) return value as T;\n return setRec(data, segments, 0, value) as T;\n}\n\nfunction setRec(node: unknown, segments: string[], i: number, value: unknown): unknown {\n const seg = segments[i];\n const isLast = i === segments.length - 1;\n\n // Decide whether the slot we're about to write into is an array index.\n const nextSeg = isLast ? undefined : segments[i + 1];\n const writingArrayChild = nextSeg !== undefined && arrayIndex(nextSeg) !== undefined;\n\n if (Array.isArray(node)) {\n const idx = arrayIndex(seg);\n if (idx === undefined) {\n throw new TypeError(`Invalid array index ${JSON.stringify(seg)} in JSON Pointer`);\n }\n const next = node.slice();\n next[idx] = isLast\n ? value\n : setRec(node[idx] ?? (writingArrayChild ? [] : {}), segments, i + 1, value);\n return next;\n }\n\n const obj = (node && typeof node === 'object' ? { ...(node as object) } : {}) as Record<\n string,\n unknown\n >;\n const previous = ownValue(obj, seg);\n setOwn(\n obj,\n seg,\n isLast ? value : setRec(previous ?? (writingArrayChild ? [] : {}), segments, i + 1, value),\n );\n return obj;\n}\n\n/**\n * Resolve a local `$ref` (e.g. `#/$defs/Foo`) against the root schema.\n * Returns the original schema if no `$ref` is present, or `undefined`\n * if the reference cannot be resolved. Cross-document refs are not\n * supported.\n */\nexport function resolveRef(\n schema: SquisqAnnotatedSchema,\n root: SquisqAnnotatedSchema,\n): SquisqAnnotatedSchema | undefined {\n if (!schema.$ref) return schema;\n const ref = schema.$ref;\n if (ref === '#') return root;\n if (!ref.startsWith('#/')) return undefined;\n try {\n return getByPointer(root, ref.slice(1)) as SquisqAnnotatedSchema | undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Evaluate a `SquisqWhen` rule against the current root data. Pure;\n * renderers call this on every render with fresh data.\n */\n\nimport type { SquisqWhen } from './types.js';\nimport { getByPointer } from './pointer.js';\n\nexport function evaluateWhen(when: SquisqWhen, rootData: unknown): boolean {\n const actual = getByPointer(rootData, when.field);\n\n if (when.equals !== undefined) {\n return deepEqual(actual, when.equals);\n }\n if (when.oneOf !== undefined) {\n return when.oneOf.some((v) => deepEqual(actual, v));\n }\n if (when.matches !== undefined) {\n if (typeof actual !== 'string') return false;\n try {\n return new RegExp(when.matches).test(actual);\n } catch {\n return false;\n }\n }\n if (when.truthy !== undefined) {\n return Boolean(actual) === when.truthy;\n }\n // No operator specified — defaults to \"field has any defined value\".\n return actual !== undefined && actual !== null;\n}\n\n/**\n * Resolve a `boolean | SquisqWhen` flag (used by `hidden`/`disabled`)\n * to a final boolean. Literal booleans pass through.\n */\nexport function resolveFlag(flag: boolean | SquisqWhen | undefined, rootData: unknown): boolean {\n if (flag === undefined) return false;\n if (typeof flag === 'boolean') return flag;\n return evaluateWhen(flag, rootData);\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false;\n return a.every((v, i) => deepEqual(v, b[i]));\n }\n if (Array.isArray(b)) return false;\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n const bk = Object.keys(bo);\n if (ak.length !== bk.length) return false;\n return ak.every((k) => deepEqual(ao[k], bo[k]));\n}\n","/**\n * Infer a JSON Schema from an example JSON value. Thin facade over\n * `genson-js` so consumers don't depend on it directly and we can swap\n * the implementation later.\n */\n\nimport { createSchema, mergeSchemas } from 'genson-js';\nimport type { SquisqAnnotatedSchema } from './types.js';\n\nexport interface InferSchemaOptions {\n /** Additional samples merged into the inferred schema for broader coverage. */\n additionalSamples?: readonly unknown[];\n}\n\nexport function inferSchema(\n sample: unknown,\n options: InferSchemaOptions = {},\n): SquisqAnnotatedSchema {\n const base = createSchema(sample) as SquisqAnnotatedSchema;\n if (!options.additionalSamples || options.additionalSamples.length === 0) {\n return base;\n }\n const extras = options.additionalSamples.map((s) => createSchema(s) as SquisqAnnotatedSchema);\n return mergeSchemas([base, ...extras] as never[]) as SquisqAnnotatedSchema;\n}\n","/**\n * Pure CSS-custom-property builder shared by `<JsonView>` (read-only, in\n * `@bendyline/squisq-react`) and `<JsonEditor>` (editable, in\n * `@bendyline/squisq-editor-react`).\n *\n * Both components derive an identical token bag from a Theme + resolved\n * Surface; they differ only in the CSS-var prefix (`--squisq-json` vs\n * `--squisq-jsonform`) so the viewer and editor can coexist on one page.\n * This function is the single source of that derivation. It is pure and\n * framework-free: it performs no `window` / `matchMedia` access — the\n * surface must already be resolved to a concrete `SurfaceScheme` (or left\n * `undefined` to skip surface application) by the calling React hook.\n */\n\nimport { applySurface, DEFAULT_THEME, resolveFontFamily } from '../schemas/index.js';\nimport type { SurfaceScheme, Theme } from '../schemas/index.js';\n\nexport interface BuildJsonFormTokensOptions {\n /**\n * CSS-var prefix WITHOUT the trailing dash, e.g. `--squisq-json` or\n * `--squisq-jsonform`. Each token key is `${prefix}-${name}`.\n */\n prefix: string;\n}\n\n/**\n * Build the CSS custom-property map for a JSON form/view surface.\n *\n * @param theme The theme to render with (falls back to `DEFAULT_THEME`).\n * @param surface Already-resolved concrete surface, or `undefined` to leave\n * the theme un-surfaced. (The `'auto'` → concrete resolution\n * is the React hook's job, so this stays pure.)\n * @param options `{ prefix }` — the CSS-var namespace.\n */\nexport function buildJsonFormTokens(\n theme: Theme | undefined,\n surface: SurfaceScheme | undefined,\n options: BuildJsonFormTokensOptions,\n): Record<string, string> {\n const { prefix } = options;\n const baseTheme = theme ?? DEFAULT_THEME;\n const finalTheme = surface ? applySurface(baseTheme, surface) : baseTheme;\n\n const titleFont = resolveFontFamily(finalTheme.typography.titleFont, 'system-ui, sans-serif');\n const bodyFont = resolveFontFamily(finalTheme.typography.bodyFont, 'system-ui, sans-serif');\n const monoFont = resolveFontFamily(\n finalTheme.typography.monoFont,\n 'ui-monospace, Consolas, monospace',\n );\n\n return {\n [`${prefix}-bg`]: finalTheme.colors.background,\n [`${prefix}-text`]: finalTheme.colors.text,\n [`${prefix}-muted`]: finalTheme.colors.textMuted,\n [`${prefix}-primary`]: finalTheme.colors.primary,\n [`${prefix}-accent`]: finalTheme.colors.secondary,\n [`${prefix}-warning`]: finalTheme.colors.warning,\n [`${prefix}-border`]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,\n [`${prefix}-input-bg`]: finalTheme.colors.backgroundLight,\n [`${prefix}-title-font`]: titleFont,\n [`${prefix}-body-font`]: bodyFont,\n [`${prefix}-mono-font`]: monoFont,\n [`${prefix}-radius`]: `${finalTheme.style.borderRadius ?? 8}px`,\n };\n}\n\n/**\n * Resolve the effective theme after surface application — the companion to\n * `buildJsonFormTokens` for callers that also need the `Theme` object (both\n * hooks return it alongside the style bag).\n */\nexport function resolveJsonFormTheme(\n theme: Theme | undefined,\n surface: SurfaceScheme | undefined,\n): Theme {\n const baseTheme = theme ?? DEFAULT_THEME;\n return surface ? applySurface(baseTheme, surface) : baseTheme;\n}\n"],"mappings":";;;;;;;AAgBA,IAAM,uBAAuB;AAC7B,IAAM,6BAA6B;AAE5B,SAAS,cAAc,QAA4C;AACxE,QAAM,OAAO,OAAO,QAAQ;AAC5B,MAAI,KAAM,QAAO;AAEjB,QAAM,OAAO,YAAY,MAAM;AAE/B,MAAI,OAAO,UAAU,SAAS,UAAU;AACtC,YAAQ,OAAO,QAAQ;AAAA,MACrB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,IACX;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,OAAO,KAAK,SAAS,GAAG;AACzC,WAAO,OAAO,KAAK,UAAU,uBAAuB,cAAc;AAAA,EACpE;AAEA,MAAI,OAAO,SAAS,OAAO,MAAO,QAAO;AAEzC,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,cAAc,MAAM,MAAM,WAAW,eAAe;AAAA,IAC7D,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO,eAAe,MAAM,IAAI,WAAW;AAAA,IAC7C,KAAK;AACH,UAAI,OAAO,OAAO,cAAc,YAAY,OAAO,YAAY,4BAA4B;AACzF,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGO,SAAS,YAAY,QAAmD;AAC7E,QAAM,IAAI,OAAO;AACjB,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,KAAK,CAAC,MAAM,MAAM,MAAM;AACvD,SAAO;AACT;AAGO,SAAS,cAAc,QAAuD;AACnF,QAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM,CAAC,IAAI,OAAO;AACrE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,YAAY,KAAK,MAAM,WAAW,WAAW;AACtD;AAEA,SAAS,eAAe,QAAwC;AAC9D,QAAM,MAAM,OAAO,WAAW,OAAO;AACrC,QAAM,MAAM,OAAO,WAAW,OAAO;AACrC,SAAO,OAAO,QAAQ,YAAY,OAAO,QAAQ;AACnD;;;AC9EO,SAAS,qBAAqB,SAAyB;AAC5D,SAAO,QAAQ,QAAQ,MAAM,IAAI,EAAE,QAAQ,OAAO,IAAI;AACxD;AAGO,SAAS,UAAU,MAAsB;AAC9C,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AAEjC,SAAO,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,oBAAoB,EAAE,KAAK,GAAG;AACjE;AAGO,SAAS,cAAc,MAAc,SAAyB;AACnE,QAAM,UAAU,UAAU,IAAI;AAC9B,kBAAgB,OAAO;AACvB,SAAO,GAAG,OAAO,IAAI,qBAAqB,OAAO,CAAC;AACpD;AAGO,SAAS,gBAAgB,SAA2B;AACzD,MAAI,YAAY,GAAI,QAAO,CAAC;AAC5B,MAAI,CAAC,QAAQ,WAAW,GAAG,GAAG;AAC5B,UAAM,IAAI,UAAU,wBAAwB,KAAK,UAAU,OAAO,CAAC,yBAAyB;AAAA,EAC9F;AACA,SAAO,QACJ,MAAM,CAAC,EACP,MAAM,GAAG,EACT,IAAI,CAAC,QAAQ;AACZ,QAAI,eAAe,KAAK,GAAG,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,UAAU,OAAO,CAAC;AAAA,MACjD;AAAA,IACF;AACA,WAAO,IAAI,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG;AAAA,EACnD,CAAC;AACL;AAGA,SAAS,WAAW,SAAqC;AACvD,MAAI,CAAC,mBAAmB,KAAK,OAAO,EAAG,QAAO;AAC9C,QAAM,QAAQ,OAAO,OAAO;AAE5B,SAAO,OAAO,cAAc,KAAK,KAAK,SAAS,aAAc,QAAQ;AACvE;AAEA,SAAS,SAAS,QAAgB,KAAsB;AACtD,SAAO,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,IAClD,OAAmC,GAAG,IACvC;AACN;AAGA,SAAS,OAAO,QAAiC,KAAa,OAAsB;AAClF,SAAO,eAAe,QAAQ,KAAK;AAAA,IACjC;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,EACZ,CAAC;AACH;AAGO,SAAS,aAAa,MAAe,MAAuB;AACjE,QAAM,WAAW,gBAAgB,UAAU,IAAI,CAAC;AAChD,MAAI,MAAe;AACnB,aAAW,OAAO,UAAU;AAC1B,QAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,YAAM,MAAM,WAAW,GAAG;AAC1B,UAAI,QAAQ,UAAa,OAAO,IAAI,OAAQ,QAAO;AACnD,YAAM,IAAI,GAAG;AAAA,IACf,WAAW,OAAO,QAAQ,UAAU;AAClC,YAAM,SAAS,KAAK,GAAG;AAAA,IACzB,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,aAAgB,MAAS,MAAc,OAAmB;AACxE,QAAM,WAAW,gBAAgB,UAAU,IAAI,CAAC;AAChD,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,SAAO,OAAO,MAAM,UAAU,GAAG,KAAK;AACxC;AAEA,SAAS,OAAO,MAAe,UAAoB,GAAW,OAAyB;AACrF,QAAM,MAAM,SAAS,CAAC;AACtB,QAAM,SAAS,MAAM,SAAS,SAAS;AAGvC,QAAM,UAAU,SAAS,SAAY,SAAS,IAAI,CAAC;AACnD,QAAM,oBAAoB,YAAY,UAAa,WAAW,OAAO,MAAM;AAE3E,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,UAAM,MAAM,WAAW,GAAG;AAC1B,QAAI,QAAQ,QAAW;AACrB,YAAM,IAAI,UAAU,uBAAuB,KAAK,UAAU,GAAG,CAAC,kBAAkB;AAAA,IAClF;AACA,UAAM,OAAO,KAAK,MAAM;AACxB,SAAK,GAAG,IAAI,SACR,QACA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,IAAI,UAAU,IAAI,GAAG,KAAK;AAC7E,WAAO;AAAA,EACT;AAEA,QAAM,MAAO,QAAQ,OAAO,SAAS,WAAW,EAAE,GAAI,KAAgB,IAAI,CAAC;AAI3E,QAAM,WAAW,SAAS,KAAK,GAAG;AAClC;AAAA,IACE;AAAA,IACA;AAAA,IACA,SAAS,QAAQ,OAAO,aAAa,oBAAoB,CAAC,IAAI,CAAC,IAAI,UAAU,IAAI,GAAG,KAAK;AAAA,EAC3F;AACA,SAAO;AACT;AAQO,SAAS,WACd,QACA,MACmC;AACnC,MAAI,CAAC,OAAO,KAAM,QAAO;AACzB,QAAM,MAAM,OAAO;AACnB,MAAI,QAAQ,IAAK,QAAO;AACxB,MAAI,CAAC,IAAI,WAAW,IAAI,EAAG,QAAO;AAClC,MAAI;AACF,WAAO,aAAa,MAAM,IAAI,MAAM,CAAC,CAAC;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACjJO,SAAS,aAAa,MAAkB,UAA4B;AACzE,QAAM,SAAS,aAAa,UAAU,KAAK,KAAK;AAEhD,MAAI,KAAK,WAAW,QAAW;AAC7B,WAAO,UAAU,QAAQ,KAAK,MAAM;AAAA,EACtC;AACA,MAAI,KAAK,UAAU,QAAW;AAC5B,WAAO,KAAK,MAAM,KAAK,CAAC,MAAM,UAAU,QAAQ,CAAC,CAAC;AAAA,EACpD;AACA,MAAI,KAAK,YAAY,QAAW;AAC9B,QAAI,OAAO,WAAW,SAAU,QAAO;AACvC,QAAI;AACF,aAAO,IAAI,OAAO,KAAK,OAAO,EAAE,KAAK,MAAM;AAAA,IAC7C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,KAAK,WAAW,QAAW;AAC7B,WAAO,QAAQ,MAAM,MAAM,KAAK;AAAA,EAClC;AAEA,SAAO,WAAW,UAAa,WAAW;AAC5C;AAMO,SAAS,YAAY,MAAwC,UAA4B;AAC9F,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,OAAO,SAAS,UAAW,QAAO;AACtC,SAAO,aAAa,MAAM,QAAQ;AACpC;AAEA,SAAS,UAAU,GAAY,GAAqB;AAClD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,MAAI,OAAO,MAAM,OAAO,EAAG,QAAO;AAClC,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,QAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,OAAQ,QAAO;AACvD,WAAO,EAAE,MAAM,CAAC,GAAG,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC;AAAA,EAC7C;AACA,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC7B,QAAM,KAAK;AACX,QAAM,KAAK;AACX,QAAM,KAAK,OAAO,KAAK,EAAE;AACzB,QAAM,KAAK,OAAO,KAAK,EAAE;AACzB,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,SAAO,GAAG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAChD;;;ACpDA,SAAS,cAAc,oBAAoB;AAQpC,SAAS,YACd,QACA,UAA8B,CAAC,GACR;AACvB,QAAM,OAAO,aAAa,MAAM;AAChC,MAAI,CAAC,QAAQ,qBAAqB,QAAQ,kBAAkB,WAAW,GAAG;AACxE,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,kBAAkB,IAAI,CAAC,MAAM,aAAa,CAAC,CAA0B;AAC5F,SAAO,aAAa,CAAC,MAAM,GAAG,MAAM,CAAY;AAClD;;;ACUO,SAAS,oBACd,OACA,SACA,SACwB;AACxB,QAAM,EAAE,OAAO,IAAI;AACnB,QAAM,YAAY,SAAS;AAC3B,QAAM,aAAa,UAAU,aAAa,WAAW,OAAO,IAAI;AAEhE,QAAM,YAAY,kBAAkB,WAAW,WAAW,WAAW,uBAAuB;AAC5F,QAAM,WAAW,kBAAkB,WAAW,WAAW,UAAU,uBAAuB;AAC1F,QAAM,WAAW;AAAA,IACf,WAAW,WAAW;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,CAAC,GAAG,MAAM,KAAK,GAAG,WAAW,OAAO;AAAA,IACpC,CAAC,GAAG,MAAM,OAAO,GAAG,WAAW,OAAO;AAAA,IACtC,CAAC,GAAG,MAAM,QAAQ,GAAG,WAAW,OAAO;AAAA,IACvC,CAAC,GAAG,MAAM,UAAU,GAAG,WAAW,OAAO;AAAA,IACzC,CAAC,GAAG,MAAM,SAAS,GAAG,WAAW,OAAO;AAAA,IACxC,CAAC,GAAG,MAAM,UAAU,GAAG,WAAW,OAAO;AAAA,IACzC,CAAC,GAAG,MAAM,SAAS,GAAG,sBAAsB,WAAW,OAAO,SAAS;AAAA,IACvE,CAAC,GAAG,MAAM,WAAW,GAAG,WAAW,OAAO;AAAA,IAC1C,CAAC,GAAG,MAAM,aAAa,GAAG;AAAA,IAC1B,CAAC,GAAG,MAAM,YAAY,GAAG;AAAA,IACzB,CAAC,GAAG,MAAM,YAAY,GAAG;AAAA,IACzB,CAAC,GAAG,MAAM,SAAS,GAAG,GAAG,WAAW,MAAM,gBAAgB,CAAC;AAAA,EAC7D;AACF;AAOO,SAAS,qBACd,OACA,SACO;AACP,QAAM,YAAY,SAAS;AAC3B,SAAO,UAAU,aAAa,WAAW,OAAO,IAAI;AACtD;","names":[]}
|