@dxos/react-ui-editor 0.8.2-main.f11618f → 0.8.2-staging.42af850
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/index.mjs +4450 -3278
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +3 -64
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/index.cjs +2701 -1528
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +3 -75
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/lib/node-esm/index.mjs +4450 -3278
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +3 -64
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/{comment.d.ts → image.d.ts} +4 -5
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/util.d.ts +14 -22
- package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +21 -0
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -0
- package/dist/types/src/{testing → components/Popover}/RefPopover.d.ts +1 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -0
- package/dist/types/src/components/Popover/index.d.ts +3 -0
- package/dist/types/src/components/Popover/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts +2 -5
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/annotations.d.ts +4 -1
- package/dist/types/src/extensions/annotations.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts +1 -2
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- 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/automerge/update-codemirror.d.ts +1 -1
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/blast.d.ts.map +1 -1
- package/dist/types/src/extensions/command/command.d.ts +1 -2
- package/dist/types/src/extensions/command/command.d.ts.map +1 -1
- package/dist/types/src/extensions/command/hint.d.ts +14 -2
- package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
- package/dist/types/src/extensions/command/index.d.ts +2 -0
- package/dist/types/src/extensions/command/index.d.ts.map +1 -1
- package/dist/types/src/extensions/command/menu.d.ts +4 -14
- package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
- package/dist/types/src/extensions/command/state.d.ts +1 -1
- package/dist/types/src/extensions/command/state.d.ts.map +1 -1
- package/dist/types/src/extensions/command/typeahead.d.ts +17 -0
- package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +2 -12
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/debug.d.ts.map +1 -1
- package/dist/types/src/extensions/dnd.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +4 -0
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +2 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/json.d.ts +7 -0
- package/dist/types/src/extensions/json.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/{editorAction.d.ts → action.d.ts} +1 -1
- package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts +2 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +1 -0
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/index.d.ts +1 -1
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
- package/dist/types/src/extensions/mention.d.ts.map +1 -1
- package/dist/types/src/extensions/modes.d.ts +5 -5
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/commands.d.ts +10 -0
- package/dist/types/src/extensions/outliner/commands.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/editor.d.ts +5 -0
- package/dist/types/src/extensions/outliner/editor.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/editor.test.d.ts +2 -0
- package/dist/types/src/extensions/outliner/editor.test.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/index.d.ts +4 -0
- package/dist/types/src/extensions/outliner/index.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/outliner.d.ts +13 -0
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/outliner.test.d.ts +2 -0
- package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/selection.d.ts +12 -0
- package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/tree.d.ts +79 -0
- package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/tree.test.d.ts +2 -0
- package/dist/types/src/extensions/outliner/tree.test.d.ts.map +1 -0
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/extensions/selection.d.ts.map +1 -1
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +0 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/Command.stories.d.ts +7 -0
- package/dist/types/src/stories/Command.stories.d.ts.map +1 -0
- package/dist/types/src/stories/{TextEditorComments.stories.d.ts → Comments.stories.d.ts} +3 -3
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -0
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +12 -0
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -0
- package/dist/types/src/stories/{TextEditorSpecial.stories.d.ts → Experimental.stories.d.ts} +3 -6
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Markdown.stories.d.ts +46 -0
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Outliner.stories.d.ts +26 -0
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +10 -0
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -0
- package/dist/types/src/stories/{TextEditorBasic.stories.d.ts → TextEditor.stories.d.ts} +9 -36
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -0
- package/dist/types/src/stories/{story-utils.d.ts → util.d.ts} +6 -6
- package/dist/types/src/stories/util.d.ts.map +1 -0
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -1
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +2 -0
- package/dist/types/src/testing/util.d.ts.map +1 -0
- package/dist/types/src/util/cursor.d.ts.map +1 -1
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/src/util/dom.d.ts.map +1 -1
- package/dist/types/src/util/facet.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -31
- package/src/components/EditorToolbar/EditorToolbar.tsx +93 -70
- package/src/components/EditorToolbar/blocks.ts +27 -6
- package/src/components/EditorToolbar/formatting.ts +34 -7
- package/src/components/EditorToolbar/headings.ts +9 -8
- package/src/components/EditorToolbar/image.ts +16 -0
- package/src/components/EditorToolbar/index.ts +7 -1
- package/src/components/EditorToolbar/lists.ts +26 -7
- package/src/components/EditorToolbar/search.ts +19 -0
- package/src/components/EditorToolbar/util.ts +16 -17
- package/src/components/EditorToolbar/view-mode.ts +9 -8
- package/src/components/Popover/RefDropdownMenu.tsx +77 -0
- package/src/{testing → components/Popover}/RefPopover.tsx +5 -4
- package/src/components/Popover/index.ts +6 -0
- package/src/components/index.ts +1 -0
- package/src/defaults.ts +10 -13
- package/src/extensions/annotations.ts +41 -64
- package/src/extensions/autocomplete.ts +5 -6
- package/src/extensions/automerge/automerge.stories.tsx +11 -14
- package/src/extensions/automerge/automerge.test.tsx +6 -5
- package/src/extensions/automerge/automerge.ts +2 -2
- package/src/extensions/automerge/defs.ts +1 -2
- package/src/extensions/automerge/sync.ts +7 -7
- package/src/extensions/automerge/update-automerge.ts +1 -1
- package/src/extensions/automerge/update-codemirror.ts +3 -4
- package/src/extensions/awareness/awareness-provider.ts +4 -4
- package/src/extensions/awareness/awareness.ts +7 -7
- package/src/extensions/blast.ts +9 -9
- package/src/extensions/command/command.ts +1 -3
- package/src/extensions/command/hint.ts +7 -7
- package/src/extensions/command/index.ts +2 -0
- package/src/extensions/command/menu.ts +75 -50
- package/src/extensions/command/typeahead.ts +116 -0
- package/src/extensions/comments.ts +4 -69
- package/src/extensions/factories.ts +13 -0
- package/src/extensions/index.ts +2 -0
- package/src/extensions/json.ts +56 -0
- package/src/extensions/markdown/bundle.ts +13 -9
- package/src/extensions/markdown/changes.ts +3 -2
- package/src/extensions/markdown/decorate.ts +15 -14
- package/src/extensions/markdown/formatting.ts +4 -4
- package/src/extensions/markdown/image.ts +2 -2
- package/src/extensions/markdown/index.ts +1 -1
- package/src/extensions/markdown/styles.ts +4 -3
- package/src/extensions/markdown/table.ts +3 -3
- package/src/extensions/modes.ts +5 -6
- package/src/extensions/outliner/commands.ts +270 -0
- package/src/extensions/outliner/editor.test.ts +33 -0
- package/src/extensions/outliner/editor.ts +184 -0
- package/src/extensions/outliner/index.ts +7 -0
- package/src/extensions/outliner/outliner.test.ts +99 -0
- package/src/extensions/outliner/outliner.ts +168 -0
- package/src/extensions/outliner/selection.ts +50 -0
- package/src/extensions/outliner/tree.test.ts +164 -0
- package/src/extensions/outliner/tree.ts +315 -0
- package/src/extensions/preview/preview.ts +5 -5
- package/src/hooks/index.ts +0 -1
- package/src/stories/Command.stories.tsx +97 -0
- package/src/stories/{TextEditorComments.stories.tsx → Comments.stories.tsx} +13 -14
- package/src/stories/EditorToolbar.stories.tsx +96 -0
- package/src/stories/{TextEditorSpecial.stories.tsx → Experimental.stories.tsx} +9 -30
- package/src/stories/Markdown.stories.tsx +121 -0
- package/src/stories/Outliner.stories.tsx +108 -0
- package/src/stories/{TextEditorPreview.stories.tsx → Preview.stories.tsx} +46 -136
- package/src/stories/{TextEditorBasic.stories.tsx → TextEditor.stories.tsx} +78 -111
- package/src/stories/{story-utils.tsx → util.tsx} +28 -31
- package/src/styles/theme.ts +15 -5
- package/src/styles/tokens.ts +1 -2
- package/src/testing/index.ts +1 -1
- package/src/testing/util.ts +5 -0
- package/dist/types/src/components/EditorToolbar/comment.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/editorAction.d.ts.map +0 -1
- package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
- package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
- package/dist/types/src/stories/InputMode.stories.d.ts +0 -57
- package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
- package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +0 -1
- package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +0 -1
- package/dist/types/src/stories/TextEditorPreview.stories.d.ts +0 -13
- package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +0 -1
- package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +0 -1
- package/dist/types/src/stories/story-utils.d.ts.map +0 -1
- package/dist/types/src/testing/RefPopover.d.ts.map +0 -1
- package/src/components/EditorToolbar/comment.ts +0 -23
- package/src/hooks/useActionHandler.ts +0 -12
- package/src/stories/InputMode.stories.tsx +0 -124
- /package/src/extensions/markdown/{editorAction.ts → action.ts} +0 -0
@@ -12,11 +12,13 @@ import { type Extension } from '@codemirror/state';
|
|
12
12
|
import { keymap } from '@codemirror/view';
|
13
13
|
|
14
14
|
import { type ThemeMode } from '@dxos/react-ui';
|
15
|
+
import { isNotFalsy } from '@dxos/util';
|
15
16
|
|
16
17
|
import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
|
17
18
|
|
18
19
|
export type MarkdownBundleOptions = {
|
19
20
|
themeMode?: ThemeMode;
|
21
|
+
indentWithTab?: boolean;
|
20
22
|
};
|
21
23
|
|
22
24
|
/**
|
@@ -27,7 +29,7 @@ export type MarkdownBundleOptions = {
|
|
27
29
|
* https://codemirror.net/docs/community
|
28
30
|
* https://codemirror.net/docs/ref/#codemirror.basicSetup
|
29
31
|
*/
|
30
|
-
export const createMarkdownExtensions = (
|
32
|
+
export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): Extension[] => {
|
31
33
|
return [
|
32
34
|
// Main extension.
|
33
35
|
// https://github.com/codemirror/lang-markdown
|
@@ -56,14 +58,16 @@ export const createMarkdownExtensions = ({ themeMode }: MarkdownBundleOptions =
|
|
56
58
|
// Custom styles.
|
57
59
|
syntaxHighlighting(markdownHighlightStyle()),
|
58
60
|
|
59
|
-
keymap.of(
|
60
|
-
|
61
|
-
|
61
|
+
keymap.of(
|
62
|
+
[
|
63
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
64
|
+
options.indentWithTab !== false && indentWithTab,
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
// https://codemirror.net/docs/ref/#commands.defaultKeymap
|
67
|
+
...defaultKeymap,
|
68
|
+
...completionKeymap,
|
69
|
+
...lintKeymap,
|
70
|
+
].filter(isNotFalsy),
|
71
|
+
),
|
68
72
|
];
|
69
73
|
};
|
@@ -56,8 +56,9 @@ export const adjustChanges = () => {
|
|
56
56
|
// Check for URL.
|
57
57
|
const url = getValidUrl(update.view.state.sliceDoc(fromB, toB));
|
58
58
|
if (url) {
|
59
|
+
// Check if pasting inside existing link.
|
59
60
|
const node = tree.resolveInner(fromA, -1);
|
60
|
-
const invalidPositions = new Set(['
|
61
|
+
const invalidPositions = new Set(['Code', 'CodeText', 'FencedCode', 'Link', 'LinkMark', 'URL']);
|
61
62
|
if (!invalidPositions.has(node?.name)) {
|
62
63
|
const replacedText = tr.startState.sliceDoc(fromA, toA);
|
63
64
|
adjustments.push({ from: fromA, to: toB, insert: createLink(url, replacedText) });
|
@@ -84,7 +85,7 @@ export const adjustChanges = () => {
|
|
84
85
|
}
|
85
86
|
}
|
86
87
|
|
87
|
-
// TODO(burdon): Is this the right way to augment changes?
|
88
|
+
// TODO(burdon): Is this the right way to augment changes? Alt: EditorState.transactionFilter
|
88
89
|
if (adjustments.length) {
|
89
90
|
setTimeout(() => {
|
90
91
|
update.view.dispatch(
|
@@ -36,7 +36,7 @@ const Unicode = {
|
|
36
36
|
//
|
37
37
|
|
38
38
|
class HorizontalRuleWidget extends WidgetType {
|
39
|
-
override toDOM() {
|
39
|
+
override toDOM(): HTMLSpanElement {
|
40
40
|
const el = document.createElement('span');
|
41
41
|
el.className = 'cm-hr';
|
42
42
|
return el;
|
@@ -51,12 +51,12 @@ class LinkButton extends WidgetType {
|
|
51
51
|
super();
|
52
52
|
}
|
53
53
|
|
54
|
-
override eq(other: this) {
|
54
|
+
override eq(other: this): boolean {
|
55
55
|
return this.url === other.url;
|
56
56
|
}
|
57
57
|
|
58
58
|
// TODO(burdon): Create icon and link directly without react?
|
59
|
-
override toDOM(view: EditorView) {
|
59
|
+
override toDOM(view: EditorView): HTMLSpanElement {
|
60
60
|
const el = document.createElement('span');
|
61
61
|
this.render(el, { url: this.url }, view);
|
62
62
|
return el;
|
@@ -68,11 +68,11 @@ class CheckboxWidget extends WidgetType {
|
|
68
68
|
super();
|
69
69
|
}
|
70
70
|
|
71
|
-
override eq(other: this) {
|
71
|
+
override eq(other: this): boolean {
|
72
72
|
return this._checked === other._checked;
|
73
73
|
}
|
74
74
|
|
75
|
-
override toDOM(view: EditorView) {
|
75
|
+
override toDOM(view: EditorView): HTMLSpanElement {
|
76
76
|
const input = document.createElement('input');
|
77
77
|
input.className = 'cm-task-checkbox dx-checkbox';
|
78
78
|
input.type = 'checkbox';
|
@@ -105,7 +105,7 @@ class CheckboxWidget extends WidgetType {
|
|
105
105
|
return span;
|
106
106
|
}
|
107
107
|
|
108
|
-
override ignoreEvent() {
|
108
|
+
override ignoreEvent(): boolean {
|
109
109
|
return false;
|
110
110
|
}
|
111
111
|
}
|
@@ -118,7 +118,7 @@ class TextWidget extends WidgetType {
|
|
118
118
|
super();
|
119
119
|
}
|
120
120
|
|
121
|
-
override toDOM() {
|
121
|
+
override toDOM(): HTMLSpanElement {
|
122
122
|
const el = document.createElement('span');
|
123
123
|
if (this.className) {
|
124
124
|
el.className = this.className;
|
@@ -129,10 +129,10 @@ class TextWidget extends WidgetType {
|
|
129
129
|
}
|
130
130
|
|
131
131
|
const hide = Decoration.replace({});
|
132
|
-
const blockQuote = Decoration.line({ class:
|
133
|
-
const fencedCodeLine = Decoration.line({ class:
|
134
|
-
const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-
|
135
|
-
const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-
|
132
|
+
const blockQuote = Decoration.line({ class: 'cm-blockquote' });
|
133
|
+
const fencedCodeLine = Decoration.line({ class: 'cm-code cm-codeblock-line' });
|
134
|
+
const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-start') });
|
135
|
+
const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-end') });
|
136
136
|
const commentBlockLine = fencedCodeLine;
|
137
137
|
const commentBlockLineFirst = fencedCodeLineFirst;
|
138
138
|
const commentBlockLineLast = fencedCodeLineLast;
|
@@ -277,7 +277,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
277
277
|
// Set indentation.
|
278
278
|
const list = getCurrentListLevel();
|
279
279
|
const width = list.type === 'OrderedList' ? orderedListIndentationWidth : bulletListIndentationWidth;
|
280
|
-
const offset = ((list.level ?? 0) + 1) * width;
|
280
|
+
const offset = (options?.listPaddingLeft ?? 0) + ((list.level ?? 0) + 1) * width;
|
281
281
|
if (node.from === line.to - 1) {
|
282
282
|
// Abort if only the hyphen is typed.
|
283
283
|
return false;
|
@@ -285,7 +285,6 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
285
285
|
|
286
286
|
// Add line decoration for the continuation indent.
|
287
287
|
// TODO(burdon): Bug if indentation is more than one indentation unit (e.g., 4 spaces) from the previous line.
|
288
|
-
|
289
288
|
deco.add(
|
290
289
|
line.from,
|
291
290
|
line.from,
|
@@ -406,7 +405,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
406
405
|
}
|
407
406
|
|
408
407
|
const first = block.from <= node.from;
|
409
|
-
const last = block.to >= node.to &&
|
408
|
+
const last = block.to >= node.to && /```$/.test(state.doc.sliceString(block.from, block.to));
|
410
409
|
deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
|
411
410
|
|
412
411
|
const editing = editingRange(state, node, focus);
|
@@ -521,6 +520,8 @@ export interface DecorateOptions {
|
|
521
520
|
selectionChangeDelay?: number;
|
522
521
|
numberedHeadings?: { from: number; to?: number };
|
523
522
|
renderLinkButton?: RenderCallback<{ url: string }>;
|
523
|
+
// TODO(burdon): Additional padding for each line.
|
524
|
+
listPaddingLeft?: number;
|
524
525
|
}
|
525
526
|
|
526
527
|
export const decorateMarkdown = (options: DecorateOptions = {}) => {
|
@@ -5,13 +5,13 @@
|
|
5
5
|
import { snippet } from '@codemirror/autocomplete';
|
6
6
|
import { syntaxTree } from '@codemirror/language';
|
7
7
|
import {
|
8
|
-
type Extension,
|
9
|
-
type StateCommand,
|
10
|
-
type EditorState,
|
11
8
|
type ChangeSpec,
|
12
|
-
type Text,
|
13
9
|
EditorSelection,
|
10
|
+
type Extension,
|
11
|
+
type EditorState,
|
14
12
|
type Line,
|
13
|
+
type StateCommand,
|
14
|
+
type Text,
|
15
15
|
} from '@codemirror/state';
|
16
16
|
import { EditorView, keymap } from '@codemirror/view';
|
17
17
|
import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
|
@@ -99,11 +99,11 @@ class ImageWidget extends WidgetType {
|
|
99
99
|
super();
|
100
100
|
}
|
101
101
|
|
102
|
-
override eq(other: this) {
|
102
|
+
override eq(other: this): boolean {
|
103
103
|
return this._url === other._url;
|
104
104
|
}
|
105
105
|
|
106
|
-
override toDOM(view: EditorView) {
|
106
|
+
override toDOM(view: EditorView): HTMLImageElement {
|
107
107
|
const img = document.createElement('img');
|
108
108
|
img.setAttribute('src', this._url);
|
109
109
|
img.setAttribute('class', 'cm-image');
|
@@ -59,11 +59,11 @@ export const formattingStyles = EditorView.theme({
|
|
59
59
|
background: 'var(--dx-cmCodeblock)',
|
60
60
|
paddingInline: '1rem !important',
|
61
61
|
},
|
62
|
-
'& .cm-codeblock-
|
62
|
+
'& .cm-codeblock-start': {
|
63
63
|
borderTopLeftRadius: '.25rem',
|
64
64
|
borderTopRightRadius: '.25rem',
|
65
65
|
},
|
66
|
-
'& .cm-codeblock-
|
66
|
+
'& .cm-codeblock-end': {
|
67
67
|
borderBottomLeftRadius: '.25rem',
|
68
68
|
borderBottomRightRadius: '.25rem',
|
69
69
|
},
|
@@ -72,8 +72,9 @@ export const formattingStyles = EditorView.theme({
|
|
72
72
|
* Task list.
|
73
73
|
*/
|
74
74
|
'& .cm-task': {
|
75
|
-
display: 'inline-
|
75
|
+
display: 'inline-flex',
|
76
76
|
width: `${bulletListIndentationWidth}px`,
|
77
|
+
height: '20px',
|
77
78
|
},
|
78
79
|
'& .cm-task-checkbox': {
|
79
80
|
display: 'grid',
|
@@ -106,14 +106,14 @@ class TableWidget extends WidgetType {
|
|
106
106
|
super();
|
107
107
|
}
|
108
108
|
|
109
|
-
override eq(other: this) {
|
109
|
+
override eq(other: this): boolean {
|
110
110
|
return (
|
111
111
|
this._table.header?.join() === other._table.header?.join() &&
|
112
112
|
this._table.rows?.join() === other._table.rows?.join()
|
113
113
|
);
|
114
114
|
}
|
115
115
|
|
116
|
-
override toDOM(view: EditorView) {
|
116
|
+
override toDOM(view: EditorView): HTMLDivElement {
|
117
117
|
const div = document.createElement('div');
|
118
118
|
const table = div.appendChild(document.createElement('table'));
|
119
119
|
|
@@ -138,7 +138,7 @@ class TableWidget extends WidgetType {
|
|
138
138
|
return div;
|
139
139
|
}
|
140
140
|
|
141
|
-
override ignoreEvent(e: Event) {
|
141
|
+
override ignoreEvent(e: Event): boolean {
|
142
142
|
return !/^mouse/.test(e.type);
|
143
143
|
}
|
144
144
|
}
|
package/src/extensions/modes.ts
CHANGED
@@ -6,18 +6,17 @@ import { type Extension } from '@codemirror/state';
|
|
6
6
|
import { keymap } from '@codemirror/view';
|
7
7
|
import { vim } from '@replit/codemirror-vim';
|
8
8
|
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
9
|
-
|
10
|
-
import { S } from '@dxos/echo-schema';
|
9
|
+
import { Schema } from 'effect';
|
11
10
|
|
12
11
|
import { singleValueFacet } from '../util';
|
13
12
|
|
14
13
|
export const EditorViewModes = ['preview', 'readonly', 'source'] as const;
|
15
|
-
export const EditorViewMode =
|
16
|
-
export type EditorViewMode =
|
14
|
+
export const EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
|
15
|
+
export type EditorViewMode = Schema.Schema.Type<typeof EditorViewMode>;
|
17
16
|
|
18
17
|
export const EditorInputModes = ['default', 'vim', 'vscode'] as const;
|
19
|
-
export const EditorInputMode =
|
20
|
-
export type EditorInputMode =
|
18
|
+
export const EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
|
19
|
+
export type EditorInputMode = Schema.Schema.Type<typeof EditorInputMode>;
|
21
20
|
|
22
21
|
export type EditorInputConfig = {
|
23
22
|
type?: string;
|
@@ -0,0 +1,270 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2025 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import { indentMore } from '@codemirror/commands';
|
6
|
+
import { getIndentUnit } from '@codemirror/language';
|
7
|
+
import { type ChangeSpec, EditorSelection, type Extension } from '@codemirror/state';
|
8
|
+
import { type Command, type EditorView, keymap } from '@codemirror/view';
|
9
|
+
|
10
|
+
import { getSelection, selectAll, selectDown, selectNone, selectUp } from './selection';
|
11
|
+
import { getRange, treeFacet } from './tree';
|
12
|
+
|
13
|
+
//
|
14
|
+
// Indentation comnmands.
|
15
|
+
//
|
16
|
+
|
17
|
+
export const indentItemMore: Command = (view: EditorView) => {
|
18
|
+
const pos = getSelection(view.state).from;
|
19
|
+
const tree = view.state.facet(treeFacet);
|
20
|
+
const current = tree.find(pos);
|
21
|
+
if (current) {
|
22
|
+
const previous = tree.prev(current);
|
23
|
+
if (previous && current.level <= previous.level) {
|
24
|
+
// TODO(burdon): Indent descendants?
|
25
|
+
indentMore(view);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
return true;
|
30
|
+
};
|
31
|
+
|
32
|
+
export const indentItemLess: Command = (view: EditorView) => {
|
33
|
+
const pos = getSelection(view.state).from;
|
34
|
+
const tree = view.state.facet(treeFacet);
|
35
|
+
const current = tree.find(pos);
|
36
|
+
if (current) {
|
37
|
+
if (current.level > 0) {
|
38
|
+
// Unindent current line and all descendants.
|
39
|
+
// NOTE: The markdown extension doesn't provide an indentation service.
|
40
|
+
const indentUnit = getIndentUnit(view.state);
|
41
|
+
const changes: ChangeSpec[] = [];
|
42
|
+
tree.traverse(current, (item) => {
|
43
|
+
const line = view.state.doc.lineAt(item.lineRange.from);
|
44
|
+
changes.push({ from: line.from, to: line.from + indentUnit });
|
45
|
+
});
|
46
|
+
|
47
|
+
if (changes.length > 0) {
|
48
|
+
view.dispatch({ changes });
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
return true;
|
54
|
+
};
|
55
|
+
|
56
|
+
//
|
57
|
+
// Moving commands.
|
58
|
+
//
|
59
|
+
|
60
|
+
export const moveItemDown: Command = (view: EditorView) => {
|
61
|
+
const pos = getSelection(view.state)?.from;
|
62
|
+
const tree = view.state.facet(treeFacet);
|
63
|
+
const current = tree.find(pos);
|
64
|
+
if (current && current.nextSibling) {
|
65
|
+
const next = current.nextSibling;
|
66
|
+
const currentContent = view.state.doc.sliceString(...getRange(tree, current));
|
67
|
+
const nextContent = view.state.doc.sliceString(...getRange(tree, next));
|
68
|
+
const changes: ChangeSpec[] = [
|
69
|
+
{
|
70
|
+
from: current.lineRange.from,
|
71
|
+
to: current.lineRange.from + currentContent.length,
|
72
|
+
insert: nextContent,
|
73
|
+
},
|
74
|
+
{
|
75
|
+
from: next.lineRange.from,
|
76
|
+
to: next.lineRange.from + nextContent.length,
|
77
|
+
insert: currentContent,
|
78
|
+
},
|
79
|
+
];
|
80
|
+
|
81
|
+
view.dispatch({
|
82
|
+
changes,
|
83
|
+
selection: EditorSelection.cursor(pos + nextContent.length + 1),
|
84
|
+
scrollIntoView: true,
|
85
|
+
});
|
86
|
+
}
|
87
|
+
|
88
|
+
return true;
|
89
|
+
};
|
90
|
+
|
91
|
+
export const moveItemUp: Command = (view: EditorView) => {
|
92
|
+
const pos = getSelection(view.state)?.from;
|
93
|
+
const tree = view.state.facet(treeFacet);
|
94
|
+
const current = tree.find(pos);
|
95
|
+
if (current && current.prevSibling) {
|
96
|
+
const prev = current.prevSibling;
|
97
|
+
const currentContent = view.state.doc.sliceString(...getRange(tree, current));
|
98
|
+
const prevContent = view.state.doc.sliceString(...getRange(tree, prev));
|
99
|
+
const changes: ChangeSpec[] = [
|
100
|
+
{
|
101
|
+
from: prev.lineRange.from,
|
102
|
+
to: prev.lineRange.from + prevContent.length,
|
103
|
+
insert: currentContent,
|
104
|
+
},
|
105
|
+
{
|
106
|
+
from: current.lineRange.from,
|
107
|
+
to: current.lineRange.from + currentContent.length,
|
108
|
+
insert: prevContent,
|
109
|
+
},
|
110
|
+
];
|
111
|
+
|
112
|
+
view.dispatch({
|
113
|
+
changes,
|
114
|
+
selection: EditorSelection.cursor(pos - prevContent.length - 1),
|
115
|
+
scrollIntoView: true,
|
116
|
+
});
|
117
|
+
}
|
118
|
+
|
119
|
+
return true;
|
120
|
+
};
|
121
|
+
|
122
|
+
//
|
123
|
+
// Misc commands.
|
124
|
+
//
|
125
|
+
|
126
|
+
export const deleteItem: Command = (view: EditorView) => {
|
127
|
+
const tree = view.state.facet(treeFacet);
|
128
|
+
const pos = getSelection(view.state).from;
|
129
|
+
const current = tree.find(pos);
|
130
|
+
if (current) {
|
131
|
+
view.dispatch({
|
132
|
+
selection: EditorSelection.cursor(current.lineRange.from),
|
133
|
+
changes: [
|
134
|
+
{
|
135
|
+
from: current.lineRange.from,
|
136
|
+
to: Math.min(current.lineRange.to + 1, view.state.doc.length),
|
137
|
+
},
|
138
|
+
],
|
139
|
+
});
|
140
|
+
}
|
141
|
+
|
142
|
+
return true;
|
143
|
+
};
|
144
|
+
|
145
|
+
export const toggleTask: Command = (view: EditorView) => {
|
146
|
+
const tree = view.state.facet(treeFacet);
|
147
|
+
const pos = getSelection(view.state)?.from;
|
148
|
+
const current = tree.find(pos);
|
149
|
+
if (current) {
|
150
|
+
const type = current.type === 'task' ? 'bullet' : 'task';
|
151
|
+
const indent = ' '.repeat(getIndentUnit(view.state) * current.level);
|
152
|
+
view.dispatch({
|
153
|
+
changes: [
|
154
|
+
{
|
155
|
+
from: current.lineRange.from,
|
156
|
+
to: current.contentRange.from,
|
157
|
+
insert: indent + (type === 'task' ? '- [ ] ' : '- '),
|
158
|
+
},
|
159
|
+
],
|
160
|
+
});
|
161
|
+
}
|
162
|
+
|
163
|
+
return true;
|
164
|
+
};
|
165
|
+
|
166
|
+
export const commands = (): Extension =>
|
167
|
+
keymap.of([
|
168
|
+
//
|
169
|
+
// Indentation.
|
170
|
+
//
|
171
|
+
{
|
172
|
+
key: 'Tab',
|
173
|
+
preventDefault: true,
|
174
|
+
run: indentItemMore,
|
175
|
+
shift: indentItemLess,
|
176
|
+
},
|
177
|
+
|
178
|
+
//
|
179
|
+
// Continuation.
|
180
|
+
//
|
181
|
+
{
|
182
|
+
key: 'Enter',
|
183
|
+
shift: (view) => {
|
184
|
+
const pos = getSelection(view.state).from;
|
185
|
+
const insert = '\n '; // TODO(burdon): Fix parsing.
|
186
|
+
view.dispatch({
|
187
|
+
changes: [{ from: pos, to: pos, insert }],
|
188
|
+
selection: EditorSelection.cursor(pos + insert.length),
|
189
|
+
});
|
190
|
+
return true;
|
191
|
+
},
|
192
|
+
},
|
193
|
+
|
194
|
+
//
|
195
|
+
// Navigation.
|
196
|
+
//
|
197
|
+
{
|
198
|
+
key: 'ArrowDown',
|
199
|
+
// Jump to next item (default moves to end of currentline).
|
200
|
+
run: (view) => {
|
201
|
+
const tree = view.state.facet(treeFacet);
|
202
|
+
const item = tree.find(getSelection(view.state).from);
|
203
|
+
if (
|
204
|
+
item &&
|
205
|
+
view.state.doc.lineAt(item.lineRange.to).number - view.state.doc.lineAt(item.lineRange.from).number === 0
|
206
|
+
) {
|
207
|
+
const next = tree.next(item);
|
208
|
+
if (next) {
|
209
|
+
view.dispatch({ selection: EditorSelection.cursor(next.contentRange.from) });
|
210
|
+
return true;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
return false;
|
215
|
+
},
|
216
|
+
},
|
217
|
+
|
218
|
+
//
|
219
|
+
// Line selection.
|
220
|
+
// TODO(burdon): Shortcut to select current item?
|
221
|
+
//
|
222
|
+
{
|
223
|
+
key: 'Mod-a',
|
224
|
+
preventDefault: true,
|
225
|
+
run: selectAll,
|
226
|
+
},
|
227
|
+
{
|
228
|
+
key: 'Escape',
|
229
|
+
preventDefault: true,
|
230
|
+
run: selectNone,
|
231
|
+
},
|
232
|
+
{
|
233
|
+
key: 'ArrowUp',
|
234
|
+
shift: selectUp,
|
235
|
+
},
|
236
|
+
{
|
237
|
+
key: 'ArrowDown',
|
238
|
+
shift: selectDown,
|
239
|
+
},
|
240
|
+
|
241
|
+
//
|
242
|
+
// Move.
|
243
|
+
//
|
244
|
+
{
|
245
|
+
key: 'Alt-ArrowDown',
|
246
|
+
preventDefault: true,
|
247
|
+
run: moveItemDown,
|
248
|
+
},
|
249
|
+
{
|
250
|
+
key: 'Alt-ArrowUp',
|
251
|
+
preventDefault: true,
|
252
|
+
run: moveItemUp,
|
253
|
+
},
|
254
|
+
//
|
255
|
+
// Delete.
|
256
|
+
//
|
257
|
+
{
|
258
|
+
key: 'Mod-Backspace',
|
259
|
+
preventDefault: true,
|
260
|
+
run: deleteItem,
|
261
|
+
},
|
262
|
+
//
|
263
|
+
// Misc.
|
264
|
+
//
|
265
|
+
{
|
266
|
+
key: 'Alt-t',
|
267
|
+
preventDefault: true,
|
268
|
+
run: toggleTask,
|
269
|
+
},
|
270
|
+
]);
|
@@ -0,0 +1,33 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2025 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
6
|
+
import { EditorSelection, EditorState } from '@codemirror/state';
|
7
|
+
import { describe, test } from 'vitest';
|
8
|
+
|
9
|
+
import { editor } from './editor';
|
10
|
+
import { outlinerTree, treeFacet } from './tree';
|
11
|
+
|
12
|
+
const extensions = [markdown({ base: markdownLanguage }), outlinerTree(), editor()];
|
13
|
+
|
14
|
+
describe('editor', () => {
|
15
|
+
test('empty', ({ expect }) => {
|
16
|
+
const state = EditorState.create({ extensions });
|
17
|
+
const tree = state.facet(treeFacet);
|
18
|
+
expect(tree).to.exist;
|
19
|
+
});
|
20
|
+
|
21
|
+
test('prevent moving out of range', ({ expect }) => {
|
22
|
+
const state = EditorState.create({ doc: '- [ ] ', extensions });
|
23
|
+
const spec = state.update({ selection: EditorSelection.cursor(1) });
|
24
|
+
expect(spec.state.selection.ranges[0].from).to.eq(6);
|
25
|
+
});
|
26
|
+
|
27
|
+
test.skip('prevent deleting task marker', ({ expect }) => {
|
28
|
+
const state = EditorState.create({ doc: '- [ ] ', extensions });
|
29
|
+
state.update({ selection: EditorSelection.cursor(6) });
|
30
|
+
const spec = state.update({ changes: { from: 5, to: 6 } });
|
31
|
+
expect(spec.state.doc.toString()).to.eq('- [ ] ');
|
32
|
+
});
|
33
|
+
});
|