@dxos/react-ui-editor 0.8.4-main.fd6878d → 0.8.4-main.fffef41
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/dist/lib/browser/{chunk-22UMM3QJ.mjs → chunk-HL3YF6WC.mjs} +2 -2
- package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +7471 -5897
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
- package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +7471 -5897
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +37 -15
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +20 -0
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
- package/dist/types/src/components/EditorContent/EditorContent.d.ts +29 -0
- package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +1 -0
- package/dist/types/src/components/EditorContent/EditorContent.stories.d.ts +26 -0
- package/dist/types/src/components/EditorContent/EditorContent.stories.d.ts.map +1 -0
- package/dist/types/src/components/EditorContent/controller.d.ts +10 -0
- package/dist/types/src/components/EditorContent/controller.d.ts.map +1 -0
- package/dist/types/src/components/EditorContent/index.d.ts +3 -0
- package/dist/types/src/components/EditorContent/index.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +36 -0
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/index.d.ts +7 -0
- package/dist/types/src/components/EditorMenuProvider/index.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts +4 -0
- package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts +28 -0
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts +47 -0
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts +34 -0
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/util.d.ts +8 -0
- package/dist/types/src/components/EditorMenuProvider/util.d.ts.map +1 -0
- package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts +16 -0
- package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -0
- package/dist/types/src/components/EditorPreviewProvider/index.d.ts +2 -0
- package/dist/types/src/components/EditorPreviewProvider/index.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +26 -2
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/actions.d.ts +39 -0
- package/dist/types/src/components/EditorToolbar/actions.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts +2 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +11 -0
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +4 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts +17 -0
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +10 -19
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +3 -3
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/autoscroll.d.ts +20 -0
- package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts +1 -1
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
- package/dist/types/src/extensions/blocks.d.ts +2 -0
- package/dist/types/src/extensions/blocks.d.ts.map +1 -0
- package/dist/types/src/extensions/bookmarks.d.ts +12 -0
- package/dist/types/src/extensions/bookmarks.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +11 -11
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/focus.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +10 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/json.d.ts +1 -1
- package/dist/types/src/extensions/json.d.ts.map +1 -1
- package/dist/types/src/extensions/listener.d.ts +8 -6
- package/dist/types/src/extensions/listener.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -3
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/modal.d.ts +7 -0
- package/dist/types/src/extensions/modal.d.ts.map +1 -0
- package/dist/types/src/extensions/modes.d.ts +1 -1
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
- package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/tree.d.ts +1 -1
- package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
- package/dist/types/src/extensions/preview/preview.d.ts +8 -8
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/extensions/replacer.d.ts +21 -0
- package/dist/types/src/extensions/replacer.d.ts.map +1 -0
- package/dist/types/src/extensions/replacer.test.d.ts +2 -0
- package/dist/types/src/extensions/replacer.test.d.ts.map +1 -0
- package/dist/types/src/extensions/scrolling.d.ts +78 -0
- package/dist/types/src/extensions/scrolling.d.ts.map +1 -0
- package/dist/types/src/extensions/state.d.ts +2 -0
- package/dist/types/src/extensions/state.d.ts.map +1 -0
- package/dist/types/src/extensions/submit.d.ts +10 -0
- package/dist/types/src/extensions/submit.d.ts.map +1 -0
- package/dist/types/src/extensions/tab.d.ts +4 -0
- package/dist/types/src/extensions/tab.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/index.d.ts +4 -0
- package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
- package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts +97 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +5 -9
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/CommandDialog.stories.d.ts +14 -0
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Comments.stories.d.ts +21 -10
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -3
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +22 -13
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +32 -43
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +15 -21
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Popover.stories.d.ts +20 -0
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +22 -7
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts +16 -0
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditor.stories.d.ts +37 -52
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +8 -9
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/stories/components/util.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/types/types.d.ts +2 -2
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util/debug.d.ts +5 -1
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/src/util/decorations.d.ts +4 -0
- package/dist/types/src/util/decorations.d.ts.map +1 -0
- package/dist/types/src/util/dom.d.ts +2 -12
- package/dist/types/src/util/dom.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +70 -68
- package/src/components/Editor/Editor.stories.tsx +89 -0
- package/src/components/Editor/Editor.tsx +160 -25
- package/src/components/EditorContent/EditorContent.stories.tsx +70 -0
- package/src/components/EditorContent/EditorContent.tsx +70 -0
- package/src/components/EditorContent/controller.ts +50 -0
- package/src/components/EditorContent/index.ts +6 -0
- package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +233 -0
- package/src/components/EditorMenuProvider/index.ts +11 -0
- package/src/components/EditorMenuProvider/menu-presets.ts +123 -0
- package/src/components/EditorMenuProvider/menu.ts +71 -0
- package/src/components/EditorMenuProvider/popover.ts +287 -0
- package/src/components/EditorMenuProvider/useEditorMenu.ts +175 -0
- package/src/components/EditorMenuProvider/util.ts +31 -0
- package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +82 -0
- package/src/components/EditorPreviewProvider/index.ts +5 -0
- package/src/components/EditorToolbar/EditorToolbar.tsx +116 -96
- package/src/components/EditorToolbar/actions.ts +86 -0
- package/src/components/EditorToolbar/blocks.ts +20 -23
- package/src/components/EditorToolbar/formatting.ts +21 -24
- package/src/components/EditorToolbar/headings.ts +16 -9
- package/src/components/EditorToolbar/image.ts +9 -5
- package/src/components/EditorToolbar/index.ts +3 -7
- package/src/components/EditorToolbar/search.ts +9 -5
- package/src/components/EditorToolbar/useEditorToolbar.ts +20 -0
- package/src/components/EditorToolbar/view-mode.ts +11 -6
- package/src/components/index.ts +8 -2
- package/src/defaults.ts +5 -2
- package/src/extensions/autocomplete/autocomplete.ts +151 -0
- package/src/extensions/autocomplete/index.ts +8 -0
- package/src/extensions/autocomplete/match.ts +46 -0
- package/src/extensions/{command → autocomplete}/placeholder.ts +21 -17
- package/src/extensions/{command → autocomplete}/typeahead.ts +6 -48
- package/src/extensions/automerge/automerge.stories.tsx +28 -21
- package/src/extensions/automerge/automerge.ts +28 -9
- package/src/extensions/automerge/cursor.ts +1 -1
- package/src/extensions/automerge/sync.ts +8 -4
- package/src/extensions/automerge/update-automerge.ts +1 -1
- package/src/extensions/autoscroll.ts +163 -0
- package/src/extensions/awareness/awareness-provider.ts +2 -2
- package/src/extensions/blocks.ts +131 -0
- package/src/extensions/bookmarks.ts +75 -0
- package/src/extensions/comments.ts +13 -8
- package/src/extensions/factories.ts +50 -32
- package/src/extensions/focus.ts +5 -4
- package/src/extensions/folding.tsx +3 -6
- package/src/extensions/hashtag.tsx +2 -2
- package/src/extensions/index.ts +10 -1
- package/src/extensions/json.ts +1 -1
- package/src/extensions/listener.ts +14 -20
- package/src/extensions/markdown/bundle.ts +39 -5
- package/src/extensions/markdown/decorate.ts +26 -17
- package/src/extensions/markdown/formatting.ts +8 -10
- package/src/extensions/markdown/highlight.ts +1 -1
- package/src/extensions/markdown/image.ts +5 -6
- package/src/extensions/markdown/link.ts +3 -0
- package/src/extensions/markdown/table.ts +13 -7
- package/src/extensions/modal.ts +24 -0
- package/src/extensions/modes.ts +2 -2
- package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +16 -21
- package/src/extensions/outliner/outliner.test.ts +1 -1
- package/src/extensions/outliner/outliner.ts +5 -5
- package/src/extensions/outliner/tree.test.ts +1 -1
- package/src/extensions/outliner/tree.ts +1 -1
- package/src/extensions/preview/index.ts +1 -1
- package/src/extensions/preview/preview.ts +69 -69
- package/src/extensions/replacer.test.ts +75 -0
- package/src/extensions/replacer.ts +93 -0
- package/src/extensions/scrolling.ts +189 -0
- package/src/extensions/selection.ts +3 -3
- package/src/extensions/state.ts +7 -0
- package/src/extensions/submit.ts +62 -0
- package/src/extensions/tab.ts +29 -0
- package/src/extensions/tags/extended-markdown.test.ts +262 -0
- package/src/extensions/tags/extended-markdown.ts +78 -0
- package/src/extensions/tags/index.ts +7 -0
- package/src/extensions/tags/streamer.ts +243 -0
- package/src/extensions/tags/xml-tags.ts +500 -0
- package/src/extensions/tags/xml-util.ts +94 -0
- package/src/extensions/typewriter.ts +1 -1
- package/src/hooks/useTextEditor.ts +31 -43
- package/src/stories/CommandDialog.stories.tsx +83 -0
- package/src/stories/Comments.stories.tsx +15 -13
- package/src/stories/EditorToolbar.stories.tsx +18 -17
- package/src/stories/Experimental.stories.tsx +15 -12
- package/src/stories/Markdown.stories.tsx +24 -21
- package/src/stories/Outliner.stories.tsx +50 -39
- package/src/stories/Popover.stories.tsx +162 -0
- package/src/stories/Preview.stories.tsx +52 -46
- package/src/stories/Tags.stories.tsx +95 -0
- package/src/stories/TextEditor.stories.tsx +30 -50
- package/src/stories/components/EditorStory.tsx +32 -20
- package/src/stories/components/util.tsx +40 -8
- package/src/styles/markdown.ts +1 -1
- package/src/styles/theme.ts +16 -13
- package/src/translations.ts +1 -1
- package/src/types/types.ts +1 -1
- package/src/util/debug.ts +7 -2
- package/src/util/decorations.ts +21 -0
- package/src/util/dom.ts +5 -27
- package/src/util/index.ts +1 -0
- package/src/util/react.tsx +1 -1
- package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -6
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
- package/dist/lib/node-esm/testing/index.mjs +0 -8
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/components/EditorToolbar/lists.d.ts +0 -19
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/util.d.ts +0 -51
- package/dist/types/src/components/EditorToolbar/util.d.ts.map +0 -1
- package/dist/types/src/components/Popover/CommandMenu.d.ts +0 -34
- package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
- package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
- package/dist/types/src/components/Popover/index.d.ts +0 -4
- package/dist/types/src/components/Popover/index.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete.d.ts +0 -13
- package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/extensions/command/action.d.ts +0 -17
- package/dist/types/src/extensions/command/action.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
- package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command.d.ts +0 -6
- package/dist/types/src/extensions/command/command.d.ts.map +0 -1
- package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
- package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/hint.d.ts +0 -24
- package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
- package/dist/types/src/extensions/command/index.d.ts +0 -7
- package/dist/types/src/extensions/command/index.d.ts.map +0 -1
- package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
- package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
- package/dist/types/src/extensions/command/state.d.ts +0 -16
- package/dist/types/src/extensions/command/state.d.ts.map +0 -1
- package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
- package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
- package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
- package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
- package/dist/types/src/stories/Command.stories.d.ts +0 -7
- package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
- package/dist/types/src/stories/CommandMenu.stories.d.ts +0 -13
- package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -3
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/src/components/EditorToolbar/lists.ts +0 -60
- package/src/components/EditorToolbar/util.ts +0 -65
- package/src/components/Popover/CommandMenu.tsx +0 -279
- package/src/components/Popover/RefDropdownMenu.tsx +0 -85
- package/src/components/Popover/RefPopover.tsx +0 -99
- package/src/components/Popover/index.ts +0 -7
- package/src/extensions/autocomplete.ts +0 -69
- package/src/extensions/command/action.ts +0 -56
- package/src/extensions/command/command-menu.ts +0 -211
- package/src/extensions/command/command.ts +0 -34
- package/src/extensions/command/hint.ts +0 -103
- package/src/extensions/command/index.ts +0 -10
- package/src/extensions/command/state.ts +0 -90
- package/src/extensions/command/useCommandMenu.ts +0 -119
- package/src/stories/Command.stories.tsx +0 -98
- package/src/stories/CommandMenu.stories.tsx +0 -160
- package/src/testing/index.ts +0 -5
- package/src/testing/util.ts +0 -7
|
@@ -2,20 +2,16 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos/lit-ui/dx-ref-tag.pcss';
|
|
6
|
-
|
|
7
5
|
import { syntaxTree } from '@codemirror/language';
|
|
8
|
-
import {
|
|
9
|
-
type EditorState,
|
|
10
|
-
type Extension,
|
|
11
|
-
type RangeSet,
|
|
12
|
-
RangeSetBuilder,
|
|
13
|
-
StateField,
|
|
14
|
-
type Transaction,
|
|
15
|
-
} from '@codemirror/state';
|
|
6
|
+
import { type EditorState, type Extension, RangeSetBuilder, StateField } from '@codemirror/state';
|
|
16
7
|
import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
|
|
17
8
|
import { type SyntaxNode } from '@lezer/common';
|
|
18
9
|
|
|
10
|
+
export type PreviewBlock = {
|
|
11
|
+
link: PreviewLinkRef;
|
|
12
|
+
el: HTMLElement;
|
|
13
|
+
};
|
|
14
|
+
|
|
19
15
|
export type PreviewLinkRef = {
|
|
20
16
|
suggest?: boolean;
|
|
21
17
|
block?: boolean;
|
|
@@ -29,13 +25,9 @@ export type PreviewLinkTarget = {
|
|
|
29
25
|
object?: any;
|
|
30
26
|
};
|
|
31
27
|
|
|
32
|
-
// TODO(wittjosiah): Remove.
|
|
33
|
-
// TODO(burdon): Handle error.
|
|
34
|
-
export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
|
|
35
|
-
|
|
36
28
|
export type PreviewOptions = {
|
|
37
|
-
addBlockContainer?: (
|
|
38
|
-
removeBlockContainer?: (
|
|
29
|
+
addBlockContainer?: (block: PreviewBlock) => void;
|
|
30
|
+
removeBlockContainer?: (block: PreviewBlock) => void;
|
|
39
31
|
};
|
|
40
32
|
|
|
41
33
|
/**
|
|
@@ -44,10 +36,16 @@ export type PreviewOptions = {
|
|
|
44
36
|
export const preview = (options: PreviewOptions = {}): Extension => {
|
|
45
37
|
return [
|
|
46
38
|
// NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
|
|
47
|
-
// "Block decorations may not be specified via plugins"
|
|
39
|
+
// "Block decorations may not be specified via plugins".
|
|
48
40
|
StateField.define<DecorationSet>({
|
|
49
41
|
create: (state) => buildDecorations(state, options),
|
|
50
|
-
update: (
|
|
42
|
+
update: (decorations, tr) => {
|
|
43
|
+
if (tr.docChanged) {
|
|
44
|
+
return buildDecorations(tr.state, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return decorations.map(tr.changes);
|
|
48
|
+
},
|
|
51
49
|
provide: (field) => [
|
|
52
50
|
EditorView.decorations.from(field),
|
|
53
51
|
EditorView.atomicRanges.of((view) => view.state.field(field)),
|
|
@@ -56,42 +54,19 @@ export const preview = (options: PreviewOptions = {}): Extension => {
|
|
|
56
54
|
];
|
|
57
55
|
};
|
|
58
56
|
|
|
59
|
-
/**
|
|
60
|
-
* Link references.
|
|
61
|
-
*
|
|
62
|
-
* [Label][dxn:echo:123] Inline reference
|
|
63
|
-
* ![Label][dxn:echo:123] Block reference
|
|
64
|
-
* ![Label][?dxn:echo:123] Suggestion
|
|
65
|
-
*/
|
|
66
|
-
export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
|
|
67
|
-
const mark = node.getChild('LinkMark');
|
|
68
|
-
const label = node.getChild('LinkLabel');
|
|
69
|
-
if (mark && label) {
|
|
70
|
-
const ref = state.sliceDoc(label.from + 1, label.to - 1);
|
|
71
|
-
return {
|
|
72
|
-
suggest: ref.startsWith('?'),
|
|
73
|
-
block: state.sliceDoc(mark.from, mark.from + 1) === '!',
|
|
74
|
-
label: state.sliceDoc(mark.to, label.from - 1),
|
|
75
|
-
ref: ref.startsWith('?') ? ref.slice(1) : ref,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
57
|
/**
|
|
81
58
|
* Echo references are represented as markdown reference links.
|
|
82
59
|
* https://www.markdownguide.org/basic-syntax/#reference-style-links
|
|
83
|
-
* [Label|block][dxn:echo:123]
|
|
84
|
-
* [Label|inline][dxn:echo:123]
|
|
85
60
|
*/
|
|
86
|
-
const buildDecorations = (state: EditorState, options: PreviewOptions) => {
|
|
61
|
+
const buildDecorations = (state: EditorState, options: PreviewOptions): DecorationSet => {
|
|
87
62
|
const builder = new RangeSetBuilder<Decoration>();
|
|
88
63
|
|
|
89
64
|
syntaxTree(state).iterate({
|
|
90
65
|
enter: (node) => {
|
|
91
66
|
switch (node.name) {
|
|
92
67
|
//
|
|
93
|
-
//
|
|
94
|
-
// [Label]
|
|
68
|
+
// Inline widget.
|
|
69
|
+
// [Label](dxn:echo:123)
|
|
95
70
|
//
|
|
96
71
|
case 'Link': {
|
|
97
72
|
const link = getLinkRef(state, node.node);
|
|
@@ -101,29 +76,32 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
|
|
|
101
76
|
node.to,
|
|
102
77
|
Decoration.replace({
|
|
103
78
|
widget: new PreviewInlineWidget(options, link),
|
|
79
|
+
side: 1,
|
|
104
80
|
}),
|
|
105
81
|
);
|
|
106
82
|
}
|
|
107
|
-
|
|
83
|
+
return false;
|
|
108
84
|
}
|
|
85
|
+
|
|
109
86
|
//
|
|
110
|
-
// Block widget.
|
|
111
|
-
// ![Label]
|
|
87
|
+
// Block widget (transclusion).
|
|
88
|
+
// 
|
|
112
89
|
//
|
|
113
90
|
case 'Image': {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
91
|
+
if (options.addBlockContainer && options.removeBlockContainer) {
|
|
92
|
+
const link = getLinkRef(state, node.node);
|
|
93
|
+
if (link) {
|
|
94
|
+
builder.add(
|
|
95
|
+
node.from,
|
|
96
|
+
node.to,
|
|
97
|
+
Decoration.replace({
|
|
98
|
+
block: true,
|
|
99
|
+
widget: new PreviewBlockWidget(options, link),
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
125
103
|
}
|
|
126
|
-
|
|
104
|
+
return false;
|
|
127
105
|
}
|
|
128
106
|
}
|
|
129
107
|
},
|
|
@@ -132,9 +110,30 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
|
|
|
132
110
|
return builder.finish();
|
|
133
111
|
};
|
|
134
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Link references.
|
|
115
|
+
* [Label](dxn:echo:123) Inline reference
|
|
116
|
+
*  Block reference
|
|
117
|
+
*/
|
|
118
|
+
export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
|
|
119
|
+
const mark = node.getChildren('LinkMark');
|
|
120
|
+
const urlNode = node.getChild('URL');
|
|
121
|
+
if (mark && urlNode) {
|
|
122
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
123
|
+
if (url.startsWith('dxn:')) {
|
|
124
|
+
const label = state.sliceDoc(mark[0].to, mark[1].from);
|
|
125
|
+
return {
|
|
126
|
+
block: state.sliceDoc(mark[0].from, mark[0].from + 1) === '!',
|
|
127
|
+
label,
|
|
128
|
+
ref: url,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
135
134
|
/**
|
|
136
135
|
* Inline widget.
|
|
137
|
-
*
|
|
136
|
+
* [Label](dxn:echo:123)
|
|
138
137
|
*/
|
|
139
138
|
class PreviewInlineWidget extends WidgetType {
|
|
140
139
|
constructor(
|
|
@@ -148,12 +147,13 @@ class PreviewInlineWidget extends WidgetType {
|
|
|
148
147
|
// return false;
|
|
149
148
|
// }
|
|
150
149
|
|
|
151
|
-
override eq(other: this)
|
|
150
|
+
override eq(other: this) {
|
|
152
151
|
return this._link.ref === other._link.ref && this._link.label === other._link.label;
|
|
153
152
|
}
|
|
154
153
|
|
|
155
|
-
override toDOM(
|
|
156
|
-
const root = document.createElement('dx-
|
|
154
|
+
override toDOM(_view: EditorView) {
|
|
155
|
+
const root = document.createElement('dx-anchor');
|
|
156
|
+
root.classList.add('dx-tag--anchor');
|
|
157
157
|
root.textContent = this._link.label;
|
|
158
158
|
root.setAttribute('refId', this._link.ref);
|
|
159
159
|
return root;
|
|
@@ -161,7 +161,7 @@ class PreviewInlineWidget extends WidgetType {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
|
-
* Block widget.
|
|
164
|
+
* Block widget (e.g., for surfaces).
|
|
165
165
|
* ![Label][dxn:echo:123]
|
|
166
166
|
*/
|
|
167
167
|
class PreviewBlockWidget extends WidgetType {
|
|
@@ -176,18 +176,18 @@ class PreviewBlockWidget extends WidgetType {
|
|
|
176
176
|
// return true;
|
|
177
177
|
// }
|
|
178
178
|
|
|
179
|
-
override eq(other: this)
|
|
179
|
+
override eq(other: this) {
|
|
180
180
|
return this._link.ref === other._link.ref;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
override toDOM(
|
|
183
|
+
override toDOM(_view: EditorView) {
|
|
184
184
|
const root = document.createElement('div');
|
|
185
185
|
root.classList.add('cm-preview-block', 'density-coarse');
|
|
186
|
-
this._options.addBlockContainer?.(this._link, root);
|
|
186
|
+
this._options.addBlockContainer?.({ link: this._link, el: root });
|
|
187
187
|
return root;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
override destroy() {
|
|
191
|
-
this._options.removeBlockContainer?.(this._link);
|
|
190
|
+
override destroy(root: HTMLDivElement) {
|
|
191
|
+
this._options.removeBlockContainer?.({ link: this._link, el: root });
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { EditorState } from '@codemirror/state';
|
|
6
|
+
import { EditorView } from '@codemirror/view';
|
|
7
|
+
import { describe, expect, test } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import { replacer } from './replacer';
|
|
10
|
+
|
|
11
|
+
describe('replacer extension', () => {
|
|
12
|
+
test('creates extension with custom replacements and simulates typing', () => {
|
|
13
|
+
const state = EditorState.create({
|
|
14
|
+
extensions: [
|
|
15
|
+
replacer({
|
|
16
|
+
replacements: [
|
|
17
|
+
{ input: ':)', output: '😊' },
|
|
18
|
+
{ input: ':(', output: '😢' },
|
|
19
|
+
],
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
doc: '',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create a minimal mock EditorView to test input handler
|
|
26
|
+
let currentState = state;
|
|
27
|
+
const mockView = {
|
|
28
|
+
get state() {
|
|
29
|
+
return currentState;
|
|
30
|
+
},
|
|
31
|
+
dispatch: (transaction: any) => {
|
|
32
|
+
currentState = transaction.state || currentState.update(transaction).state;
|
|
33
|
+
},
|
|
34
|
+
} as any;
|
|
35
|
+
|
|
36
|
+
// Get the input handler from the extension
|
|
37
|
+
const extensions = currentState.facet(EditorView.inputHandler);
|
|
38
|
+
const inputHandler = extensions[0];
|
|
39
|
+
|
|
40
|
+
// Test typing ':' first - should not trigger replacement.
|
|
41
|
+
let handled = inputHandler(mockView, 0, 0, ':', () =>
|
|
42
|
+
mockView.state.update({ changes: { from: 0, to: 0, insert: ':' } }),
|
|
43
|
+
);
|
|
44
|
+
expect(handled).toBe(false); // Should not handle single ':'
|
|
45
|
+
|
|
46
|
+
// Manually insert ':' to simulate first character.
|
|
47
|
+
mockView.dispatch({
|
|
48
|
+
changes: { from: 0, to: 0, insert: ':' },
|
|
49
|
+
selection: { anchor: 1 },
|
|
50
|
+
});
|
|
51
|
+
expect(mockView.state.doc.toString()).toBe(':');
|
|
52
|
+
|
|
53
|
+
// Test typing ')' which should trigger replacement.
|
|
54
|
+
// The input handler is called with the position where the character will be inserted.
|
|
55
|
+
// and it should handle the replacement before the character is actually inserted.
|
|
56
|
+
handled = inputHandler(mockView, 1, 1, ')', () =>
|
|
57
|
+
mockView.state.update({ changes: { from: 1, to: 1, insert: ')' } }),
|
|
58
|
+
);
|
|
59
|
+
expect(handled).toBe(true); // Should handle and replace ':)'
|
|
60
|
+
expect(mockView.state.doc.toString()).toBe('😊');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('creates extension with default replacements', () => {
|
|
64
|
+
const state = EditorState.create({
|
|
65
|
+
extensions: [replacer()],
|
|
66
|
+
doc: 'test',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(state.doc.toString()).toBe('test');
|
|
70
|
+
|
|
71
|
+
// Verify the extension is installed.
|
|
72
|
+
const inputHandlers = state.facet(EditorView.inputHandler);
|
|
73
|
+
expect(inputHandlers.length).toBeGreaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Extension } from '@codemirror/state';
|
|
6
|
+
import { EditorView } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
type Replacement = {
|
|
9
|
+
input: string;
|
|
10
|
+
output: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default character replacements for common typography.
|
|
15
|
+
*/
|
|
16
|
+
export const defaultReplacements: Replacement[] = [
|
|
17
|
+
{ input: '--', output: '—' },
|
|
18
|
+
{ input: '...', output: '…' },
|
|
19
|
+
{ input: '->', output: '→' },
|
|
20
|
+
{ input: '<-', output: '←' },
|
|
21
|
+
{ input: '=>', output: '⇒' },
|
|
22
|
+
{ input: '<=>', output: '⇔' },
|
|
23
|
+
{ input: '+-', output: '±' },
|
|
24
|
+
{ input: '!=', output: '≠' },
|
|
25
|
+
{ input: '<=', output: '≤' },
|
|
26
|
+
{ input: '>=', output: '≥' },
|
|
27
|
+
{ input: '(c)', output: '©' },
|
|
28
|
+
{ input: 'EUR', output: '€' },
|
|
29
|
+
{ input: 'GBP', output: '£' },
|
|
30
|
+
{ input: 'BTC', output: '₿' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Options for the replacer extension.
|
|
35
|
+
*/
|
|
36
|
+
export interface ReplacerOptions {
|
|
37
|
+
replacements?: Replacement[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a CodeMirror extension that automatically replaces typed character sequences.
|
|
42
|
+
*/
|
|
43
|
+
export const replacer = ({ replacements = defaultReplacements }: ReplacerOptions = {}): Extension => {
|
|
44
|
+
// Sort replacements by input length (longest first) to handle overlapping patterns correctly.
|
|
45
|
+
const sortedReplacements = [...replacements].sort((a, b) => b.input.length - a.input.length);
|
|
46
|
+
|
|
47
|
+
return EditorView.inputHandler.of((view, from, to, insert) => {
|
|
48
|
+
// Only process single character insertions for performance.
|
|
49
|
+
if (insert.length !== 1) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const state = view.state;
|
|
54
|
+
const doc = state.doc;
|
|
55
|
+
|
|
56
|
+
// Get the text before the insertion point to check for patterns.
|
|
57
|
+
const lineStart = doc.lineAt(from).from;
|
|
58
|
+
const textBefore = doc.sliceString(lineStart, from);
|
|
59
|
+
const textWithInsert = textBefore + insert;
|
|
60
|
+
|
|
61
|
+
// Check each replacement pattern.
|
|
62
|
+
for (const replacement of sortedReplacements) {
|
|
63
|
+
if (textWithInsert.endsWith(replacement.input)) {
|
|
64
|
+
const range = {
|
|
65
|
+
from: from - replacement.input.length + 1,
|
|
66
|
+
to: from,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Ensure we don't go before the line start.
|
|
70
|
+
if (range.from < lineStart) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Create the replacement transaction.
|
|
75
|
+
view.dispatch(
|
|
76
|
+
state.update({
|
|
77
|
+
changes: {
|
|
78
|
+
...range,
|
|
79
|
+
insert: replacement.output,
|
|
80
|
+
},
|
|
81
|
+
selection: {
|
|
82
|
+
anchor: range.from + replacement.output.length,
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
});
|
|
93
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { StateEffect } from '@codemirror/state';
|
|
6
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for smooth scrolling behavior.
|
|
10
|
+
*/
|
|
11
|
+
export type SmoothScrollOptions = {
|
|
12
|
+
/**
|
|
13
|
+
* Additional offset from the target line in pixels.
|
|
14
|
+
* Positive values scroll past the line, negative values stop before it.
|
|
15
|
+
* @default 0
|
|
16
|
+
*/
|
|
17
|
+
offset?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Position of the target line in the viewport.
|
|
20
|
+
* - 'start': Line appears at the start (top) of the screen
|
|
21
|
+
* - 'end': Line appears at the end (bottom) of the screen
|
|
22
|
+
* @default 'start'
|
|
23
|
+
*/
|
|
24
|
+
position?: 'start' | 'end';
|
|
25
|
+
/**
|
|
26
|
+
* Whether to use smooth scrolling.
|
|
27
|
+
* @default 'smooth'
|
|
28
|
+
*/
|
|
29
|
+
behavior?: ScrollBehavior;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parameters for the scroll to line effect.
|
|
34
|
+
*/
|
|
35
|
+
export type ScrollToLineParams = {
|
|
36
|
+
/**
|
|
37
|
+
* The line number to scroll to (1-based).
|
|
38
|
+
*/
|
|
39
|
+
line: number;
|
|
40
|
+
/**
|
|
41
|
+
* Optional configuration to override default scroll behavior.
|
|
42
|
+
*/
|
|
43
|
+
options?: SmoothScrollOptions;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* StateEffect for triggering smooth scroll to a specific line.
|
|
48
|
+
*/
|
|
49
|
+
export const scrollToLineEffect = StateEffect.define<ScrollToLineParams>();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Extension that provides smooth scrolling to specific lines in the editor.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // Add to editor extensions.
|
|
57
|
+
* const extensions = [
|
|
58
|
+
* smoothScroll()
|
|
59
|
+
* ];
|
|
60
|
+
*
|
|
61
|
+
* // Trigger scroll to line 42.
|
|
62
|
+
* view.dispatch({
|
|
63
|
+
* effects: scrollToLineEffect.of({ line: 42 })
|
|
64
|
+
* });
|
|
65
|
+
*
|
|
66
|
+
* // Scroll with custom options.
|
|
67
|
+
* view.dispatch({
|
|
68
|
+
* effects: scrollToLineEffect.of({ line: 100, options: { offset: -50 } })
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Scroll so line appears at end (bottom) of screen.
|
|
72
|
+
* view.dispatch({
|
|
73
|
+
* effects: scrollToLineEffect.of({ line: 50, options: { position: 'end' } })
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export const smoothScroll = ({ offset = 0, position = 'start' }: Partial<SmoothScrollOptions> = {}) => {
|
|
78
|
+
// ViewPlugin to manage scroll animations.
|
|
79
|
+
const scrollPlugin = ViewPlugin.fromClass(
|
|
80
|
+
class SmoothScrollPlugin {
|
|
81
|
+
constructor(private readonly view: EditorView) {}
|
|
82
|
+
|
|
83
|
+
// No-op.
|
|
84
|
+
destroy() {}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Perform smooth scroll to the specified line.
|
|
88
|
+
*/
|
|
89
|
+
scrollToLine(lineNumber: number, options: SmoothScrollOptions) {
|
|
90
|
+
const { offset: animOffset = 0, position: animPosition, behavior } = options;
|
|
91
|
+
const doc = this.view.state.doc;
|
|
92
|
+
const scroller = this.view.scrollDOM;
|
|
93
|
+
|
|
94
|
+
// Convert 1-based line number to 0-based.
|
|
95
|
+
const targetLine = Math.max(0, lineNumber - 1);
|
|
96
|
+
if (behavior === 'instant') {
|
|
97
|
+
requestAnimationFrame(() => {
|
|
98
|
+
this.view.dispatch({
|
|
99
|
+
selection: { anchor: doc.line(targetLine + 1).from },
|
|
100
|
+
scrollIntoView: true,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Get the position of the target line.
|
|
107
|
+
if (targetLine >= doc.lines) {
|
|
108
|
+
// Line doesn't exist, scroll to end.
|
|
109
|
+
const targetScrollTop = scroller.scrollHeight - scroller.clientHeight + (animOffset || 0);
|
|
110
|
+
this.animateScroll(scroller, targetScrollTop);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const lineStart = doc.line(targetLine + 1).from;
|
|
115
|
+
const coords = this.view.coordsAtPos(lineStart);
|
|
116
|
+
if (!coords) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Calculate target scroll position based on position option.
|
|
121
|
+
const currentScrollTop = scroller.scrollTop;
|
|
122
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
123
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
124
|
+
|
|
125
|
+
let targetScrollTop: number;
|
|
126
|
+
if (animPosition === 'end') {
|
|
127
|
+
// Position line at end (bottom) of viewport.
|
|
128
|
+
// Calculate how far down we need to scroll so the line's bottom aligns with viewport bottom.
|
|
129
|
+
targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + animOffset;
|
|
130
|
+
} else {
|
|
131
|
+
// Default: position line at start (top) of viewport.
|
|
132
|
+
targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + animOffset;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Clamp to valid scroll range.
|
|
136
|
+
const clampedScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
|
|
137
|
+
this.animateScroll(scroller, clampedScrollTop);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Animate scroll using browser's built-in smooth scrolling.
|
|
142
|
+
*/
|
|
143
|
+
private animateScroll(element: HTMLElement, targetScrollTop: number) {
|
|
144
|
+
if (Math.abs(targetScrollTop - element.scrollTop) < 1) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Use browser's built-in smooth scrolling.
|
|
149
|
+
element.scrollTo({
|
|
150
|
+
top: targetScrollTop,
|
|
151
|
+
behavior: 'smooth',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return [
|
|
158
|
+
scrollPlugin,
|
|
159
|
+
|
|
160
|
+
// Update listener to handle scroll effects.
|
|
161
|
+
EditorView.updateListener.of((update) => {
|
|
162
|
+
update.transactions.forEach((transaction) => {
|
|
163
|
+
for (const effect of transaction.effects) {
|
|
164
|
+
if (effect.is(scrollToLineEffect)) {
|
|
165
|
+
const { line, options = {} } = effect.value;
|
|
166
|
+
const plugin = update.view.plugin(scrollPlugin);
|
|
167
|
+
if (plugin) {
|
|
168
|
+
plugin.scrollToLine(line, { offset, position, ...options });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}),
|
|
174
|
+
];
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Helper function to scroll to a specific line.
|
|
179
|
+
* This is a convenience function that can be used directly with an EditorView.
|
|
180
|
+
*
|
|
181
|
+
* @param view - The CodeMirror EditorView instance
|
|
182
|
+
* @param line - The line number to scroll to (1-based)
|
|
183
|
+
* @param options - Optional scroll configuration
|
|
184
|
+
*/
|
|
185
|
+
export const scrollToLine = (view: EditorView, line: number, options?: SmoothScrollOptions) => {
|
|
186
|
+
view.dispatch({
|
|
187
|
+
effects: scrollToLineEffect.of({ line, options }),
|
|
188
|
+
});
|
|
189
|
+
};
|
|
@@ -7,7 +7,7 @@ import { EditorView, keymap } from '@codemirror/view';
|
|
|
7
7
|
|
|
8
8
|
import { debounce } from '@dxos/async';
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
|
-
import {
|
|
10
|
+
import { isTruthy } from '@dxos/util';
|
|
11
11
|
|
|
12
12
|
import { singleValueFacet } from '../util';
|
|
13
13
|
|
|
@@ -86,7 +86,7 @@ export const selectionState = ({ getState, setState }: Partial<EditorStateStore>
|
|
|
86
86
|
getState &&
|
|
87
87
|
keymap.of([
|
|
88
88
|
{
|
|
89
|
-
key: '
|
|
89
|
+
key: 'Ctrl-r', // TODO(burdon): Setting to jump back to selection.
|
|
90
90
|
run: (view) => {
|
|
91
91
|
const state = getState(view.state.facet(documentId));
|
|
92
92
|
if (state) {
|
|
@@ -96,5 +96,5 @@ export const selectionState = ({ getState, setState }: Partial<EditorStateStore>
|
|
|
96
96
|
},
|
|
97
97
|
},
|
|
98
98
|
]),
|
|
99
|
-
].filter(
|
|
99
|
+
].filter(isTruthy);
|
|
100
100
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Extension, Prec } from '@codemirror/state';
|
|
6
|
+
import { keymap } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
export type SubmitOptions = {
|
|
9
|
+
fireIfEmpty?: boolean;
|
|
10
|
+
onSubmit?: (text: string) => boolean | void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handles Enter and Shift-Enter.
|
|
15
|
+
*/
|
|
16
|
+
export const submit = ({ fireIfEmpty = false, onSubmit }: SubmitOptions = {}): Extension => {
|
|
17
|
+
return [
|
|
18
|
+
Prec.highest(
|
|
19
|
+
keymap.of([
|
|
20
|
+
{
|
|
21
|
+
key: 'Enter',
|
|
22
|
+
preventDefault: true,
|
|
23
|
+
run: (view) => {
|
|
24
|
+
const text = view.state.doc.toString().trim();
|
|
25
|
+
if (onSubmit && (fireIfEmpty || text.length > 0)) {
|
|
26
|
+
const reset = onSubmit(text);
|
|
27
|
+
if (reset) {
|
|
28
|
+
// Clear the document after calling onEnter.
|
|
29
|
+
view.dispatch({
|
|
30
|
+
changes: {
|
|
31
|
+
from: 0,
|
|
32
|
+
to: view.state.doc.length,
|
|
33
|
+
insert: '',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'Shift-Enter',
|
|
44
|
+
preventDefault: true,
|
|
45
|
+
run: (view) => {
|
|
46
|
+
view.dispatch({
|
|
47
|
+
changes: {
|
|
48
|
+
from: view.state.selection.main.head,
|
|
49
|
+
insert: '\n',
|
|
50
|
+
},
|
|
51
|
+
selection: {
|
|
52
|
+
anchor: view.state.selection.main.head + 1,
|
|
53
|
+
head: view.state.selection.main.head + 1,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
]),
|
|
60
|
+
),
|
|
61
|
+
];
|
|
62
|
+
};
|