@latentic/live-markdown 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -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 +15 -0
- package/dist/codemirror/math/mathWidget.js +36 -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 +35 -880
- package/dist/index.js +128 -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,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__publicField
|
|
3
|
+
} from "../../chunk-V6TY7KAL.js";
|
|
4
|
+
import { WidgetType } from "@codemirror/view";
|
|
5
|
+
import {
|
|
6
|
+
getCachedMermaidSvg,
|
|
7
|
+
renderMermaidToSvg,
|
|
8
|
+
warmMermaidPng
|
|
9
|
+
} from "./mermaidRender.js";
|
|
10
|
+
const measuredHeights = /* @__PURE__ */ new Map();
|
|
11
|
+
const MEASURED_CACHE_CAP = 100;
|
|
12
|
+
function rememberHeight(source, height) {
|
|
13
|
+
if (measuredHeights.size >= MEASURED_CACHE_CAP) {
|
|
14
|
+
const oldest = measuredHeights.keys().next().value;
|
|
15
|
+
if (oldest !== void 0) measuredHeights.delete(oldest);
|
|
16
|
+
}
|
|
17
|
+
measuredHeights.set(source, height);
|
|
18
|
+
}
|
|
19
|
+
function estimateMermaidHeight(source) {
|
|
20
|
+
const lines = source === "" ? 1 : source.split("\n").length;
|
|
21
|
+
return Math.min(520, Math.max(140, 96 + lines * 32));
|
|
22
|
+
}
|
|
23
|
+
const containerSources = /* @__PURE__ */ new WeakMap();
|
|
24
|
+
class MermaidWidget extends WidgetType {
|
|
25
|
+
constructor(source, selected = false) {
|
|
26
|
+
super();
|
|
27
|
+
__publicField(this, "source", source);
|
|
28
|
+
__publicField(this, "selected", selected);
|
|
29
|
+
}
|
|
30
|
+
/** Identity = source + selected. Edits elsewhere shift the fence's position
|
|
31
|
+
* but must not tear down the diagram DOM; a selected flip alone updates
|
|
32
|
+
* the class in place via {@link updateDOM}. */
|
|
33
|
+
eq(other) {
|
|
34
|
+
return other.source === this.source && other.selected === this.selected;
|
|
35
|
+
}
|
|
36
|
+
updateDOM(dom) {
|
|
37
|
+
if (containerSources.get(dom) !== this.source) return false;
|
|
38
|
+
dom.classList.toggle("cm-mermaid-block--selected", this.selected);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
toDOM(view) {
|
|
42
|
+
const container = document.createElement("div");
|
|
43
|
+
container.className = "cm-mermaid-block";
|
|
44
|
+
if (this.selected) container.classList.add("cm-mermaid-block--selected");
|
|
45
|
+
containerSources.set(container, this.source);
|
|
46
|
+
container.setAttribute("role", "img");
|
|
47
|
+
container.setAttribute("aria-label", "Mermaid diagram \u2014 click to select, double-click to edit");
|
|
48
|
+
container.title = "Click to select \xB7 double-click to edit";
|
|
49
|
+
container.addEventListener("mousedown", (event) => {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
if (event.detail >= 2) {
|
|
52
|
+
revealSource(view, container);
|
|
53
|
+
} else {
|
|
54
|
+
selectFenceBlock(view, container);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const cached = getCachedMermaidSvg(this.source);
|
|
58
|
+
if (cached) {
|
|
59
|
+
this.fill(container, cached, view);
|
|
60
|
+
} else {
|
|
61
|
+
container.classList.add("cm-mermaid-block--pending");
|
|
62
|
+
container.textContent = "Rendering diagram\u2026";
|
|
63
|
+
void renderMermaidToSvg(this.source).then((result) => {
|
|
64
|
+
if (!container.isConnected) return;
|
|
65
|
+
container.classList.remove("cm-mermaid-block--pending");
|
|
66
|
+
container.textContent = "";
|
|
67
|
+
this.fill(container, result, view);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return container;
|
|
71
|
+
}
|
|
72
|
+
fill(container, result, view) {
|
|
73
|
+
if (result.ok) {
|
|
74
|
+
container.innerHTML = result.svg;
|
|
75
|
+
window.setTimeout(() => void warmMermaidPng(this.source), 300);
|
|
76
|
+
} else {
|
|
77
|
+
container.classList.add("cm-mermaid-block--error");
|
|
78
|
+
const title = document.createElement("div");
|
|
79
|
+
title.className = "cm-mermaid-error__title";
|
|
80
|
+
title.textContent = "Mermaid couldn't render this diagram \u2014 double-click to edit the source";
|
|
81
|
+
const message = document.createElement("pre");
|
|
82
|
+
message.className = "cm-mermaid-error__message";
|
|
83
|
+
message.textContent = result.message;
|
|
84
|
+
container.append(title, message);
|
|
85
|
+
}
|
|
86
|
+
const edit = document.createElement("span");
|
|
87
|
+
edit.className = "cm-mermaid-edit";
|
|
88
|
+
edit.setAttribute("role", "button");
|
|
89
|
+
edit.setAttribute("aria-label", "Edit diagram source");
|
|
90
|
+
edit.title = "Edit source";
|
|
91
|
+
edit.textContent = "Edit";
|
|
92
|
+
edit.addEventListener("mousedown", (event) => {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
event.stopPropagation();
|
|
95
|
+
revealSource(view, container);
|
|
96
|
+
});
|
|
97
|
+
container.appendChild(edit);
|
|
98
|
+
view.requestMeasure({
|
|
99
|
+
read: () => {
|
|
100
|
+
if (container.isConnected && container.offsetHeight > 0) {
|
|
101
|
+
rememberHeight(this.source, container.offsetHeight);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** The widget owns its events (click-to-edit); CM must not also react. */
|
|
107
|
+
ignoreEvent() {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
get estimatedHeight() {
|
|
111
|
+
return measuredHeights.get(this.source) ?? estimateMermaidHeight(this.source);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function revealSource(view, container) {
|
|
115
|
+
const fenceStart = view.posAtDOM(container);
|
|
116
|
+
const opener = view.state.doc.lineAt(fenceStart);
|
|
117
|
+
view.dispatch({
|
|
118
|
+
selection: { anchor: Math.min(opener.to + 1, view.state.doc.length) }
|
|
119
|
+
});
|
|
120
|
+
view.focus();
|
|
121
|
+
}
|
|
122
|
+
function selectFenceBlock(view, container) {
|
|
123
|
+
const fenceStart = view.posAtDOM(container);
|
|
124
|
+
const doc = view.state.doc;
|
|
125
|
+
const opener = doc.lineAt(fenceStart);
|
|
126
|
+
const markChar = opener.text.trim()[0];
|
|
127
|
+
const markCount = opener.text.trim().length - opener.text.trim().replace(/^(`+|~+)/, "").length;
|
|
128
|
+
const closerRe = new RegExp(`^\\s*\\${markChar}{${Math.max(markCount, 3)},}\\s*$`);
|
|
129
|
+
let end = opener.to;
|
|
130
|
+
for (let lineNumber = opener.number + 1; lineNumber <= doc.lines; lineNumber += 1) {
|
|
131
|
+
const line = doc.line(lineNumber);
|
|
132
|
+
end = line.to;
|
|
133
|
+
if (closerRe.test(line.text)) break;
|
|
134
|
+
}
|
|
135
|
+
view.dispatch({ selection: { anchor: opener.from, head: end } });
|
|
136
|
+
view.focus();
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
MermaidWidget,
|
|
140
|
+
estimateMermaidHeight
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=mermaidWidget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/mermaid/mermaidWidget.ts"],"sourcesContent":["/**\n * Mermaid diagram widget — a closed ```mermaid fence rendered as a diagram\n * block. Clicking the diagram moves the caret into the fence, which flips the\n * block back to source (mermaidPlugin owns that swap).\n *\n * Rendering itself (lazy mermaid load, SVG cache, clipboard PNG warm-up) lives\n * in [mermaidRender](./mermaidRender.ts) — this file owns only the widget:\n * measured heights are cached by source so `estimatedHeight` answers with the\n * real height and re-created widgets never shift the scroll position (the\n * #120 contract).\n */\n\nimport { EditorView, WidgetType } from \"@codemirror/view\";\n\nimport {\n getCachedMermaidSvg,\n renderMermaidToSvg,\n warmMermaidPng,\n type MermaidRenderResult,\n} from \"./mermaidRender\";\n\ntype RenderResult = MermaidRenderResult;\n\nconst measuredHeights = new Map<string, number>();\nconst MEASURED_CACHE_CAP = 100;\n\nfunction rememberHeight(source: string, height: number): void {\n if (measuredHeights.size >= MEASURED_CACHE_CAP) {\n const oldest = measuredHeights.keys().next().value;\n if (oldest !== undefined) measuredHeights.delete(oldest);\n }\n measuredHeights.set(source, height);\n}\n\n/** Pre-measure estimate: diagram height doesn't map cleanly onto source\n * lines, but ~a node-row per line is the right order of magnitude, and the\n * measured cache replaces the guess after the first real render. */\nexport function estimateMermaidHeight(source: string): number {\n const lines = source === \"\" ? 1 : source.split(\"\\n\").length;\n return Math.min(520, Math.max(140, 96 + lines * 32));\n}\n\n/** The rendered source per container, so `updateDOM` can tell a\n * selection-only flip (toggle a class in place) from a source change\n * (rebuild + re-render). */\nconst containerSources = new WeakMap<HTMLElement, string>();\n\nexport class MermaidWidget extends WidgetType {\n constructor(\n readonly source: string,\n /** The fence is covered by a (spanning) selection — native selection\n * paints nothing over a block widget, so the widget shows its own\n * selected state. */\n readonly selected = false,\n ) {\n super();\n }\n\n /** Identity = source + selected. Edits elsewhere shift the fence's position\n * but must not tear down the diagram DOM; a selected flip alone updates\n * the class in place via {@link updateDOM}. */\n override eq(other: MermaidWidget): boolean {\n return other.source === this.source && other.selected === this.selected;\n }\n\n override updateDOM(dom: HTMLElement): boolean {\n if (containerSources.get(dom) !== this.source) return false;\n dom.classList.toggle(\"cm-mermaid-block--selected\", this.selected);\n return true;\n }\n\n override toDOM(view: EditorView): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = \"cm-mermaid-block\";\n if (this.selected) container.classList.add(\"cm-mermaid-block--selected\");\n containerSources.set(container, this.source);\n container.setAttribute(\"role\", \"img\");\n container.setAttribute(\"aria-label\", \"Mermaid diagram — click to select, double-click to edit\");\n container.title = \"Click to select · double-click to edit\";\n // A diagram is an OBJECT first: click selects the whole fence (still\n // rendered — the reveal rule ignores spanning selections), so ⌘C copies\n // it without ever flashing to source. Editing is the deliberate action:\n // double-click, or the Edit chip.\n container.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n if (event.detail >= 2) {\n revealSource(view, container);\n } else {\n selectFenceBlock(view, container);\n }\n });\n\n const cached = getCachedMermaidSvg(this.source);\n if (cached) {\n this.fill(container, cached, view);\n } else {\n container.classList.add(\"cm-mermaid-block--pending\");\n container.textContent = \"Rendering diagram…\";\n void renderMermaidToSvg(this.source).then((result) => {\n if (!container.isConnected) return;\n container.classList.remove(\"cm-mermaid-block--pending\");\n container.textContent = \"\";\n this.fill(container, result, view);\n });\n }\n return container;\n }\n\n private fill(container: HTMLElement, result: RenderResult, view: EditorView): void {\n if (result.ok) {\n container.innerHTML = result.svg;\n // Rasterise a clipboard PNG in the background — the copy event is\n // synchronous, so it can only embed diagrams warmed ahead of time.\n // Deferred off the paint path (NOT requestIdleCallback: WebKit never\n // runs it while the window is unfocused).\n window.setTimeout(() => void warmMermaidPng(this.source), 300);\n } else {\n container.classList.add(\"cm-mermaid-block--error\");\n const title = document.createElement(\"div\");\n title.className = \"cm-mermaid-error__title\";\n title.textContent = \"Mermaid couldn't render this diagram — double-click to edit the source\";\n const message = document.createElement(\"pre\");\n message.className = \"cm-mermaid-error__message\";\n message.textContent = result.message;\n container.append(title, message);\n }\n // The hover-revealed Edit chip is the pointer path INTO the source\n // (alongside double-click); a plain click selects the block instead.\n // Present in the ERROR state too — a broken diagram is exactly where\n // the edit affordance matters most.\n const edit = document.createElement(\"span\");\n edit.className = \"cm-mermaid-edit\";\n edit.setAttribute(\"role\", \"button\");\n edit.setAttribute(\"aria-label\", \"Edit diagram source\");\n edit.title = \"Edit source\";\n edit.textContent = \"Edit\";\n edit.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n event.stopPropagation();\n revealSource(view, container);\n });\n container.appendChild(edit);\n // Record the real height in a controlled measure pass — never let CM\n // discover the layout shift mid-scroll.\n view.requestMeasure({\n read: () => {\n if (container.isConnected && container.offsetHeight > 0) {\n rememberHeight(this.source, container.offsetHeight);\n }\n },\n });\n }\n\n /** The widget owns its events (click-to-edit); CM must not also react. */\n override ignoreEvent(): boolean {\n return true;\n }\n\n override get estimatedHeight(): number {\n return measuredHeights.get(this.source) ?? estimateMermaidHeight(this.source);\n }\n}\n\n/** Move the caret onto the fence's first content line (the opener when the\n * fence is empty) — the plugin sees the selection inside and reveals the\n * source in the widget's place. */\nfunction revealSource(view: EditorView, container: HTMLElement): void {\n const fenceStart = view.posAtDOM(container);\n const opener = view.state.doc.lineAt(fenceStart);\n view.dispatch({\n selection: { anchor: Math.min(opener.to + 1, view.state.doc.length) },\n });\n view.focus();\n}\n\n/** Select the whole fence as one block. Both endpoints sit ON the fence's\n * boundaries — not inside — so the diagram stays rendered while selected,\n * and ⌘C picks up the full fence markdown (which the clipboard enrichment\n * turns back into the diagram). */\nfunction selectFenceBlock(view: EditorView, container: HTMLElement): void {\n const fenceStart = view.posAtDOM(container);\n const doc = view.state.doc;\n const opener = doc.lineAt(fenceStart);\n const markChar = opener.text.trim()[0];\n const markCount = opener.text.trim().length - opener.text.trim().replace(/^(`+|~+)/, \"\").length;\n const closerRe = new RegExp(`^\\\\s*\\\\${markChar}{${Math.max(markCount, 3)},}\\\\s*$`);\n let end = opener.to;\n for (let lineNumber = opener.number + 1; lineNumber <= doc.lines; lineNumber += 1) {\n const line = doc.line(lineNumber);\n end = line.to;\n if (closerRe.test(line.text)) break;\n }\n view.dispatch({ selection: { anchor: opener.from, head: end } });\n view.focus();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Extension } from '@codemirror/state';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drawn selection over a virtualized viewport (#166).
|
|
5
|
+
*
|
|
6
|
+
* CodeMirror renders only the visible viewport, and the browser's native
|
|
7
|
+
* `::selection` can only highlight DOM that exists — so with native painting,
|
|
8
|
+
* a Cmd+A or Shift-extended selection shows highlight only on whatever
|
|
9
|
+
* happened to be rendered, and scrolling away and back loses it entirely.
|
|
10
|
+
* This layer paints selection ranges FROM LOGICAL STATE
|
|
11
|
+
* (`state.selection` × the current viewport), so the highlight is correct at
|
|
12
|
+
* every scroll position by construction.
|
|
13
|
+
*
|
|
14
|
+
* Why not CM6's own `drawSelection()`: its wrapped-line handling reverse-maps
|
|
15
|
+
* the editor's far-left/right edge through `posAtCoords` per endpoint, and on
|
|
16
|
+
* lines that start with hidden markers + widgets that probe jitters between
|
|
17
|
+
* before/after the hidden prefix, flashing between one-piece and three-piece
|
|
18
|
+
* painting per drag update (#90). The rects here derive only from FORWARD
|
|
19
|
+
* geometry — `coordsAtPos` of the actual endpoints plus line-block boxes —
|
|
20
|
+
* with no reverse edge probe anywhere:
|
|
21
|
+
*
|
|
22
|
+
* * endpoints in the same visual row → one rect between their coords;
|
|
23
|
+
* * a wrapped or multi-line range → a partial rect for the first row, a
|
|
24
|
+
* partial rect for the last row, and ONE full-width rect for everything
|
|
25
|
+
* between (which also spans block widgets and hidden rows);
|
|
26
|
+
* * a block widget wholly inside a range additionally gets a translucent
|
|
27
|
+
* ABOVE-content tint (`cm-selectionWidgetTint`): widgets paint opaque
|
|
28
|
+
* backgrounds over a below-content layer, so without it a selected
|
|
29
|
+
* table/diagram would show no feedback at all.
|
|
30
|
+
*
|
|
31
|
+
* Known coarseness: partial rows of RTL text paint full-width (bidi span
|
|
32
|
+
* splitting is deliberately not reimplemented here); LTR documents paint
|
|
33
|
+
* glyph-accurately.
|
|
34
|
+
*
|
|
35
|
+
* The native `::selection` stays for the tablev2 cell editors (their own
|
|
36
|
+
* contenteditable islands) and is made transparent everywhere else — the DOM
|
|
37
|
+
* selection itself is untouched, so IME, copy, and focus behavior keep
|
|
38
|
+
* working on the real selection.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
declare const drawnSelection: Extension;
|
|
42
|
+
|
|
43
|
+
export { drawnSelection };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__publicField
|
|
3
|
+
} from "../chunk-V6TY7KAL.js";
|
|
4
|
+
import { Prec } from "@codemirror/state";
|
|
5
|
+
import {
|
|
6
|
+
BlockType,
|
|
7
|
+
Direction,
|
|
8
|
+
EditorView,
|
|
9
|
+
layer,
|
|
10
|
+
RectangleMarker
|
|
11
|
+
} from "@codemirror/view";
|
|
12
|
+
function layerBase(view) {
|
|
13
|
+
const rect = view.scrollDOM.getBoundingClientRect();
|
|
14
|
+
const left = view.textDirection === Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
|
|
15
|
+
return {
|
|
16
|
+
left: left - view.scrollDOM.scrollLeft * view.scaleX,
|
|
17
|
+
top: rect.top - view.scrollDOM.scrollTop * view.scaleY
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function contentEdges(view) {
|
|
21
|
+
const rect = view.contentDOM.getBoundingClientRect();
|
|
22
|
+
const line = view.contentDOM.querySelector(".cm-line");
|
|
23
|
+
const style = line ? window.getComputedStyle(line) : null;
|
|
24
|
+
const padLeft = style ? parseInt(style.paddingLeft) || 0 : 0;
|
|
25
|
+
const padRight = style ? parseInt(style.paddingRight) || 0 : 0;
|
|
26
|
+
return { left: rect.left + padLeft, right: rect.right - padRight };
|
|
27
|
+
}
|
|
28
|
+
function isTextBlock(block) {
|
|
29
|
+
return block.type === BlockType.Text || Array.isArray(block.type);
|
|
30
|
+
}
|
|
31
|
+
class Rect {
|
|
32
|
+
constructor(left, top, right, bottom) {
|
|
33
|
+
__publicField(this, "left", left);
|
|
34
|
+
__publicField(this, "top", top);
|
|
35
|
+
__publicField(this, "right", right);
|
|
36
|
+
__publicField(this, "bottom", bottom);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function rectsForRange(view, range, edges) {
|
|
40
|
+
if (range.to <= view.viewport.from || range.from >= view.viewport.to) return [];
|
|
41
|
+
const from = Math.max(range.from, view.viewport.from);
|
|
42
|
+
const to = Math.min(range.to, view.viewport.to);
|
|
43
|
+
const startBlock = view.lineBlockAt(from);
|
|
44
|
+
const endBlock = view.lineBlockAt(to);
|
|
45
|
+
const contentTop = view.contentDOM.getBoundingClientRect().top;
|
|
46
|
+
const startCoords = isTextBlock(startBlock) ? view.coordsAtPos(from, 1) : null;
|
|
47
|
+
const endCoords = isTextBlock(endBlock) ? view.coordsAtPos(to, -1) : null;
|
|
48
|
+
const startTop = startCoords ? startCoords.top : contentTop + startBlock.top;
|
|
49
|
+
const startBottom = startCoords ? startCoords.bottom : contentTop + startBlock.bottom;
|
|
50
|
+
const endTop = endCoords ? endCoords.top : contentTop + endBlock.top;
|
|
51
|
+
const endBottom = endCoords ? endCoords.bottom : contentTop + endBlock.bottom;
|
|
52
|
+
const startLeft = startCoords ? startCoords.left : edges.left;
|
|
53
|
+
const endRight = endCoords ? endCoords.right : edges.right;
|
|
54
|
+
if (endTop - startTop < 1) {
|
|
55
|
+
return [new Rect(startLeft, startTop, Math.max(endRight, startLeft), endBottom)];
|
|
56
|
+
}
|
|
57
|
+
const rects = [
|
|
58
|
+
// Partial first row: selection start to the row's right edge.
|
|
59
|
+
new Rect(startLeft, startTop, edges.right, startBottom),
|
|
60
|
+
// Everything between the first and last rows — full width. Wrapped rows
|
|
61
|
+
// of the endpoint lines, whole middle lines, and block widgets all fall
|
|
62
|
+
// inside this band, which is what makes the shape probe-free.
|
|
63
|
+
new Rect(edges.left, startBottom, edges.right, endTop),
|
|
64
|
+
// Partial last row: the row's left edge to the selection end.
|
|
65
|
+
new Rect(edges.left, endTop, endRight, endBottom)
|
|
66
|
+
];
|
|
67
|
+
return rects.filter((r) => r.bottom > r.top || r.right > r.left);
|
|
68
|
+
}
|
|
69
|
+
const selectionLayer = layer({
|
|
70
|
+
above: false,
|
|
71
|
+
class: "cm-selectionLayer",
|
|
72
|
+
update: (update) => update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged,
|
|
73
|
+
markers(view) {
|
|
74
|
+
const base = layerBase(view);
|
|
75
|
+
const edges = contentEdges(view);
|
|
76
|
+
const markers = [];
|
|
77
|
+
for (const range of view.state.selection.ranges) {
|
|
78
|
+
if (range.empty) continue;
|
|
79
|
+
for (const rect of rectsForRange(view, range, edges)) {
|
|
80
|
+
markers.push(
|
|
81
|
+
new RectangleMarker(
|
|
82
|
+
"cm-selectionBackground",
|
|
83
|
+
rect.left - base.left,
|
|
84
|
+
rect.top - base.top,
|
|
85
|
+
Math.max(0, rect.right - rect.left),
|
|
86
|
+
rect.bottom - rect.top
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return markers;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const widgetTintLayer = layer({
|
|
95
|
+
above: true,
|
|
96
|
+
class: "cm-selectionWidgetLayer",
|
|
97
|
+
update: (update) => update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged,
|
|
98
|
+
markers(view) {
|
|
99
|
+
const covering = view.state.selection.ranges.filter((r) => !r.empty);
|
|
100
|
+
if (covering.length === 0) return [];
|
|
101
|
+
const base = layerBase(view);
|
|
102
|
+
const edges = contentEdges(view);
|
|
103
|
+
const contentTop = view.contentDOM.getBoundingClientRect().top;
|
|
104
|
+
const markers = [];
|
|
105
|
+
for (const block of view.viewportLineBlocks) {
|
|
106
|
+
if (isTextBlock(block)) continue;
|
|
107
|
+
if (!covering.some((r) => r.from <= block.from && r.to >= block.to)) continue;
|
|
108
|
+
markers.push(
|
|
109
|
+
new RectangleMarker(
|
|
110
|
+
"cm-selectionWidgetTint",
|
|
111
|
+
edges.left - base.left,
|
|
112
|
+
contentTop + block.top - base.top,
|
|
113
|
+
Math.max(0, edges.right - edges.left),
|
|
114
|
+
block.bottom - block.top
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return markers;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const hideNativeSelection = Prec.highest(
|
|
122
|
+
EditorView.theme({
|
|
123
|
+
".cm-content ::selection, .cm-content::selection": {
|
|
124
|
+
backgroundColor: "transparent"
|
|
125
|
+
},
|
|
126
|
+
'.cm-content [contenteditable="plaintext-only"] ::selection, .cm-content [contenteditable="plaintext-only"]::selection': {
|
|
127
|
+
backgroundColor: "var(--cds-highlight, #d0e2ff)"
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
const drawnSelection = [selectionLayer, widgetTintLayer, hideNativeSelection];
|
|
132
|
+
export {
|
|
133
|
+
drawnSelection
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=selectionLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/codemirror/selectionLayer.ts"],"sourcesContent":["/**\n * Drawn selection over a virtualized viewport (#166).\n *\n * CodeMirror renders only the visible viewport, and the browser's native\n * `::selection` can only highlight DOM that exists — so with native painting,\n * a Cmd+A or Shift-extended selection shows highlight only on whatever\n * happened to be rendered, and scrolling away and back loses it entirely.\n * This layer paints selection ranges FROM LOGICAL STATE\n * (`state.selection` × the current viewport), so the highlight is correct at\n * every scroll position by construction.\n *\n * Why not CM6's own `drawSelection()`: its wrapped-line handling reverse-maps\n * the editor's far-left/right edge through `posAtCoords` per endpoint, and on\n * lines that start with hidden markers + widgets that probe jitters between\n * before/after the hidden prefix, flashing between one-piece and three-piece\n * painting per drag update (#90). The rects here derive only from FORWARD\n * geometry — `coordsAtPos` of the actual endpoints plus line-block boxes —\n * with no reverse edge probe anywhere:\n *\n * * endpoints in the same visual row → one rect between their coords;\n * * a wrapped or multi-line range → a partial rect for the first row, a\n * partial rect for the last row, and ONE full-width rect for everything\n * between (which also spans block widgets and hidden rows);\n * * a block widget wholly inside a range additionally gets a translucent\n * ABOVE-content tint (`cm-selectionWidgetTint`): widgets paint opaque\n * backgrounds over a below-content layer, so without it a selected\n * table/diagram would show no feedback at all.\n *\n * Known coarseness: partial rows of RTL text paint full-width (bidi span\n * splitting is deliberately not reimplemented here); LTR documents paint\n * glyph-accurately.\n *\n * The native `::selection` stays for the tablev2 cell editors (their own\n * contenteditable islands) and is made transparent everywhere else — the DOM\n * selection itself is untouched, so IME, copy, and focus behavior keep\n * working on the real selection.\n */\n\nimport { Prec, type Extension, type SelectionRange } from \"@codemirror/state\";\nimport {\n BlockType,\n Direction,\n EditorView,\n layer,\n RectangleMarker,\n type BlockInfo,\n} from \"@codemirror/view\";\n\n/** Client-rect → document-space base, mirroring CM6's own layer arithmetic. */\nfunction layerBase(view: EditorView): { left: number; top: number } {\n const rect = view.scrollDOM.getBoundingClientRect();\n const left =\n view.textDirection === Direction.LTR\n ? rect.left\n : rect.right - view.scrollDOM.clientWidth * view.scaleX;\n return {\n left: left - view.scrollDOM.scrollLeft * view.scaleX,\n top: rect.top - view.scrollDOM.scrollTop * view.scaleY,\n };\n}\n\ninterface ContentEdges {\n left: number;\n right: number;\n}\n\n/** The horizontal band text can occupy, in client coordinates. */\nfunction contentEdges(view: EditorView): ContentEdges {\n const rect = view.contentDOM.getBoundingClientRect();\n const line = view.contentDOM.querySelector(\".cm-line\");\n const style = line ? window.getComputedStyle(line) : null;\n const padLeft = style ? parseInt(style.paddingLeft) || 0 : 0;\n const padRight = style ? parseInt(style.paddingRight) || 0 : 0;\n return { left: rect.left + padLeft, right: rect.right - padRight };\n}\n\nfunction isTextBlock(block: BlockInfo): boolean {\n return block.type === BlockType.Text || Array.isArray(block.type);\n}\n\nclass Rect {\n constructor(\n readonly left: number,\n readonly top: number,\n readonly right: number,\n readonly bottom: number,\n ) {}\n}\n\n/**\n * Selection rectangles for one range, clamped to the viewport. Every rect is\n * derived from forward endpoint geometry; positions whose coords are\n * unavailable (jsdom, or an endpoint inside a replaced range) degrade to the\n * enclosing block's box rather than throwing.\n */\nfunction rectsForRange(view: EditorView, range: SelectionRange, edges: ContentEdges): Rect[] {\n if (range.to <= view.viewport.from || range.from >= view.viewport.to) return [];\n const from = Math.max(range.from, view.viewport.from);\n const to = Math.min(range.to, view.viewport.to);\n const startBlock = view.lineBlockAt(from);\n const endBlock = view.lineBlockAt(to);\n const contentTop = view.contentDOM.getBoundingClientRect().top;\n\n // A selection endpoint sitting on a widget block has no glyph coords; its\n // \"row\" is the widget's whole box.\n const startCoords = isTextBlock(startBlock) ? view.coordsAtPos(from, 1) : null;\n const endCoords = isTextBlock(endBlock) ? view.coordsAtPos(to, -1) : null;\n const startTop = startCoords ? startCoords.top : contentTop + startBlock.top;\n const startBottom = startCoords ? startCoords.bottom : contentTop + startBlock.bottom;\n const endTop = endCoords ? endCoords.top : contentTop + endBlock.top;\n const endBottom = endCoords ? endCoords.bottom : contentTop + endBlock.bottom;\n const startLeft = startCoords ? startCoords.left : edges.left;\n const endRight = endCoords ? endCoords.right : edges.right;\n\n // Same visual row: row boxes share their top edge, so the endpoint tops\n // are equal by construction (adjacent rows differ by a full row height).\n if (endTop - startTop < 1) {\n return [new Rect(startLeft, startTop, Math.max(endRight, startLeft), endBottom)];\n }\n\n const rects: Rect[] = [\n // Partial first row: selection start to the row's right edge.\n new Rect(startLeft, startTop, edges.right, startBottom),\n // Everything between the first and last rows — full width. Wrapped rows\n // of the endpoint lines, whole middle lines, and block widgets all fall\n // inside this band, which is what makes the shape probe-free.\n new Rect(edges.left, startBottom, edges.right, endTop),\n // Partial last row: the row's left edge to the selection end.\n new Rect(edges.left, endTop, endRight, endBottom),\n ];\n return rects.filter((r) => r.bottom > r.top || r.right > r.left);\n}\n\nconst selectionLayer = layer({\n above: false,\n class: \"cm-selectionLayer\",\n update: (update) =>\n update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged,\n markers(view) {\n const base = layerBase(view);\n const edges = contentEdges(view);\n const markers: RectangleMarker[] = [];\n for (const range of view.state.selection.ranges) {\n if (range.empty) continue;\n for (const rect of rectsForRange(view, range, edges)) {\n markers.push(\n new RectangleMarker(\n \"cm-selectionBackground\",\n rect.left - base.left,\n rect.top - base.top,\n Math.max(0, rect.right - rect.left),\n rect.bottom - rect.top,\n ),\n );\n }\n }\n return markers;\n },\n});\n\n/** Translucent tint ABOVE block widgets wholly covered by a selection — the\n * below-content band is hidden behind their opaque backgrounds. */\nconst widgetTintLayer = layer({\n above: true,\n class: \"cm-selectionWidgetLayer\",\n update: (update) =>\n update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged,\n markers(view) {\n const covering = view.state.selection.ranges.filter((r) => !r.empty);\n if (covering.length === 0) return [];\n const base = layerBase(view);\n const edges = contentEdges(view);\n const contentTop = view.contentDOM.getBoundingClientRect().top;\n const markers: RectangleMarker[] = [];\n for (const block of view.viewportLineBlocks) {\n if (isTextBlock(block)) continue;\n if (!covering.some((r) => r.from <= block.from && r.to >= block.to)) continue;\n markers.push(\n new RectangleMarker(\n \"cm-selectionWidgetTint\",\n edges.left - base.left,\n contentTop + block.top - base.top,\n Math.max(0, edges.right - edges.left),\n block.bottom - block.top,\n ),\n );\n }\n return markers;\n },\n});\n\n// The drawn layer replaces the native highlight; the DOM selection itself\n// stays (IME, copy, focus). The tablev2 cell editors are separate\n// contenteditable islands that keep the native paint.\nconst hideNativeSelection = Prec.highest(\n EditorView.theme({\n \".cm-content ::selection, .cm-content::selection\": {\n backgroundColor: \"transparent\",\n },\n '.cm-content [contenteditable=\"plaintext-only\"] ::selection, .cm-content [contenteditable=\"plaintext-only\"]::selection':\n {\n backgroundColor: \"var(--cds-highlight, #d0e2ff)\",\n },\n }),\n);\n\nexport const drawnSelection: Extension = [selectionLayer, widgetTintLayer, hideNativeSelection];\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { WidgetType } from "@codemirror/view";
|
|
3
|
+
class CellDividerWidget extends WidgetType {
|
|
4
|
+
eq(_other) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
toDOM(_view) {
|
|
8
|
+
const span = document.createElement("span");
|
|
9
|
+
span.className = "cm-table-divider";
|
|
10
|
+
return span;
|
|
11
|
+
}
|
|
12
|
+
ignoreEvent() {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
CellDividerWidget
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=cellDividerWidget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/table/cellDividerWidget.ts"],"sourcesContent":["import { EditorView, WidgetType } from \"@codemirror/view\";\n\n\nexport class CellDividerWidget extends WidgetType {\n override eq(_other: CellDividerWidget): boolean {\n return true;\n }\n override toDOM(_view: EditorView): HTMLElement {\n const span = document.createElement(\"span\");\n span.className = \"cm-table-divider\";\n return span;\n }\n override ignoreEvent(): boolean {\n return false;\n }\n}\n"],"mappings":";AAAA,SAAqB,kBAAkB;AAGhC,MAAM,0BAA0B,WAAW;AAAA,EACvC,GAAG,QAAoC;AAC9C,WAAO;AAAA,EACT;AAAA,EACS,MAAM,OAAgC;AAC7C,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EACS,cAAuB;AAC9B,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ArmedTable, armedTable, armedTableField, setArmedTable, tableArmedHighlight } from './tableArmed.js';
|
|
2
|
+
export { tableField } from './tableField.js';
|
|
3
|
+
export { addColumnAfter, addColumnBefore, addRowAbove, addRowBelow, deleteColumn, deleteRow, deleteTable } from './tableEditCommands.js';
|
|
4
|
+
import '@codemirror/state';
|
|
5
|
+
import '@codemirror/view';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/table/index.ts"],"sourcesContent":["/**\n * GFM tables, v1: the pipe-source model, the atomic block widget StateField,\n * and the editing/navigation commands. `tablev2/` (the in-cell editing\n * surfaces) bridges into these by concrete module path.\n */\nexport * from \"./tableArmed\";\nexport * from \"./tableField\";\nexport * from \"./tableEditCommands\";\n"],"mappings":"AAKA,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as _codemirror_state from '@codemirror/state';
|
|
2
|
+
import { EditorState, StateField } from '@codemirror/state';
|
|
3
|
+
import { ViewPlugin, EditorView, ViewUpdate } from '@codemirror/view';
|
|
4
|
+
|
|
5
|
+
/** Which edge of a table the caret is parked against. `"end"` is the trailing
|
|
6
|
+
* edge (a Backspace from the line below); `"start"` is the leading edge (a
|
|
7
|
+
* Delete from the line above). The edge decides where the green line is drawn
|
|
8
|
+
* — under the table or over it. */
|
|
9
|
+
interface ArmedTable {
|
|
10
|
+
from: number;
|
|
11
|
+
edge: "start" | "end";
|
|
12
|
+
}
|
|
13
|
+
/** Arm (with an `ArmedTable`) or disarm (with null) the two-step table delete.
|
|
14
|
+
* Dispatched by the delete normalizer when its first press parks the caret. */
|
|
15
|
+
declare const setArmedTable: _codemirror_state.StateEffectType<ArmedTable | null>;
|
|
16
|
+
/** The armed table, or null. Set only by `setArmedTable`; cleared by any cursor
|
|
17
|
+
* move or document edit, so navigating onto a table edge never arms it. */
|
|
18
|
+
declare const armedTableField: StateField<ArmedTable | null>;
|
|
19
|
+
declare function armedTable(state: EditorState): ArmedTable | null;
|
|
20
|
+
declare class TableArmedHighlighter {
|
|
21
|
+
constructor(view: EditorView);
|
|
22
|
+
update(update: ViewUpdate): void;
|
|
23
|
+
private sync;
|
|
24
|
+
}
|
|
25
|
+
declare const tableArmedHighlight: ViewPlugin<TableArmedHighlighter, undefined>;
|
|
26
|
+
|
|
27
|
+
export { type ArmedTable, armedTable, armedTableField, setArmedTable, tableArmedHighlight };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { StateEffect, StateField } from "@codemirror/state";
|
|
3
|
+
import { ViewPlugin } from "@codemirror/view";
|
|
4
|
+
const setArmedTable = StateEffect.define();
|
|
5
|
+
const armedTableField = StateField.define({
|
|
6
|
+
create: () => null,
|
|
7
|
+
update(value, tr) {
|
|
8
|
+
for (const effect of tr.effects) if (effect.is(setArmedTable)) return effect.value;
|
|
9
|
+
if (tr.selection || tr.docChanged) return null;
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
function armedTable(state) {
|
|
14
|
+
return state.field(armedTableField, false) ?? null;
|
|
15
|
+
}
|
|
16
|
+
class TableArmedHighlighter {
|
|
17
|
+
constructor(view) {
|
|
18
|
+
this.sync(view);
|
|
19
|
+
}
|
|
20
|
+
update(update) {
|
|
21
|
+
if (update.selectionSet || update.docChanged || update.viewportChanged) {
|
|
22
|
+
this.sync(update.view);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
sync(view) {
|
|
26
|
+
const armed = armedTable(view.state);
|
|
27
|
+
view.dom.classList.toggle("cm-table-arming", armed !== null);
|
|
28
|
+
for (const wrap of view.dom.querySelectorAll(".cm-table-wrap")) {
|
|
29
|
+
const isArmed = armed !== null && Number(wrap.dataset.tableFrom) === armed.from;
|
|
30
|
+
wrap.classList.toggle("cm-table-armed", isArmed);
|
|
31
|
+
if (armed && isArmed) wrap.dataset.armedEdge = armed.edge;
|
|
32
|
+
else delete wrap.dataset.armedEdge;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const tableArmedHighlight = ViewPlugin.fromClass(TableArmedHighlighter);
|
|
37
|
+
export {
|
|
38
|
+
armedTable,
|
|
39
|
+
armedTableField,
|
|
40
|
+
setArmedTable,
|
|
41
|
+
tableArmedHighlight
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=tableArmed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/table/tableArmed.ts"],"sourcesContent":["/**\n * Visible \"armed for deletion\" state for the two-step table delete.\n *\n * The first Backspace/Delete next to a table parks the caret at the table's\n * edge instead of editing its hidden source (see deleteNormalizer), and records\n * that intent here via `setArmedTable`. While armed, this plugin outlines the\n * table and draws a green \"cursor behind the table\" line at the parked edge —\n * the cue that the next press removes it (Zettlr's affordance) — and the theme\n * hides the real caret.\n *\n * Arming is EXPLICIT, not inferred from the caret resting at an edge: ordinary\n * navigation lands the caret on a table edge too, and that must not arm\n * anything or hide the cursor. Any later move or edit disarms.\n */\n\nimport { StateEffect, StateField, type EditorState } from \"@codemirror/state\";\nimport { EditorView, ViewPlugin, type ViewUpdate } from \"@codemirror/view\";\n\n/** Which edge of a table the caret is parked against. `\"end\"` is the trailing\n * edge (a Backspace from the line below); `\"start\"` is the leading edge (a\n * Delete from the line above). The edge decides where the green line is drawn\n * — under the table or over it. */\nexport interface ArmedTable {\n from: number;\n edge: \"start\" | \"end\";\n}\n\n/** Arm (with an `ArmedTable`) or disarm (with null) the two-step table delete.\n * Dispatched by the delete normalizer when its first press parks the caret. */\nexport const setArmedTable = StateEffect.define<ArmedTable | null>();\n\n/** The armed table, or null. Set only by `setArmedTable`; cleared by any cursor\n * move or document edit, so navigating onto a table edge never arms it. */\nexport const armedTableField = StateField.define<ArmedTable | null>({\n create: () => null,\n update(value, tr) {\n for (const effect of tr.effects) if (effect.is(setArmedTable)) return effect.value;\n if (tr.selection || tr.docChanged) return null;\n return value;\n },\n});\n\nexport function armedTable(state: EditorState): ArmedTable | null {\n return state.field(armedTableField, false) ?? null;\n}\n\nclass TableArmedHighlighter {\n constructor(view: EditorView) {\n this.sync(view);\n }\n\n update(update: ViewUpdate): void {\n if (update.selectionSet || update.docChanged || update.viewportChanged) {\n this.sync(update.view);\n }\n }\n\n private sync(view: EditorView): void {\n const armed = armedTable(view.state);\n // Root class lets the theme hide the parked caret — it would otherwise blink\n // on the blank line just past the table (table.to is a block boundary CM\n // paints below the widget), competing with the outline + green edge line.\n view.dom.classList.toggle(\"cm-table-arming\", armed !== null);\n for (const wrap of view.dom.querySelectorAll<HTMLElement>(\".cm-table-wrap\")) {\n const isArmed = armed !== null && Number(wrap.dataset.tableFrom) === armed.from;\n wrap.classList.toggle(\"cm-table-armed\", isArmed);\n if (armed && isArmed) wrap.dataset.armedEdge = armed.edge;\n else delete wrap.dataset.armedEdge;\n }\n }\n}\n\nexport const tableArmedHighlight = ViewPlugin.fromClass(TableArmedHighlighter);\n"],"mappings":";AAeA,SAAS,aAAa,kBAAoC;AAC1D,SAAqB,kBAAmC;AAajD,MAAM,gBAAgB,YAAY,OAA0B;AAI5D,MAAM,kBAAkB,WAAW,OAA0B;AAAA,EAClE,QAAQ,MAAM;AAAA,EACd,OAAO,OAAO,IAAI;AAChB,eAAW,UAAU,GAAG,QAAS,KAAI,OAAO,GAAG,aAAa,EAAG,QAAO,OAAO;AAC7E,QAAI,GAAG,aAAa,GAAG,WAAY,QAAO;AAC1C,WAAO;AAAA,EACT;AACF,CAAC;AAEM,SAAS,WAAW,OAAuC;AAChE,SAAO,MAAM,MAAM,iBAAiB,KAAK,KAAK;AAChD;AAEA,MAAM,sBAAsB;AAAA,EAC1B,YAAY,MAAkB;AAC5B,SAAK,KAAK,IAAI;AAAA,EAChB;AAAA,EAEA,OAAO,QAA0B;AAC/B,QAAI,OAAO,gBAAgB,OAAO,cAAc,OAAO,iBAAiB;AACtE,WAAK,KAAK,OAAO,IAAI;AAAA,IACvB;AAAA,EACF;AAAA,EAEQ,KAAK,MAAwB;AACnC,UAAM,QAAQ,WAAW,KAAK,KAAK;AAInC,SAAK,IAAI,UAAU,OAAO,mBAAmB,UAAU,IAAI;AAC3D,eAAW,QAAQ,KAAK,IAAI,iBAA8B,gBAAgB,GAAG;AAC3E,YAAM,UAAU,UAAU,QAAQ,OAAO,KAAK,QAAQ,SAAS,MAAM,MAAM;AAC3E,WAAK,UAAU,OAAO,kBAAkB,OAAO;AAC/C,UAAI,SAAS,QAAS,MAAK,QAAQ,YAAY,MAAM;AAAA,UAChD,QAAO,KAAK,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,MAAM,sBAAsB,WAAW,UAAU,qBAAqB;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { InlineScanRule } from '../core/inlineScan.js';
|
|
2
|
+
import '@codemirror/state';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders a table cell's inline HTML into safe DOM.
|
|
6
|
+
*
|
|
7
|
+
* The string is pre-rendered by {@link renderInlineCell} (markdown markup →
|
|
8
|
+
* `<span>`s carrying the editor's `.cm-*` mark classes) and may also carry
|
|
9
|
+
* literal inline HTML from the source — most commonly `<br>` to stack several
|
|
10
|
+
* lines in one cell — so it can't go in as `textContent`. It's sanitized through
|
|
11
|
+
* DOMPurify with an inline-only allow-list: emphasis, code, links, and `<br>`
|
|
12
|
+
* pass through; block, script, and event-handler markup is stripped. `class` is
|
|
13
|
+
* allowed (CSS classes are inert — no XSS surface) so the mark styling survives;
|
|
14
|
+
* DOMPurify also neutralises unsafe `href` schemes (e.g. `javascript:`).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
declare function renderCellInto(el: HTMLElement, source: string, rules: readonly InlineScanRule[]): void;
|
|
18
|
+
|
|
19
|
+
export { renderCellInto };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import DOMPurify from "dompurify";
|
|
3
|
+
const CELL_SANITIZE_CONFIG = {
|
|
4
|
+
ALLOWED_TAGS: ["br", "b", "strong", "i", "em", "code", "sub", "sup", "del", "s", "u", "span", "a"],
|
|
5
|
+
ALLOWED_ATTR: ["href", "class"],
|
|
6
|
+
RETURN_TRUSTED_TYPE: false
|
|
7
|
+
};
|
|
8
|
+
function renderCellInto(el, source, rules) {
|
|
9
|
+
el.innerHTML = DOMPurify.sanitize(source, CELL_SANITIZE_CONFIG);
|
|
10
|
+
for (const rule of rules) rule.hydrate?.(el);
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
renderCellInto
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=tableCell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/table/tableCell.ts"],"sourcesContent":["/**\n * Renders a table cell's inline HTML into safe DOM.\n *\n * The string is pre-rendered by {@link renderInlineCell} (markdown markup →\n * `<span>`s carrying the editor's `.cm-*` mark classes) and may also carry\n * literal inline HTML from the source — most commonly `<br>` to stack several\n * lines in one cell — so it can't go in as `textContent`. It's sanitized through\n * DOMPurify with an inline-only allow-list: emphasis, code, links, and `<br>`\n * pass through; block, script, and event-handler markup is stripped. `class` is\n * allowed (CSS classes are inert — no XSS surface) so the mark styling survives;\n * DOMPurify also neutralises unsafe `href` schemes (e.g. `javascript:`).\n */\n\nimport DOMPurify from \"dompurify\";\n\nimport { type InlineScanRule } from \"../core/inlineScan\";\n\nconst CELL_SANITIZE_CONFIG = {\n ALLOWED_TAGS: [\"br\", \"b\", \"strong\", \"i\", \"em\", \"code\", \"sub\", \"sup\", \"del\", \"s\", \"u\", \"span\", \"a\"],\n ALLOWED_ATTR: [\"href\", \"class\"],\n RETURN_TRUSTED_TYPE: false,\n};\n\nexport function renderCellInto(\n el: HTMLElement,\n source: string,\n rules: readonly InlineScanRule[],\n): void {\n el.innerHTML = DOMPurify.sanitize(source, CELL_SANITIZE_CONFIG) as unknown as string;\n // Only after sanitising: a hydrate generates trusted DOM (KaTeX) from the\n // inert text left in the element it replaces.\n for (const rule of rules) rule.hydrate?.(el);\n}\n"],"mappings":";AAaA,OAAO,eAAe;AAItB,MAAM,uBAAuB;AAAA,EAC3B,cAAc,CAAC,MAAM,KAAK,UAAU,KAAK,MAAM,QAAQ,OAAO,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAG;AAAA,EACjG,cAAc,CAAC,QAAQ,OAAO;AAAA,EAC9B,qBAAqB;AACvB;AAEO,SAAS,eACd,IACA,QACA,OACM;AACN,KAAG,YAAY,UAAU,SAAS,QAAQ,oBAAoB;AAG9D,aAAW,QAAQ,MAAO,MAAK,UAAU,EAAE;AAC7C;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TableModel, TableCellData } from './tableModel.js';
|
|
2
|
+
import '@codemirror/state';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pure geometry for moving the caret between table cells. A table is rendered as
|
|
6
|
+
* one atomic block whose cells are edited in per-cell subviews; navigation maps
|
|
7
|
+
* a (row, col) + direction to the next cell or to an exit out of the table. Row 0
|
|
8
|
+
* is the header; the `| --- |` delimiter row is not navigable. Kept pure (no
|
|
9
|
+
* EditorView) so the step math is unit-testable; mounting lives in
|
|
10
|
+
* {@link mountCellSubview}.
|
|
11
|
+
*/
|
|
12
|
+
/** Navigable rows: the header plus every body row. */
|
|
13
|
+
declare function rowCount(model: TableModel): number;
|
|
14
|
+
/** The cell at (row, col) — row 0 is the header — or null when out of range. */
|
|
15
|
+
declare function cellAt(model: TableModel, row: number, col: number): TableCellData;
|
|
16
|
+
/** The (row, col) of the cell whose source starts at `from`, or null. */
|
|
17
|
+
declare function positionOf(model: TableModel, from: number): {
|
|
18
|
+
row: number;
|
|
19
|
+
col: number;
|
|
20
|
+
} | null;
|
|
21
|
+
type NavDir = "next" | "prev" | "up" | "down";
|
|
22
|
+
type ExitEdge = "above" | "below" | "before" | "after";
|
|
23
|
+
type NavTarget = {
|
|
24
|
+
kind: "cell";
|
|
25
|
+
row: number;
|
|
26
|
+
col: number;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "exit";
|
|
29
|
+
edge: ExitEdge;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Where `dir` moves from (row, col): an adjacent cell, or an exit edge when it
|
|
33
|
+
* leaves the table. `next`/`prev` (Tab / shift-Tab, or Left/Right at a cell's
|
|
34
|
+
* text edge) wrap across rows in reading order; `up`/`down` stay in the column.
|
|
35
|
+
*/
|
|
36
|
+
declare function stepCell(model: TableModel, row: number, col: number, dir: NavDir): NavTarget;
|
|
37
|
+
/** {@link stepCell} on bare grid dimensions — the tablev2 bridge has the
|
|
38
|
+
* dimensions but no model object. Single home for the step math. */
|
|
39
|
+
declare function stepGrid(rows: number, cols: number, row: number, col: number, dir: NavDir): NavTarget;
|
|
40
|
+
/**
|
|
41
|
+
* Where the caret lands when leaving the table at `edge`. The table is one atomic
|
|
42
|
+
* block spanning [from, to]: a caret at `from` is shoved forward past the whole
|
|
43
|
+
* block by CodeMirror, and a caret at `to` stays on the block's last (hidden) row
|
|
44
|
+
* — either boundary keeps the caret on the grid, so a stray arrow press is then
|
|
45
|
+
* needed to clear it. Stepping one position outward reaches the real neighbouring
|
|
46
|
+
* line: `from - 1` ends the line above, `to + 1` starts the line below (the blank
|
|
47
|
+
* line a GFM table is separated by). Both are clamped to the document.
|
|
48
|
+
*/
|
|
49
|
+
declare function exitCaretPos(edge: ExitEdge, from: number, to: number, docLength: number): number;
|
|
50
|
+
|
|
51
|
+
export { type ExitEdge, type NavDir, type NavTarget, cellAt, exitCaretPos, positionOf, rowCount, stepCell, stepGrid };
|