@dxos/react-ui-editor 0.8.4-main.dedc0f3 → 0.8.4-main.e8ec1fe
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 +5638 -5200
- 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 +5638 -5200
- 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/index.d.ts +2 -1
- package/dist/types/src/components/EditorToolbar/index.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/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 +1 -1
- 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 +14 -4
- package/dist/types/src/extensions/autoscroll.d.ts.map +1 -1
- 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 +10 -5
- 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 +8 -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.map +1 -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/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/preview/preview.d.ts +6 -4
- 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/streamer.d.ts.map +1 -1
- package/dist/types/src/extensions/tags/xml-tags.d.ts +42 -16
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
- 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/{Command.stories.d.ts → CommandDialog.stories.d.ts} +2 -3
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Comments.stories.d.ts +3 -4
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -2
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +3 -4
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +3 -4
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +0 -1
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/{CommandMenu.stories.d.ts → Popover.stories.d.ts} +6 -6
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +4 -4
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts +0 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +3 -5
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +7 -5
- 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/types/types.d.ts +1 -1
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util/debug.d.ts +4 -0
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +58 -59
- 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 +101 -91
- package/src/components/EditorToolbar/{lists.ts → actions.ts} +46 -16
- package/src/components/EditorToolbar/blocks.ts +2 -1
- package/src/components/EditorToolbar/formatting.ts +2 -1
- package/src/components/EditorToolbar/headings.ts +8 -5
- package/src/components/EditorToolbar/image.ts +1 -1
- package/src/components/EditorToolbar/index.ts +3 -7
- package/src/components/EditorToolbar/search.ts +1 -1
- package/src/components/EditorToolbar/useEditorToolbar.ts +20 -0
- package/src/components/EditorToolbar/view-mode.ts +2 -1
- package/src/components/index.ts +8 -2
- package/src/extensions/{autocomplete.ts → autocomplete/autocomplete.ts} +8 -76
- 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 +9 -9
- 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 +98 -86
- 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 +2 -1
- package/src/extensions/factories.ts +47 -16
- 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 +8 -1
- package/src/extensions/json.ts +1 -1
- package/src/extensions/listener.ts +14 -20
- package/src/extensions/markdown/bundle.ts +16 -4
- package/src/extensions/markdown/decorate.ts +9 -8
- package/src/extensions/markdown/formatting.ts +8 -10
- package/src/extensions/markdown/highlight.ts +1 -1
- package/src/extensions/markdown/image.ts +2 -2
- package/src/extensions/markdown/link.ts +3 -0
- package/src/extensions/markdown/table.ts +6 -6
- package/src/extensions/modal.ts +24 -0
- package/src/extensions/modes.ts +2 -2
- package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +15 -20
- package/src/extensions/outliner/outliner.test.ts +1 -1
- package/src/extensions/outliner/outliner.ts +4 -4
- package/src/extensions/outliner/tree.test.ts +1 -1
- package/src/extensions/preview/index.ts +1 -1
- package/src/extensions/preview/preview.ts +14 -14
- 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 +2 -1
- package/src/extensions/tags/streamer.ts +4 -5
- package/src/extensions/tags/xml-tags.ts +320 -155
- package/src/extensions/typewriter.ts +1 -1
- package/src/hooks/useTextEditor.ts +31 -43
- package/src/stories/{Command.stories.tsx → CommandDialog.stories.tsx} +20 -27
- package/src/stories/Comments.stories.tsx +8 -9
- package/src/stories/EditorToolbar.stories.tsx +14 -13
- package/src/stories/Experimental.stories.tsx +7 -7
- package/src/stories/Markdown.stories.tsx +6 -6
- package/src/stories/Outliner.stories.tsx +40 -31
- package/src/stories/Popover.stories.tsx +162 -0
- package/src/stories/Preview.stories.tsx +46 -42
- package/src/stories/Tags.stories.tsx +24 -10
- package/src/stories/TextEditor.stories.tsx +10 -35
- package/src/stories/components/EditorStory.tsx +29 -16
- package/src/stories/components/util.tsx +40 -8
- package/src/styles/markdown.ts +1 -1
- package/src/styles/theme.ts +13 -11
- package/src/types/types.ts +1 -1
- package/src/util/debug.ts +5 -0
- package/src/util/index.ts +0 -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 -14
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts +0 -37
- 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 -26
- 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 -19
- 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.map +0 -1
- 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/dist/types/src/util/domino.d.ts +0 -18
- package/dist/types/src/util/domino.d.ts.map +0 -1
- package/src/components/EditorToolbar/util.ts +0 -76
- package/src/components/Popover/CommandMenu.tsx +0 -279
- package/src/components/Popover/RefDropdownMenu.tsx +0 -89
- package/src/components/Popover/RefPopover.tsx +0 -117
- package/src/components/Popover/index.ts +0 -7
- 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/CommandMenu.stories.tsx +0 -160
- package/src/testing/index.ts +0 -5
- package/src/testing/util.ts +0 -7
- package/src/util/domino.ts +0 -51
|
@@ -98,11 +98,11 @@ class ImageWidget extends WidgetType {
|
|
|
98
98
|
super();
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
override eq(other: this)
|
|
101
|
+
override eq(other: this) {
|
|
102
102
|
return this._url === other._url;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
override toDOM(view: EditorView)
|
|
105
|
+
override toDOM(view: EditorView) {
|
|
106
106
|
const img = document.createElement('img');
|
|
107
107
|
img.setAttribute('src', this._url);
|
|
108
108
|
img.setAttribute('class', 'cm-image');
|
|
@@ -112,14 +112,18 @@ class TableWidget extends WidgetType {
|
|
|
112
112
|
super();
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
override eq(other: this)
|
|
115
|
+
override eq(other: this) {
|
|
116
116
|
return (
|
|
117
117
|
this._table.header?.join() === other._table.header?.join() &&
|
|
118
118
|
this._table.rows?.join() === other._table.rows?.join()
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
override
|
|
122
|
+
override ignoreEvent(e: Event): boolean {
|
|
123
|
+
return !/^mouse/.test(e.type);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override toDOM(_view: EditorView) {
|
|
123
127
|
const div = document.createElement('div');
|
|
124
128
|
const table = div.appendChild(document.createElement('table'));
|
|
125
129
|
|
|
@@ -143,8 +147,4 @@ class TableWidget extends WidgetType {
|
|
|
143
147
|
|
|
144
148
|
return div;
|
|
145
149
|
}
|
|
146
|
-
|
|
147
|
-
override ignoreEvent(e: Event): boolean {
|
|
148
|
-
return !/^mouse/.test(e.type);
|
|
149
|
-
}
|
|
150
150
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { StateEffect, StateField } from '@codemirror/state';
|
|
6
|
+
|
|
7
|
+
export const modalStateEffect = StateEffect.define<boolean>();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Determines if a modal dialog (e.g., popover) is active.
|
|
11
|
+
*/
|
|
12
|
+
export const modalStateField = StateField.define<boolean>({
|
|
13
|
+
create: () => false,
|
|
14
|
+
update: (value, tr) => {
|
|
15
|
+
let newValue = value;
|
|
16
|
+
for (const effect of tr.effects) {
|
|
17
|
+
if (effect.is(modalStateEffect)) {
|
|
18
|
+
newValue = effect.value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return newValue;
|
|
23
|
+
},
|
|
24
|
+
});
|
package/src/extensions/modes.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { singleValueFacet } from '../util';
|
|
|
11
11
|
|
|
12
12
|
export type EditorInputConfig = {
|
|
13
13
|
type?: string;
|
|
14
|
-
|
|
14
|
+
ignoreEscape?: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export const editorInputMode = singleValueFacet<EditorInputConfig>({});
|
|
@@ -26,7 +26,7 @@ export const InputModeExtensions: { [mode: string]: Extension } = {
|
|
|
26
26
|
vim: [
|
|
27
27
|
// https://github.com/replit/codemirror-vim
|
|
28
28
|
vim(),
|
|
29
|
-
editorInputMode.of({ type: 'vim',
|
|
29
|
+
editorInputMode.of({ type: 'vim', ignoreEscape: true }),
|
|
30
30
|
keymap.of([
|
|
31
31
|
{
|
|
32
32
|
key: 'Alt-Escape',
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Extension } from '@codemirror/state';
|
|
5
6
|
import { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
|
6
7
|
|
|
7
8
|
import { type CleanupFn, addEventListener } from '@dxos/async';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export type FloatingMenuOptions = {
|
|
10
|
+
export type MenuOptions = {
|
|
12
11
|
icon?: string;
|
|
13
12
|
height?: number;
|
|
14
13
|
padding?: number;
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
// TODO(burdon): Replace with popover.
|
|
17
|
+
export const menu = (options: MenuOptions = {}): Extension => [
|
|
18
18
|
ViewPlugin.fromClass(
|
|
19
19
|
class {
|
|
20
20
|
view: EditorView;
|
|
@@ -34,12 +34,10 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
34
34
|
{
|
|
35
35
|
const icon = document.createElement('dx-icon');
|
|
36
36
|
icon.setAttribute('icon', options.icon ?? 'ph--dots-three-vertical--regular');
|
|
37
|
-
const button = document.createElement('button');
|
|
38
|
-
button.appendChild(icon);
|
|
39
37
|
|
|
40
38
|
this.tag = document.createElement('dx-anchor');
|
|
41
|
-
this.tag.classList.add('cm-
|
|
42
|
-
this.tag.appendChild(
|
|
39
|
+
this.tag.classList.add('cm-popover-trigger');
|
|
40
|
+
this.tag.appendChild(icon);
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
container.appendChild(this.tag);
|
|
@@ -65,12 +63,12 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
// TODO(burdon): Timer to fade in/out.
|
|
68
|
-
if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
|
|
66
|
+
/*if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
|
|
69
67
|
this.tag.style.display = 'none';
|
|
70
68
|
this.tag.classList.add('opacity-10');
|
|
71
69
|
} else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
|
|
72
|
-
this.tag.style.display = '
|
|
73
|
-
} else if (
|
|
70
|
+
this.tag.style.display = '';
|
|
71
|
+
} else */ if (
|
|
74
72
|
update.docChanged ||
|
|
75
73
|
update.focusChanged ||
|
|
76
74
|
update.geometryChanged ||
|
|
@@ -99,7 +97,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
99
97
|
|
|
100
98
|
this.tag.style.top = `${offsetTop}px`;
|
|
101
99
|
this.tag.style.left = `${offsetLeft}px`;
|
|
102
|
-
this.tag.style.display = '
|
|
100
|
+
this.tag.style.display = '';
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
scheduleUpdate() {
|
|
@@ -113,21 +111,18 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
113
111
|
),
|
|
114
112
|
|
|
115
113
|
EditorView.theme({
|
|
116
|
-
'.cm-
|
|
114
|
+
'.cm-popover-trigger': {
|
|
117
115
|
position: 'fixed',
|
|
118
116
|
padding: '0',
|
|
119
117
|
border: 'none',
|
|
120
118
|
opacity: '0',
|
|
121
|
-
},
|
|
122
|
-
'[data-has-focus] & .cm-ref-tag': {
|
|
123
|
-
opacity: '1',
|
|
124
|
-
},
|
|
125
|
-
'.cm-ref-tag button': {
|
|
126
119
|
display: 'grid',
|
|
127
|
-
|
|
128
|
-
justifyContent: 'center',
|
|
120
|
+
placeContent: 'center',
|
|
129
121
|
width: '2rem',
|
|
130
122
|
height: '2rem',
|
|
131
123
|
},
|
|
124
|
+
'&:focus-within .cm-popover-trigger': {
|
|
125
|
+
opacity: '1',
|
|
126
|
+
},
|
|
132
127
|
}),
|
|
133
128
|
];
|
|
@@ -6,7 +6,7 @@ import { EditorSelection, EditorState } from '@codemirror/state';
|
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
7
|
import { describe, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { str } from '../../
|
|
9
|
+
import { str } from '../../util';
|
|
10
10
|
import { createMarkdownExtensions } from '../markdown';
|
|
11
11
|
|
|
12
12
|
import { indentItemLess, indentItemMore, moveItemDown, moveItemUp } from './commands';
|
|
@@ -7,11 +7,11 @@ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate
|
|
|
7
7
|
|
|
8
8
|
import { mx } from '@dxos/react-ui-theme';
|
|
9
9
|
|
|
10
|
-
import { floatingMenu } from '../command';
|
|
11
10
|
import { decorateMarkdown } from '../markdown';
|
|
12
11
|
|
|
13
12
|
import { commands } from './commands';
|
|
14
13
|
import { editor } from './editor';
|
|
14
|
+
import { menu } from './menu';
|
|
15
15
|
import { selectionCompartment, selectionEquals, selectionFacet } from './selection';
|
|
16
16
|
import { outlinerTree, treeFacet } from './tree';
|
|
17
17
|
|
|
@@ -52,7 +52,7 @@ export const outliner = (_options: OutlinerProps = {}): Extension => [
|
|
|
52
52
|
editor(),
|
|
53
53
|
|
|
54
54
|
// Floating menu.
|
|
55
|
-
|
|
55
|
+
menu(),
|
|
56
56
|
|
|
57
57
|
// Line decorations.
|
|
58
58
|
decorations(),
|
|
@@ -157,9 +157,9 @@ const decorations = () => [
|
|
|
157
157
|
},
|
|
158
158
|
|
|
159
159
|
'.cm-list-item-focused': {
|
|
160
|
-
borderColor: 'var(--dx-
|
|
160
|
+
borderColor: 'var(--dx-neutralFocusIndicator)',
|
|
161
161
|
},
|
|
162
|
-
'
|
|
162
|
+
'&:focus-within .cm-list-item-selected': {
|
|
163
163
|
borderColor: 'var(--dx-separator)',
|
|
164
164
|
},
|
|
165
165
|
}),
|
|
@@ -6,8 +6,8 @@ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
|
|
6
6
|
import { EditorState } from '@codemirror/state';
|
|
7
7
|
import { beforeEach, describe, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { str } from '../../testing';
|
|
10
9
|
import { type Range } from '../../types';
|
|
10
|
+
import { str } from '../../util';
|
|
11
11
|
|
|
12
12
|
import { type Item, listItemToString, outlinerTree, treeFacet } from './tree';
|
|
13
13
|
|
|
@@ -7,9 +7,12 @@ import { type EditorState, type Extension, RangeSetBuilder, StateField } from '@
|
|
|
7
7
|
import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
|
|
8
8
|
import { type SyntaxNode } from '@lezer/common';
|
|
9
9
|
|
|
10
|
+
export type PreviewBlock = {
|
|
11
|
+
link: PreviewLinkRef;
|
|
12
|
+
el: HTMLElement;
|
|
13
|
+
};
|
|
14
|
+
|
|
10
15
|
export type PreviewLinkRef = {
|
|
11
|
-
/** @deprecated */
|
|
12
|
-
// TODO(burdon): Remove?
|
|
13
16
|
suggest?: boolean;
|
|
14
17
|
block?: boolean;
|
|
15
18
|
label: string;
|
|
@@ -22,12 +25,9 @@ export type PreviewLinkTarget = {
|
|
|
22
25
|
object?: any;
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
// TODO(wittjosiah): Remove.
|
|
26
|
-
export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
|
|
27
|
-
|
|
28
28
|
export type PreviewOptions = {
|
|
29
|
-
addBlockContainer?: (
|
|
30
|
-
removeBlockContainer?: (
|
|
29
|
+
addBlockContainer?: (block: PreviewBlock) => void;
|
|
30
|
+
removeBlockContainer?: (block: PreviewBlock) => void;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -147,11 +147,11 @@ class PreviewInlineWidget extends WidgetType {
|
|
|
147
147
|
// return false;
|
|
148
148
|
// }
|
|
149
149
|
|
|
150
|
-
override eq(other: this)
|
|
150
|
+
override eq(other: this) {
|
|
151
151
|
return this._link.ref === other._link.ref && this._link.label === other._link.label;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
override toDOM(_view: EditorView)
|
|
154
|
+
override toDOM(_view: EditorView) {
|
|
155
155
|
const root = document.createElement('dx-anchor');
|
|
156
156
|
root.classList.add('dx-tag--anchor');
|
|
157
157
|
root.textContent = this._link.label;
|
|
@@ -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(_view: EditorView)
|
|
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
|
+
};
|