@dxos/react-ui-editor 0.8.4-main.e098934 → 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 +4960 -4414
- 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 +4960 -4414
- 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/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 -3
- 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 +57 -58
- 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 -92
- 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/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} +9 -9
- package/src/extensions/outliner/outliner.test.ts +1 -1
- package/src/extensions/outliner/outliner.ts +3 -3
- package/src/extensions/outliner/tree.test.ts +1 -1
- package/src/extensions/preview/index.ts +1 -1
- package/src/extensions/preview/preview.ts +14 -11
- 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 -31
- 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 +20 -20
- 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 -76
- 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 -78
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/components/CommandMenu/CommandMenu.d.ts +0 -38
- package/dist/types/src/components/CommandMenu/CommandMenu.d.ts.map +0 -1
- package/dist/types/src/components/CommandMenu/index.d.ts +0 -2
- package/dist/types/src/components/CommandMenu/index.d.ts.map +0 -1
- 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/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 -25
- 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/PreviewPopover.d.ts +0 -20
- package/dist/types/src/testing/PreviewPopover.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- 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/CommandMenu/CommandMenu.tsx +0 -346
- package/src/components/CommandMenu/index.ts +0 -5
- package/src/components/EditorToolbar/util.ts +0 -76
- package/src/extensions/command/action.ts +0 -55
- 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 -115
- package/src/stories/CommandMenu.stories.tsx +0 -158
- package/src/testing/PreviewPopover.tsx +0 -78
- package/src/testing/index.ts +0 -6
- 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;
|
|
@@ -36,7 +36,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
36
36
|
icon.setAttribute('icon', options.icon ?? 'ph--dots-three-vertical--regular');
|
|
37
37
|
|
|
38
38
|
this.tag = document.createElement('dx-anchor');
|
|
39
|
-
this.tag.classList.add('cm-
|
|
39
|
+
this.tag.classList.add('cm-popover-trigger');
|
|
40
40
|
this.tag.appendChild(icon);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -63,12 +63,12 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// TODO(burdon): Timer to fade in/out.
|
|
66
|
-
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)))) {
|
|
67
67
|
this.tag.style.display = 'none';
|
|
68
68
|
this.tag.classList.add('opacity-10');
|
|
69
69
|
} else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
|
|
70
70
|
this.tag.style.display = '';
|
|
71
|
-
} else if (
|
|
71
|
+
} else */ if (
|
|
72
72
|
update.docChanged ||
|
|
73
73
|
update.focusChanged ||
|
|
74
74
|
update.geometryChanged ||
|
|
@@ -111,7 +111,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
111
111
|
),
|
|
112
112
|
|
|
113
113
|
EditorView.theme({
|
|
114
|
-
'.cm-
|
|
114
|
+
'.cm-popover-trigger': {
|
|
115
115
|
position: 'fixed',
|
|
116
116
|
padding: '0',
|
|
117
117
|
border: 'none',
|
|
@@ -121,7 +121,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
121
121
|
width: '2rem',
|
|
122
122
|
height: '2rem',
|
|
123
123
|
},
|
|
124
|
-
'&:focus-within .cm-
|
|
124
|
+
'&:focus-within .cm-popover-trigger': {
|
|
125
125
|
opacity: '1',
|
|
126
126
|
},
|
|
127
127
|
}),
|
|
@@ -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,7 +157,7 @@ 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)',
|
|
@@ -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;
|
|
@@ -23,8 +26,8 @@ export type PreviewLinkTarget = {
|
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
export type PreviewOptions = {
|
|
26
|
-
addBlockContainer?: (
|
|
27
|
-
removeBlockContainer?: (
|
|
29
|
+
addBlockContainer?: (block: PreviewBlock) => void;
|
|
30
|
+
removeBlockContainer?: (block: PreviewBlock) => void;
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
/**
|
|
@@ -144,11 +147,11 @@ class PreviewInlineWidget extends WidgetType {
|
|
|
144
147
|
// return false;
|
|
145
148
|
// }
|
|
146
149
|
|
|
147
|
-
override eq(other: this)
|
|
150
|
+
override eq(other: this) {
|
|
148
151
|
return this._link.ref === other._link.ref && this._link.label === other._link.label;
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
override toDOM(_view: EditorView)
|
|
154
|
+
override toDOM(_view: EditorView) {
|
|
152
155
|
const root = document.createElement('dx-anchor');
|
|
153
156
|
root.classList.add('dx-tag--anchor');
|
|
154
157
|
root.textContent = this._link.label;
|
|
@@ -173,18 +176,18 @@ class PreviewBlockWidget extends WidgetType {
|
|
|
173
176
|
// return true;
|
|
174
177
|
// }
|
|
175
178
|
|
|
176
|
-
override eq(other: this)
|
|
179
|
+
override eq(other: this) {
|
|
177
180
|
return this._link.ref === other._link.ref;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
override toDOM(_view: EditorView)
|
|
183
|
+
override toDOM(_view: EditorView) {
|
|
181
184
|
const root = document.createElement('div');
|
|
182
185
|
root.classList.add('cm-preview-block', 'density-coarse');
|
|
183
|
-
this._options.addBlockContainer?.(this._link, root);
|
|
186
|
+
this._options.addBlockContainer?.({ link: this._link, el: root });
|
|
184
187
|
return root;
|
|
185
188
|
}
|
|
186
189
|
|
|
187
|
-
override destroy() {
|
|
188
|
-
this._options.removeBlockContainer?.(this._link);
|
|
190
|
+
override destroy(root: HTMLDivElement) {
|
|
191
|
+
this._options.removeBlockContainer?.({ link: this._link, el: root });
|
|
189
192
|
}
|
|
190
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
|
};
|