phormat 0.0.5
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/README.md +102 -0
- package/dist/assets/example-test.svelte +228 -0
- package/dist/debug/JsonDebug.svelte +19 -0
- package/dist/debug/JsonDebug.svelte.d.ts +6 -0
- package/dist/debug/JsonDebugNode.svelte +138 -0
- package/dist/debug/JsonDebugNode.svelte.d.ts +11 -0
- package/dist/demo/documentLifecycle.d.ts +57 -0
- package/dist/demo/documentLifecycle.js +196 -0
- package/dist/demo/index.d.ts +2 -0
- package/dist/demo/index.js +2 -0
- package/dist/editor/BlockEditor.svelte +112 -0
- package/dist/editor/BlockEditor.svelte.d.ts +7 -0
- package/dist/editor/BlockRow.svelte +76 -0
- package/dist/editor/BlockRow.svelte.d.ts +20 -0
- package/dist/editor/Editor.svelte +103 -0
- package/dist/editor/Editor.svelte.d.ts +7 -0
- package/dist/editor/MarkdownEditor.svelte.d.ts +160 -0
- package/dist/editor/MarkdownEditor.svelte.js +1119 -0
- package/dist/editor/NestedRowChrome.svelte +160 -0
- package/dist/editor/NestedRowChrome.svelte.d.ts +20 -0
- package/dist/editor/PhormatEditor.svelte +206 -0
- package/dist/editor/PhormatEditor.svelte.d.ts +26 -0
- package/dist/editor/RawMarkdownPanel.svelte +39 -0
- package/dist/editor/RawMarkdownPanel.svelte.d.ts +6 -0
- package/dist/editor/block-specs/BlockSpecMissing.svelte +12 -0
- package/dist/editor/block-specs/BlockSpecMissing.svelte.d.ts +7 -0
- package/dist/editor/block-specs/blockquote/BlockquoteRenderer.svelte +32 -0
- package/dist/editor/block-specs/blockquote/BlockquoteRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/blockquote/commands.d.ts +2 -0
- package/dist/editor/block-specs/blockquote/commands.js +65 -0
- package/dist/editor/block-specs/blockquote/index.d.ts +6 -0
- package/dist/editor/block-specs/blockquote/index.js +5 -0
- package/dist/editor/block-specs/blockquote/inlineSecondPass.d.ts +6 -0
- package/dist/editor/block-specs/blockquote/inlineSecondPass.js +35 -0
- package/dist/editor/block-specs/blockquote/parse.d.ts +16 -0
- package/dist/editor/block-specs/blockquote/parse.js +188 -0
- package/dist/editor/block-specs/blockquote/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/blockquote/reconcile.js +9 -0
- package/dist/editor/block-specs/blockquote/serialize.d.ts +6 -0
- package/dist/editor/block-specs/blockquote/serialize.js +55 -0
- package/dist/editor/block-specs/blockquote/types.d.ts +20 -0
- package/dist/editor/block-specs/blockquote/types.js +1 -0
- package/dist/editor/block-specs/blockquoteSpec.d.ts +4 -0
- package/dist/editor/block-specs/blockquoteSpec.js +37 -0
- package/dist/editor/block-specs/callout/CalloutRenderer.svelte +38 -0
- package/dist/editor/block-specs/callout/CalloutRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/callout/CalloutTypeIcon.svelte +14 -0
- package/dist/editor/block-specs/callout/CalloutTypeIcon.svelte.d.ts +7 -0
- package/dist/editor/block-specs/callout/commands.d.ts +2 -0
- package/dist/editor/block-specs/callout/commands.js +65 -0
- package/dist/editor/block-specs/callout/icons.d.ts +10 -0
- package/dist/editor/block-specs/callout/icons.js +52 -0
- package/dist/editor/block-specs/callout/index.d.ts +9 -0
- package/dist/editor/block-specs/callout/index.js +7 -0
- package/dist/editor/block-specs/callout/inlineSecondPass.d.ts +6 -0
- package/dist/editor/block-specs/callout/inlineSecondPass.js +9 -0
- package/dist/editor/block-specs/callout/parse.d.ts +9 -0
- package/dist/editor/block-specs/callout/parse.js +83 -0
- package/dist/editor/block-specs/callout/parseBody.d.ts +6 -0
- package/dist/editor/block-specs/callout/parseBody.js +290 -0
- package/dist/editor/block-specs/callout/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/callout/reconcile.js +8 -0
- package/dist/editor/block-specs/callout/serialize.d.ts +5 -0
- package/dist/editor/block-specs/callout/serialize.js +65 -0
- package/dist/editor/block-specs/callout/types.d.ts +7 -0
- package/dist/editor/block-specs/callout/types.js +1 -0
- package/dist/editor/block-specs/calloutSpec.d.ts +2 -0
- package/dist/editor/block-specs/calloutSpec.js +23 -0
- package/dist/editor/block-specs/codeFence/CodeFenceRenderer.svelte +18 -0
- package/dist/editor/block-specs/codeFence/CodeFenceRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/codeFence/commands.d.ts +2 -0
- package/dist/editor/block-specs/codeFence/commands.js +1 -0
- package/dist/editor/block-specs/codeFence/index.d.ts +5 -0
- package/dist/editor/block-specs/codeFence/index.js +4 -0
- package/dist/editor/block-specs/codeFence/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/codeFence/inlineSecondPass.js +5 -0
- package/dist/editor/block-specs/codeFence/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/codeFence/reconcile.js +5 -0
- package/dist/editor/block-specs/codeFence/serialize.d.ts +2 -0
- package/dist/editor/block-specs/codeFence/serialize.js +8 -0
- package/dist/editor/block-specs/codeFence/types.d.ts +6 -0
- package/dist/editor/block-specs/codeFence/types.js +1 -0
- package/dist/editor/block-specs/codeFenceSpec.d.ts +2 -0
- package/dist/editor/block-specs/codeFenceSpec.js +23 -0
- package/dist/editor/block-specs/columns/ColumnsRenderer.svelte +18 -0
- package/dist/editor/block-specs/columns/ColumnsRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/columns/EditableColumns.svelte +16 -0
- package/dist/editor/block-specs/columns/EditableColumns.svelte.d.ts +4 -0
- package/dist/editor/block-specs/columns/commands.d.ts +2 -0
- package/dist/editor/block-specs/columns/commands.js +1 -0
- package/dist/editor/block-specs/columns/index.d.ts +6 -0
- package/dist/editor/block-specs/columns/index.js +5 -0
- package/dist/editor/block-specs/columns/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/columns/inlineSecondPass.js +5 -0
- package/dist/editor/block-specs/columns/parse.d.ts +20 -0
- package/dist/editor/block-specs/columns/parse.js +111 -0
- package/dist/editor/block-specs/columns/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/columns/reconcile.js +6 -0
- package/dist/editor/block-specs/columns/serialize.d.ts +2 -0
- package/dist/editor/block-specs/columns/serialize.js +16 -0
- package/dist/editor/block-specs/columns/types.d.ts +4 -0
- package/dist/editor/block-specs/columns/types.js +1 -0
- package/dist/editor/block-specs/columnsSpec.d.ts +2 -0
- package/dist/editor/block-specs/columnsSpec.js +24 -0
- package/dist/editor/block-specs/heading/HeaderRenderer.svelte +30 -0
- package/dist/editor/block-specs/heading/HeaderRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/heading/commands.d.ts +2 -0
- package/dist/editor/block-specs/heading/commands.js +41 -0
- package/dist/editor/block-specs/heading/edit.d.ts +10 -0
- package/dist/editor/block-specs/heading/edit.js +33 -0
- package/dist/editor/block-specs/heading/index.d.ts +7 -0
- package/dist/editor/block-specs/heading/index.js +6 -0
- package/dist/editor/block-specs/heading/inlineSecondPass.d.ts +4 -0
- package/dist/editor/block-specs/heading/inlineSecondPass.js +20 -0
- package/dist/editor/block-specs/heading/parse.d.ts +5 -0
- package/dist/editor/block-specs/heading/parse.js +18 -0
- package/dist/editor/block-specs/heading/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/heading/reconcile.js +8 -0
- package/dist/editor/block-specs/heading/serialize.d.ts +2 -0
- package/dist/editor/block-specs/heading/serialize.js +9 -0
- package/dist/editor/block-specs/heading/types.d.ts +5 -0
- package/dist/editor/block-specs/heading/types.js +1 -0
- package/dist/editor/block-specs/headingSpec.d.ts +2 -0
- package/dist/editor/block-specs/headingSpec.js +28 -0
- package/dist/editor/block-specs/horizontalRule/HorizontalRuleRenderer.svelte +15 -0
- package/dist/editor/block-specs/horizontalRule/HorizontalRuleRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/horizontalRule/commands.d.ts +2 -0
- package/dist/editor/block-specs/horizontalRule/commands.js +1 -0
- package/dist/editor/block-specs/horizontalRule/index.d.ts +6 -0
- package/dist/editor/block-specs/horizontalRule/index.js +5 -0
- package/dist/editor/block-specs/horizontalRule/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/horizontalRule/inlineSecondPass.js +5 -0
- package/dist/editor/block-specs/horizontalRule/parse.d.ts +2 -0
- package/dist/editor/block-specs/horizontalRule/parse.js +15 -0
- package/dist/editor/block-specs/horizontalRule/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/horizontalRule/reconcile.js +5 -0
- package/dist/editor/block-specs/horizontalRule/serialize.d.ts +2 -0
- package/dist/editor/block-specs/horizontalRule/serialize.js +3 -0
- package/dist/editor/block-specs/horizontalRule/types.d.ts +3 -0
- package/dist/editor/block-specs/horizontalRule/types.js +1 -0
- package/dist/editor/block-specs/horizontalRuleSpec.d.ts +2 -0
- package/dist/editor/block-specs/horizontalRuleSpec.js +24 -0
- package/dist/editor/block-specs/list/ListRenderer.svelte +70 -0
- package/dist/editor/block-specs/list/ListRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/list/commands.d.ts +2 -0
- package/dist/editor/block-specs/list/commands.js +65 -0
- package/dist/editor/block-specs/list/index.d.ts +6 -0
- package/dist/editor/block-specs/list/index.js +5 -0
- package/dist/editor/block-specs/list/inlineSecondPass.d.ts +6 -0
- package/dist/editor/block-specs/list/inlineSecondPass.js +78 -0
- package/dist/editor/block-specs/list/parse.d.ts +11 -0
- package/dist/editor/block-specs/list/parse.js +312 -0
- package/dist/editor/block-specs/list/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/list/reconcile.js +6 -0
- package/dist/editor/block-specs/list/serialize.d.ts +4 -0
- package/dist/editor/block-specs/list/serialize.js +83 -0
- package/dist/editor/block-specs/list/types.d.ts +27 -0
- package/dist/editor/block-specs/list/types.js +1 -0
- package/dist/editor/block-specs/listSpec.d.ts +4 -0
- package/dist/editor/block-specs/listSpec.js +24 -0
- package/dist/editor/block-specs/paragraph/ParagraphRenderer.svelte +22 -0
- package/dist/editor/block-specs/paragraph/ParagraphRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/paragraph/commands.d.ts +2 -0
- package/dist/editor/block-specs/paragraph/commands.js +41 -0
- package/dist/editor/block-specs/paragraph/edit.d.ts +10 -0
- package/dist/editor/block-specs/paragraph/edit.js +69 -0
- package/dist/editor/block-specs/paragraph/index.d.ts +7 -0
- package/dist/editor/block-specs/paragraph/index.js +6 -0
- package/dist/editor/block-specs/paragraph/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/paragraph/inlineSecondPass.js +25 -0
- package/dist/editor/block-specs/paragraph/parse.d.ts +9 -0
- package/dist/editor/block-specs/paragraph/parse.js +76 -0
- package/dist/editor/block-specs/paragraph/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/paragraph/reconcile.js +9 -0
- package/dist/editor/block-specs/paragraph/serialize.d.ts +4 -0
- package/dist/editor/block-specs/paragraph/serialize.js +39 -0
- package/dist/editor/block-specs/paragraph/types.d.ts +10 -0
- package/dist/editor/block-specs/paragraph/types.js +1 -0
- package/dist/editor/block-specs/paragraphSpec.d.ts +2 -0
- package/dist/editor/block-specs/paragraphSpec.js +24 -0
- package/dist/editor/block-specs/registry.d.ts +10 -0
- package/dist/editor/block-specs/registry.js +34 -0
- package/dist/editor/block-specs/table/TableRenderer.svelte +53 -0
- package/dist/editor/block-specs/table/TableRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/table/commands.d.ts +2 -0
- package/dist/editor/block-specs/table/commands.js +1 -0
- package/dist/editor/block-specs/table/edit.d.ts +10 -0
- package/dist/editor/block-specs/table/edit.js +127 -0
- package/dist/editor/block-specs/table/editCommands.d.ts +18 -0
- package/dist/editor/block-specs/table/editCommands.js +87 -0
- package/dist/editor/block-specs/table/index.d.ts +8 -0
- package/dist/editor/block-specs/table/index.js +6 -0
- package/dist/editor/block-specs/table/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/table/inlineSecondPass.js +22 -0
- package/dist/editor/block-specs/table/parse.d.ts +17 -0
- package/dist/editor/block-specs/table/parse.js +181 -0
- package/dist/editor/block-specs/table/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/table/reconcile.js +6 -0
- package/dist/editor/block-specs/table/serialize.d.ts +3 -0
- package/dist/editor/block-specs/table/serialize.js +32 -0
- package/dist/editor/block-specs/table/types.d.ts +11 -0
- package/dist/editor/block-specs/table/types.js +1 -0
- package/dist/editor/block-specs/tableSpec.d.ts +2 -0
- package/dist/editor/block-specs/tableSpec.js +25 -0
- package/dist/editor/block-specs/toc/EditableToc.svelte +16 -0
- package/dist/editor/block-specs/toc/EditableToc.svelte.d.ts +4 -0
- package/dist/editor/block-specs/toc/TocRenderer.svelte +22 -0
- package/dist/editor/block-specs/toc/TocRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/toc/commands.d.ts +2 -0
- package/dist/editor/block-specs/toc/commands.js +1 -0
- package/dist/editor/block-specs/toc/index.d.ts +6 -0
- package/dist/editor/block-specs/toc/index.js +5 -0
- package/dist/editor/block-specs/toc/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/toc/inlineSecondPass.js +5 -0
- package/dist/editor/block-specs/toc/parse.d.ts +10 -0
- package/dist/editor/block-specs/toc/parse.js +43 -0
- package/dist/editor/block-specs/toc/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/toc/reconcile.js +5 -0
- package/dist/editor/block-specs/toc/serialize.d.ts +2 -0
- package/dist/editor/block-specs/toc/serialize.js +6 -0
- package/dist/editor/block-specs/toc/types.d.ts +8 -0
- package/dist/editor/block-specs/toc/types.js +1 -0
- package/dist/editor/block-specs/tocSpec.d.ts +2 -0
- package/dist/editor/block-specs/tocSpec.js +24 -0
- package/dist/editor/block-specs/toggle/ToggleRenderer.svelte +19 -0
- package/dist/editor/block-specs/toggle/ToggleRenderer.svelte.d.ts +21 -0
- package/dist/editor/block-specs/toggle/commands.d.ts +2 -0
- package/dist/editor/block-specs/toggle/commands.js +1 -0
- package/dist/editor/block-specs/toggle/index.d.ts +7 -0
- package/dist/editor/block-specs/toggle/index.js +5 -0
- package/dist/editor/block-specs/toggle/inlineSecondPass.d.ts +3 -0
- package/dist/editor/block-specs/toggle/inlineSecondPass.js +5 -0
- package/dist/editor/block-specs/toggle/parse.d.ts +9 -0
- package/dist/editor/block-specs/toggle/parse.js +78 -0
- package/dist/editor/block-specs/toggle/reconcile.d.ts +2 -0
- package/dist/editor/block-specs/toggle/reconcile.js +6 -0
- package/dist/editor/block-specs/toggle/serialize.d.ts +4 -0
- package/dist/editor/block-specs/toggle/serialize.js +13 -0
- package/dist/editor/block-specs/toggle/types.d.ts +5 -0
- package/dist/editor/block-specs/toggle/types.js +1 -0
- package/dist/editor/block-specs/toggleSpec.d.ts +2 -0
- package/dist/editor/block-specs/toggleSpec.js +24 -0
- package/dist/editor/block-specs/types.d.ts +26 -0
- package/dist/editor/block-specs/types.js +1 -0
- package/dist/editor/blocks/EditableBlockquote.svelte +52 -0
- package/dist/editor/blocks/EditableBlockquote.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableBlockquoteNodes.svelte +134 -0
- package/dist/editor/blocks/EditableBlockquoteNodes.svelte.d.ts +14 -0
- package/dist/editor/blocks/EditableBlockquoteParagraph.svelte +44 -0
- package/dist/editor/blocks/EditableBlockquoteParagraph.svelte.d.ts +12 -0
- package/dist/editor/blocks/EditableCallout.svelte +182 -0
- package/dist/editor/blocks/EditableCallout.svelte.d.ts +11 -0
- package/dist/editor/blocks/EditableCalloutListItemRow.svelte +169 -0
- package/dist/editor/blocks/EditableCalloutListItemRow.svelte.d.ts +15 -0
- package/dist/editor/blocks/EditableCalloutNodes.svelte +91 -0
- package/dist/editor/blocks/EditableCalloutNodes.svelte.d.ts +13 -0
- package/dist/editor/blocks/EditableCalloutParagraph.svelte +39 -0
- package/dist/editor/blocks/EditableCalloutParagraph.svelte.d.ts +12 -0
- package/dist/editor/blocks/EditableCodeBlock.svelte +43 -0
- package/dist/editor/blocks/EditableCodeBlock.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableHeading.svelte +161 -0
- package/dist/editor/blocks/EditableHeading.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableHorizontalRule.svelte +20 -0
- package/dist/editor/blocks/EditableHorizontalRule.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableInlineSurface.svelte +423 -0
- package/dist/editor/blocks/EditableInlineSurface.svelte.d.ts +26 -0
- package/dist/editor/blocks/EditableList.svelte +77 -0
- package/dist/editor/blocks/EditableList.svelte.d.ts +17 -0
- package/dist/editor/blocks/EditableListItemRow.svelte +231 -0
- package/dist/editor/blocks/EditableListItemRow.svelte.d.ts +19 -0
- package/dist/editor/blocks/EditableParagraph.svelte +208 -0
- package/dist/editor/blocks/EditableParagraph.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableParagraphNodes.svelte +89 -0
- package/dist/editor/blocks/EditableParagraphNodes.svelte.d.ts +15 -0
- package/dist/editor/blocks/EditableTable.svelte +83 -0
- package/dist/editor/blocks/EditableTable.svelte.d.ts +10 -0
- package/dist/editor/blocks/EditableTableCell.svelte +89 -0
- package/dist/editor/blocks/EditableTableCell.svelte.d.ts +15 -0
- package/dist/editor/blocks/EditableToggle.svelte +94 -0
- package/dist/editor/blocks/EditableToggle.svelte.d.ts +10 -0
- package/dist/editor/commandMenu.d.ts +18 -0
- package/dist/editor/commandMenu.js +83 -0
- package/dist/editor/commandRegistry.d.ts +48 -0
- package/dist/editor/commandRegistry.js +133 -0
- package/dist/editor/context/assetBaseUrl.d.ts +6 -0
- package/dist/editor/context/assetBaseUrl.js +20 -0
- package/dist/editor/dom/editorHistoryKeydown.d.ts +6 -0
- package/dist/editor/dom/editorHistoryKeydown.js +31 -0
- package/dist/editor/dom/plainEnterKeydown.d.ts +12 -0
- package/dist/editor/dom/plainEnterKeydown.js +53 -0
- package/dist/editor/dom/plaintextCaret.d.ts +12 -0
- package/dist/editor/dom/plaintextCaret.js +61 -0
- package/dist/editor/dom/syncDocumentSelectionFocus.d.ts +17 -0
- package/dist/editor/dom/syncDocumentSelectionFocus.js +56 -0
- package/dist/editor/draftPersistence.d.ts +27 -0
- package/dist/editor/draftPersistence.js +83 -0
- package/dist/editor/editorHistoryHandle.d.ts +16 -0
- package/dist/editor/editorHistoryHandle.js +22 -0
- package/dist/editor/inlineChrome/InlineChromeCoordinator.svelte +174 -0
- package/dist/editor/inlineChrome/InlineChromeCoordinator.svelte.d.ts +8 -0
- package/dist/editor/inlineChrome/InlineFormattingToolbar.svelte +181 -0
- package/dist/editor/inlineChrome/InlineFormattingToolbar.svelte.d.ts +3 -0
- package/dist/editor/inlineChrome/inlineChromeActions.d.ts +17 -0
- package/dist/editor/inlineChrome/inlineChromeActions.js +148 -0
- package/dist/editor/inlineChrome/inlineChromeState.svelte.d.ts +23 -0
- package/dist/editor/inlineChrome/inlineChromeState.svelte.js +21 -0
- package/dist/editor/inlineChrome/inlineToolbarIcons.d.ts +14 -0
- package/dist/editor/inlineChrome/inlineToolbarIcons.js +14 -0
- package/dist/editor/inlineChrome/inlineWrapActive.d.ts +3 -0
- package/dist/editor/inlineChrome/inlineWrapActive.js +19 -0
- package/dist/editor/inlineChrome/selectionRect.d.ts +2 -0
- package/dist/editor/inlineChrome/selectionRect.js +23 -0
- package/dist/editor/model/blockEditCommands.d.ts +73 -0
- package/dist/editor/model/blockEditCommands.js +719 -0
- package/dist/editor/model/blockTree.d.ts +39 -0
- package/dist/editor/model/blockTree.js +185 -0
- package/dist/editor/model/blockTreeDrop.d.ts +9 -0
- package/dist/editor/model/blockTreeDrop.js +295 -0
- package/dist/editor/model/blockTreeMutations.d.ts +10 -0
- package/dist/editor/model/blockTreeMutations.js +68 -0
- package/dist/editor/model/calloutEditCommands.d.ts +39 -0
- package/dist/editor/model/calloutEditCommands.js +305 -0
- package/dist/editor/model/documentSelection.d.ts +8 -0
- package/dist/editor/model/documentSelection.js +56 -0
- package/dist/editor/model/documentSelectionFromSource.d.ts +3 -0
- package/dist/editor/model/documentSelectionFromSource.js +45 -0
- package/dist/editor/model/documentToRenderBlocks.d.ts +3 -0
- package/dist/editor/model/documentToRenderBlocks.js +13 -0
- package/dist/editor/model/editorPipeline.bench.d.ts +1 -0
- package/dist/editor/model/editorPipeline.bench.js +27 -0
- package/dist/editor/model/editorTransactions.d.ts +20 -0
- package/dist/editor/model/editorTransactions.js +68 -0
- package/dist/editor/model/emptyParagraph.d.ts +11 -0
- package/dist/editor/model/emptyParagraph.js +25 -0
- package/dist/editor/model/enterCommands.d.ts +16 -0
- package/dist/editor/model/enterCommands.js +288 -0
- package/dist/editor/model/extensionSyntaxWarnings.d.ts +29 -0
- package/dist/editor/model/extensionSyntaxWarnings.js +122 -0
- package/dist/editor/model/findBlockAtSourceOffset.d.ts +3 -0
- package/dist/editor/model/findBlockAtSourceOffset.js +20 -0
- package/dist/editor/model/fixtures/serialization-fidelity/reorder-gaps.svelte +4 -0
- package/dist/editor/model/focusOutcome.d.ts +25 -0
- package/dist/editor/model/focusOutcome.js +1 -0
- package/dist/editor/model/incrementalReparse.d.ts +16 -0
- package/dist/editor/model/incrementalReparse.js +111 -0
- package/dist/editor/model/index.d.ts +22 -0
- package/dist/editor/model/index.js +19 -0
- package/dist/editor/model/inlineDraftCommit.d.ts +17 -0
- package/dist/editor/model/inlineDraftCommit.js +187 -0
- package/dist/editor/model/inlineDraftMerge.d.ts +8 -0
- package/dist/editor/model/inlineDraftMerge.js +81 -0
- package/dist/editor/model/inlineEditCommands.d.ts +28 -0
- package/dist/editor/model/inlineEditCommands.js +581 -0
- package/dist/editor/model/inlinePlain.d.ts +3 -0
- package/dist/editor/model/inlinePlain.js +28 -0
- package/dist/editor/model/inlineSecondPass.d.ts +4 -0
- package/dist/editor/model/inlineSecondPass.js +47 -0
- package/dist/editor/model/line-utils.d.ts +14 -0
- package/dist/editor/model/line-utils.js +38 -0
- package/dist/editor/model/listEditCommands.d.ts +56 -0
- package/dist/editor/model/listEditCommands.js +299 -0
- package/dist/editor/model/nestedContainerTransactions.d.ts +36 -0
- package/dist/editor/model/nestedContainerTransactions.js +336 -0
- package/dist/editor/model/parseMarkdownDocument.d.ts +2 -0
- package/dist/editor/model/parseMarkdownDocument.js +324 -0
- package/dist/editor/model/quoteEditCommands.d.ts +39 -0
- package/dist/editor/model/quoteEditCommands.js +323 -0
- package/dist/editor/model/reconcileBlockIds.d.ts +9 -0
- package/dist/editor/model/reconcileBlockIds.js +297 -0
- package/dist/editor/model/reparseOptions.d.ts +12 -0
- package/dist/editor/model/reparseOptions.js +1 -0
- package/dist/editor/model/resolveEnterPlainSnapshot.d.ts +6 -0
- package/dist/editor/model/resolveEnterPlainSnapshot.js +23 -0
- package/dist/editor/model/richInlines.d.ts +16 -0
- package/dist/editor/model/richInlines.js +494 -0
- package/dist/editor/model/serializeInlineNodes.d.ts +3 -0
- package/dist/editor/model/serializeInlineNodes.js +45 -0
- package/dist/editor/model/serializeMarkdownDocument.d.ts +30 -0
- package/dist/editor/model/serializeMarkdownDocument.js +150 -0
- package/dist/editor/model/structureParseWarnings.d.ts +10 -0
- package/dist/editor/model/structureParseWarnings.js +60 -0
- package/dist/editor/model/syntheticInlineRanges.d.ts +3 -0
- package/dist/editor/model/syntheticInlineRanges.js +36 -0
- package/dist/editor/model/types.d.ts +185 -0
- package/dist/editor/model/types.js +26 -0
- package/dist/editor/plugins/autolinks/AutolinkRenderer.svelte +20 -0
- package/dist/editor/plugins/autolinks/AutolinkRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/autolinks/index.d.ts +2 -0
- package/dist/editor/plugins/autolinks/index.js +4 -0
- package/dist/editor/plugins/autolinks/parse-autolinks.d.ts +2 -0
- package/dist/editor/plugins/autolinks/parse-autolinks.js +68 -0
- package/dist/editor/plugins/autolinks/types.d.ts +6 -0
- package/dist/editor/plugins/autolinks/types.js +1 -0
- package/dist/editor/plugins/emphasis/EmphasisRenderer.svelte +15 -0
- package/dist/editor/plugins/emphasis/EmphasisRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/emphasis/index.d.ts +2 -0
- package/dist/editor/plugins/emphasis/index.js +4 -0
- package/dist/editor/plugins/emphasis/parse-emphasis.d.ts +2 -0
- package/dist/editor/plugins/emphasis/parse-emphasis.js +188 -0
- package/dist/editor/plugins/emphasis/types.d.ts +24 -0
- package/dist/editor/plugins/emphasis/types.js +1 -0
- package/dist/editor/plugins/escaping/EscapingRenderer.svelte +18 -0
- package/dist/editor/plugins/escaping/EscapingRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/escaping/index.d.ts +2 -0
- package/dist/editor/plugins/escaping/index.js +4 -0
- package/dist/editor/plugins/escaping/parse-escaping.d.ts +6 -0
- package/dist/editor/plugins/escaping/parse-escaping.js +26 -0
- package/dist/editor/plugins/escaping/types.d.ts +10 -0
- package/dist/editor/plugins/escaping/types.js +1 -0
- package/dist/editor/plugins/extended-task-list/ExtendedTaskListRenderer.svelte +25 -0
- package/dist/editor/plugins/extended-task-list/ExtendedTaskListRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/extended-task-list/index.d.ts +2 -0
- package/dist/editor/plugins/extended-task-list/index.js +4 -0
- package/dist/editor/plugins/extended-task-list/parse-extended-task-list.d.ts +2 -0
- package/dist/editor/plugins/extended-task-list/parse-extended-task-list.js +35 -0
- package/dist/editor/plugins/extended-task-list/types.d.ts +6 -0
- package/dist/editor/plugins/extended-task-list/types.js +1 -0
- package/dist/editor/plugins/highlight/HighlightRenderer.svelte +16 -0
- package/dist/editor/plugins/highlight/HighlightRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/highlight/index.d.ts +2 -0
- package/dist/editor/plugins/highlight/index.js +4 -0
- package/dist/editor/plugins/highlight/parse-highlights.d.ts +4 -0
- package/dist/editor/plugins/highlight/parse-highlights.js +49 -0
- package/dist/editor/plugins/highlight/types.d.ts +6 -0
- package/dist/editor/plugins/highlight/types.js +1 -0
- package/dist/editor/plugins/index.d.ts +1 -0
- package/dist/editor/plugins/index.js +1 -0
- package/dist/editor/plugins/links-images/LinksImagesRenderer.svelte +53 -0
- package/dist/editor/plugins/links-images/LinksImagesRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/links-images/index.d.ts +2 -0
- package/dist/editor/plugins/links-images/index.js +4 -0
- package/dist/editor/plugins/links-images/parse-image-layout.d.ts +28 -0
- package/dist/editor/plugins/links-images/parse-image-layout.js +147 -0
- package/dist/editor/plugins/links-images/parse-links-images.d.ts +25 -0
- package/dist/editor/plugins/links-images/parse-links-images.js +236 -0
- package/dist/editor/plugins/links-images/resolve-asset-url.d.ts +5 -0
- package/dist/editor/plugins/links-images/resolve-asset-url.js +21 -0
- package/dist/editor/plugins/links-images/types.d.ts +11 -0
- package/dist/editor/plugins/links-images/types.js +1 -0
- package/dist/editor/plugins/mention/MentionRenderer.svelte +18 -0
- package/dist/editor/plugins/mention/MentionRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/mention/index.d.ts +2 -0
- package/dist/editor/plugins/mention/index.js +4 -0
- package/dist/editor/plugins/mention/parse-mentions.d.ts +2 -0
- package/dist/editor/plugins/mention/parse-mentions.js +54 -0
- package/dist/editor/plugins/mention/types.d.ts +14 -0
- package/dist/editor/plugins/mention/types.js +1 -0
- package/dist/editor/plugins/raw-text/RawTextRenderer.svelte +19 -0
- package/dist/editor/plugins/raw-text/RawTextRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/raw-text/index.d.ts +2 -0
- package/dist/editor/plugins/raw-text/index.js +4 -0
- package/dist/editor/plugins/raw-text/types.d.ts +4 -0
- package/dist/editor/plugins/raw-text/types.js +1 -0
- package/dist/editor/plugins/shared/InlineNodes.svelte +113 -0
- package/dist/editor/plugins/shared/InlineNodes.svelte.d.ts +21 -0
- package/dist/editor/plugins/shared/InlineNodesEdit.svelte +78 -0
- package/dist/editor/plugins/shared/InlineNodesEdit.svelte.d.ts +21 -0
- package/dist/editor/plugins/strikethrough/StrikethroughRenderer.svelte +9 -0
- package/dist/editor/plugins/strikethrough/StrikethroughRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/strikethrough/index.d.ts +2 -0
- package/dist/editor/plugins/strikethrough/index.js +4 -0
- package/dist/editor/plugins/strikethrough/parse-strikethrough.d.ts +3 -0
- package/dist/editor/plugins/strikethrough/parse-strikethrough.js +38 -0
- package/dist/editor/plugins/strikethrough/types.d.ts +4 -0
- package/dist/editor/plugins/strikethrough/types.js +1 -0
- package/dist/editor/plugins/synced-block/SyncedBlockRenderer.svelte +16 -0
- package/dist/editor/plugins/synced-block/SyncedBlockRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/synced-block/index.d.ts +2 -0
- package/dist/editor/plugins/synced-block/index.js +4 -0
- package/dist/editor/plugins/synced-block/parse-synced-blocks.d.ts +2 -0
- package/dist/editor/plugins/synced-block/parse-synced-blocks.js +15 -0
- package/dist/editor/plugins/synced-block/types.d.ts +4 -0
- package/dist/editor/plugins/synced-block/types.js +1 -0
- package/dist/editor/plugins/types.d.ts +19 -0
- package/dist/editor/plugins/types.js +1 -0
- package/dist/editor/plugins/wiki-link/WikiLinkRenderer.svelte +15 -0
- package/dist/editor/plugins/wiki-link/WikiLinkRenderer.svelte.d.ts +21 -0
- package/dist/editor/plugins/wiki-link/index.d.ts +2 -0
- package/dist/editor/plugins/wiki-link/index.js +4 -0
- package/dist/editor/plugins/wiki-link/parse-wiki-links.d.ts +2 -0
- package/dist/editor/plugins/wiki-link/parse-wiki-links.js +30 -0
- package/dist/editor/plugins/wiki-link/types.d.ts +8 -0
- package/dist/editor/plugins/wiki-link/types.js +1 -0
- package/dist/editor/taskCheckboxStates.d.ts +5 -0
- package/dist/editor/taskCheckboxStates.js +28 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/styles/index.css +6 -0
- package/package.json +84 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
import { headingDisplayPlain, headingMarkdown, headingPlainOffsetToSourceOffset } from '../block-specs/heading/edit';
|
|
2
|
+
import { paragraphBlockFromPlainText, paragraphBlockToPlainText, paragraphDisplayPlain, paragraphPlainOffsetToSourceOffset, serializeParagraphData } from '../block-specs/paragraph';
|
|
3
|
+
import { applyEditorTransaction, deleteBlockTransaction, duplicateBlockTransaction } from './editorTransactions';
|
|
4
|
+
import { EMPTY_PARAGRAPH_MARKER, normalizeEmptySource } from './emptyParagraph';
|
|
5
|
+
import { clampSelection } from './types';
|
|
6
|
+
import { cloneInlineNodeDeep, inlineTextNode, mergePlainTypingIntoInlines, wrapInlineSelection } from './inlineEditCommands';
|
|
7
|
+
import { inlineNodesToPlainText } from './inlinePlain';
|
|
8
|
+
import { applyInlineSecondPass } from './inlineSecondPass';
|
|
9
|
+
import { parseMarkdownDocument } from './parseMarkdownDocument';
|
|
10
|
+
import { serializeInlineNodes } from './serializeInlineNodes';
|
|
11
|
+
export const EDITOR_HISTORY_LIMIT = 120;
|
|
12
|
+
export function replaceSourceRange(source, start, end, replacement) {
|
|
13
|
+
return source.slice(0, start) + replacement + source.slice(end);
|
|
14
|
+
}
|
|
15
|
+
/** Serialized block markdown often omits the final line terminator that `sourceRange.end` still covers. */
|
|
16
|
+
export function normalizeBlockSliceReplacement(source, start, end, replacement) {
|
|
17
|
+
if (end > start &&
|
|
18
|
+
source.charCodeAt(end - 1) === 10 &&
|
|
19
|
+
replacement.length > 0 &&
|
|
20
|
+
!replacement.endsWith('\n')) {
|
|
21
|
+
return replacement + '\n';
|
|
22
|
+
}
|
|
23
|
+
return replacement;
|
|
24
|
+
}
|
|
25
|
+
export function replaceBlockSourceRange(source, start, end, replacement) {
|
|
26
|
+
return replaceSourceRange(source, start, end, normalizeBlockSliceReplacement(source, start, end, replacement));
|
|
27
|
+
}
|
|
28
|
+
export { paragraphBlockFromPlainText, paragraphBlockToPlainText, paragraphDisplayPlain, paragraphPlainOffsetToSourceOffset } from '../block-specs/paragraph';
|
|
29
|
+
export { headingBodySourceRange, headingDisplayPlain, headingMarkdown, headingPlainOffsetToSourceOffset } from '../block-specs/heading/edit';
|
|
30
|
+
/** Replace paragraph or heading body from a plain-text edit; preserves `inlines` when a single-character delta patches the AST. */
|
|
31
|
+
export function updateBlockPlainText(source, doc, blockIndex, plainText, kind, selectionPlainOffset) {
|
|
32
|
+
const block = doc.blocks[blockIndex];
|
|
33
|
+
if (!block)
|
|
34
|
+
return { ok: false, error: 'Invalid block index.' };
|
|
35
|
+
if (block.sourceRange.synthetic || block.sourceRange.start < 0) {
|
|
36
|
+
return { ok: false, error: 'Block is not editable.' };
|
|
37
|
+
}
|
|
38
|
+
let replacement;
|
|
39
|
+
if (kind === 'paragraph' && block.type === 'paragraph') {
|
|
40
|
+
const prev = block.data;
|
|
41
|
+
if (block.inlines?.length) {
|
|
42
|
+
const oldPlain = inlineNodesToPlainText(block.inlines);
|
|
43
|
+
if (plainText === oldPlain || plainText === `${oldPlain}\n`) {
|
|
44
|
+
const anchor = block.sourceRange.end;
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
source,
|
|
48
|
+
selection: clampSelection({ anchor, focus: anchor }, source.length)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const merged = mergePlainTypingIntoInlines(block.inlines, plainText);
|
|
52
|
+
if (merged) {
|
|
53
|
+
replacement = serializeInlineNodes(merged);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
replacement = serializeParagraphData(paragraphBlockFromPlainText(plainText, prev.startLineIndex));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
replacement = serializeParagraphData(paragraphBlockFromPlainText(plainText, prev.startLineIndex));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (kind === 'heading' && block.type === 'heading') {
|
|
64
|
+
const prev = block.data;
|
|
65
|
+
if (block.inlines?.length) {
|
|
66
|
+
const merged = mergePlainTypingIntoInlines(block.inlines, plainText);
|
|
67
|
+
if (merged) {
|
|
68
|
+
replacement = headingMarkdown(prev.level, serializeInlineNodes(merged));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
replacement = headingMarkdown(prev.level, plainText);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
replacement = headingMarkdown(prev.level, plainText);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return { ok: false, error: 'Block kind mismatch.' };
|
|
80
|
+
}
|
|
81
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
82
|
+
const co = Math.max(0, selectionPlainOffset);
|
|
83
|
+
let anchor;
|
|
84
|
+
if (kind === 'paragraph') {
|
|
85
|
+
const prev = block.data;
|
|
86
|
+
const tmpBlock = {
|
|
87
|
+
type: 'paragraph',
|
|
88
|
+
id: 'tmp',
|
|
89
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
90
|
+
data: paragraphBlockFromPlainText(plainText, prev.startLineIndex)
|
|
91
|
+
};
|
|
92
|
+
const inner = paragraphPlainOffsetToSourceOffset(tmpBlock, Math.min(co, plainText.length));
|
|
93
|
+
anchor = block.sourceRange.start + inner;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const prev = block.data;
|
|
97
|
+
const tmpHeadingBlock = {
|
|
98
|
+
type: 'heading',
|
|
99
|
+
id: 'tmp',
|
|
100
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
101
|
+
data: { ...prev, text: plainText }
|
|
102
|
+
};
|
|
103
|
+
const inner = headingPlainOffsetToSourceOffset(tmpHeadingBlock, replacement, Math.min(co, plainText.length));
|
|
104
|
+
anchor = block.sourceRange.start + inner;
|
|
105
|
+
}
|
|
106
|
+
const sel = clampSelection({ anchor, focus: anchor }, newSource.length);
|
|
107
|
+
return { ok: true, source: newSource, selection: sel };
|
|
108
|
+
}
|
|
109
|
+
/** Wrap plain-text selection in Markdown inline spans (paragraph or heading body). */
|
|
110
|
+
export function applyInlineWrap(source, doc, blockIndex, plainStart, plainEnd, kind, linkHref) {
|
|
111
|
+
const block = doc.blocks[blockIndex];
|
|
112
|
+
if (!block)
|
|
113
|
+
return { ok: false, error: 'Invalid block index.' };
|
|
114
|
+
if (block.sourceRange.synthetic || block.sourceRange.start < 0) {
|
|
115
|
+
return { ok: false, error: 'Block is not editable.' };
|
|
116
|
+
}
|
|
117
|
+
const a = Math.min(plainStart, plainEnd);
|
|
118
|
+
const b = Math.max(plainStart, plainEnd);
|
|
119
|
+
if (block.type === 'paragraph') {
|
|
120
|
+
const p = block.data;
|
|
121
|
+
const baseNodes = block.inlines?.length
|
|
122
|
+
? block.inlines.map(cloneInlineNodeDeep)
|
|
123
|
+
: [inlineTextNode(paragraphBlockToPlainText(p))];
|
|
124
|
+
const wrapped = wrapInlineSelection(baseNodes, a, b, kind, linkHref);
|
|
125
|
+
if (!wrapped)
|
|
126
|
+
return { ok: false, error: 'Could not apply inline format.' };
|
|
127
|
+
const replacement = serializeInlineNodes(wrapped);
|
|
128
|
+
const newPlain = inlineNodesToPlainText(wrapped);
|
|
129
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
130
|
+
const tmpBlock = {
|
|
131
|
+
type: 'paragraph',
|
|
132
|
+
id: 'tmp',
|
|
133
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
134
|
+
data: paragraphBlockFromPlainText(newPlain, p.startLineIndex)
|
|
135
|
+
};
|
|
136
|
+
const inner = paragraphPlainOffsetToSourceOffset(tmpBlock, Math.min(b, newPlain.length));
|
|
137
|
+
const anchor = block.sourceRange.start + inner;
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
source: newSource,
|
|
141
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (block.type === 'heading') {
|
|
145
|
+
const hb = block.data;
|
|
146
|
+
const baseNodes = block.inlines?.length
|
|
147
|
+
? block.inlines.map(cloneInlineNodeDeep)
|
|
148
|
+
: [inlineTextNode(hb.text)];
|
|
149
|
+
const wrapped = wrapInlineSelection(baseNodes, a, b, kind, linkHref);
|
|
150
|
+
if (!wrapped)
|
|
151
|
+
return { ok: false, error: 'Could not apply inline format.' };
|
|
152
|
+
const bodyMd = serializeInlineNodes(wrapped);
|
|
153
|
+
const replacement = headingMarkdown(hb.level, bodyMd);
|
|
154
|
+
const newPlain = inlineNodesToPlainText(wrapped);
|
|
155
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
156
|
+
const tmpHeadingBlock = {
|
|
157
|
+
type: 'heading',
|
|
158
|
+
id: 'tmp',
|
|
159
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
160
|
+
data: { ...hb, text: newPlain },
|
|
161
|
+
inlines: wrapped
|
|
162
|
+
};
|
|
163
|
+
const inner = headingPlainOffsetToSourceOffset(tmpHeadingBlock, replacement, Math.min(b, newPlain.length));
|
|
164
|
+
const anchor = block.sourceRange.start + inner;
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
source: newSource,
|
|
168
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { ok: false, error: 'Inline formatting applies to paragraphs and headings only.' };
|
|
172
|
+
}
|
|
173
|
+
/** Turn `# title` / `- item` / `1. item` plain lines into the corresponding Markdown block slice. */
|
|
174
|
+
export function isStructuralMarkdownShortcutLine(plainText) {
|
|
175
|
+
return paragraphShortcutMarkdown(plainText) !== null;
|
|
176
|
+
}
|
|
177
|
+
/** Whether Space, Enter, or blur should run **structural markdown shortcut** for this plain line. */
|
|
178
|
+
export function isStructuralMarkdownShortcutTrigger(plainText, trigger, options) {
|
|
179
|
+
if (paragraphShortcutMarkdown(plainText) !== null)
|
|
180
|
+
return true;
|
|
181
|
+
if (!options?.allowMarkerOnlyList || trigger === 'blur')
|
|
182
|
+
return false;
|
|
183
|
+
return markerOnlyListShortcutMarkdown(plainText) !== null;
|
|
184
|
+
}
|
|
185
|
+
/** Marker-only list/task prefixes completed by Space or Enter (not blur). Paragraph bodies only. */
|
|
186
|
+
export function markerOnlyListShortcutMarkdown(plainText) {
|
|
187
|
+
const lines = plainText.split('\n');
|
|
188
|
+
if (lines.length !== 1)
|
|
189
|
+
return null;
|
|
190
|
+
const line = lines[0] ?? '';
|
|
191
|
+
const task = line.match(/^-\s+\[([ xX/>])\]$/);
|
|
192
|
+
if (task)
|
|
193
|
+
return `- [${task[1]}] `;
|
|
194
|
+
const bullet = line.match(/^[-*+]$/);
|
|
195
|
+
if (bullet)
|
|
196
|
+
return `${line} `;
|
|
197
|
+
const ordered = line.match(/^(\d+)$/);
|
|
198
|
+
if (ordered)
|
|
199
|
+
return `${ordered[1]}. `;
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
export function paragraphShortcutMarkdown(plainText) {
|
|
203
|
+
const lines = plainText.split('\n');
|
|
204
|
+
if (lines.length !== 1)
|
|
205
|
+
return null;
|
|
206
|
+
const line = lines[0] ?? '';
|
|
207
|
+
const hm = line.match(/^(#{1,6})\s+(.*)$/);
|
|
208
|
+
if (hm) {
|
|
209
|
+
const level = hm[1].length;
|
|
210
|
+
return headingMarkdown(level, hm[2].trimEnd());
|
|
211
|
+
}
|
|
212
|
+
const taskEmpty = line.match(/^-\s+\[([ xX/>])\]\s+$/);
|
|
213
|
+
if (taskEmpty)
|
|
214
|
+
return `- [${taskEmpty[1]}] `;
|
|
215
|
+
const task = line.match(/^-\s+\[([ xX/>])\]\s+(.+)$/);
|
|
216
|
+
if (task) {
|
|
217
|
+
const body = task[2].trimEnd();
|
|
218
|
+
return `- [${task[1]}] ${body}`;
|
|
219
|
+
}
|
|
220
|
+
const bulletEmpty = line.match(/^([-*+])\s+$/);
|
|
221
|
+
if (bulletEmpty)
|
|
222
|
+
return `${bulletEmpty[1]} `;
|
|
223
|
+
const um = line.match(/^([-*+])\s+(.+)$/);
|
|
224
|
+
if (um && !um[2].trimStart().startsWith('[')) {
|
|
225
|
+
const marker = um[1];
|
|
226
|
+
const body = um[2].trimEnd();
|
|
227
|
+
return `${marker} ${body}`;
|
|
228
|
+
}
|
|
229
|
+
const orderedEmpty = line.match(/^(\d+)\.\s+$/);
|
|
230
|
+
if (orderedEmpty)
|
|
231
|
+
return `${orderedEmpty[1]}. `;
|
|
232
|
+
const om = line.match(/^(\d+)\.\s+(.*)$/);
|
|
233
|
+
if (om) {
|
|
234
|
+
const n = om[1];
|
|
235
|
+
const body = om[2].trimEnd();
|
|
236
|
+
return body.length > 0 ? `${n}. ${body}` : `${n}. `;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
function listShortcutBodyPlainLength(plainText, md) {
|
|
241
|
+
const task = plainText.match(/^-\s+\[([ xX/>])\](?:\s+(.*))?$/);
|
|
242
|
+
if (task && /^-\s+\[/.test(md))
|
|
243
|
+
return (task[2] ?? '').trimEnd().length;
|
|
244
|
+
const bullet = plainText.match(/^([-*+])(?:\s+(.*))?$/);
|
|
245
|
+
if (bullet && /^[-*+]\s/.test(md))
|
|
246
|
+
return (bullet[2] ?? '').trimEnd().length;
|
|
247
|
+
const ordered = plainText.match(/^(\d+)(?:\.\s+(.*))?$/);
|
|
248
|
+
if (ordered && /^\d+\.\s/.test(md))
|
|
249
|
+
return (ordered[2] ?? '').trimEnd().length;
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
/** Focus after **structural markdown shortcut** conversion (heading body plain or list item body). */
|
|
253
|
+
export function buildStructuralShortcutFocusOutcome(doc, blockIndex, plainText, md) {
|
|
254
|
+
const block = doc.blocks[blockIndex];
|
|
255
|
+
if (!block)
|
|
256
|
+
return { mode: 'blockId', blockId: '', plain: { start: 0, end: 0 } };
|
|
257
|
+
const hm = plainText.match(/^(#{1,6})\s+(.*)$/);
|
|
258
|
+
if (hm && /^#{1,6}\s/.test(md)) {
|
|
259
|
+
const bodyLen = hm[2].trimEnd().length;
|
|
260
|
+
return {
|
|
261
|
+
mode: 'sourceOffset',
|
|
262
|
+
offset: block.sourceRange.start,
|
|
263
|
+
plain: { start: bodyLen, end: bodyLen }
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (/^-\s+\[/.test(md) || /^[-*+]\s/.test(md) || /^\d+\.\s/.test(md)) {
|
|
267
|
+
const bodyLen = listShortcutBodyPlainLength(plainText, md);
|
|
268
|
+
return {
|
|
269
|
+
mode: 'sourceOffset',
|
|
270
|
+
offset: block.sourceRange.start,
|
|
271
|
+
plain: { start: bodyLen, end: bodyLen }
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return { mode: 'blockId', blockId: block.id, plain: { start: 0, end: 0 } };
|
|
275
|
+
}
|
|
276
|
+
/** Apply line-start markdown shortcut on a top-level **paragraph** or **heading** body. */
|
|
277
|
+
export function applyStructuralMarkdownShortcut(source, doc, blockIndex, plainText) {
|
|
278
|
+
const block = doc.blocks[blockIndex];
|
|
279
|
+
if (!block || (block.type !== 'paragraph' && block.type !== 'heading')) {
|
|
280
|
+
return { ok: false, error: 'Unsupported block type for shortcut.' };
|
|
281
|
+
}
|
|
282
|
+
if (block.sourceRange.synthetic || block.sourceRange.start < 0) {
|
|
283
|
+
return { ok: false, error: 'Block is not editable.' };
|
|
284
|
+
}
|
|
285
|
+
const md = paragraphShortcutMarkdown(plainText) ??
|
|
286
|
+
(block.type === 'paragraph' ? markerOnlyListShortcutMarkdown(plainText) : null);
|
|
287
|
+
if (!md)
|
|
288
|
+
return { ok: false, error: 'No shortcut applied.' };
|
|
289
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, md);
|
|
290
|
+
const focusAfter = buildStructuralShortcutFocusOutcome(doc, blockIndex, plainText, md);
|
|
291
|
+
const sel = clampSelection({ anchor: block.sourceRange.start + md.length, focus: block.sourceRange.start + md.length }, newSource.length);
|
|
292
|
+
return { ok: true, source: newSource, selection: sel, focusAfter };
|
|
293
|
+
}
|
|
294
|
+
/** @deprecated Prefer {@link applyStructuralMarkdownShortcut}. */
|
|
295
|
+
export function applyParagraphShortcut(source, doc, blockIndex, plainText) {
|
|
296
|
+
const r = applyStructuralMarkdownShortcut(source, doc, blockIndex, plainText);
|
|
297
|
+
if (!r.ok)
|
|
298
|
+
return r;
|
|
299
|
+
const { focusAfter: _, ...rest } = r;
|
|
300
|
+
return rest;
|
|
301
|
+
}
|
|
302
|
+
export function splitBlockAtPlainOffset(source, doc, blockIndex, plainOffset, kind) {
|
|
303
|
+
const block = doc.blocks[blockIndex];
|
|
304
|
+
if (!block)
|
|
305
|
+
return { ok: false, error: 'Invalid block index.' };
|
|
306
|
+
if (block.sourceRange.synthetic || block.sourceRange.start < 0) {
|
|
307
|
+
return { ok: false, error: 'Block is not splittable.' };
|
|
308
|
+
}
|
|
309
|
+
if (kind === 'paragraph' && block.type === 'paragraph') {
|
|
310
|
+
const p = block.data;
|
|
311
|
+
const plain = paragraphDisplayPlain(block);
|
|
312
|
+
if (plainOffset < 0 || plainOffset > plain.length) {
|
|
313
|
+
return { ok: false, error: 'Caret out of range.' };
|
|
314
|
+
}
|
|
315
|
+
if (plainOffset === 0 || plainOffset === plain.length) {
|
|
316
|
+
return { ok: false, error: 'Use empty paragraph insert for boundary split.' };
|
|
317
|
+
}
|
|
318
|
+
const left = plain.slice(0, plainOffset);
|
|
319
|
+
const right = plain.slice(plainOffset);
|
|
320
|
+
const leftMd = serializeParagraphData(paragraphBlockFromPlainText(left, p.startLineIndex));
|
|
321
|
+
const rightMd = serializeParagraphData(paragraphBlockFromPlainText(right, p.startLineIndex + left.split('\n').length));
|
|
322
|
+
const replacement = `${leftMd}\n\n${rightMd}`;
|
|
323
|
+
const newSource = replaceSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
324
|
+
const caretPlainInSecond = 0;
|
|
325
|
+
const para2ApproxStart = block.sourceRange.start + leftMd.length + 2;
|
|
326
|
+
const anchor = para2ApproxStart + caretPlainInSecond;
|
|
327
|
+
return {
|
|
328
|
+
ok: true,
|
|
329
|
+
source: newSource,
|
|
330
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (kind === 'heading' && block.type === 'heading') {
|
|
334
|
+
const hb = block.data;
|
|
335
|
+
const text = headingDisplayPlain(block);
|
|
336
|
+
if (plainOffset < 0 || plainOffset > text.length) {
|
|
337
|
+
return { ok: false, error: 'Caret out of range.' };
|
|
338
|
+
}
|
|
339
|
+
if (plainOffset === 0 || plainOffset === text.length) {
|
|
340
|
+
return { ok: false, error: 'Use empty paragraph insert for boundary split.' };
|
|
341
|
+
}
|
|
342
|
+
const left = text.slice(0, plainOffset).trimEnd();
|
|
343
|
+
const right = text.slice(plainOffset).trimStart();
|
|
344
|
+
const leftMd = headingMarkdown(hb.level, left);
|
|
345
|
+
const rightMd = serializeParagraphData(paragraphBlockFromPlainText(right, hb.lineIndex + 1));
|
|
346
|
+
const replacement = `${leftMd}\n\n${rightMd}`;
|
|
347
|
+
const newSource = replaceSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
348
|
+
const anchor = block.sourceRange.start + leftMd.length + 2;
|
|
349
|
+
return {
|
|
350
|
+
ok: true,
|
|
351
|
+
source: newSource,
|
|
352
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return { ok: false, error: 'Split not supported for this block type.' };
|
|
356
|
+
}
|
|
357
|
+
/** Insert an empty paragraph before (`after === false`) or after the block. */
|
|
358
|
+
export function insertEmptyParagraphRelative(source, doc, blockIndex, after) {
|
|
359
|
+
const block = doc.blocks[blockIndex];
|
|
360
|
+
if (!block) {
|
|
361
|
+
const normalized = normalizeEmptySource(source);
|
|
362
|
+
return {
|
|
363
|
+
ok: true,
|
|
364
|
+
source: normalized,
|
|
365
|
+
selection: clampSelection({ anchor: 0, focus: 0 }, normalized.length)
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const emptyBody = EMPTY_PARAGRAPH_MARKER;
|
|
369
|
+
let pos;
|
|
370
|
+
let insertion;
|
|
371
|
+
if (after) {
|
|
372
|
+
pos = block.sourceRange.end;
|
|
373
|
+
let suffix = '';
|
|
374
|
+
for (let i = blockIndex + 1; i < doc.blocks.length; i++) {
|
|
375
|
+
const candidate = doc.blocks[i];
|
|
376
|
+
if (candidate.sourceRange.start >= 0) {
|
|
377
|
+
if (candidate.sourceRange.start === pos)
|
|
378
|
+
suffix = '\n';
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
insertion = `\n\n${emptyBody}${suffix}`;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
pos = block.sourceRange.start;
|
|
386
|
+
insertion = `${emptyBody}\n\n`;
|
|
387
|
+
}
|
|
388
|
+
const newSource = replaceSourceRange(source, pos, pos, insertion);
|
|
389
|
+
const caret = after ? pos + 2 + emptyBody.length : pos + emptyBody.length;
|
|
390
|
+
return {
|
|
391
|
+
ok: true,
|
|
392
|
+
source: newSource,
|
|
393
|
+
selection: clampSelection({ anchor: caret, focus: caret }, newSource.length)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
/** Source transaction for inserting a canonical empty **paragraph** sibling. */
|
|
397
|
+
export function buildEmptyParagraphInsertTransaction(source, doc, blockIndex, after, txId) {
|
|
398
|
+
const block = doc.blocks[blockIndex];
|
|
399
|
+
if (!block)
|
|
400
|
+
return { ok: false, error: 'Invalid block index.' };
|
|
401
|
+
if (block.sourceRange.start < 0)
|
|
402
|
+
return { ok: false, error: 'Block is not editable.' };
|
|
403
|
+
const before = source;
|
|
404
|
+
const edit = insertEmptyParagraphRelative(before, doc, blockIndex, after);
|
|
405
|
+
const pos = after ? block.sourceRange.end : block.sourceRange.start;
|
|
406
|
+
const insertion = edit.source.slice(pos, pos + (edit.source.length - before.length));
|
|
407
|
+
return {
|
|
408
|
+
ok: true,
|
|
409
|
+
transaction: {
|
|
410
|
+
id: txId,
|
|
411
|
+
operations: [{ type: 'replaceSlice', range: { start: pos, end: pos }, text: insertion }],
|
|
412
|
+
selection: edit.selection
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
export function mergeWithPreviousBlock(source, doc, blockIndex) {
|
|
417
|
+
if (blockIndex <= 0)
|
|
418
|
+
return { ok: false, error: 'Nothing to merge into.' };
|
|
419
|
+
const prev = doc.blocks[blockIndex - 1];
|
|
420
|
+
const curr = doc.blocks[blockIndex];
|
|
421
|
+
if (prev.sourceRange.synthetic || curr.sourceRange.synthetic) {
|
|
422
|
+
return { ok: false, error: 'Cannot merge synthetic blocks.' };
|
|
423
|
+
}
|
|
424
|
+
const start = prev.sourceRange.start;
|
|
425
|
+
const end = curr.sourceRange.end;
|
|
426
|
+
let replacement;
|
|
427
|
+
let anchor;
|
|
428
|
+
let focusAfter;
|
|
429
|
+
if (prev.type === 'paragraph' && curr.type === 'paragraph') {
|
|
430
|
+
const a = prev.data;
|
|
431
|
+
const b = curr.data;
|
|
432
|
+
const leftMd = serializeParagraphData(a).replace(/[ \t]+$/, '');
|
|
433
|
+
const rightMd = serializeParagraphData(b).replace(/^[ \t]+/, '');
|
|
434
|
+
const leftPlain = paragraphDisplayPlain(prev).replace(/[ \t]+$/, '');
|
|
435
|
+
const rightPlain = paragraphDisplayPlain(curr).replace(/^[ \t]+/, '');
|
|
436
|
+
const separator = leftMd.length > 0 && rightMd.length > 0 ? ' ' : '';
|
|
437
|
+
const plainSeparator = leftPlain.length > 0 && rightPlain.length > 0 ? 1 : 0;
|
|
438
|
+
replacement = `${leftMd}${separator}${rightMd}`;
|
|
439
|
+
anchor = start + leftMd.length + separator.length;
|
|
440
|
+
const joinPlain = leftPlain.length + plainSeparator;
|
|
441
|
+
focusAfter = {
|
|
442
|
+
mode: 'blockId',
|
|
443
|
+
blockId: prev.id,
|
|
444
|
+
plain: { start: joinPlain, end: joinPlain }
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
else if (prev.type === 'heading' && curr.type === 'heading') {
|
|
448
|
+
const a = prev.data;
|
|
449
|
+
const b = curr.data;
|
|
450
|
+
const level = Math.min(a.level, b.level);
|
|
451
|
+
const mergedText = `${a.text} ${b.text}`.trim();
|
|
452
|
+
replacement = headingMarkdown(level, mergedText);
|
|
453
|
+
const caretPlain = Math.min(a.text.length + 1, mergedText.length);
|
|
454
|
+
anchor = start + headingMarkdown(level, mergedText.slice(0, caretPlain)).length;
|
|
455
|
+
focusAfter = {
|
|
456
|
+
mode: 'blockId',
|
|
457
|
+
blockId: prev.id,
|
|
458
|
+
plain: { start: caretPlain, end: caretPlain }
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
else if (prev.type === 'heading' && curr.type === 'paragraph') {
|
|
462
|
+
const h = prev.data;
|
|
463
|
+
const p = curr.data;
|
|
464
|
+
const mergedBody = `${h.text} ${paragraphBlockToPlainText(p)}`.replace(/\n/g, ' ').trim();
|
|
465
|
+
replacement = headingMarkdown(h.level, mergedBody);
|
|
466
|
+
const caretPlain = Math.min(h.text.length + 1, mergedBody.length);
|
|
467
|
+
anchor = start + headingMarkdown(h.level, mergedBody.slice(0, caretPlain)).length;
|
|
468
|
+
const mergedHeading = {
|
|
469
|
+
type: 'heading',
|
|
470
|
+
id: 'tmp',
|
|
471
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
472
|
+
data: { ...h, text: mergedBody }
|
|
473
|
+
};
|
|
474
|
+
const joinPlain = headingDisplayPlain(mergedHeading).length - paragraphDisplayPlain(curr).length;
|
|
475
|
+
focusAfter = {
|
|
476
|
+
mode: 'blockId',
|
|
477
|
+
blockId: prev.id,
|
|
478
|
+
plain: { start: joinPlain, end: joinPlain }
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
else if (prev.type === 'paragraph' && curr.type === 'heading') {
|
|
482
|
+
const p = prev.data;
|
|
483
|
+
const h = curr.data;
|
|
484
|
+
const mergedPlain = `${paragraphBlockToPlainText(p)}\n${headingMarkdown(h.level, h.text)}`;
|
|
485
|
+
const caretPlainInMerged = paragraphBlockToPlainText(p).length + 1;
|
|
486
|
+
replacement = serializeParagraphData(paragraphBlockFromPlainText(mergedPlain, p.startLineIndex));
|
|
487
|
+
const mergedBlock = {
|
|
488
|
+
type: 'paragraph',
|
|
489
|
+
id: 'tmp',
|
|
490
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
491
|
+
data: paragraphBlockFromPlainText(mergedPlain, p.startLineIndex)
|
|
492
|
+
};
|
|
493
|
+
anchor = start + paragraphPlainOffsetToSourceOffset(mergedBlock, caretPlainInMerged);
|
|
494
|
+
const joinPlain = paragraphDisplayPlain(mergedBlock).length - headingDisplayPlain(curr).length;
|
|
495
|
+
focusAfter = {
|
|
496
|
+
mode: 'blockId',
|
|
497
|
+
blockId: prev.id,
|
|
498
|
+
plain: { start: joinPlain, end: joinPlain }
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
return { ok: false, error: 'Merge not supported for this block pair.' };
|
|
503
|
+
}
|
|
504
|
+
const normalizedReplacement = normalizeBlockSliceReplacement(source, start, end, replacement);
|
|
505
|
+
const newSource = replaceSourceRange(source, start, end, normalizedReplacement);
|
|
506
|
+
return {
|
|
507
|
+
ok: true,
|
|
508
|
+
source: newSource,
|
|
509
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length),
|
|
510
|
+
focusAfter
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
/** Replace the raw markdown slice for one block (used for list/code/blockquote textareas). */
|
|
514
|
+
export function replaceBlockMarkdownSlice(source, doc, blockIndex, replacement) {
|
|
515
|
+
const block = doc.blocks[blockIndex];
|
|
516
|
+
const err = assertEditableBlock(block, 'replace');
|
|
517
|
+
if (err)
|
|
518
|
+
return err;
|
|
519
|
+
const start = block.sourceRange.start;
|
|
520
|
+
const end = block.sourceRange.end;
|
|
521
|
+
const newSource = replaceBlockSourceRange(source, start, end, replacement);
|
|
522
|
+
const anchor = Math.min(start + replacement.length, newSource.length);
|
|
523
|
+
return {
|
|
524
|
+
ok: true,
|
|
525
|
+
source: newSource,
|
|
526
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
export function deleteBlock(source, doc, blockIndex) {
|
|
530
|
+
const built = deleteBlockTransaction(source, doc, blockIndex);
|
|
531
|
+
if (!built.ok)
|
|
532
|
+
return built;
|
|
533
|
+
return applyEditorTransaction(source, built.transaction);
|
|
534
|
+
}
|
|
535
|
+
function blockSlice(source, block) {
|
|
536
|
+
return source.slice(block.sourceRange.start, block.sourceRange.end);
|
|
537
|
+
}
|
|
538
|
+
function assertEditableBlock(block, label) {
|
|
539
|
+
if (!block)
|
|
540
|
+
return { ok: false, error: `Invalid block index (${label}).` };
|
|
541
|
+
if (block.sourceRange.synthetic || block.sourceRange.start < 0) {
|
|
542
|
+
return { ok: false, error: 'Block is not editable.' };
|
|
543
|
+
}
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
/** Inserts a copy of the block's markdown after it; caret at start of duplicate body region. */
|
|
547
|
+
export function duplicateBlock(source, doc, blockIndex) {
|
|
548
|
+
const built = duplicateBlockTransaction(source, doc, blockIndex);
|
|
549
|
+
if (!built.ok)
|
|
550
|
+
return built;
|
|
551
|
+
return applyEditorTransaction(source, built.transaction);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Reorder two adjacent serialized blocks as `lower + inter + upper` while keeping the same
|
|
555
|
+
* total length when the stock join would collapse a `\n\n` top-level break into a single `\n`.
|
|
556
|
+
*/
|
|
557
|
+
function swapMarkdownAdjacentSlices(lowerMd, gap, upperMd) {
|
|
558
|
+
if (gap === '\n' && upperMd.endsWith('\n') && !lowerMd.endsWith('\n')) {
|
|
559
|
+
return `${lowerMd}\n\n${upperMd.slice(0, -1)}`;
|
|
560
|
+
}
|
|
561
|
+
return `${lowerMd}${gap}${upperMd}`;
|
|
562
|
+
}
|
|
563
|
+
/** Swap two adjacent blocks so the lower-index block moves earlier (same as moving block at `lowerIndex` up). */
|
|
564
|
+
export function swapAdjacentBlocks(source, doc, upperBlockIndex) {
|
|
565
|
+
if (upperBlockIndex < 0 || upperBlockIndex >= doc.blocks.length - 1) {
|
|
566
|
+
return { ok: false, error: 'No adjacent pair to swap.' };
|
|
567
|
+
}
|
|
568
|
+
const upper = doc.blocks[upperBlockIndex];
|
|
569
|
+
const lower = doc.blocks[upperBlockIndex + 1];
|
|
570
|
+
const errU = assertEditableBlock(upper, 'upper');
|
|
571
|
+
if (errU)
|
|
572
|
+
return errU;
|
|
573
|
+
const errL = assertEditableBlock(lower, 'lower');
|
|
574
|
+
if (errL)
|
|
575
|
+
return errL;
|
|
576
|
+
const spanStart = upper.sourceRange.start;
|
|
577
|
+
const spanEnd = lower.sourceRange.end;
|
|
578
|
+
const upperMd = blockSlice(source, upper);
|
|
579
|
+
const gap = source.slice(upper.sourceRange.end, lower.sourceRange.start);
|
|
580
|
+
const lowerMd = blockSlice(source, lower);
|
|
581
|
+
/** Preserves a paragraph/heading boundary when the lower slice has no trailing `\n` but the upper ends with one and the stored gap is a single `\n` (blank line row). */
|
|
582
|
+
const swapped = swapMarkdownAdjacentSlices(lowerMd, gap, upperMd);
|
|
583
|
+
const newSource = replaceSourceRange(source, spanStart, spanEnd, swapped);
|
|
584
|
+
const anchor = spanStart;
|
|
585
|
+
return {
|
|
586
|
+
ok: true,
|
|
587
|
+
source: newSource,
|
|
588
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
export function moveBlockUp(source, doc, blockIndex) {
|
|
592
|
+
if (blockIndex <= 0)
|
|
593
|
+
return { ok: false, error: 'Cannot move first block up.' };
|
|
594
|
+
return swapAdjacentBlocks(source, doc, blockIndex - 1);
|
|
595
|
+
}
|
|
596
|
+
export function moveBlockDown(source, doc, blockIndex) {
|
|
597
|
+
if (blockIndex >= doc.blocks.length - 1)
|
|
598
|
+
return { ok: false, error: 'Cannot move last block down.' };
|
|
599
|
+
return swapAdjacentBlocks(source, doc, blockIndex);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Reorders blocks by moving one block to a new index via repeated {@link swapAdjacentBlocks},
|
|
603
|
+
* reparsing between swaps so ranges stay valid. Preserves irregular inter-block gaps that would
|
|
604
|
+
* be lost by joining slices with a single `\n\n`.
|
|
605
|
+
*/
|
|
606
|
+
export function moveBlockToIndex(source, doc, fromIndex, toIndex) {
|
|
607
|
+
const n = doc.blocks.length;
|
|
608
|
+
if (n === 0)
|
|
609
|
+
return { ok: false, error: 'Document has no blocks.' };
|
|
610
|
+
if (fromIndex < 0 || fromIndex >= n || toIndex < 0 || toIndex >= n) {
|
|
611
|
+
return { ok: false, error: 'Invalid block index.' };
|
|
612
|
+
}
|
|
613
|
+
if (fromIndex === toIndex) {
|
|
614
|
+
const b = doc.blocks[fromIndex];
|
|
615
|
+
const err = assertEditableBlock(b, 'move');
|
|
616
|
+
if (err)
|
|
617
|
+
return err;
|
|
618
|
+
const anchor = b.sourceRange.start;
|
|
619
|
+
return {
|
|
620
|
+
ok: true,
|
|
621
|
+
source,
|
|
622
|
+
selection: clampSelection({ anchor, focus: anchor }, source.length)
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
for (const b of doc.blocks) {
|
|
626
|
+
const err = assertEditableBlock(b, 'reorder');
|
|
627
|
+
if (err)
|
|
628
|
+
return err;
|
|
629
|
+
}
|
|
630
|
+
let s = source;
|
|
631
|
+
const fileName = doc.fileName;
|
|
632
|
+
let idx = fromIndex;
|
|
633
|
+
while (idx < toIndex) {
|
|
634
|
+
const fresh = applyInlineSecondPass(parseMarkdownDocument(s, fileName));
|
|
635
|
+
const r = swapAdjacentBlocks(s, fresh, idx);
|
|
636
|
+
if (!r.ok)
|
|
637
|
+
return r;
|
|
638
|
+
s = r.source;
|
|
639
|
+
idx++;
|
|
640
|
+
}
|
|
641
|
+
while (idx > toIndex) {
|
|
642
|
+
const fresh = applyInlineSecondPass(parseMarkdownDocument(s, fileName));
|
|
643
|
+
const r = swapAdjacentBlocks(s, fresh, idx - 1);
|
|
644
|
+
if (!r.ok)
|
|
645
|
+
return r;
|
|
646
|
+
s = r.source;
|
|
647
|
+
idx--;
|
|
648
|
+
}
|
|
649
|
+
const finalDoc = applyInlineSecondPass(parseMarkdownDocument(s, fileName));
|
|
650
|
+
const moved = finalDoc.blocks[toIndex];
|
|
651
|
+
if (!moved || moved.sourceRange.synthetic || moved.sourceRange.start < 0) {
|
|
652
|
+
return { ok: false, error: 'Reorder produced invalid block.' };
|
|
653
|
+
}
|
|
654
|
+
const anchor = moved.sourceRange.start;
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
source: s,
|
|
658
|
+
selection: clampSelection({ anchor, focus: anchor }, s.length)
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
export function convertParagraphToHeading(source, doc, blockIndex, level) {
|
|
662
|
+
const block = doc.blocks[blockIndex];
|
|
663
|
+
const err = assertEditableBlock(block, 'convert');
|
|
664
|
+
if (err)
|
|
665
|
+
return err;
|
|
666
|
+
if (block.type !== 'paragraph')
|
|
667
|
+
return { ok: false, error: 'Block is not a paragraph.' };
|
|
668
|
+
const plain = paragraphBlockToPlainText(block.data).trimEnd();
|
|
669
|
+
const replacement = headingMarkdown(level, plain);
|
|
670
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
671
|
+
const prefixLen = replacement.length - plain.length;
|
|
672
|
+
const anchor = block.sourceRange.start + prefixLen + plain.length;
|
|
673
|
+
return {
|
|
674
|
+
ok: true,
|
|
675
|
+
source: newSource,
|
|
676
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
export function convertHeadingLevel(source, doc, blockIndex, level) {
|
|
680
|
+
const block = doc.blocks[blockIndex];
|
|
681
|
+
const err = assertEditableBlock(block, 'convert');
|
|
682
|
+
if (err)
|
|
683
|
+
return err;
|
|
684
|
+
if (block.type !== 'heading')
|
|
685
|
+
return { ok: false, error: 'Block is not a heading.' };
|
|
686
|
+
const hb = block.data;
|
|
687
|
+
const replacement = headingMarkdown(level, hb.text);
|
|
688
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
689
|
+
const prefixLen = replacement.length - hb.text.length;
|
|
690
|
+
const anchor = block.sourceRange.start + prefixLen + hb.text.length;
|
|
691
|
+
return {
|
|
692
|
+
ok: true,
|
|
693
|
+
source: newSource,
|
|
694
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
export function convertHeadingToParagraph(source, doc, blockIndex) {
|
|
698
|
+
const block = doc.blocks[blockIndex];
|
|
699
|
+
const err = assertEditableBlock(block, 'convert');
|
|
700
|
+
if (err)
|
|
701
|
+
return err;
|
|
702
|
+
if (block.type !== 'heading')
|
|
703
|
+
return { ok: false, error: 'Block is not a heading.' };
|
|
704
|
+
const hb = block.data;
|
|
705
|
+
const replacement = serializeParagraphData(paragraphBlockFromPlainText(hb.text, hb.lineIndex));
|
|
706
|
+
const newSource = replaceBlockSourceRange(source, block.sourceRange.start, block.sourceRange.end, replacement);
|
|
707
|
+
const tmp = {
|
|
708
|
+
type: 'paragraph',
|
|
709
|
+
id: 'tmp',
|
|
710
|
+
sourceRange: { start: 0, end: replacement.length, startLine: 0, endLine: 0 },
|
|
711
|
+
data: paragraphBlockFromPlainText(hb.text, hb.lineIndex)
|
|
712
|
+
};
|
|
713
|
+
const anchor = block.sourceRange.start + paragraphPlainOffsetToSourceOffset(tmp, hb.text.length);
|
|
714
|
+
return {
|
|
715
|
+
ok: true,
|
|
716
|
+
source: newSource,
|
|
717
|
+
selection: clampSelection({ anchor, focus: anchor }, newSource.length)
|
|
718
|
+
};
|
|
719
|
+
}
|