@latentic/live-markdown 0.3.1 → 0.4.1
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/CHANGELOG.md +44 -0
- package/README.md +2 -2
- package/dist/chunk-V6TY7KAL.js +8 -0
- package/dist/chunk-V6TY7KAL.js.map +1 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.d.ts +106 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.js +482 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.js.map +1 -0
- package/dist/codemirror/byteOffset.d.ts +17 -0
- package/dist/codemirror/byteOffset.js +19 -0
- package/dist/codemirror/byteOffset.js.map +1 -0
- package/dist/codemirror/caretLayer.d.ts +19 -0
- package/dist/codemirror/caretLayer.js +44 -0
- package/dist/codemirror/caretLayer.js.map +1 -0
- package/dist/codemirror/clipboard/copyRich.d.ts +37 -0
- package/dist/codemirror/clipboard/copyRich.js +72 -0
- package/dist/codemirror/clipboard/copyRich.js.map +1 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.d.ts +20 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.js +91 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.js.map +1 -0
- package/dist/codemirror/clipboard/pasteMarkdown.d.ts +18 -0
- package/dist/codemirror/clipboard/pasteMarkdown.js +41 -0
- package/dist/codemirror/clipboard/pasteMarkdown.js.map +1 -0
- package/dist/codemirror/code/codeHighlight.d.ts +13 -0
- package/dist/codemirror/code/codeHighlight.js +15 -0
- package/dist/codemirror/code/codeHighlight.js.map +1 -0
- package/dist/codemirror/code/codeLangAffordance.d.ts +17 -0
- package/dist/codemirror/code/codeLangAffordance.js +80 -0
- package/dist/codemirror/code/codeLangAffordance.js.map +1 -0
- package/dist/codemirror/code/codeLanguageMenu.d.ts +40 -0
- package/dist/codemirror/code/codeLanguageMenu.js +184 -0
- package/dist/codemirror/code/codeLanguageMenu.js.map +1 -0
- package/dist/codemirror/code/codePalette.d.ts +39 -0
- package/dist/codemirror/code/codePalette.js +53 -0
- package/dist/codemirror/code/codePalette.js.map +1 -0
- package/dist/codemirror/code/fenceAutoClose.d.ts +30 -0
- package/dist/codemirror/code/fenceAutoClose.js +171 -0
- package/dist/codemirror/code/fenceAutoClose.js.map +1 -0
- package/dist/codemirror/code/fenceCaretGuard.d.ts +28 -0
- package/dist/codemirror/code/fenceCaretGuard.js +66 -0
- package/dist/codemirror/code/fenceCaretGuard.js.map +1 -0
- package/dist/codemirror/code/fenceDeleteGuards.d.ts +22 -0
- package/dist/codemirror/code/fenceDeleteGuards.js +105 -0
- package/dist/codemirror/code/fenceDeleteGuards.js.map +1 -0
- package/dist/codemirror/code/fenceTabIndent.d.ts +8 -0
- package/dist/codemirror/code/fenceTabIndent.js +50 -0
- package/dist/codemirror/code/fenceTabIndent.js.map +1 -0
- package/dist/codemirror/code/highlightFence.d.ts +25 -0
- package/dist/codemirror/code/highlightFence.js +45 -0
- package/dist/codemirror/code/highlightFence.js.map +1 -0
- package/dist/codemirror/code/index.d.ts +9 -0
- package/dist/codemirror/code/index.js +8 -0
- package/dist/codemirror/code/index.js.map +1 -0
- package/dist/codemirror/core/codeContext.d.ts +32 -0
- package/dist/codemirror/core/codeContext.js +22 -0
- package/dist/codemirror/core/codeContext.js.map +1 -0
- package/dist/codemirror/core/editorTestHarness.d.ts +31 -0
- package/dist/codemirror/core/editorTestHarness.js +69 -0
- package/dist/codemirror/core/editorTestHarness.js.map +1 -0
- package/dist/codemirror/core/editorTheme.d.ts +39 -0
- package/dist/codemirror/core/editorTheme.js +498 -0
- package/dist/codemirror/core/editorTheme.js.map +1 -0
- package/dist/codemirror/core/hostFacets.d.ts +58 -0
- package/dist/codemirror/core/hostFacets.js +27 -0
- package/dist/codemirror/core/hostFacets.js.map +1 -0
- package/dist/codemirror/core/hrWidget.d.ts +22 -0
- package/dist/codemirror/core/hrWidget.js +22 -0
- package/dist/codemirror/core/hrWidget.js.map +1 -0
- package/dist/codemirror/core/htmlEscape.d.ts +6 -0
- package/dist/codemirror/core/htmlEscape.js +12 -0
- package/dist/codemirror/core/htmlEscape.js.map +1 -0
- package/dist/codemirror/core/index.d.ts +13 -0
- package/dist/codemirror/core/index.js +9 -0
- package/dist/codemirror/core/index.js.map +1 -0
- package/dist/codemirror/core/inlineScan.d.ts +49 -0
- package/dist/codemirror/core/inlineScan.js +31 -0
- package/dist/codemirror/core/inlineScan.js.map +1 -0
- package/dist/codemirror/core/lineStructure.d.ts +47 -0
- package/dist/codemirror/core/lineStructure.js +64 -0
- package/dist/codemirror/core/lineStructure.js.map +1 -0
- package/dist/codemirror/core/linkResolution.d.ts +36 -0
- package/dist/codemirror/core/linkResolution.js +58 -0
- package/dist/codemirror/core/linkResolution.js.map +1 -0
- package/dist/codemirror/core/paint.d.ts +118 -0
- package/dist/codemirror/core/paint.js +42 -0
- package/dist/codemirror/core/paint.js.map +1 -0
- package/dist/codemirror/core/parseToEnd.d.ts +24 -0
- package/dist/codemirror/core/parseToEnd.js +38 -0
- package/dist/codemirror/core/parseToEnd.js.map +1 -0
- package/dist/codemirror/core/plugin.d.ts +32 -0
- package/dist/codemirror/core/plugin.js +161 -0
- package/dist/codemirror/core/plugin.js.map +1 -0
- package/dist/codemirror/core/registry.d.ts +38 -0
- package/dist/codemirror/core/registry.js +133 -0
- package/dist/codemirror/core/registry.js.map +1 -0
- package/dist/codemirror/core/testKeys.d.ts +17 -0
- package/dist/codemirror/core/testKeys.js +11 -0
- package/dist/codemirror/core/testKeys.js.map +1 -0
- package/dist/codemirror/core/treeAt.d.ts +31 -0
- package/dist/codemirror/core/treeAt.js +10 -0
- package/dist/codemirror/core/treeAt.js.map +1 -0
- package/dist/codemirror/extensions/footnoteExtension.d.ts +9 -0
- package/dist/codemirror/extensions/footnoteExtension.js +14 -0
- package/dist/codemirror/extensions/footnoteExtension.js.map +1 -0
- package/dist/codemirror/extensions/highlightExtension.d.ts +9 -0
- package/dist/codemirror/extensions/highlightExtension.js +14 -0
- package/dist/codemirror/extensions/highlightExtension.js.map +1 -0
- package/dist/codemirror/extensions/index.d.ts +12 -0
- package/dist/codemirror/extensions/index.js +19 -0
- package/dist/codemirror/extensions/index.js.map +1 -0
- package/dist/codemirror/extensions/loader.d.ts +24 -0
- package/dist/codemirror/extensions/loader.js +33 -0
- package/dist/codemirror/extensions/loader.js.map +1 -0
- package/dist/codemirror/extensions/mathExtension.d.ts +9 -0
- package/dist/codemirror/extensions/mathExtension.js +14 -0
- package/dist/codemirror/extensions/mathExtension.js.map +1 -0
- package/dist/codemirror/extensions/mermaidExtension.d.ts +9 -0
- package/dist/codemirror/extensions/mermaidExtension.js +12 -0
- package/dist/codemirror/extensions/mermaidExtension.js.map +1 -0
- package/dist/codemirror/extensions/tableExtension.d.ts +13 -0
- package/dist/codemirror/extensions/tableExtension.js +39 -0
- package/dist/codemirror/extensions/tableExtension.js.map +1 -0
- package/dist/codemirror/extensions/types.d.ts +39 -0
- package/dist/codemirror/extensions/types.js +1 -0
- package/dist/codemirror/extensions/types.js.map +1 -0
- package/dist/codemirror/extensions/wikilinkExtension.d.ts +9 -0
- package/dist/codemirror/extensions/wikilinkExtension.js +14 -0
- package/dist/codemirror/extensions/wikilinkExtension.js.map +1 -0
- package/dist/codemirror/features/blockCommandSteps.d.ts +20 -0
- package/dist/codemirror/features/blockCommandSteps.js +83 -0
- package/dist/codemirror/features/blockCommandSteps.js.map +1 -0
- package/dist/codemirror/footnote/footnotePlugin.d.ts +10 -0
- package/dist/codemirror/footnote/footnotePlugin.js +78 -0
- package/dist/codemirror/footnote/footnotePlugin.js.map +1 -0
- package/dist/codemirror/footnote/footnoteScanRule.d.ts +9 -0
- package/dist/codemirror/footnote/footnoteScanRule.js +12 -0
- package/dist/codemirror/footnote/footnoteScanRule.js.map +1 -0
- package/dist/codemirror/footnote/index.d.ts +2 -0
- package/dist/codemirror/footnote/index.js +2 -0
- package/dist/codemirror/footnote/index.js.map +1 -0
- package/dist/codemirror/format/blockCommands.d.ts +17 -0
- package/dist/codemirror/format/blockCommands.js +176 -0
- package/dist/codemirror/format/blockCommands.js.map +1 -0
- package/dist/codemirror/format/formatCommands.d.ts +11 -0
- package/dist/codemirror/format/formatCommands.js +132 -0
- package/dist/codemirror/format/formatCommands.js.map +1 -0
- package/dist/codemirror/format/index.d.ts +4 -0
- package/dist/codemirror/format/index.js +3 -0
- package/dist/codemirror/format/index.js.map +1 -0
- package/dist/codemirror/highlight/highlightPlugin.d.ts +10 -0
- package/dist/codemirror/highlight/highlightPlugin.js +66 -0
- package/dist/codemirror/highlight/highlightPlugin.js.map +1 -0
- package/dist/codemirror/highlight/highlightScanRule.d.ts +8 -0
- package/dist/codemirror/highlight/highlightScanRule.js +12 -0
- package/dist/codemirror/highlight/highlightScanRule.js.map +1 -0
- package/dist/codemirror/highlight/index.d.ts +2 -0
- package/dist/codemirror/highlight/index.js +2 -0
- package/dist/codemirror/highlight/index.js.map +1 -0
- package/dist/codemirror/html/htmlWidget.d.ts +23 -0
- package/dist/codemirror/html/htmlWidget.js +67 -0
- package/dist/codemirror/html/htmlWidget.js.map +1 -0
- package/dist/codemirror/html/index.d.ts +4 -0
- package/dist/codemirror/html/index.js +2 -0
- package/dist/codemirror/html/index.js.map +1 -0
- package/dist/codemirror/image/imageActionMenu.d.ts +14 -0
- package/dist/codemirror/image/imageActionMenu.js +112 -0
- package/dist/codemirror/image/imageActionMenu.js.map +1 -0
- package/dist/codemirror/image/imageEditEvent.d.ts +20 -0
- package/dist/codemirror/image/imageEditEvent.js +6 -0
- package/dist/codemirror/image/imageEditEvent.js.map +1 -0
- package/dist/codemirror/image/imageInsertHandlers.d.ts +13 -0
- package/dist/codemirror/image/imageInsertHandlers.js +64 -0
- package/dist/codemirror/image/imageInsertHandlers.js.map +1 -0
- package/dist/codemirror/image/imageWidget.d.ts +49 -0
- package/dist/codemirror/image/imageWidget.js +83 -0
- package/dist/codemirror/image/imageWidget.js.map +1 -0
- package/dist/codemirror/image/index.d.ts +8 -0
- package/dist/codemirror/image/index.js +5 -0
- package/dist/codemirror/image/index.js.map +1 -0
- package/dist/codemirror/interaction/clickModel.d.ts +43 -0
- package/dist/codemirror/interaction/clickModel.js +99 -0
- package/dist/codemirror/interaction/clickModel.js.map +1 -0
- package/dist/codemirror/interaction/cursorModel.d.ts +10 -0
- package/dist/codemirror/interaction/cursorModel.js +58 -0
- package/dist/codemirror/interaction/cursorModel.js.map +1 -0
- package/dist/codemirror/interaction/deleteNormalizer.d.ts +18 -0
- package/dist/codemirror/interaction/deleteNormalizer.js +157 -0
- package/dist/codemirror/interaction/deleteNormalizer.js.map +1 -0
- package/dist/codemirror/interaction/flankingGuard.d.ts +20 -0
- package/dist/codemirror/interaction/flankingGuard.js +48 -0
- package/dist/codemirror/interaction/flankingGuard.js.map +1 -0
- package/dist/codemirror/interaction/index.d.ts +7 -0
- package/dist/codemirror/interaction/index.js +6 -0
- package/dist/codemirror/interaction/index.js.map +1 -0
- package/dist/codemirror/interaction/visiblePosition.d.ts +43 -0
- package/dist/codemirror/interaction/visiblePosition.js +98 -0
- package/dist/codemirror/interaction/visiblePosition.js.map +1 -0
- package/dist/codemirror/list/bulletWidget.d.ts +61 -0
- package/dist/codemirror/list/bulletWidget.js +84 -0
- package/dist/codemirror/list/bulletWidget.js.map +1 -0
- package/dist/codemirror/list/index.d.ts +4 -0
- package/dist/codemirror/list/index.js +3 -0
- package/dist/codemirror/list/index.js.map +1 -0
- package/dist/codemirror/list/listContinuation.d.ts +28 -0
- package/dist/codemirror/list/listContinuation.js +61 -0
- package/dist/codemirror/list/listContinuation.js.map +1 -0
- package/dist/codemirror/list/listIndent.d.ts +8 -0
- package/dist/codemirror/list/listIndent.js +95 -0
- package/dist/codemirror/list/listIndent.js.map +1 -0
- package/dist/codemirror/list/taskCheckboxWidget.d.ts +42 -0
- package/dist/codemirror/list/taskCheckboxWidget.js +47 -0
- package/dist/codemirror/list/taskCheckboxWidget.js.map +1 -0
- package/dist/codemirror/math/index.d.ts +3 -0
- package/dist/codemirror/math/index.js +2 -0
- package/dist/codemirror/math/index.js.map +1 -0
- package/dist/codemirror/math/mathPlugin.d.ts +10 -0
- package/dist/codemirror/math/mathPlugin.js +73 -0
- package/dist/codemirror/math/mathPlugin.js.map +1 -0
- package/dist/codemirror/math/mathScanRule.d.ts +16 -0
- package/dist/codemirror/math/mathScanRule.js +18 -0
- package/dist/codemirror/math/mathScanRule.js.map +1 -0
- package/dist/codemirror/math/mathWidget.d.ts +17 -0
- package/dist/codemirror/math/mathWidget.js +58 -0
- package/dist/codemirror/math/mathWidget.js.map +1 -0
- package/dist/codemirror/mermaid/index.d.ts +4 -0
- package/dist/codemirror/mermaid/index.js +3 -0
- package/dist/codemirror/mermaid/index.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidPlugin.d.ts +25 -0
- package/dist/codemirror/mermaid/mermaidPlugin.js +78 -0
- package/dist/codemirror/mermaid/mermaidPlugin.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidRender.d.ts +42 -0
- package/dist/codemirror/mermaid/mermaidRender.js +136 -0
- package/dist/codemirror/mermaid/mermaidRender.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidWidget.d.ts +42 -0
- package/dist/codemirror/mermaid/mermaidWidget.js +142 -0
- package/dist/codemirror/mermaid/mermaidWidget.js.map +1 -0
- package/dist/codemirror/selectionLayer.d.ts +43 -0
- package/dist/codemirror/selectionLayer.js +135 -0
- package/dist/codemirror/selectionLayer.js.map +1 -0
- package/dist/codemirror/table/cellDividerWidget.d.ts +9 -0
- package/dist/codemirror/table/cellDividerWidget.js +19 -0
- package/dist/codemirror/table/cellDividerWidget.js.map +1 -0
- package/dist/codemirror/table/index.d.ts +5 -0
- package/dist/codemirror/table/index.js +4 -0
- package/dist/codemirror/table/index.js.map +1 -0
- package/dist/codemirror/table/tableArmed.d.ts +27 -0
- package/dist/codemirror/table/tableArmed.js +43 -0
- package/dist/codemirror/table/tableArmed.js.map +1 -0
- package/dist/codemirror/table/tableCell.d.ts +19 -0
- package/dist/codemirror/table/tableCell.js +15 -0
- package/dist/codemirror/table/tableCell.js.map +1 -0
- package/dist/codemirror/table/tableCellNav.d.ts +51 -0
- package/dist/codemirror/table/tableCellNav.js +47 -0
- package/dist/codemirror/table/tableCellNav.js.map +1 -0
- package/dist/codemirror/table/tableContextMenu.d.ts +22 -0
- package/dist/codemirror/table/tableContextMenu.js +139 -0
- package/dist/codemirror/table/tableContextMenu.js.map +1 -0
- package/dist/codemirror/table/tableEditCommands.d.ts +29 -0
- package/dist/codemirror/table/tableEditCommands.js +86 -0
- package/dist/codemirror/table/tableEditCommands.js.map +1 -0
- package/dist/codemirror/table/tableExcerpt.d.ts +29 -0
- package/dist/codemirror/table/tableExcerpt.js +34 -0
- package/dist/codemirror/table/tableExcerpt.js.map +1 -0
- package/dist/codemirror/table/tableField.d.ts +6 -0
- package/dist/codemirror/table/tableField.js +37 -0
- package/dist/codemirror/table/tableField.js.map +1 -0
- package/dist/codemirror/table/tableGeometry.d.ts +33 -0
- package/dist/codemirror/table/tableGeometry.js +28 -0
- package/dist/codemirror/table/tableGeometry.js.map +1 -0
- package/dist/codemirror/table/tableInline.d.ts +37 -0
- package/dist/codemirror/table/tableInline.js +105 -0
- package/dist/codemirror/table/tableInline.js.map +1 -0
- package/dist/codemirror/table/tableModel.d.ts +45 -0
- package/dist/codemirror/table/tableModel.js +98 -0
- package/dist/codemirror/table/tableModel.js.map +1 -0
- package/dist/codemirror/tablev2/bridgeRules.d.ts +48 -0
- package/dist/codemirror/tablev2/bridgeRules.js +42 -0
- package/dist/codemirror/tablev2/bridgeRules.js.map +1 -0
- package/dist/codemirror/tablev2/cellEditingSurface.d.ts +79 -0
- package/dist/codemirror/tablev2/cellEditingSurface.js +70 -0
- package/dist/codemirror/tablev2/cellEditingSurface.js.map +1 -0
- package/dist/codemirror/tablev2/cellText.d.ts +11 -0
- package/dist/codemirror/tablev2/cellText.js +12 -0
- package/dist/codemirror/tablev2/cellText.js.map +1 -0
- package/dist/codemirror/tablev2/inlineCellSurface.d.ts +38 -0
- package/dist/codemirror/tablev2/inlineCellSurface.js +162 -0
- package/dist/codemirror/tablev2/inlineCellSurface.js.map +1 -0
- package/dist/codemirror/tablev2/overlayCellSurface.d.ts +36 -0
- package/dist/codemirror/tablev2/overlayCellSurface.js +150 -0
- package/dist/codemirror/tablev2/overlayCellSurface.js.map +1 -0
- package/dist/codemirror/tablev2/tableEditingSteps.d.ts +12 -0
- package/dist/codemirror/tablev2/tableEditingSteps.js +518 -0
- package/dist/codemirror/tablev2/tableEditingSteps.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.d.ts +23 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.js +89 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Interaction.d.ts +29 -0
- package/dist/codemirror/tablev2/tableV2Interaction.js +224 -0
- package/dist/codemirror/tablev2/tableV2Interaction.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Selection.d.ts +45 -0
- package/dist/codemirror/tablev2/tableV2Selection.js +81 -0
- package/dist/codemirror/tablev2/tableV2Selection.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Sync.d.ts +13 -0
- package/dist/codemirror/tablev2/tableV2Sync.js +13 -0
- package/dist/codemirror/tablev2/tableV2Sync.js.map +1 -0
- package/dist/codemirror/tablev2/tableWidgetV2.d.ts +46 -0
- package/dist/codemirror/tablev2/tableWidgetV2.js +139 -0
- package/dist/codemirror/tablev2/tableWidgetV2.js.map +1 -0
- package/dist/codemirror/updateBus.d.ts +29 -0
- package/dist/codemirror/updateBus.js +26 -0
- package/dist/codemirror/updateBus.js.map +1 -0
- package/dist/codemirror/wikilink/index.d.ts +3 -0
- package/dist/codemirror/wikilink/index.js +2 -0
- package/dist/codemirror/wikilink/index.js.map +1 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.d.ts +40 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.js +89 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.js.map +1 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.d.ts +9 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.js +13 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.js.map +1 -0
- package/dist/frontmatter.d.ts +79 -0
- package/dist/frontmatter.js +58 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/imageInsert.d.ts +66 -0
- package/dist/imageInsert.js +111 -0
- package/dist/imageInsert.js.map +1 -0
- package/dist/imageSrcResolver.d.ts +55 -0
- package/dist/imageSrcResolver.js +49 -0
- package/dist/imageSrcResolver.js.map +1 -0
- package/dist/index.d.ts +36 -880
- package/dist/index.js +130 -5996
- package/dist/index.js.map +1 -1
- package/dist/links/wikilink.d.ts +26 -0
- package/dist/links/wikilink.js +54 -0
- package/dist/links/wikilink.js.map +1 -0
- package/dist/links/workspaceLink.d.ts +34 -0
- package/dist/links/workspaceLink.js +60 -0
- package/dist/links/workspaceLink.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__publicField
|
|
3
|
+
} from "../../chunk-V6TY7KAL.js";
|
|
4
|
+
import { syntaxTree } from "@codemirror/language";
|
|
5
|
+
import { StateField } from "@codemirror/state";
|
|
6
|
+
import { Decoration, EditorView, WidgetType } from "@codemirror/view";
|
|
7
|
+
import { inlineScanRulesFacet } from "../core/inlineScan.js";
|
|
8
|
+
import { renderCellInto } from "../table/tableCell.js";
|
|
9
|
+
import { parseTableNode } from "../table/tableModel.js";
|
|
10
|
+
import { tableV2Sync } from "./tableV2Sync.js";
|
|
11
|
+
function fillCell(el, cell, row, col, rules) {
|
|
12
|
+
renderCellInto(el, cell.html, rules);
|
|
13
|
+
el.dataset.row = String(row);
|
|
14
|
+
el.dataset.col = String(col);
|
|
15
|
+
el.dataset.cellFrom = String(cell.from);
|
|
16
|
+
}
|
|
17
|
+
const ROW_LINE_PX = 28;
|
|
18
|
+
const HEADER_PX = 30;
|
|
19
|
+
const ROW_CHARS_PER_LINE = 85;
|
|
20
|
+
function estimateTableHeight(data) {
|
|
21
|
+
let height = HEADER_PX;
|
|
22
|
+
for (const row of data.rows) {
|
|
23
|
+
const rowChars = row.reduce((sum, cell) => sum + Math.max(1, cell.to - cell.from), 0);
|
|
24
|
+
height += Math.max(1, Math.ceil(rowChars / ROW_CHARS_PER_LINE)) * ROW_LINE_PX;
|
|
25
|
+
}
|
|
26
|
+
return height;
|
|
27
|
+
}
|
|
28
|
+
class TableWidgetV2 extends WidgetType {
|
|
29
|
+
constructor(data, sourceFrom, sourceTo) {
|
|
30
|
+
super();
|
|
31
|
+
__publicField(this, "data", data);
|
|
32
|
+
__publicField(this, "sourceFrom", sourceFrom);
|
|
33
|
+
__publicField(this, "sourceTo", sourceTo);
|
|
34
|
+
__publicField(this, "heightEstimate");
|
|
35
|
+
this.heightEstimate = estimateTableHeight(data);
|
|
36
|
+
}
|
|
37
|
+
eq(other) {
|
|
38
|
+
return other.sourceFrom === this.sourceFrom && other.sourceTo === this.sourceTo && JSON.stringify(other.data) === JSON.stringify(this.data);
|
|
39
|
+
}
|
|
40
|
+
toDOM(view) {
|
|
41
|
+
const rules = view.state.facet(inlineScanRulesFacet);
|
|
42
|
+
const wrap = document.createElement("div");
|
|
43
|
+
wrap.className = "cm-tablev2-wrap cm-table-wrap";
|
|
44
|
+
wrap.dataset.tablev2From = String(this.sourceFrom);
|
|
45
|
+
wrap.dataset.tableFrom = String(this.sourceFrom);
|
|
46
|
+
const table = document.createElement("table");
|
|
47
|
+
table.className = "cm-table-widget";
|
|
48
|
+
const thead = document.createElement("thead");
|
|
49
|
+
const headRow = document.createElement("tr");
|
|
50
|
+
this.data.header.forEach((cell, col) => {
|
|
51
|
+
const th = document.createElement("th");
|
|
52
|
+
fillCell(th, cell, 0, col, rules);
|
|
53
|
+
const align = this.data.alignments[col];
|
|
54
|
+
if (align) th.style.textAlign = align;
|
|
55
|
+
headRow.appendChild(th);
|
|
56
|
+
});
|
|
57
|
+
thead.appendChild(headRow);
|
|
58
|
+
table.appendChild(thead);
|
|
59
|
+
const tbody = document.createElement("tbody");
|
|
60
|
+
this.data.rows.forEach((cells, r) => {
|
|
61
|
+
const tr = document.createElement("tr");
|
|
62
|
+
cells.forEach((cell, col) => {
|
|
63
|
+
const td = document.createElement("td");
|
|
64
|
+
fillCell(td, cell, r + 1, col, rules);
|
|
65
|
+
const align = this.data.alignments[col];
|
|
66
|
+
if (align) td.style.textAlign = align;
|
|
67
|
+
tr.appendChild(td);
|
|
68
|
+
});
|
|
69
|
+
tbody.appendChild(tr);
|
|
70
|
+
});
|
|
71
|
+
table.appendChild(tbody);
|
|
72
|
+
wrap.appendChild(table);
|
|
73
|
+
return wrap;
|
|
74
|
+
}
|
|
75
|
+
updateDOM(dom, view) {
|
|
76
|
+
const rules = view.state.facet(inlineScanRulesFacet);
|
|
77
|
+
if (dom.dataset.tablev2From === void 0) return false;
|
|
78
|
+
const rows = dom.querySelectorAll("tr");
|
|
79
|
+
if (rows.length !== 1 + this.data.rows.length) return false;
|
|
80
|
+
const grid = [this.data.header, ...this.data.rows];
|
|
81
|
+
for (let r = 0; r < grid.length; r++) {
|
|
82
|
+
const cells = rows[r].children;
|
|
83
|
+
if (cells.length !== grid[r].length) return false;
|
|
84
|
+
}
|
|
85
|
+
dom.dataset.tablev2From = String(this.sourceFrom);
|
|
86
|
+
dom.dataset.tableFrom = String(this.sourceFrom);
|
|
87
|
+
for (let r = 0; r < grid.length; r++) {
|
|
88
|
+
const cells = rows[r].children;
|
|
89
|
+
grid[r].forEach((cell, col) => {
|
|
90
|
+
fillCell(cells[col], cell, r, col, rules);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
// The surface owns all pointer/keyboard interaction inside the grid; CM
|
|
96
|
+
// resolving those events against the atomic widget would fight it.
|
|
97
|
+
ignoreEvent() {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
get estimatedHeight() {
|
|
101
|
+
return this.heightEstimate;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function buildDecorations(state) {
|
|
105
|
+
const ranges = [];
|
|
106
|
+
syntaxTree(state).iterate({
|
|
107
|
+
enter(node) {
|
|
108
|
+
if (node.name !== "Table") return;
|
|
109
|
+
for (const model of parseTableNode(state, node.node)) {
|
|
110
|
+
ranges.push(
|
|
111
|
+
Decoration.replace({
|
|
112
|
+
widget: new TableWidgetV2(model.data, model.from, model.to),
|
|
113
|
+
block: true
|
|
114
|
+
}).range(model.from, model.to)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return Decoration.set(ranges, true);
|
|
120
|
+
}
|
|
121
|
+
function tableV2(surface) {
|
|
122
|
+
const field = StateField.define({
|
|
123
|
+
create: buildDecorations,
|
|
124
|
+
update(value, tr) {
|
|
125
|
+
if (tr.docChanged || syntaxTree(tr.state) !== syntaxTree(tr.startState)) {
|
|
126
|
+
return buildDecorations(tr.state);
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
},
|
|
130
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
131
|
+
});
|
|
132
|
+
return [field, tableV2Sync(surface)];
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
TableWidgetV2,
|
|
136
|
+
estimateTableHeight,
|
|
137
|
+
tableV2
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=tableWidgetV2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/tablev2/tableWidgetV2.ts"],"sourcesContent":["/**\n * V2 table widget (ADR 0001): pure render, stable lifecycle, no editors inside.\n *\n * Lifecycle contract — the make-or-break constraint:\n * - `eq` compares source anchors + data, so ANY doc change (offsets shift)\n * makes CM consult `updateDOM`.\n * - `updateDOM` patches cell contents IN PLACE (element identity preserved)\n * and re-stamps the source anchor; it declines (→ full recreate) only when\n * the grid SHAPE changed.\n * - After either path, the tableV2 extension calls the active surface's\n * `reanchor` so an in-progress cell edit survives.\n *\n * Editing is delegated entirely to a {@link CellEditingSurface}; this file has\n * no caret, focus, or selection logic by design.\n */\n\nimport { syntaxTree } from \"@codemirror/language\";\nimport { type EditorState, StateField, type Extension, type Range } from \"@codemirror/state\";\nimport { Decoration, type DecorationSet, EditorView, WidgetType } from \"@codemirror/view\";\n\nimport { inlineScanRulesFacet, type InlineScanRule } from \"../core/inlineScan\";\nimport { renderCellInto } from \"../table/tableCell\";\nimport { parseTableNode, type TableCellData, type TableData } from \"../table/tableModel\";\nimport { type CellEditingSurface } from \"./cellEditingSurface\";\nimport { tableV2Sync } from \"./tableV2Sync\";\n\nfunction fillCell(\n el: HTMLElement,\n cell: TableCellData,\n row: number,\n col: number,\n rules: readonly InlineScanRule[],\n): void {\n renderCellInto(el, cell.html, rules);\n el.dataset.row = String(row);\n el.dataset.col = String(col);\n // The structure menu's row/column hover-tint locates cells by source pos.\n el.dataset.cellFrom = String(cell.from);\n}\n\n/** One rendered text line inside a cell (theme line-height + padding). */\nconst ROW_LINE_PX = 28;\n/** Header row + top/bottom borders. */\nconst HEADER_PX = 30;\n/** Characters a full table row fits per rendered line at the panel's usual\n * width. Auto layout shares this across columns proportionally to content,\n * so per-ROW totals predict wrapping far better than any per-column guess. */\nconst ROW_CHARS_PER_LINE = 85;\n\n/**\n * Wrap-aware height estimate for an unmeasured table. CM6 places everything\n * below an unmeasured widget using this number; the previous one-line-per-row\n * assumption under-estimated prose-heavy tables by 3-5×, so a fast scroll ran\n * on geometry that was off by hundreds of px per table — and the correction,\n * landing mid-interaction, yanked the viewport and re-anchored selections\n * onto whatever content shifted under the pointer.\n */\nexport function estimateTableHeight(data: TableData): number {\n let height = HEADER_PX;\n for (const row of data.rows) {\n const rowChars = row.reduce((sum, cell) => sum + Math.max(1, cell.to - cell.from), 0);\n height += Math.max(1, Math.ceil(rowChars / ROW_CHARS_PER_LINE)) * ROW_LINE_PX;\n }\n return height;\n}\n\nexport class TableWidgetV2 extends WidgetType {\n private readonly heightEstimate: number;\n\n constructor(\n readonly data: TableData,\n readonly sourceFrom: number,\n readonly sourceTo: number,\n ) {\n super();\n this.heightEstimate = estimateTableHeight(data);\n }\n\n override eq(other: TableWidgetV2): boolean {\n return (\n other.sourceFrom === this.sourceFrom &&\n other.sourceTo === this.sourceTo &&\n JSON.stringify(other.data) === JSON.stringify(this.data)\n );\n }\n\n override toDOM(view: EditorView): HTMLElement {\n const rules = view.state.facet(inlineScanRulesFacet);\n const wrap = document.createElement(\"div\");\n // Both generations of class/stamp: cm-table-wrap carries the existing\n // theme + armed-delete styling; the v2 markers are what the surface reads.\n wrap.className = \"cm-tablev2-wrap cm-table-wrap\";\n wrap.dataset.tablev2From = String(this.sourceFrom);\n wrap.dataset.tableFrom = String(this.sourceFrom);\n\n const table = document.createElement(\"table\");\n table.className = \"cm-table-widget\";\n const thead = document.createElement(\"thead\");\n const headRow = document.createElement(\"tr\");\n this.data.header.forEach((cell, col) => {\n const th = document.createElement(\"th\");\n fillCell(th, cell, 0, col, rules);\n const align = this.data.alignments[col];\n if (align) th.style.textAlign = align;\n headRow.appendChild(th);\n });\n thead.appendChild(headRow);\n table.appendChild(thead);\n\n const tbody = document.createElement(\"tbody\");\n this.data.rows.forEach((cells, r) => {\n const tr = document.createElement(\"tr\");\n cells.forEach((cell, col) => {\n const td = document.createElement(\"td\");\n fillCell(td, cell, r + 1, col, rules);\n const align = this.data.alignments[col];\n if (align) td.style.textAlign = align;\n tr.appendChild(td);\n });\n tbody.appendChild(tr);\n });\n table.appendChild(tbody);\n wrap.appendChild(table);\n return wrap;\n }\n\n override updateDOM(dom: HTMLElement, view: EditorView): boolean {\n const rules = view.state.facet(inlineScanRulesFacet);\n if (dom.dataset.tablev2From === undefined) return false;\n const rows = dom.querySelectorAll(\"tr\");\n if (rows.length !== 1 + this.data.rows.length) return false;\n const grid: TableCellData[][] = [this.data.header, ...this.data.rows];\n for (let r = 0; r < grid.length; r++) {\n const cells = rows[r].children;\n if (cells.length !== grid[r].length) return false;\n }\n dom.dataset.tablev2From = String(this.sourceFrom);\n dom.dataset.tableFrom = String(this.sourceFrom);\n for (let r = 0; r < grid.length; r++) {\n const cells = rows[r].children;\n grid[r].forEach((cell, col) => {\n fillCell(cells[col] as HTMLElement, cell, r, col, rules);\n });\n }\n return true;\n }\n\n // The surface owns all pointer/keyboard interaction inside the grid; CM\n // resolving those events against the atomic widget would fight it.\n override ignoreEvent(): boolean {\n return true;\n }\n\n override get estimatedHeight(): number {\n return this.heightEstimate;\n }\n}\n\nfunction buildDecorations(state: EditorState): DecorationSet {\n const ranges: Range<Decoration>[] = [];\n syntaxTree(state).iterate({\n enter(node) {\n if (node.name !== \"Table\") return;\n for (const model of parseTableNode(state, node.node)) {\n ranges.push(\n Decoration.replace({\n widget: new TableWidgetV2(model.data, model.from, model.to),\n block: true,\n }).range(model.from, model.to),\n );\n }\n },\n });\n return Decoration.set(ranges, true);\n}\n\n/** The V2 table extension: widget field + surface synchronisation. */\nexport function tableV2(surface: CellEditingSurface): Extension {\n const field = StateField.define<DecorationSet>({\n create: buildDecorations,\n update(value, tr) {\n if (tr.docChanged || syntaxTree(tr.state) !== syntaxTree(tr.startState)) {\n return buildDecorations(tr.state);\n }\n return value;\n },\n provide: (f) => EditorView.decorations.from(f),\n });\n return [field, tableV2Sync(surface)];\n}\n"],"mappings":";;;AAgBA,SAAS,kBAAkB;AAC3B,SAA2B,kBAA8C;AACzE,SAAS,YAAgC,YAAY,kBAAkB;AAEvE,SAAS,4BAAiD;AAC1D,SAAS,sBAAsB;AAC/B,SAAS,sBAA0D;AAEnE,SAAS,mBAAmB;AAE5B,SAAS,SACP,IACA,MACA,KACA,KACA,OACM;AACN,iBAAe,IAAI,KAAK,MAAM,KAAK;AACnC,KAAG,QAAQ,MAAM,OAAO,GAAG;AAC3B,KAAG,QAAQ,MAAM,OAAO,GAAG;AAE3B,KAAG,QAAQ,WAAW,OAAO,KAAK,IAAI;AACxC;AAGA,MAAM,cAAc;AAEpB,MAAM,YAAY;AAIlB,MAAM,qBAAqB;AAUpB,SAAS,oBAAoB,MAAyB;AAC3D,MAAI,SAAS;AACb,aAAW,OAAO,KAAK,MAAM;AAC3B,UAAM,WAAW,IAAI,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,IAAI,GAAG,CAAC;AACpF,cAAU,KAAK,IAAI,GAAG,KAAK,KAAK,WAAW,kBAAkB,CAAC,IAAI;AAAA,EACpE;AACA,SAAO;AACT;AAEO,MAAM,sBAAsB,WAAW;AAAA,EAG5C,YACW,MACA,YACA,UACT;AACA,UAAM;AAJG;AACA;AACA;AALX,wBAAiB;AAQf,SAAK,iBAAiB,oBAAoB,IAAI;AAAA,EAChD;AAAA,EAES,GAAG,OAA+B;AACzC,WACE,MAAM,eAAe,KAAK,cAC1B,MAAM,aAAa,KAAK,YACxB,KAAK,UAAU,MAAM,IAAI,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,EAE3D;AAAA,EAES,MAAM,MAA+B;AAC5C,UAAM,QAAQ,KAAK,MAAM,MAAM,oBAAoB;AACnD,UAAM,OAAO,SAAS,cAAc,KAAK;AAGzC,SAAK,YAAY;AACjB,SAAK,QAAQ,cAAc,OAAO,KAAK,UAAU;AACjD,SAAK,QAAQ,YAAY,OAAO,KAAK,UAAU;AAE/C,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,YAAY;AAClB,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,UAAU,SAAS,cAAc,IAAI;AAC3C,SAAK,KAAK,OAAO,QAAQ,CAAC,MAAM,QAAQ;AACtC,YAAM,KAAK,SAAS,cAAc,IAAI;AACtC,eAAS,IAAI,MAAM,GAAG,KAAK,KAAK;AAChC,YAAM,QAAQ,KAAK,KAAK,WAAW,GAAG;AACtC,UAAI,MAAO,IAAG,MAAM,YAAY;AAChC,cAAQ,YAAY,EAAE;AAAA,IACxB,CAAC;AACD,UAAM,YAAY,OAAO;AACzB,UAAM,YAAY,KAAK;AAEvB,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,SAAK,KAAK,KAAK,QAAQ,CAAC,OAAO,MAAM;AACnC,YAAM,KAAK,SAAS,cAAc,IAAI;AACtC,YAAM,QAAQ,CAAC,MAAM,QAAQ;AAC3B,cAAM,KAAK,SAAS,cAAc,IAAI;AACtC,iBAAS,IAAI,MAAM,IAAI,GAAG,KAAK,KAAK;AACpC,cAAM,QAAQ,KAAK,KAAK,WAAW,GAAG;AACtC,YAAI,MAAO,IAAG,MAAM,YAAY;AAChC,WAAG,YAAY,EAAE;AAAA,MACnB,CAAC;AACD,YAAM,YAAY,EAAE;AAAA,IACtB,CAAC;AACD,UAAM,YAAY,KAAK;AACvB,SAAK,YAAY,KAAK;AACtB,WAAO;AAAA,EACT;AAAA,EAES,UAAU,KAAkB,MAA2B;AAC9D,UAAM,QAAQ,KAAK,MAAM,MAAM,oBAAoB;AACnD,QAAI,IAAI,QAAQ,gBAAgB,OAAW,QAAO;AAClD,UAAM,OAAO,IAAI,iBAAiB,IAAI;AACtC,QAAI,KAAK,WAAW,IAAI,KAAK,KAAK,KAAK,OAAQ,QAAO;AACtD,UAAM,OAA0B,CAAC,KAAK,KAAK,QAAQ,GAAG,KAAK,KAAK,IAAI;AACpE,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,QAAQ,KAAK,CAAC,EAAE;AACtB,UAAI,MAAM,WAAW,KAAK,CAAC,EAAE,OAAQ,QAAO;AAAA,IAC9C;AACA,QAAI,QAAQ,cAAc,OAAO,KAAK,UAAU;AAChD,QAAI,QAAQ,YAAY,OAAO,KAAK,UAAU;AAC9C,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,QAAQ,KAAK,CAAC,EAAE;AACtB,WAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,QAAQ;AAC7B,iBAAS,MAAM,GAAG,GAAkB,MAAM,GAAG,KAAK,KAAK;AAAA,MACzD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAIS,cAAuB;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,IAAa,kBAA0B;AACrC,WAAO,KAAK;AAAA,EACd;AACF;AAEA,SAAS,iBAAiB,OAAmC;AAC3D,QAAM,SAA8B,CAAC;AACrC,aAAW,KAAK,EAAE,QAAQ;AAAA,IACxB,MAAM,MAAM;AACV,UAAI,KAAK,SAAS,QAAS;AAC3B,iBAAW,SAAS,eAAe,OAAO,KAAK,IAAI,GAAG;AACpD,eAAO;AAAA,UACL,WAAW,QAAQ;AAAA,YACjB,QAAQ,IAAI,cAAc,MAAM,MAAM,MAAM,MAAM,MAAM,EAAE;AAAA,YAC1D,OAAO;AAAA,UACT,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,EAAE;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO,WAAW,IAAI,QAAQ,IAAI;AACpC;AAGO,SAAS,QAAQ,SAAwC;AAC9D,QAAM,QAAQ,WAAW,OAAsB;AAAA,IAC7C,QAAQ;AAAA,IACR,OAAO,OAAO,IAAI;AAChB,UAAI,GAAG,cAAc,WAAW,GAAG,KAAK,MAAM,WAAW,GAAG,UAAU,GAAG;AACvE,eAAO,iBAAiB,GAAG,KAAK;AAAA,MAClC;AACA,aAAO;AAAA,IACT;AAAA,IACA,SAAS,CAAC,MAAM,WAAW,YAAY,KAAK,CAAC;AAAA,EAC/C,CAAC;AACD,SAAO,CAAC,OAAO,YAAY,OAAO,CAAC;AACrC;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EditorView, ViewUpdate, ViewPlugin } from '@codemirror/view';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Editor-update fan-out that survives `view.setState`.
|
|
5
|
+
*
|
|
6
|
+
* Reactive chrome (toolbar pressed-states, the selection-actions bubble)
|
|
7
|
+
* needs a callback on every selection/doc change. Injecting an
|
|
8
|
+
* `EditorView.updateListener` with `StateEffect.appendConfig` only patches
|
|
9
|
+
* the CURRENT state's configuration — the next `view.setState` (tab switch,
|
|
10
|
+
* file reload) builds a fresh state from the base extensions and the
|
|
11
|
+
* listener silently dies, freezing the chrome at the previous document's
|
|
12
|
+
* context.
|
|
13
|
+
*
|
|
14
|
+
* The bus lives IN the base extensions, so every created state carries it,
|
|
15
|
+
* while subscriptions key on the `EditorView` — the object that is stable
|
|
16
|
+
* across state swaps.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Include once in the editor's base extension list. */
|
|
20
|
+
declare const updateBus: ViewPlugin<{
|
|
21
|
+
update(update: ViewUpdate): void;
|
|
22
|
+
}, undefined>;
|
|
23
|
+
/**
|
|
24
|
+
* Subscribe to every update of `view`, across all its future states.
|
|
25
|
+
* Returns the unsubscribe function — call it in the effect cleanup.
|
|
26
|
+
*/
|
|
27
|
+
declare function onEditorUpdate(view: EditorView, fn: (update: ViewUpdate) => void): () => void;
|
|
28
|
+
|
|
29
|
+
export { onEditorUpdate, updateBus };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../chunk-V6TY7KAL.js";
|
|
2
|
+
import { ViewPlugin } from "@codemirror/view";
|
|
3
|
+
const subscribers = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
const updateBus = ViewPlugin.define((view) => ({
|
|
5
|
+
update(update) {
|
|
6
|
+
const set = subscribers.get(view);
|
|
7
|
+
if (!set) return;
|
|
8
|
+
for (const fn of [...set]) fn(update);
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
11
|
+
function onEditorUpdate(view, fn) {
|
|
12
|
+
let set = subscribers.get(view);
|
|
13
|
+
if (!set) {
|
|
14
|
+
set = /* @__PURE__ */ new Set();
|
|
15
|
+
subscribers.set(view, set);
|
|
16
|
+
}
|
|
17
|
+
set.add(fn);
|
|
18
|
+
return () => {
|
|
19
|
+
set.delete(fn);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
onEditorUpdate,
|
|
24
|
+
updateBus
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=updateBus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/codemirror/updateBus.ts"],"sourcesContent":["/**\n * Editor-update fan-out that survives `view.setState`.\n *\n * Reactive chrome (toolbar pressed-states, the selection-actions bubble)\n * needs a callback on every selection/doc change. Injecting an\n * `EditorView.updateListener` with `StateEffect.appendConfig` only patches\n * the CURRENT state's configuration — the next `view.setState` (tab switch,\n * file reload) builds a fresh state from the base extensions and the\n * listener silently dies, freezing the chrome at the previous document's\n * context.\n *\n * The bus lives IN the base extensions, so every created state carries it,\n * while subscriptions key on the `EditorView` — the object that is stable\n * across state swaps.\n */\n\nimport { ViewPlugin, type EditorView, type ViewUpdate } from \"@codemirror/view\";\n\nconst subscribers = new WeakMap<EditorView, Set<(update: ViewUpdate) => void>>();\n\n/** Include once in the editor's base extension list. */\nexport const updateBus = ViewPlugin.define((view) => ({\n update(update: ViewUpdate) {\n const set = subscribers.get(view);\n if (!set) return;\n for (const fn of [...set]) fn(update);\n },\n}));\n\n/**\n * Subscribe to every update of `view`, across all its future states.\n * Returns the unsubscribe function — call it in the effect cleanup.\n */\nexport function onEditorUpdate(\n view: EditorView,\n fn: (update: ViewUpdate) => void,\n): () => void {\n let set = subscribers.get(view);\n if (!set) {\n set = new Set();\n subscribers.set(view, set);\n }\n set.add(fn);\n return () => {\n set.delete(fn);\n };\n}\n"],"mappings":";AAgBA,SAAS,kBAAoD;AAE7D,MAAM,cAAc,oBAAI,QAAuD;AAGxE,MAAM,YAAY,WAAW,OAAO,CAAC,UAAU;AAAA,EACpD,OAAO,QAAoB;AACzB,UAAM,MAAM,YAAY,IAAI,IAAI;AAChC,QAAI,CAAC,IAAK;AACV,eAAW,MAAM,CAAC,GAAG,GAAG,EAAG,IAAG,MAAM;AAAA,EACtC;AACF,EAAE;AAMK,SAAS,eACd,MACA,IACY;AACZ,MAAI,MAAM,YAAY,IAAI,IAAI;AAC9B,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,gBAAY,IAAI,MAAM,GAAG;AAAA,EAC3B;AACA,MAAI,IAAI,EAAE;AACV,SAAO,MAAM;AACX,QAAI,OAAO,EAAE;AAAA,EACf;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/wikilink/index.ts"],"sourcesContent":["/**\n * Wikilinks: `[[Note]]` / `[[Note|label]]` rendered as navigable links.\n * Target resolution lives in `src/links/`; this folder is the editor surface.\n */\nexport * from \"./wikilinkPlugin\";\n"],"mappings":"AAIA,cAAc;","names":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Facet } from '@codemirror/state';
|
|
2
|
+
import { ViewPlugin, DecorationSet, ViewUpdate } from '@codemirror/view';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wikilink decoration plugin — recognises `[[target]]` and
|
|
6
|
+
* `[[target|alias]]` spans in the visible viewport, hides the
|
|
7
|
+
* `[[` / `|target` / `]]` markup, and styles the visible label as
|
|
8
|
+
* a clickable link.
|
|
9
|
+
*
|
|
10
|
+
* Why a separate plugin from `markdownDecorationsPlugin`: wikilinks
|
|
11
|
+
* aren't part of CommonMark / GFM, so Lezer's parser doesn't emit a
|
|
12
|
+
* node for them. We scan the visible text directly, skipping matches
|
|
13
|
+
* in code contexts (`codeContext.ts`) where `[[…]]` is literal. Same
|
|
14
|
+
* shape as the Tiptap `wikilinkExtension` — port of that logic, not
|
|
15
|
+
* a new design.
|
|
16
|
+
*
|
|
17
|
+
* Click navigation lives in `clickModel.ts` (it shares the
|
|
18
|
+
* link-target resolution path).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
declare const WIKILINK_RE: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* Workspace-relative current file path. Wired by the editor's React
|
|
24
|
+
* shell so the navigation click can resolve relative-style
|
|
25
|
+
* wikilinks (`./note`, `../note`).
|
|
26
|
+
*/
|
|
27
|
+
declare const wikilinkFromPathFacet: Facet<string | undefined, string | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Set of every workspace-relative path. Used to resolve a wikilink
|
|
30
|
+
* target to a real file. Combine prefers the first registered set
|
|
31
|
+
* (the editor only adds one).
|
|
32
|
+
*/
|
|
33
|
+
declare const wikilinkTargetsFacet: Facet<ReadonlySet<string>, ReadonlySet<string>>;
|
|
34
|
+
declare const wikilinkPlugin: ViewPlugin<{
|
|
35
|
+
decorations: DecorationSet;
|
|
36
|
+
atomic: DecorationSet;
|
|
37
|
+
update(update: ViewUpdate): void;
|
|
38
|
+
}, undefined>;
|
|
39
|
+
|
|
40
|
+
export { WIKILINK_RE, wikilinkFromPathFacet, wikilinkPlugin, wikilinkTargetsFacet };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__publicField
|
|
3
|
+
} from "../../chunk-V6TY7KAL.js";
|
|
4
|
+
import { Facet } from "@codemirror/state";
|
|
5
|
+
import {
|
|
6
|
+
Decoration,
|
|
7
|
+
EditorView,
|
|
8
|
+
ViewPlugin
|
|
9
|
+
} from "@codemirror/view";
|
|
10
|
+
import { parseWikilinkBody } from "../../links/wikilink.js";
|
|
11
|
+
import { inCode, viewportTree } from "../core/codeContext.js";
|
|
12
|
+
const WIKILINK_RE = /\[\[([^\]\n]+?)\]\]/g;
|
|
13
|
+
const HIDE = Decoration.replace({});
|
|
14
|
+
const linkMark = Decoration.mark({ class: "cm-wikilink" });
|
|
15
|
+
const wikilinkFromPathFacet = Facet.define({
|
|
16
|
+
combine: (vals) => vals[0]
|
|
17
|
+
});
|
|
18
|
+
const wikilinkTargetsFacet = Facet.define({
|
|
19
|
+
combine: (vals) => vals[0] ?? /* @__PURE__ */ new Set()
|
|
20
|
+
});
|
|
21
|
+
function buildDecorations(view) {
|
|
22
|
+
const builder = [];
|
|
23
|
+
const atomicBuilder = [];
|
|
24
|
+
const tree = viewportTree(view);
|
|
25
|
+
for (const { from, to } of view.visibleRanges) {
|
|
26
|
+
const text = view.state.sliceDoc(from, to);
|
|
27
|
+
WIKILINK_RE.lastIndex = 0;
|
|
28
|
+
let match;
|
|
29
|
+
while ((match = WIKILINK_RE.exec(text)) !== null) {
|
|
30
|
+
const body = match[1] ?? "";
|
|
31
|
+
const { target } = parseWikilinkBody(body);
|
|
32
|
+
if (target === "") continue;
|
|
33
|
+
const matchStart = from + match.index;
|
|
34
|
+
const matchEnd = matchStart + match[0].length;
|
|
35
|
+
if (inCode(tree, matchStart) || inCode(tree, matchEnd - 2)) continue;
|
|
36
|
+
const bodyStart = matchStart + 2;
|
|
37
|
+
const bodyEnd = matchEnd - 2;
|
|
38
|
+
const pipe = body.indexOf("|");
|
|
39
|
+
let labelStart = bodyStart;
|
|
40
|
+
let labelEnd = bodyEnd;
|
|
41
|
+
if (pipe !== -1) {
|
|
42
|
+
const aliasRaw = body.slice(pipe + 1);
|
|
43
|
+
if (aliasRaw.trim() !== "") {
|
|
44
|
+
labelStart = bodyStart + pipe + 1;
|
|
45
|
+
} else {
|
|
46
|
+
labelEnd = bodyStart + pipe;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
builder.push(HIDE.range(matchStart, labelStart));
|
|
50
|
+
atomicBuilder.push(HIDE.range(matchStart, labelStart));
|
|
51
|
+
builder.push(linkMark.range(labelStart, labelEnd));
|
|
52
|
+
builder.push(HIDE.range(labelEnd, matchEnd));
|
|
53
|
+
atomicBuilder.push(HIDE.range(labelEnd, matchEnd));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
decorations: Decoration.set(builder, true),
|
|
58
|
+
atomic: Decoration.set(atomicBuilder, true)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const wikilinkPlugin = ViewPlugin.fromClass(
|
|
62
|
+
class {
|
|
63
|
+
constructor(view) {
|
|
64
|
+
__publicField(this, "decorations");
|
|
65
|
+
__publicField(this, "atomic");
|
|
66
|
+
const built = buildDecorations(view);
|
|
67
|
+
this.decorations = built.decorations;
|
|
68
|
+
this.atomic = built.atomic;
|
|
69
|
+
}
|
|
70
|
+
update(update) {
|
|
71
|
+
if (update.docChanged || update.viewportChanged) {
|
|
72
|
+
const built = buildDecorations(update.view);
|
|
73
|
+
this.decorations = built.decorations;
|
|
74
|
+
this.atomic = built.atomic;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
decorations: (v) => v.decorations,
|
|
80
|
+
provide: (plugin) => EditorView.atomicRanges.of((view) => view.plugin(plugin)?.atomic ?? Decoration.none)
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
export {
|
|
84
|
+
WIKILINK_RE,
|
|
85
|
+
wikilinkFromPathFacet,
|
|
86
|
+
wikilinkPlugin,
|
|
87
|
+
wikilinkTargetsFacet
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=wikilinkPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/wikilink/wikilinkPlugin.ts"],"sourcesContent":["/**\n * Wikilink decoration plugin — recognises `[[target]]` and\n * `[[target|alias]]` spans in the visible viewport, hides the\n * `[[` / `|target` / `]]` markup, and styles the visible label as\n * a clickable link.\n *\n * Why a separate plugin from `markdownDecorationsPlugin`: wikilinks\n * aren't part of CommonMark / GFM, so Lezer's parser doesn't emit a\n * node for them. We scan the visible text directly, skipping matches\n * in code contexts (`codeContext.ts`) where `[[…]]` is literal. Same\n * shape as the Tiptap `wikilinkExtension` — port of that logic, not\n * a new design.\n *\n * Click navigation lives in `clickModel.ts` (it shares the\n * link-target resolution path).\n */\n\nimport { Facet, type Range } from \"@codemirror/state\";\nimport {\n Decoration,\n type DecorationSet,\n EditorView,\n ViewPlugin,\n type PluginValue,\n type ViewUpdate,\n} from \"@codemirror/view\";\n\nimport { parseWikilinkBody } from \"../../links/wikilink\";\nimport { inCode, viewportTree } from \"../core/codeContext\";\n\nexport const WIKILINK_RE = /\\[\\[([^\\]\\n]+?)\\]\\]/g;\n\n/** Hides the `[[ … | ]]` syntax characters. */\nconst HIDE = Decoration.replace({});\n\n/** Class on the visible label so CSS can paint it as a link. */\nconst linkMark = Decoration.mark({ class: \"cm-wikilink\" });\n\n/**\n * Workspace-relative current file path. Wired by the editor's React\n * shell so the navigation click can resolve relative-style\n * wikilinks (`./note`, `../note`).\n */\nexport const wikilinkFromPathFacet = Facet.define<string | undefined, string | undefined>({\n combine: (vals) => vals[0],\n});\n\n/**\n * Set of every workspace-relative path. Used to resolve a wikilink\n * target to a real file. Combine prefers the first registered set\n * (the editor only adds one).\n */\nexport const wikilinkTargetsFacet = Facet.define<\n ReadonlySet<string>,\n ReadonlySet<string>\n>({\n combine: (vals) => vals[0] ?? new Set(),\n});\n\n/**\n * Build the decoration set for the visible viewport. Walks visible\n * ranges, scans each line's text for `[[…]]` matches, and emits\n * hide-replace + mark decorations.\n */\nfunction buildDecorations(view: EditorView): { decorations: DecorationSet; atomic: DecorationSet } {\n const builder: Range<Decoration>[] = [];\n const atomicBuilder: Range<Decoration>[] = [];\n const tree = viewportTree(view);\n\n for (const { from, to } of view.visibleRanges) {\n const text = view.state.sliceDoc(from, to);\n WIKILINK_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = WIKILINK_RE.exec(text)) !== null) {\n const body = match[1] ?? \"\";\n const { target } = parseWikilinkBody(body);\n if (target === \"\") continue;\n\n const matchStart = from + match.index;\n const matchEnd = matchStart + match[0].length;\n if (inCode(tree, matchStart) || inCode(tree, matchEnd - 2)) continue;\n const bodyStart = matchStart + 2; // after `[[`\n const bodyEnd = matchEnd - 2; // before `]]`\n\n // Compute the visible-label range. `[[target|alias]]` shows\n // `alias`; `[[target]]` shows `target`.\n const pipe = body.indexOf(\"|\");\n let labelStart = bodyStart;\n let labelEnd = bodyEnd;\n if (pipe !== -1) {\n const aliasRaw = body.slice(pipe + 1);\n if (aliasRaw.trim() !== \"\") {\n labelStart = bodyStart + pipe + 1;\n } else {\n labelEnd = bodyStart + pipe;\n }\n }\n\n // Hide `[[` (and the `target|` prefix if there's an alias).\n builder.push(HIDE.range(matchStart, labelStart));\n atomicBuilder.push(HIDE.range(matchStart, labelStart));\n // Style the visible label.\n builder.push(linkMark.range(labelStart, labelEnd));\n // Hide `]]` (and the trailing target if the alias was empty).\n builder.push(HIDE.range(labelEnd, matchEnd));\n atomicBuilder.push(HIDE.range(labelEnd, matchEnd));\n }\n }\n\n // Decorations must be sorted by `from` then `startSide`. The\n // produced order from the regex scan is already document-order so\n // `sort: true` is a cheap defensive insurance.\n return {\n decorations: Decoration.set(builder, true),\n atomic: Decoration.set(atomicBuilder, true),\n };\n}\n\nexport const wikilinkPlugin = ViewPlugin.fromClass(\n class implements PluginValue {\n decorations: DecorationSet;\n atomic: DecorationSet;\n constructor(view: EditorView) {\n const built = buildDecorations(view);\n this.decorations = built.decorations;\n this.atomic = built.atomic;\n }\n update(update: ViewUpdate) {\n if (update.docChanged || update.viewportChanged) {\n const built = buildDecorations(update.view);\n this.decorations = built.decorations;\n this.atomic = built.atomic;\n }\n }\n },\n {\n decorations: (v) => v.decorations,\n provide: (plugin) =>\n EditorView.atomicRanges.of((view) => view.plugin(plugin)?.atomic ?? Decoration.none),\n },\n);\n"],"mappings":";;;AAiBA,SAAS,aAAyB;AAClC;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,yBAAyB;AAClC,SAAS,QAAQ,oBAAoB;AAE9B,MAAM,cAAc;AAG3B,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC;AAGlC,MAAM,WAAW,WAAW,KAAK,EAAE,OAAO,cAAc,CAAC;AAOlD,MAAM,wBAAwB,MAAM,OAA+C;AAAA,EACxF,SAAS,CAAC,SAAS,KAAK,CAAC;AAC3B,CAAC;AAOM,MAAM,uBAAuB,MAAM,OAGxC;AAAA,EACA,SAAS,CAAC,SAAS,KAAK,CAAC,KAAK,oBAAI,IAAI;AACxC,CAAC;AAOD,SAAS,iBAAiB,MAAyE;AACjG,QAAM,UAA+B,CAAC;AACtC,QAAM,gBAAqC,CAAC;AAC5C,QAAM,OAAO,aAAa,IAAI;AAE9B,aAAW,EAAE,MAAM,GAAG,KAAK,KAAK,eAAe;AAC7C,UAAM,OAAO,KAAK,MAAM,SAAS,MAAM,EAAE;AACzC,gBAAY,YAAY;AACxB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,IAAI,OAAO,MAAM;AAChD,YAAM,OAAO,MAAM,CAAC,KAAK;AACzB,YAAM,EAAE,OAAO,IAAI,kBAAkB,IAAI;AACzC,UAAI,WAAW,GAAI;AAEnB,YAAM,aAAa,OAAO,MAAM;AAChC,YAAM,WAAW,aAAa,MAAM,CAAC,EAAE;AACvC,UAAI,OAAO,MAAM,UAAU,KAAK,OAAO,MAAM,WAAW,CAAC,EAAG;AAC5D,YAAM,YAAY,aAAa;AAC/B,YAAM,UAAU,WAAW;AAI3B,YAAM,OAAO,KAAK,QAAQ,GAAG;AAC7B,UAAI,aAAa;AACjB,UAAI,WAAW;AACf,UAAI,SAAS,IAAI;AACf,cAAM,WAAW,KAAK,MAAM,OAAO,CAAC;AACpC,YAAI,SAAS,KAAK,MAAM,IAAI;AAC1B,uBAAa,YAAY,OAAO;AAAA,QAClC,OAAO;AACL,qBAAW,YAAY;AAAA,QACzB;AAAA,MACF;AAGA,cAAQ,KAAK,KAAK,MAAM,YAAY,UAAU,CAAC;AAC/C,oBAAc,KAAK,KAAK,MAAM,YAAY,UAAU,CAAC;AAErD,cAAQ,KAAK,SAAS,MAAM,YAAY,QAAQ,CAAC;AAEjD,cAAQ,KAAK,KAAK,MAAM,UAAU,QAAQ,CAAC;AAC3C,oBAAc,KAAK,KAAK,MAAM,UAAU,QAAQ,CAAC;AAAA,IACnD;AAAA,EACF;AAKA,SAAO;AAAA,IACL,aAAa,WAAW,IAAI,SAAS,IAAI;AAAA,IACzC,QAAQ,WAAW,IAAI,eAAe,IAAI;AAAA,EAC5C;AACF;AAEO,MAAM,iBAAiB,WAAW;AAAA,EACvC,MAA6B;AAAA,IAG3B,YAAY,MAAkB;AAF9B;AACA;AAEE,YAAM,QAAQ,iBAAiB,IAAI;AACnC,WAAK,cAAc,MAAM;AACzB,WAAK,SAAS,MAAM;AAAA,IACtB;AAAA,IACA,OAAO,QAAoB;AACzB,UAAI,OAAO,cAAc,OAAO,iBAAiB;AAC/C,cAAM,QAAQ,iBAAiB,OAAO,IAAI;AAC1C,aAAK,cAAc,MAAM;AACzB,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,aAAa,CAAC,MAAM,EAAE;AAAA,IACtB,SAAS,CAAC,WACR,WAAW,aAAa,GAAG,CAAC,SAAS,KAAK,OAAO,MAAM,GAAG,UAAU,WAAW,IAAI;AAAA,EACvF;AACF;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InlineScanRule } from '../core/inlineScan.js';
|
|
2
|
+
import '@codemirror/state';
|
|
3
|
+
|
|
4
|
+
/** `[[target]]` / `[[target|alias]]` for the string renderers — the label the
|
|
5
|
+
* editor shows, styled by the same `.cm-wikilink` rule as body text. */
|
|
6
|
+
|
|
7
|
+
declare const wikilinkScanRule: InlineScanRule;
|
|
8
|
+
|
|
9
|
+
export { wikilinkScanRule };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { parseWikilinkBody } from "../../links/wikilink.js";
|
|
3
|
+
import { escapeText } from "../core/htmlEscape.js";
|
|
4
|
+
import { WIKILINK_RE } from "./wikilinkPlugin.js";
|
|
5
|
+
const wikilinkScanRule = {
|
|
6
|
+
name: "wikilink",
|
|
7
|
+
pattern: WIKILINK_RE,
|
|
8
|
+
render: (match) => `<span class="cm-wikilink">${escapeText(parseWikilinkBody(match[1] ?? "").label)}</span>`
|
|
9
|
+
};
|
|
10
|
+
export {
|
|
11
|
+
wikilinkScanRule
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=wikilinkScanRule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/wikilink/wikilinkScanRule.ts"],"sourcesContent":["/** `[[target]]` / `[[target|alias]]` for the string renderers — the label the\n * editor shows, styled by the same `.cm-wikilink` rule as body text. */\n\nimport { parseWikilinkBody } from \"../../links/wikilink\";\nimport { escapeText } from \"../core/htmlEscape\";\nimport { type InlineScanRule } from \"../core/inlineScan\";\nimport { WIKILINK_RE } from \"./wikilinkPlugin\";\n\nexport const wikilinkScanRule: InlineScanRule = {\n name: \"wikilink\",\n pattern: WIKILINK_RE,\n render: (match) =>\n `<span class=\"cm-wikilink\">${escapeText(parseWikilinkBody(match[1] ?? \"\").label)}</span>`,\n};\n"],"mappings":";AAGA,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAE3B,SAAS,mBAAmB;AAErB,MAAM,mBAAmC;AAAA,EAC9C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ,CAAC,UACP,6BAA6B,WAAW,kBAAkB,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC;AACpF;","names":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML frontmatter parsing + serialization.
|
|
3
|
+
*
|
|
4
|
+
* Convention: `--- yaml ---` block at the very top of a markdown
|
|
5
|
+
* file (before any other content). Format:
|
|
6
|
+
*
|
|
7
|
+
* ---
|
|
8
|
+
* status: draft
|
|
9
|
+
* tags:
|
|
10
|
+
* - application
|
|
11
|
+
* - fellowship
|
|
12
|
+
* ---
|
|
13
|
+
*
|
|
14
|
+
* # Real content starts here
|
|
15
|
+
*
|
|
16
|
+
* This module separates the YAML chunk from the prose chunk so
|
|
17
|
+
* the WYSIWYG editor can render only the prose (no raw `key:
|
|
18
|
+
* value` lines bleeding into the user's writing surface) while a
|
|
19
|
+
* separate properties UI shows / edits the frontmatter.
|
|
20
|
+
*
|
|
21
|
+
* Round-trip rules:
|
|
22
|
+
* - If a file has no frontmatter on disk, we MUST NOT add one
|
|
23
|
+
* on save just because the properties panel exists.
|
|
24
|
+
* - If a file has frontmatter, we serialize it using the same
|
|
25
|
+
* keys/order the user wrote (best effort — yaml lib preserves
|
|
26
|
+
* scalar formatting but not always blank lines / comments).
|
|
27
|
+
* - The fence marker is always exactly `---` on its own line.
|
|
28
|
+
* Some Obsidian users prefer `+++` (TOML) — out of scope for
|
|
29
|
+
* v1; we round-trip those as body content unchanged.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* The frontmatter value type. Each entry is whatever YAML can
|
|
33
|
+
* decode to — strings, numbers, booleans, arrays, nested objects.
|
|
34
|
+
* The properties UI handles primitives + flat arrays cleanly;
|
|
35
|
+
* nested objects fall back to a raw YAML editor.
|
|
36
|
+
*/
|
|
37
|
+
type FrontmatterValue = string | number | boolean | null | FrontmatterValue[] | {
|
|
38
|
+
[key: string]: FrontmatterValue;
|
|
39
|
+
};
|
|
40
|
+
type Frontmatter = Record<string, FrontmatterValue>;
|
|
41
|
+
interface MarkdownDocument {
|
|
42
|
+
/** Parsed YAML key/values. `null` means "no frontmatter block present". */
|
|
43
|
+
frontmatter: Frontmatter | null;
|
|
44
|
+
/** The prose portion — everything after the closing `---`. */
|
|
45
|
+
body: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Split a markdown string into its frontmatter + body. Tolerant
|
|
49
|
+
* of:
|
|
50
|
+
* - No frontmatter at all → `frontmatter: null, body: input`
|
|
51
|
+
* - Malformed YAML → `frontmatter: null, body: input` (we treat
|
|
52
|
+
* the whole thing as prose rather than show parse errors)
|
|
53
|
+
* - Empty frontmatter block (`---\n---`) → `frontmatter: {}`
|
|
54
|
+
*/
|
|
55
|
+
declare function parseFrontmatter(markdown: string): MarkdownDocument;
|
|
56
|
+
/**
|
|
57
|
+
* Recombine frontmatter + body into a markdown string. Inverse
|
|
58
|
+
* of `parseFrontmatter` for legal inputs.
|
|
59
|
+
*
|
|
60
|
+
* - `frontmatter: null` → just the body, no `---` fences added
|
|
61
|
+
* - `frontmatter: {}` → no fences either (an empty frontmatter
|
|
62
|
+
* block is semantically the same as "no frontmatter" and we
|
|
63
|
+
* prefer the cleaner shape on save)
|
|
64
|
+
* - Otherwise → `---\n<yaml>\n---\n<body>`
|
|
65
|
+
*/
|
|
66
|
+
declare function serializeMarkdown(doc: MarkdownDocument): string;
|
|
67
|
+
/**
|
|
68
|
+
* Helper: update a single frontmatter field in a markdown string
|
|
69
|
+
* without touching the body. Used by the Properties UI when the
|
|
70
|
+
* user edits one value at a time.
|
|
71
|
+
*
|
|
72
|
+
* If the file had no frontmatter, this adds one with just the
|
|
73
|
+
* one field. If the value is `null` and the key exists, removes
|
|
74
|
+
* the key (and removes the whole frontmatter block if it becomes
|
|
75
|
+
* empty).
|
|
76
|
+
*/
|
|
77
|
+
declare function setFrontmatterField(markdown: string, key: string, value: FrontmatterValue | null): string;
|
|
78
|
+
|
|
79
|
+
export { type Frontmatter, type FrontmatterValue, type MarkdownDocument, parseFrontmatter, serializeMarkdown, setFrontmatterField };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import "./chunk-V6TY7KAL.js";
|
|
2
|
+
import { parse, stringify } from "yaml";
|
|
3
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
|
|
4
|
+
function parseFrontmatter(markdown) {
|
|
5
|
+
const match = markdown.match(FRONTMATTER_RE);
|
|
6
|
+
if (!match) {
|
|
7
|
+
return { frontmatter: null, body: markdown };
|
|
8
|
+
}
|
|
9
|
+
const yamlBody = match[1];
|
|
10
|
+
const afterFence = markdown.slice(match[0].length);
|
|
11
|
+
try {
|
|
12
|
+
const parsed = parse(yamlBody);
|
|
13
|
+
if (parsed == null) {
|
|
14
|
+
return { frontmatter: {}, body: afterFence };
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
17
|
+
return { frontmatter: null, body: markdown };
|
|
18
|
+
}
|
|
19
|
+
return { frontmatter: parsed, body: afterFence };
|
|
20
|
+
} catch {
|
|
21
|
+
return { frontmatter: null, body: markdown };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function serializeMarkdown(doc) {
|
|
25
|
+
if (doc.frontmatter == null || Object.keys(doc.frontmatter).length === 0) {
|
|
26
|
+
return doc.body;
|
|
27
|
+
}
|
|
28
|
+
const yamlBlock = stringify(doc.frontmatter, {
|
|
29
|
+
// Match the conventional GitHub / Obsidian dialect: 2-space
|
|
30
|
+
// indent for lists, no leading dashes shift, plain string
|
|
31
|
+
// style when possible.
|
|
32
|
+
indent: 2,
|
|
33
|
+
lineWidth: 0
|
|
34
|
+
}).trimEnd();
|
|
35
|
+
return `---
|
|
36
|
+
${yamlBlock}
|
|
37
|
+
---
|
|
38
|
+
${doc.body}`;
|
|
39
|
+
}
|
|
40
|
+
function setFrontmatterField(markdown, key, value) {
|
|
41
|
+
const doc = parseFrontmatter(markdown);
|
|
42
|
+
const current = doc.frontmatter ?? {};
|
|
43
|
+
if (value === null) {
|
|
44
|
+
delete current[key];
|
|
45
|
+
} else {
|
|
46
|
+
current[key] = value;
|
|
47
|
+
}
|
|
48
|
+
return serializeMarkdown({
|
|
49
|
+
frontmatter: Object.keys(current).length === 0 ? null : current,
|
|
50
|
+
body: doc.body
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
parseFrontmatter,
|
|
55
|
+
serializeMarkdown,
|
|
56
|
+
setFrontmatterField
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/frontmatter.ts"],"sourcesContent":["/**\n * YAML frontmatter parsing + serialization.\n *\n * Convention: `--- yaml ---` block at the very top of a markdown\n * file (before any other content). Format:\n *\n * ---\n * status: draft\n * tags:\n * - application\n * - fellowship\n * ---\n *\n * # Real content starts here\n *\n * This module separates the YAML chunk from the prose chunk so\n * the WYSIWYG editor can render only the prose (no raw `key:\n * value` lines bleeding into the user's writing surface) while a\n * separate properties UI shows / edits the frontmatter.\n *\n * Round-trip rules:\n * - If a file has no frontmatter on disk, we MUST NOT add one\n * on save just because the properties panel exists.\n * - If a file has frontmatter, we serialize it using the same\n * keys/order the user wrote (best effort — yaml lib preserves\n * scalar formatting but not always blank lines / comments).\n * - The fence marker is always exactly `---` on its own line.\n * Some Obsidian users prefer `+++` (TOML) — out of scope for\n * v1; we round-trip those as body content unchanged.\n */\n\nimport { parse, stringify } from \"yaml\";\n\n/**\n * The frontmatter value type. Each entry is whatever YAML can\n * decode to — strings, numbers, booleans, arrays, nested objects.\n * The properties UI handles primitives + flat arrays cleanly;\n * nested objects fall back to a raw YAML editor.\n */\nexport type FrontmatterValue = string | number | boolean | null | FrontmatterValue[] | { [key: string]: FrontmatterValue };\nexport type Frontmatter = Record<string, FrontmatterValue>;\n\nexport interface MarkdownDocument {\n /** Parsed YAML key/values. `null` means \"no frontmatter block present\". */\n frontmatter: Frontmatter | null;\n /** The prose portion — everything after the closing `---`. */\n body: string;\n}\n\n/**\n * Regex matches the frontmatter block at the start of a file:\n * a `---` line, then any content (lazy), then a `---` line.\n * Multiline mode + dot-matches-newline. The block must start at\n * the very first character — frontmatter in the middle of a doc\n * doesn't count.\n *\n * Capture groups:\n * 1. The raw YAML body between the fences (no fences included)\n * 2. The optional newline after the closing fence\n */\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(\\r?\\n)?/;\n\n/**\n * Split a markdown string into its frontmatter + body. Tolerant\n * of:\n * - No frontmatter at all → `frontmatter: null, body: input`\n * - Malformed YAML → `frontmatter: null, body: input` (we treat\n * the whole thing as prose rather than show parse errors)\n * - Empty frontmatter block (`---\\n---`) → `frontmatter: {}`\n */\nexport function parseFrontmatter(markdown: string): MarkdownDocument {\n const match = markdown.match(FRONTMATTER_RE);\n if (!match) {\n return { frontmatter: null, body: markdown };\n }\n const yamlBody = match[1];\n const afterFence = markdown.slice(match[0].length);\n try {\n const parsed = parse(yamlBody) as unknown;\n // Empty block → empty record; primitive at top level → not\n // a valid frontmatter shape, treat as no frontmatter.\n if (parsed == null) {\n return { frontmatter: {}, body: afterFence };\n }\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { frontmatter: null, body: markdown };\n }\n return { frontmatter: parsed as Frontmatter, body: afterFence };\n } catch {\n // Malformed YAML — surface as plain content. Better to show\n // the raw chars than to drop user-visible bytes silently.\n return { frontmatter: null, body: markdown };\n }\n}\n\n/**\n * Recombine frontmatter + body into a markdown string. Inverse\n * of `parseFrontmatter` for legal inputs.\n *\n * - `frontmatter: null` → just the body, no `---` fences added\n * - `frontmatter: {}` → no fences either (an empty frontmatter\n * block is semantically the same as \"no frontmatter\" and we\n * prefer the cleaner shape on save)\n * - Otherwise → `---\\n<yaml>\\n---\\n<body>`\n */\nexport function serializeMarkdown(doc: MarkdownDocument): string {\n if (doc.frontmatter == null || Object.keys(doc.frontmatter).length === 0) {\n return doc.body;\n }\n const yamlBlock = stringify(doc.frontmatter, {\n // Match the conventional GitHub / Obsidian dialect: 2-space\n // indent for lists, no leading dashes shift, plain string\n // style when possible.\n indent: 2,\n lineWidth: 0,\n }).trimEnd();\n // Exactly one newline between the closing fence and the body.\n // That single newline gets consumed by `FRONTMATTER_RE` on\n // re-parse, so `body` round-trips byte-identical. If the\n // caller wants a blank line between the frontmatter and a\n // heading, they should include the leading `\\n` in `body`.\n return `---\\n${yamlBlock}\\n---\\n${doc.body}`;\n}\n\n/**\n * Helper: update a single frontmatter field in a markdown string\n * without touching the body. Used by the Properties UI when the\n * user edits one value at a time.\n *\n * If the file had no frontmatter, this adds one with just the\n * one field. If the value is `null` and the key exists, removes\n * the key (and removes the whole frontmatter block if it becomes\n * empty).\n */\nexport function setFrontmatterField(\n markdown: string,\n key: string,\n value: FrontmatterValue | null,\n): string {\n const doc = parseFrontmatter(markdown);\n const current: Frontmatter = doc.frontmatter ?? {};\n if (value === null) {\n delete current[key];\n } else {\n current[key] = value;\n }\n return serializeMarkdown({\n frontmatter: Object.keys(current).length === 0 ? null : current,\n body: doc.body,\n });\n}\n"],"mappings":";AA+BA,SAAS,OAAO,iBAAiB;AA6BjC,MAAM,iBAAiB;AAUhB,SAAS,iBAAiB,UAAoC;AACnE,QAAM,QAAQ,SAAS,MAAM,cAAc;AAC3C,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,aAAa,MAAM,MAAM,SAAS;AAAA,EAC7C;AACA,QAAM,WAAW,MAAM,CAAC;AACxB,QAAM,aAAa,SAAS,MAAM,MAAM,CAAC,EAAE,MAAM;AACjD,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ;AAG7B,QAAI,UAAU,MAAM;AAClB,aAAO,EAAE,aAAa,CAAC,GAAG,MAAM,WAAW;AAAA,IAC7C;AACA,QAAI,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACvD,aAAO,EAAE,aAAa,MAAM,MAAM,SAAS;AAAA,IAC7C;AACA,WAAO,EAAE,aAAa,QAAuB,MAAM,WAAW;AAAA,EAChE,QAAQ;AAGN,WAAO,EAAE,aAAa,MAAM,MAAM,SAAS;AAAA,EAC7C;AACF;AAYO,SAAS,kBAAkB,KAA+B;AAC/D,MAAI,IAAI,eAAe,QAAQ,OAAO,KAAK,IAAI,WAAW,EAAE,WAAW,GAAG;AACxE,WAAO,IAAI;AAAA,EACb;AACA,QAAM,YAAY,UAAU,IAAI,aAAa;AAAA;AAAA;AAAA;AAAA,IAI3C,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,CAAC,EAAE,QAAQ;AAMX,SAAO;AAAA,EAAQ,SAAS;AAAA;AAAA,EAAU,IAAI,IAAI;AAC5C;AAYO,SAAS,oBACd,UACA,KACA,OACQ;AACR,QAAM,MAAM,iBAAiB,QAAQ;AACrC,QAAM,UAAuB,IAAI,eAAe,CAAC;AACjD,MAAI,UAAU,MAAM;AAClB,WAAO,QAAQ,GAAG;AAAA,EACpB,OAAO;AACL,YAAQ,GAAG,IAAI;AAAA,EACjB;AACA,SAAO,kBAAkB;AAAA,IACvB,aAAa,OAAO,KAAK,OAAO,EAAE,WAAW,IAAI,OAAO;AAAA,IACxD,MAAM,IAAI;AAAA,EACZ,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-insertion pipeline. Used by both paste-from-clipboard and
|
|
3
|
+
* drag-and-drop. Both produce a `Blob`; this module turns the blob
|
|
4
|
+
* into a markdown-reference-able path:
|
|
5
|
+
*
|
|
6
|
+
* * When a `saveBytes` writer is injected (a desktop host): writes
|
|
7
|
+
* the bytes to `images/<timestamp>-<hash>.<ext>` and returns the
|
|
8
|
+
* relative path. The markdown reference is portable across
|
|
9
|
+
* machines because it's a real on-disk file.
|
|
10
|
+
* * Otherwise (plain browser, or the write fails): falls back to a
|
|
11
|
+
* data URL embedded directly in the markdown. The markdown file
|
|
12
|
+
* bloats but the image stays visible through page reload without
|
|
13
|
+
* needing a binary file API.
|
|
14
|
+
*
|
|
15
|
+
* The writer is injected rather than imported so this module stays
|
|
16
|
+
* environment-agnostic — the editor reads it off a CM6 facet
|
|
17
|
+
* ({@link saveImageBytesFacet}) the host wires up.
|
|
18
|
+
*
|
|
19
|
+
* Returned `markdownReference` is what the caller drops into
|
|
20
|
+
* ``. `warning` is set in the data-URL fallback case so
|
|
21
|
+
* the caller can surface a note to the user.
|
|
22
|
+
*/
|
|
23
|
+
interface ImageInsertOptions {
|
|
24
|
+
blob: Blob;
|
|
25
|
+
/**
|
|
26
|
+
* Persist the bytes at a workspace-relative path. `null`/omitted ⇒
|
|
27
|
+
* inline as a data URL (browser fallback).
|
|
28
|
+
*/
|
|
29
|
+
saveBytes?: ((relPath: string, bytes: Uint8Array) => Promise<void>) | null;
|
|
30
|
+
/**
|
|
31
|
+
* Alt text the user typed (currently always synthesized as
|
|
32
|
+
* "pasted-image" or "dropped-image"; in phase 3d the inline
|
|
33
|
+
* rename UI lets the user edit it post-insert).
|
|
34
|
+
*/
|
|
35
|
+
alt?: string;
|
|
36
|
+
}
|
|
37
|
+
interface ImageInsertResult {
|
|
38
|
+
markdownReference: string;
|
|
39
|
+
alt: string;
|
|
40
|
+
warning?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Save the blob and return the markdown reference. Errors are
|
|
44
|
+
* structured (the caller decides whether to retry with a fallback
|
|
45
|
+
* or surface a notification).
|
|
46
|
+
*/
|
|
47
|
+
declare function insertImageBlob(opts: ImageInsertOptions): Promise<ImageInsertResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Build a markdown image insertion string from the result. Keeps
|
|
50
|
+
* the call site (handlePaste / handleDrop) tidy.
|
|
51
|
+
*/
|
|
52
|
+
declare function buildImageMarkdown(result: ImageInsertResult): string;
|
|
53
|
+
/**
|
|
54
|
+
* Scan a DataTransferItemList for image blobs. Returns every
|
|
55
|
+
* image found, in order. Empty array when nothing matches —
|
|
56
|
+
* caller treats that as "fall through to text paste".
|
|
57
|
+
*/
|
|
58
|
+
declare function extractImageBlobs(items: DataTransferItemList | null | undefined): Blob[];
|
|
59
|
+
/**
|
|
60
|
+
* Scan a FileList (drag-and-drop's `dataTransfer.files`) for
|
|
61
|
+
* image files. Drag-drop and paste use different shapes; this
|
|
62
|
+
* keeps the two paths symmetrical at the call site.
|
|
63
|
+
*/
|
|
64
|
+
declare function extractImageFiles(files: FileList | null | undefined): File[];
|
|
65
|
+
|
|
66
|
+
export { type ImageInsertOptions, type ImageInsertResult, buildImageMarkdown, extractImageBlobs, extractImageFiles, insertImageBlob };
|