@dxos/react-ui-editor 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d
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 +2979 -2478
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +2979 -2478
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +37 -21
- 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.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 +4 -4
- 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 +7 -6
- 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 +8 -3
- package/dist/types/src/extensions/index.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/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/xml-tags.d.ts +41 -16
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +3 -7
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts +3 -3
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
- package/dist/types/src/stories/Markdown.stories.d.ts +3 -3
- 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 -5
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +4 -3
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +3 -4
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +11 -8
- 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/util/debug.d.ts +4 -0
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +44 -46
- package/src/components/Editor/Editor.stories.tsx +89 -0
- package/src/components/Editor/Editor.tsx +157 -57
- 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 -92
- 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-menu → autocomplete}/placeholder.ts +21 -17
- package/src/extensions/{command-dialog → autocomplete}/typeahead.ts +6 -48
- package/src/extensions/automerge/automerge.stories.tsx +4 -3
- 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 +75 -69
- package/src/extensions/awareness/awareness-provider.ts +12 -12
- 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 +22 -24
- package/src/extensions/folding.tsx +1 -2
- package/src/extensions/index.ts +8 -3
- package/src/extensions/listener.ts +14 -20
- package/src/extensions/markdown/bundle.ts +14 -2
- package/src/extensions/markdown/decorate.ts +8 -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/{floating-menu.ts → outliner/menu.ts} +7 -5
- 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 +1 -1
- 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/xml-tags.ts +310 -203
- package/src/extensions/typewriter.ts +1 -1
- package/src/hooks/useTextEditor.ts +25 -25
- package/src/stories/CommandDialog.stories.tsx +13 -19
- package/src/stories/Comments.stories.tsx +3 -4
- package/src/stories/EditorToolbar.stories.tsx +8 -8
- package/src/stories/Experimental.stories.tsx +1 -1
- package/src/stories/Markdown.stories.tsx +1 -1
- package/src/stories/Outliner.stories.tsx +15 -15
- package/src/stories/Popover.stories.tsx +162 -0
- package/src/stories/Preview.stories.tsx +41 -37
- package/src/stories/Tags.stories.tsx +19 -5
- package/src/stories/TextEditor.stories.tsx +4 -30
- package/src/stories/components/EditorStory.tsx +31 -20
- package/src/stories/components/util.tsx +40 -8
- package/src/styles/markdown.ts +1 -1
- package/src/styles/theme.ts +2 -1
- package/src/util/debug.ts +5 -0
- package/dist/lib/browser/testing/index.mjs +0 -76
- package/dist/lib/browser/testing/index.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-dialog/action.d.ts +0 -17
- package/dist/types/src/extensions/command-dialog/action.d.ts.map +0 -1
- package/dist/types/src/extensions/command-dialog/command-dialog.d.ts +0 -6
- package/dist/types/src/extensions/command-dialog/command-dialog.d.ts.map +0 -1
- package/dist/types/src/extensions/command-dialog/hint.d.ts +0 -19
- package/dist/types/src/extensions/command-dialog/hint.d.ts.map +0 -1
- package/dist/types/src/extensions/command-dialog/index.d.ts +0 -4
- package/dist/types/src/extensions/command-dialog/index.d.ts.map +0 -1
- package/dist/types/src/extensions/command-dialog/state.d.ts +0 -16
- package/dist/types/src/extensions/command-dialog/state.d.ts.map +0 -1
- package/dist/types/src/extensions/command-dialog/typeahead.d.ts +0 -22
- package/dist/types/src/extensions/command-dialog/typeahead.d.ts.map +0 -1
- package/dist/types/src/extensions/command-menu/command-menu.d.ts +0 -20
- package/dist/types/src/extensions/command-menu/command-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command-menu/index.d.ts +0 -3
- package/dist/types/src/extensions/command-menu/index.d.ts.map +0 -1
- package/dist/types/src/extensions/command-menu/placeholder.d.ts +0 -10
- package/dist/types/src/extensions/command-menu/placeholder.d.ts.map +0 -1
- package/dist/types/src/extensions/command-menu/useCommandMenu.d.ts +0 -24
- package/dist/types/src/extensions/command-menu/useCommandMenu.d.ts.map +0 -1
- package/dist/types/src/extensions/floating-menu.d.ts +0 -7
- package/dist/types/src/extensions/floating-menu.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/src/components/CommandMenu/CommandMenu.tsx +0 -348
- package/src/components/CommandMenu/index.ts +0 -5
- package/src/components/EditorToolbar/util.ts +0 -76
- package/src/extensions/command-dialog/action.ts +0 -55
- package/src/extensions/command-dialog/command-dialog.ts +0 -34
- package/src/extensions/command-dialog/hint.ts +0 -103
- package/src/extensions/command-dialog/index.ts +0 -7
- package/src/extensions/command-dialog/state.ts +0 -90
- package/src/extensions/command-menu/command-menu.ts +0 -210
- package/src/extensions/command-menu/index.ts +0 -6
- package/src/extensions/command-menu/useCommandMenu.ts +0 -134
- 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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
6
|
+
import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
const paragraphBlockPlugin = ViewPlugin.fromClass(
|
|
11
|
+
class {
|
|
12
|
+
decorations: DecorationSet;
|
|
13
|
+
|
|
14
|
+
constructor(view: EditorView) {
|
|
15
|
+
this.decorations = this.build(view);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
update(update: ViewUpdate) {
|
|
19
|
+
if (update.docChanged || update.viewportChanged) {
|
|
20
|
+
this.decorations = this.build(update.view);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
build({ state }: EditorView) {
|
|
25
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
26
|
+
|
|
27
|
+
// Helper: commit a block from blockStart to endLine (inclusive).
|
|
28
|
+
const pushBlock = (fromLine: number, toLine: number) => {
|
|
29
|
+
// Add line decorations for each line in the block.
|
|
30
|
+
for (let lineNum = fromLine; lineNum <= toLine; lineNum++) {
|
|
31
|
+
const line = state.doc.line(lineNum);
|
|
32
|
+
builder.add(
|
|
33
|
+
line.from,
|
|
34
|
+
line.from,
|
|
35
|
+
Decoration.line({
|
|
36
|
+
class: mx(
|
|
37
|
+
'block-line',
|
|
38
|
+
fromLine === toLine && 'block-single',
|
|
39
|
+
lineNum === fromLine && 'block-first',
|
|
40
|
+
lineNum > fromLine && lineNum < toLine && 'block-middle',
|
|
41
|
+
lineNum === toLine && 'block-last',
|
|
42
|
+
),
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let blockStart: number | null = null;
|
|
49
|
+
let consecutiveBlankLines = 0;
|
|
50
|
+
const totalLines = state.doc.lines;
|
|
51
|
+
for (let i = 1; i <= totalLines; i++) {
|
|
52
|
+
const line = state.doc.line(i);
|
|
53
|
+
const isBlank = /^\s*$/.test(line.text);
|
|
54
|
+
|
|
55
|
+
if (!isBlank) {
|
|
56
|
+
// Reset blank line counter.
|
|
57
|
+
consecutiveBlankLines = 0;
|
|
58
|
+
// Start a new block if we're not already in one.
|
|
59
|
+
if (blockStart === null) {
|
|
60
|
+
blockStart = i;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// Increment blank line counter.
|
|
64
|
+
consecutiveBlankLines++;
|
|
65
|
+
|
|
66
|
+
// End the current block if we have 2+ consecutive blank lines.
|
|
67
|
+
if (consecutiveBlankLines >= 2 && blockStart !== null) {
|
|
68
|
+
pushBlock(blockStart, i - consecutiveBlankLines);
|
|
69
|
+
blockStart = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle any remaining block at the end of the document.
|
|
75
|
+
if (blockStart !== null) {
|
|
76
|
+
// Find the last non-blank line for the block end.
|
|
77
|
+
let lastNonBlankLine = totalLines;
|
|
78
|
+
while (lastNonBlankLine >= blockStart) {
|
|
79
|
+
const line = state.doc.line(lastNonBlankLine);
|
|
80
|
+
if (!/^\s*$/.test(line.text)) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
lastNonBlankLine--;
|
|
84
|
+
}
|
|
85
|
+
if (lastNonBlankLine >= blockStart) {
|
|
86
|
+
pushBlock(blockStart, lastNonBlankLine);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return builder.finish();
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
decorations: (v) => v.decorations,
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
export const blocks = () => [
|
|
99
|
+
paragraphBlockPlugin,
|
|
100
|
+
EditorView.baseTheme({
|
|
101
|
+
'.cm-line.block-line': {
|
|
102
|
+
paddingLeft: '0.75rem',
|
|
103
|
+
paddingRight: '0.75rem',
|
|
104
|
+
borderLeft: '1px solid var(--dx-subduedSeparator)',
|
|
105
|
+
borderRight: '1px solid var(--dx-subduedSeparator)',
|
|
106
|
+
},
|
|
107
|
+
'.cm-line.block-single': {
|
|
108
|
+
border: '1px solid var(--dx-subduedSeparator)',
|
|
109
|
+
borderRadius: '6px',
|
|
110
|
+
paddingTop: '0.5rem',
|
|
111
|
+
paddingBottom: '0.5rem',
|
|
112
|
+
marginTop: '0.5rem',
|
|
113
|
+
marginBottom: '0.5rem',
|
|
114
|
+
},
|
|
115
|
+
'.cm-line.block-first': {
|
|
116
|
+
borderTop: '1px solid var(--dx-subduedSeparator)',
|
|
117
|
+
borderTopLeftRadius: '6px',
|
|
118
|
+
borderTopRightRadius: '6px',
|
|
119
|
+
paddingTop: '0.5rem',
|
|
120
|
+
marginTop: '0.5rem',
|
|
121
|
+
},
|
|
122
|
+
'.cm-line.block-middle': {},
|
|
123
|
+
'.cm-line.block-last': {
|
|
124
|
+
borderBottom: '1px solid var(--dx-subduedSeparator)',
|
|
125
|
+
borderBottomLeftRadius: '6px',
|
|
126
|
+
borderBottomRightRadius: '6px',
|
|
127
|
+
paddingBottom: '0.5rem',
|
|
128
|
+
marginBottom: '0.5rem',
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Extension, Prec, StateEffect, StateField } from '@codemirror/state';
|
|
6
|
+
import { keymap } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
type Bookmark = {
|
|
9
|
+
id: string;
|
|
10
|
+
pos: number;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const addBookmark = StateEffect.define<Bookmark>();
|
|
15
|
+
export const removeBookmark = StateEffect.define<string>();
|
|
16
|
+
export const clearBookmarks = StateEffect.define<void>();
|
|
17
|
+
|
|
18
|
+
export const bookmarks = (): Extension => {
|
|
19
|
+
return [
|
|
20
|
+
bookmarksField,
|
|
21
|
+
Prec.highest(
|
|
22
|
+
keymap.of([
|
|
23
|
+
{
|
|
24
|
+
key: 'Mod-ArrowUp',
|
|
25
|
+
run: (view) => {
|
|
26
|
+
const bookmarks = view.state.field(bookmarksField);
|
|
27
|
+
console.log('up', bookmarks);
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'Mod-ArrowDown',
|
|
33
|
+
run: (view) => {
|
|
34
|
+
const bookmarks = view.state.field(bookmarksField);
|
|
35
|
+
console.log('down', bookmarks);
|
|
36
|
+
return true;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
]),
|
|
40
|
+
),
|
|
41
|
+
];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type BookmarkFieldState = {
|
|
45
|
+
bookmarks: Bookmark[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const bookmarksField = StateField.define<BookmarkFieldState>({
|
|
49
|
+
create: (): BookmarkFieldState => ({
|
|
50
|
+
bookmarks: [],
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
update: (value: BookmarkFieldState, tr): BookmarkFieldState => {
|
|
54
|
+
// Map bookmark positions through document changes.
|
|
55
|
+
let bookmarks = value.bookmarks.map((bookmark) => ({
|
|
56
|
+
...bookmark,
|
|
57
|
+
pos: tr.changes.mapPos(bookmark.pos),
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Process effects.
|
|
61
|
+
for (const effect of tr.effects) {
|
|
62
|
+
if (effect.is(addBookmark)) {
|
|
63
|
+
bookmarks = [...bookmarks, effect.value];
|
|
64
|
+
} else if (effect.is(removeBookmark)) {
|
|
65
|
+
bookmarks = bookmarks.filter((b) => b.id !== effect.value);
|
|
66
|
+
} else if (effect.is(clearBookmarks)) {
|
|
67
|
+
bookmarks = [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Sort bookmarks by position.
|
|
72
|
+
bookmarks.sort(({ pos: a }, { pos: b }) => a - b);
|
|
73
|
+
return { bookmarks };
|
|
74
|
+
},
|
|
75
|
+
});
|
|
@@ -104,13 +104,14 @@ export const commentsState = StateField.define<CommentsState>({
|
|
|
104
104
|
const styles = EditorView.theme({
|
|
105
105
|
'.cm-comment, .cm-comment-current': {
|
|
106
106
|
padding: '3px 0',
|
|
107
|
+
color: 'var(--dx-cmCommentText)',
|
|
107
108
|
backgroundColor: 'var(--dx-cmCommentSurface)',
|
|
108
109
|
},
|
|
109
110
|
'.cm-comment > span, .cm-comment-current > span': {
|
|
110
111
|
boxDecorationBreak: 'clone',
|
|
111
112
|
boxShadow: '0 0 1px 3px var(--dx-cmCommentSurface)',
|
|
112
113
|
backgroundColor: 'var(--dx-cmCommentSurface)',
|
|
113
|
-
color: 'var(--dx-
|
|
114
|
+
color: 'var(--dx-cmCommentText)',
|
|
114
115
|
cursor: 'pointer',
|
|
115
116
|
},
|
|
116
117
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
6
|
import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
7
|
-
import { bracketMatching } from '@codemirror/language';
|
|
7
|
+
import { HighlightStyle, bracketMatching, syntaxHighlighting } from '@codemirror/language';
|
|
8
8
|
import { searchKeymap } from '@codemirror/search';
|
|
9
9
|
import { type ChangeSpec, EditorState, type Extension, type TransactionSpec } from '@codemirror/state';
|
|
10
10
|
import {
|
|
@@ -19,14 +19,15 @@ import {
|
|
|
19
19
|
placeholder,
|
|
20
20
|
scrollPastEnd,
|
|
21
21
|
} from '@codemirror/view';
|
|
22
|
-
import {
|
|
22
|
+
import { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';
|
|
23
23
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
24
24
|
import merge from 'lodash.merge';
|
|
25
25
|
|
|
26
26
|
import { generateName } from '@dxos/display-name';
|
|
27
|
+
import { type DocAccessor } from '@dxos/echo-db';
|
|
27
28
|
import { log } from '@dxos/log';
|
|
28
|
-
import { type
|
|
29
|
-
import { type Identity } from '@dxos/
|
|
29
|
+
import { type Messenger } from '@dxos/protocols';
|
|
30
|
+
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
30
31
|
import { type ThemeMode } from '@dxos/react-ui';
|
|
31
32
|
import { type HuePalette } from '@dxos/react-ui-theme';
|
|
32
33
|
import { hexToHue, isTruthy } from '@dxos/util';
|
|
@@ -44,7 +45,9 @@ import { focus } from './focus';
|
|
|
44
45
|
|
|
45
46
|
export const filterChars = (chars: RegExp) => {
|
|
46
47
|
return EditorState.transactionFilter.of((transaction) => {
|
|
47
|
-
if (!transaction.docChanged)
|
|
48
|
+
if (!transaction.docChanged) {
|
|
49
|
+
return transaction;
|
|
50
|
+
}
|
|
48
51
|
|
|
49
52
|
const changes: ChangeSpec[] = [];
|
|
50
53
|
transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
@@ -62,6 +65,7 @@ export const filterChars = (chars: RegExp) => {
|
|
|
62
65
|
if (changes.length) {
|
|
63
66
|
return [transaction, { changes, sequential: true } as TransactionSpec];
|
|
64
67
|
}
|
|
68
|
+
|
|
65
69
|
return transaction;
|
|
66
70
|
});
|
|
67
71
|
};
|
|
@@ -87,7 +91,6 @@ export type BasicExtensionsOptions = {
|
|
|
87
91
|
lineNumbers?: boolean;
|
|
88
92
|
/** If false then do not set a max-width or side margin on the editor. */
|
|
89
93
|
lineWrapping?: boolean;
|
|
90
|
-
monospace?: boolean;
|
|
91
94
|
placeholder?: string;
|
|
92
95
|
/** If true user cannot edit the text, but they can still select and copy it. */
|
|
93
96
|
readOnly?: boolean;
|
|
@@ -136,7 +139,6 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
136
139
|
props.history && history(),
|
|
137
140
|
props.lineNumbers && [lineNumbers(), editorGutter],
|
|
138
141
|
props.lineWrapping && EditorView.lineWrapping,
|
|
139
|
-
props.monospace && editorMonospace,
|
|
140
142
|
props.placeholder && placeholder(props.placeholder),
|
|
141
143
|
props.readOnly !== undefined && EditorState.readOnly.of(props.readOnly),
|
|
142
144
|
props.scrollPastEnd && scrollPastEnd(),
|
|
@@ -173,6 +175,7 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
173
175
|
|
|
174
176
|
export type ThemeExtensionsOptions = {
|
|
175
177
|
themeMode?: ThemeMode;
|
|
178
|
+
monospace?: boolean;
|
|
176
179
|
styles?: ThemeStyles;
|
|
177
180
|
syntaxHighlighting?: boolean;
|
|
178
181
|
slots?: {
|
|
@@ -180,6 +183,7 @@ export type ThemeExtensionsOptions = {
|
|
|
180
183
|
className?: string;
|
|
181
184
|
};
|
|
182
185
|
scroll?: {
|
|
186
|
+
// NOTE: Do not apply vertical padding to scroll container.
|
|
183
187
|
className?: string;
|
|
184
188
|
};
|
|
185
189
|
content?: {
|
|
@@ -190,7 +194,7 @@ export type ThemeExtensionsOptions = {
|
|
|
190
194
|
|
|
191
195
|
export const grow: ThemeExtensionsOptions['slots'] = {
|
|
192
196
|
editor: {
|
|
193
|
-
className: '
|
|
197
|
+
className: 'bs-full is-full',
|
|
194
198
|
},
|
|
195
199
|
} as const;
|
|
196
200
|
|
|
@@ -202,13 +206,6 @@ export const fullWidth: ThemeExtensionsOptions['slots'] = {
|
|
|
202
206
|
|
|
203
207
|
export const defaultThemeSlots = grow;
|
|
204
208
|
|
|
205
|
-
const semanticTokensSettings = {
|
|
206
|
-
settings: {
|
|
207
|
-
background: 'var(--dx-baseSurface)',
|
|
208
|
-
foreground: 'var(--dx-baseText)',
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
|
|
212
209
|
export const defaultStyles = {
|
|
213
210
|
dark: vscodeDarkStyle,
|
|
214
211
|
light: vscodeLightStyle,
|
|
@@ -219,17 +216,18 @@ export const defaultStyles = {
|
|
|
219
216
|
*/
|
|
220
217
|
export const createThemeExtensions = ({
|
|
221
218
|
themeMode,
|
|
219
|
+
monospace,
|
|
222
220
|
styles,
|
|
223
|
-
syntaxHighlighting:
|
|
221
|
+
syntaxHighlighting: syntaxHighlightingProp,
|
|
224
222
|
slots: slotsParam,
|
|
225
223
|
}: ThemeExtensionsOptions = {}): Extension => {
|
|
226
224
|
const slots = defaultsDeep({}, slotsParam, defaultThemeSlots);
|
|
227
225
|
return [
|
|
228
226
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
229
227
|
EditorView.baseTheme(styles ? merge({}, defaultTheme, styles) : defaultTheme),
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
monospace && editorMonospace,
|
|
229
|
+
syntaxHighlightingProp &&
|
|
230
|
+
syntaxHighlighting(HighlightStyle.define(themeMode === 'dark' ? defaultStyles.dark : defaultStyles.light)),
|
|
233
231
|
slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
|
|
234
232
|
slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
|
|
235
233
|
slots.scroll?.className &&
|
|
@@ -250,24 +248,24 @@ export const createThemeExtensions = ({
|
|
|
250
248
|
export type DataExtensionsProps<T> = {
|
|
251
249
|
id: string;
|
|
252
250
|
text?: DocAccessor<T>;
|
|
253
|
-
|
|
251
|
+
messenger?: Messenger;
|
|
254
252
|
identity?: Identity | null;
|
|
255
253
|
};
|
|
256
254
|
|
|
257
|
-
// TODO(burdon): Move out of react-ui-editor
|
|
258
|
-
export const createDataExtensions = <T>({ id, text,
|
|
255
|
+
// TODO(burdon): Move out of react-ui-editor?
|
|
256
|
+
export const createDataExtensions = <T>({ id, text, messenger, identity }: DataExtensionsProps<T>): Extension[] => {
|
|
259
257
|
const extensions: Extension[] = [];
|
|
260
258
|
if (text) {
|
|
261
259
|
extensions.push(automerge(text));
|
|
262
260
|
}
|
|
263
261
|
|
|
264
|
-
if (
|
|
262
|
+
if (messenger && identity) {
|
|
265
263
|
const peerId = identity?.identityKey.toHex();
|
|
266
264
|
const hue = (identity?.profile?.data?.hue as HuePalette | undefined) ?? hexToHue(peerId ?? '0');
|
|
267
265
|
extensions.push(
|
|
268
266
|
awareness(
|
|
269
267
|
new SpaceAwarenessProvider({
|
|
270
|
-
|
|
268
|
+
messenger,
|
|
271
269
|
channel: `awareness.${id}`,
|
|
272
270
|
peerId: identity.identityKey.toHex(),
|
|
273
271
|
info: {
|
|
@@ -25,8 +25,7 @@ export const folding = (_props: FoldingOptions = {}): Extension => [
|
|
|
25
25
|
foldGutter({
|
|
26
26
|
markerDOM: (open) => {
|
|
27
27
|
return renderRoot(
|
|
28
|
-
Domino.of('div').classNames('flex
|
|
29
|
-
// TODO(burdon): Use sprite directly.
|
|
28
|
+
Domino.of('div').classNames('flex bs-full items-center').build(),
|
|
30
29
|
<Icon icon='ph--caret-right--bold' size={3} classNames={['mx-3 cursor-pointer', open && 'rotate-90']} />,
|
|
31
30
|
);
|
|
32
31
|
},
|
package/src/extensions/index.ts
CHANGED
|
@@ -8,13 +8,12 @@ export * from './autoscroll';
|
|
|
8
8
|
export * from './automerge';
|
|
9
9
|
export * from './awareness';
|
|
10
10
|
export * from './blast';
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './
|
|
11
|
+
export * from './blocks';
|
|
12
|
+
export * from './bookmarks';
|
|
13
13
|
export * from './comments';
|
|
14
14
|
export * from './debug';
|
|
15
15
|
export * from './dnd';
|
|
16
16
|
export * from './factories';
|
|
17
|
-
export * from './floating-menu';
|
|
18
17
|
export * from './focus';
|
|
19
18
|
export * from './folding';
|
|
20
19
|
export * from './hashtag';
|
|
@@ -22,9 +21,15 @@ export * from './json';
|
|
|
22
21
|
export * from './listener';
|
|
23
22
|
export * from './markdown';
|
|
24
23
|
export * from './mention';
|
|
24
|
+
export * from './modal';
|
|
25
25
|
export * from './modes';
|
|
26
26
|
export * from './outliner';
|
|
27
27
|
export * from './preview';
|
|
28
|
+
export * from './replacer';
|
|
28
29
|
export * from './selection';
|
|
30
|
+
export * from './scrolling';
|
|
31
|
+
export * from './state';
|
|
32
|
+
export * from './submit';
|
|
33
|
+
export * from './tab';
|
|
29
34
|
export * from './tags';
|
|
30
35
|
export * from './typewriter';
|
|
@@ -5,34 +5,28 @@
|
|
|
5
5
|
import { type Extension } from '@codemirror/state';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
7
|
|
|
8
|
+
import { isNonNullable } from '@dxos/util';
|
|
9
|
+
|
|
8
10
|
import { documentId } from './selection';
|
|
9
11
|
|
|
10
12
|
export type ListenerOptions = {
|
|
11
|
-
onFocus?: (focusing: boolean) => void;
|
|
12
|
-
onChange?: (
|
|
13
|
+
onFocus?: (event: { id: string; focusing: boolean }) => void;
|
|
14
|
+
onChange?: (event: { id: string; text: string }) => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
/**
|
|
16
|
-
* Event listener.
|
|
17
|
-
* @deprecated Use EditorView.updateListener and listen for specific update events.
|
|
18
|
-
*/
|
|
19
17
|
export const listener = ({ onFocus, onChange }: ListenerOptions): Extension => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
EditorView.focusChangeEffect.of((_, focusing) => {
|
|
25
|
-
onFocus(focusing);
|
|
18
|
+
return [
|
|
19
|
+
onFocus &&
|
|
20
|
+
EditorView.focusChangeEffect.of((state, focusing) => {
|
|
21
|
+
onFocus({ id: state.facet(documentId), focusing });
|
|
26
22
|
return null;
|
|
27
23
|
}),
|
|
28
|
-
);
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
onChange &&
|
|
26
|
+
EditorView.updateListener.of(({ state, docChanged }) => {
|
|
27
|
+
if (docChanged) {
|
|
28
|
+
onChange({ id: state.facet(documentId), text: state.doc.toString() });
|
|
29
|
+
}
|
|
34
30
|
}),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return extensions;
|
|
31
|
+
].filter(isNonNullable);
|
|
38
32
|
};
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { completionKeymap } from '@codemirror/autocomplete';
|
|
6
6
|
import { defaultKeymap, indentWithTab } from '@codemirror/commands';
|
|
7
|
+
import { jsonLanguage } from '@codemirror/lang-json';
|
|
7
8
|
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
|
8
|
-
import {
|
|
9
|
+
import { xml } from '@codemirror/lang-xml';
|
|
10
|
+
import { LanguageDescription, syntaxHighlighting } from '@codemirror/language';
|
|
9
11
|
import { languages } from '@codemirror/language-data';
|
|
10
12
|
import { type Extension } from '@codemirror/state';
|
|
11
13
|
import { keymap } from '@codemirror/view';
|
|
@@ -43,6 +45,7 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
|
|
|
43
45
|
base: markdownLanguage,
|
|
44
46
|
|
|
45
47
|
// Languages for syntax highlighting fenced code blocks.
|
|
48
|
+
defaultCodeLanguage: jsonLanguage,
|
|
46
49
|
codeLanguages: languages,
|
|
47
50
|
|
|
48
51
|
// Don't complete HTML tags.
|
|
@@ -66,12 +69,21 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
|
|
|
66
69
|
|
|
67
70
|
// https://codemirror.net/docs/ref/#commands.defaultKeymap
|
|
68
71
|
...defaultKeymap,
|
|
72
|
+
|
|
73
|
+
// TODO(burdon): Remove?
|
|
69
74
|
...completionKeymap,
|
|
70
75
|
].filter(isTruthy),
|
|
71
76
|
),
|
|
72
77
|
];
|
|
73
78
|
};
|
|
74
79
|
|
|
80
|
+
const xmlLanguageDesc = LanguageDescription.of({
|
|
81
|
+
name: 'xml',
|
|
82
|
+
alias: ['html', 'xhtml'],
|
|
83
|
+
extensions: ['xml', 'xhtml'],
|
|
84
|
+
load: async () => xml(),
|
|
85
|
+
});
|
|
86
|
+
|
|
75
87
|
/**
|
|
76
88
|
* Default customizations.
|
|
77
89
|
* https://github.com/lezer-parser/markdown/blob/main/src/markdown.ts
|
|
@@ -89,5 +101,5 @@ const noSetExtHeading: MarkdownConfig = {
|
|
|
89
101
|
* Remove HTML and XML parsing.
|
|
90
102
|
*/
|
|
91
103
|
const noHtml: MarkdownConfig = {
|
|
92
|
-
remove: ['HTMLBlock', 'HTMLTag'],
|
|
104
|
+
// remove: ['HTMLBlock', 'HTMLTag'],
|
|
93
105
|
};
|
|
@@ -52,12 +52,12 @@ class LinkButton extends WidgetType {
|
|
|
52
52
|
super();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
override eq(other: this)
|
|
55
|
+
override eq(other: this) {
|
|
56
56
|
return this.url === other.url;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// TODO(burdon): Create icon and link directly without react?
|
|
60
|
-
override toDOM(view: EditorView)
|
|
60
|
+
override toDOM(view: EditorView) {
|
|
61
61
|
const el = document.createElement('span');
|
|
62
62
|
this.render(el, { url: this.url }, view);
|
|
63
63
|
return el;
|
|
@@ -69,11 +69,15 @@ class CheckboxWidget extends WidgetType {
|
|
|
69
69
|
super();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
override eq(other: this)
|
|
72
|
+
override eq(other: this) {
|
|
73
73
|
return this._checked === other._checked;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
override
|
|
76
|
+
override ignoreEvent() {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override toDOM(view: EditorView) {
|
|
77
81
|
const input = document.createElement('input');
|
|
78
82
|
input.className = 'cm-task-checkbox dx-checkbox';
|
|
79
83
|
input.type = 'checkbox';
|
|
@@ -105,10 +109,6 @@ class CheckboxWidget extends WidgetType {
|
|
|
105
109
|
span.appendChild(input);
|
|
106
110
|
return span;
|
|
107
111
|
}
|
|
108
|
-
|
|
109
|
-
override ignoreEvent(): boolean {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
class TextWidget extends WidgetType {
|
|
@@ -15,12 +15,8 @@ import {
|
|
|
15
15
|
} from '@codemirror/state';
|
|
16
16
|
import { EditorView, type ViewUpdate, keymap } from '@codemirror/view';
|
|
17
17
|
import { type SyntaxNode, type SyntaxNodeRef } from '@lezer/common';
|
|
18
|
-
import { useCallback, useMemo } from 'react';
|
|
19
18
|
|
|
20
19
|
import { debounceAndThrottle } from '@dxos/async';
|
|
21
|
-
import { type Live } from '@dxos/live-object';
|
|
22
|
-
|
|
23
|
-
import { type EditorToolbarState } from '../../components';
|
|
24
20
|
|
|
25
21
|
// Markdown refs:
|
|
26
22
|
// https://github.github.com/gfm
|
|
@@ -1251,17 +1247,19 @@ export const getFormatting = (state: EditorState): Formatting => {
|
|
|
1251
1247
|
/**
|
|
1252
1248
|
* Hook provides an extension to compute the current formatting state.
|
|
1253
1249
|
*/
|
|
1254
|
-
export const
|
|
1255
|
-
|
|
1250
|
+
export const formattingListener = (stateProvider: () => Formatting | undefined, delay = 100): Extension => {
|
|
1251
|
+
return EditorView.updateListener.of(
|
|
1256
1252
|
debounceAndThrottle((update: ViewUpdate) => {
|
|
1257
1253
|
if (update.docChanged || update.selectionSet) {
|
|
1254
|
+
const state = stateProvider();
|
|
1255
|
+
if (!state) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1258
1259
|
Object.entries(getFormatting(update.state)).forEach(([key, active]) => {
|
|
1259
1260
|
state[key as keyof Formatting] = active as any;
|
|
1260
1261
|
});
|
|
1261
1262
|
}
|
|
1262
|
-
},
|
|
1263
|
-
[state],
|
|
1263
|
+
}, delay),
|
|
1264
1264
|
);
|
|
1265
|
-
|
|
1266
|
-
return useMemo(() => EditorView.updateListener.of(handleUpdate), [handleUpdate]);
|
|
1267
1265
|
};
|
|
@@ -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
|
+
});
|