@dxos/react-ui-editor 0.8.4-main.c1de068 → 0.8.4-main.e098934
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 +2074 -996
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +71 -1
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/index.mjs +2074 -996
- 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 +71 -1
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/components/{Popover → CommandMenu}/CommandMenu.d.ts +10 -6
- package/dist/types/src/components/CommandMenu/CommandMenu.d.ts.map +1 -0
- package/dist/types/src/components/CommandMenu/index.d.ts +2 -0
- package/dist/types/src/components/CommandMenu/index.d.ts.map +1 -0
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/util.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts +20 -7
- 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 +9 -18
- package/dist/types/src/extensions/automerge/automerge.stories.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.map +1 -1
- package/dist/types/src/extensions/autoscroll.d.ts +10 -0
- package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
- package/dist/types/src/extensions/command/action.d.ts +1 -1
- package/dist/types/src/extensions/command/action.d.ts.map +1 -1
- package/dist/types/src/extensions/command/command-menu.d.ts +1 -1
- package/dist/types/src/extensions/command/command-menu.d.ts.map +1 -1
- package/dist/types/src/extensions/command/command.d.ts.map +1 -1
- package/dist/types/src/extensions/command/floating-menu.d.ts.map +1 -1
- package/dist/types/src/extensions/command/hint.d.ts +2 -7
- package/dist/types/src/extensions/command/hint.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.map +1 -1
- package/dist/types/src/extensions/command/useCommandMenu.d.ts +3 -4
- package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/dnd.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +2 -7
- package/dist/types/src/extensions/factories.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/markdown/action.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.test.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/link.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
- package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
- package/dist/types/src/extensions/preview/preview.d.ts +3 -6
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/index.d.ts +4 -0
- package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
- package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts +71 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/Command.stories.d.ts +12 -4
- package/dist/types/src/stories/Command.stories.d.ts.map +1 -1
- package/dist/types/src/stories/CommandMenu.stories.d.ts +10 -3
- package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts +21 -9
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -2
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +22 -12
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +32 -42
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +15 -20
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts +21 -6
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts +17 -0
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditor.stories.d.ts +38 -51
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +3 -6
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/testing/PreviewPopover.d.ts +20 -0
- package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +1 -0
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/types/types.d.ts +1 -1
- package/dist/types/src/util/cursor.d.ts.map +1 -1
- package/dist/types/src/util/debug.d.ts +1 -1
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/src/util/decorations.d.ts +4 -0
- package/dist/types/src/util/decorations.d.ts.map +1 -0
- package/dist/types/src/util/dom.d.ts +2 -12
- package/dist/types/src/util/dom.d.ts.map +1 -1
- package/dist/types/src/util/domino.d.ts +18 -0
- package/dist/types/src/util/domino.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +57 -51
- package/src/components/{Popover → CommandMenu}/CommandMenu.tsx +93 -26
- package/src/components/{Popover → CommandMenu}/index.ts +0 -2
- package/src/components/Editor/Editor.tsx +1 -1
- package/src/components/EditorToolbar/EditorToolbar.tsx +40 -30
- package/src/components/EditorToolbar/blocks.ts +21 -24
- package/src/components/EditorToolbar/formatting.ts +22 -25
- package/src/components/EditorToolbar/headings.ts +10 -5
- package/src/components/EditorToolbar/image.ts +8 -4
- package/src/components/EditorToolbar/lists.ts +16 -19
- package/src/components/EditorToolbar/search.ts +8 -4
- package/src/components/EditorToolbar/util.ts +16 -5
- package/src/components/EditorToolbar/view-mode.ts +11 -6
- package/src/components/index.ts +1 -1
- package/src/defaults.ts +5 -2
- package/src/extensions/autocomplete.ts +204 -54
- package/src/extensions/automerge/automerge.stories.tsx +25 -18
- package/src/extensions/automerge/automerge.ts +4 -3
- package/src/extensions/automerge/defs.ts +1 -1
- package/src/extensions/automerge/sync.ts +1 -1
- package/src/extensions/automerge/update-automerge.ts +1 -1
- package/src/extensions/autoscroll.ts +157 -0
- package/src/extensions/awareness/awareness.ts +2 -2
- package/src/extensions/command/action.ts +1 -2
- package/src/extensions/command/command-menu.ts +7 -6
- package/src/extensions/command/command.ts +3 -3
- package/src/extensions/command/floating-menu.ts +10 -15
- package/src/extensions/command/hint.ts +2 -1
- package/src/extensions/command/placeholder.ts +1 -1
- package/src/extensions/command/state.ts +4 -3
- package/src/extensions/command/typeahead.ts +2 -2
- package/src/extensions/command/useCommandMenu.ts +6 -9
- package/src/extensions/comments.ts +18 -13
- package/src/extensions/dnd.ts +1 -1
- package/src/extensions/factories.ts +9 -21
- package/src/extensions/folding.tsx +2 -2
- package/src/extensions/index.ts +2 -0
- package/src/extensions/markdown/action.ts +2 -1
- package/src/extensions/markdown/bundle.ts +25 -3
- package/src/extensions/markdown/changes.ts +1 -1
- package/src/extensions/markdown/decorate.ts +23 -14
- package/src/extensions/markdown/formatting.test.ts +6 -6
- package/src/extensions/markdown/formatting.ts +3 -3
- package/src/extensions/markdown/highlight.ts +1 -1
- package/src/extensions/markdown/image.ts +3 -4
- package/src/extensions/markdown/link.ts +3 -0
- package/src/extensions/markdown/table.ts +7 -1
- package/src/extensions/mention.ts +1 -1
- package/src/extensions/outliner/outliner.test.ts +3 -2
- package/src/extensions/outliner/outliner.ts +6 -5
- package/src/extensions/outliner/selection.ts +1 -1
- package/src/extensions/outliner/tree.test.ts +2 -1
- package/src/extensions/outliner/tree.ts +2 -2
- package/src/extensions/preview/preview.ts +59 -62
- package/src/extensions/tags/extended-markdown.test.ts +261 -0
- package/src/extensions/tags/extended-markdown.ts +78 -0
- package/src/extensions/tags/index.ts +7 -0
- package/src/extensions/tags/streamer.ts +244 -0
- package/src/extensions/tags/xml-tags.ts +335 -0
- package/src/extensions/tags/xml-util.ts +94 -0
- package/src/hooks/useTextEditor.ts +3 -15
- package/src/stories/Command.stories.tsx +24 -31
- package/src/stories/CommandMenu.stories.tsx +28 -29
- package/src/stories/Comments.stories.tsx +10 -6
- package/src/stories/EditorToolbar.stories.tsx +8 -8
- package/src/stories/Experimental.stories.tsx +12 -8
- package/src/stories/Markdown.stories.tsx +21 -17
- package/src/stories/Outliner.stories.tsx +42 -30
- package/src/stories/Preview.stories.tsx +30 -29
- package/src/stories/Tags.stories.tsx +81 -0
- package/src/stories/TextEditor.stories.tsx +40 -34
- package/src/stories/components/EditorStory.tsx +9 -10
- package/src/styles/theme.ts +8 -6
- package/src/testing/PreviewPopover.tsx +78 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/util.ts +2 -0
- package/src/translations.ts +1 -1
- package/src/util/cursor.ts +2 -1
- package/src/util/debug.ts +2 -2
- package/src/util/decorations.ts +21 -0
- package/src/util/dom.ts +5 -27
- package/src/util/domino.ts +51 -0
- package/src/util/index.ts +2 -0
- package/src/util/react.tsx +1 -1
- package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
- package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
- package/dist/types/src/components/Popover/index.d.ts +0 -4
- package/dist/types/src/components/Popover/index.d.ts.map +0 -1
- package/src/components/Popover/RefDropdownMenu.tsx +0 -85
- package/src/components/Popover/RefPopover.tsx +0 -99
@@ -0,0 +1,157 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2025 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import { StateEffect } from '@codemirror/state';
|
6
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
7
|
+
|
8
|
+
import { Domino } from '../util';
|
9
|
+
|
10
|
+
const lineHeight = 24;
|
11
|
+
|
12
|
+
export const scrollToBottomEffect = StateEffect.define<any>();
|
13
|
+
|
14
|
+
export type AutoScrollOptions = {
|
15
|
+
overscroll: number;
|
16
|
+
throttle: number;
|
17
|
+
};
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
|
21
|
+
*/
|
22
|
+
// TODO(burdon): Reconcile with transcript-extension.
|
23
|
+
export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Partial<AutoScrollOptions> = {}) => {
|
24
|
+
let isThrottled = false;
|
25
|
+
let isPinned = true;
|
26
|
+
let timeout: NodeJS.Timeout | undefined;
|
27
|
+
let buttonContainer: HTMLDivElement;
|
28
|
+
let lastScrollTop = 0;
|
29
|
+
let scrollCounter = 0;
|
30
|
+
|
31
|
+
const hideScrollbar = (view: EditorView) => {
|
32
|
+
view.scrollDOM.classList.add('cm-hide-scrollbar');
|
33
|
+
clearTimeout(timeout);
|
34
|
+
timeout = setTimeout(() => {
|
35
|
+
view.scrollDOM.classList.remove('cm-hide-scrollbar');
|
36
|
+
}, 1_000);
|
37
|
+
};
|
38
|
+
|
39
|
+
const scrollToBottom = (view: EditorView) => {
|
40
|
+
isPinned = true;
|
41
|
+
scrollCounter = 0;
|
42
|
+
buttonContainer?.classList.add('opacity-0');
|
43
|
+
requestAnimationFrame(() => {
|
44
|
+
hideScrollbar(view);
|
45
|
+
view.scrollDOM.scrollTo({
|
46
|
+
top: view.scrollDOM.scrollHeight,
|
47
|
+
behavior: 'smooth',
|
48
|
+
});
|
49
|
+
});
|
50
|
+
};
|
51
|
+
|
52
|
+
return [
|
53
|
+
// Scroll button.
|
54
|
+
ViewPlugin.fromClass(
|
55
|
+
class {
|
56
|
+
constructor(view: EditorView) {
|
57
|
+
const scroller = view.scrollDOM.parentElement;
|
58
|
+
buttonContainer = Domino.of('div')
|
59
|
+
.classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
|
60
|
+
.child(
|
61
|
+
Domino.of('button')
|
62
|
+
.classNames('dx-button bg-accentSurface')
|
63
|
+
.data('density', 'fine')
|
64
|
+
.child(Domino.of<any>('dx-icon').attr('icon', 'ph--arrow-down--regular'))
|
65
|
+
.on('click', () => {
|
66
|
+
scrollToBottom(view);
|
67
|
+
}),
|
68
|
+
)
|
69
|
+
.build();
|
70
|
+
scroller?.appendChild(buttonContainer);
|
71
|
+
}
|
72
|
+
},
|
73
|
+
),
|
74
|
+
|
75
|
+
// Update listener for logging when scrolling is needed.
|
76
|
+
EditorView.updateListener.of((update) => {
|
77
|
+
// Listen for effects.
|
78
|
+
update.transactions.forEach((transaction) => {
|
79
|
+
for (const effect of transaction.effects) {
|
80
|
+
if (effect.is(scrollToBottomEffect)) {
|
81
|
+
scrollToBottom(update.view);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
});
|
85
|
+
|
86
|
+
if (update.docChanged && isPinned && !isThrottled) {
|
87
|
+
const distanceFromBottom = calcDistance(update.view.scrollDOM);
|
88
|
+
|
89
|
+
// Keep pinned.
|
90
|
+
if (distanceFromBottom >= overscroll) {
|
91
|
+
isThrottled = true;
|
92
|
+
requestAnimationFrame(() => {
|
93
|
+
scrollToBottom(update.view);
|
94
|
+
});
|
95
|
+
|
96
|
+
// Reset throttle.
|
97
|
+
setTimeout(() => {
|
98
|
+
isThrottled = false;
|
99
|
+
}, throttle);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}),
|
103
|
+
|
104
|
+
// Detect user scroll.
|
105
|
+
// NOTE: Multiple scroll events are triggered during programmatic smooth scrolling.
|
106
|
+
EditorView.domEventHandlers({
|
107
|
+
scroll: (event, view) => {
|
108
|
+
const scroller = view.scrollDOM;
|
109
|
+
// Suspect delta goes positive when rendering widgets, so count positive deltas.
|
110
|
+
// TODO(burdon): Detect user scroll directly (wheel, touch, keys, etc.)
|
111
|
+
if (lastScrollTop > scroller.scrollTop) {
|
112
|
+
scrollCounter++;
|
113
|
+
}
|
114
|
+
lastScrollTop = scroller.scrollTop;
|
115
|
+
const distanceFromBottom = calcDistance(scroller);
|
116
|
+
if (distanceFromBottom === 0) {
|
117
|
+
// Pin to bottom.
|
118
|
+
isPinned = true;
|
119
|
+
buttonContainer?.classList.add('opacity-0');
|
120
|
+
scrollCounter = 0;
|
121
|
+
} else if (scrollCounter > 3) {
|
122
|
+
// Break pin if user scrolls up.
|
123
|
+
isPinned = false;
|
124
|
+
buttonContainer?.classList.remove('opacity-0');
|
125
|
+
}
|
126
|
+
},
|
127
|
+
}),
|
128
|
+
|
129
|
+
EditorView.theme({
|
130
|
+
'.cm-scroller': {
|
131
|
+
paddingBottom: `${overscroll}px`,
|
132
|
+
scrollbarWidth: 'thin',
|
133
|
+
},
|
134
|
+
'.cm-scroller.cm-hide-scrollbar': {
|
135
|
+
scrollbarWidth: 'none',
|
136
|
+
},
|
137
|
+
'.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
|
138
|
+
display: 'none',
|
139
|
+
},
|
140
|
+
|
141
|
+
// TODO(burdon): IconButton.
|
142
|
+
'.cm-scroll-button': {
|
143
|
+
position: 'absolute',
|
144
|
+
bottom: '0.5rem',
|
145
|
+
right: '1rem',
|
146
|
+
},
|
147
|
+
}),
|
148
|
+
];
|
149
|
+
};
|
150
|
+
|
151
|
+
const calcDistance = (scroller: HTMLElement) => {
|
152
|
+
const scrollTop = scroller.scrollTop;
|
153
|
+
const scrollHeight = scroller.scrollHeight;
|
154
|
+
const clientHeight = scroller.clientHeight;
|
155
|
+
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
|
156
|
+
return distanceFromBottom;
|
157
|
+
};
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { Annotation, type Extension,
|
5
|
+
import { Annotation, type Extension, type Range, RangeSet } from '@codemirror/state';
|
6
6
|
import {
|
7
7
|
Decoration,
|
8
8
|
type DecorationSet,
|
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
import { Event } from '@dxos/async';
|
17
17
|
import { Context } from '@dxos/context';
|
18
18
|
|
19
|
-
import {
|
19
|
+
import { Cursor, type CursorConverter, singleValueFacet } from '../../util';
|
20
20
|
|
21
21
|
export interface AwarenessProvider {
|
22
22
|
remoteStateChange: Event<void>;
|
@@ -3,7 +3,7 @@
|
|
3
3
|
//
|
4
4
|
|
5
5
|
import { StateEffect } from '@codemirror/state';
|
6
|
-
import { type
|
6
|
+
import { type Command, type EditorView, type KeyBinding } from '@codemirror/view';
|
7
7
|
|
8
8
|
import { commandState } from './state';
|
9
9
|
|
@@ -46,7 +46,6 @@ export const closeCommand: Command = (view: EditorView) => {
|
|
46
46
|
export const commandKeyBindings: readonly KeyBinding[] = [
|
47
47
|
{
|
48
48
|
key: '/',
|
49
|
-
preventDefault: true,
|
50
49
|
run: openCommand,
|
51
50
|
},
|
52
51
|
{
|
@@ -2,12 +2,13 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import {
|
6
|
-
import { EditorView, ViewPlugin, type ViewUpdate,
|
5
|
+
import { Prec, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
|
6
|
+
import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, keymap } from '@codemirror/view';
|
7
7
|
|
8
|
-
import { placeholder, type PlaceholderOptions } from './placeholder';
|
9
8
|
import { type Range } from '../../types';
|
10
9
|
|
10
|
+
import { type PlaceholderOptions, placeholder } from './placeholder';
|
11
|
+
|
11
12
|
export type CommandMenuOptions = {
|
12
13
|
trigger: string | string[];
|
13
14
|
placeholder?: Partial<PlaceholderOptions>;
|
@@ -37,13 +38,13 @@ export const commandMenu = (options: CommandMenuOptions) => {
|
|
37
38
|
// Check if we should show the widget - only if cursor is within the active command range.
|
38
39
|
const shouldShowWidget = activeRange && selection.head >= activeRange.from && selection.head <= activeRange.to;
|
39
40
|
if (shouldShowWidget) {
|
40
|
-
// Create mark decoration that wraps the entire line content in a dx-
|
41
|
+
// Create mark decoration that wraps the entire line content in a dx-anchor.
|
41
42
|
builder.add(
|
42
43
|
activeRange.from,
|
43
44
|
activeRange.to,
|
44
45
|
Decoration.mark({
|
45
|
-
tagName: 'dx-
|
46
|
-
class: 'cm-
|
46
|
+
tagName: 'dx-anchor',
|
47
|
+
class: 'cm-floating-menu-trigger',
|
47
48
|
attributes: {
|
48
49
|
'data-auto-trigger': 'true',
|
49
50
|
'data-trigger': trigger!,
|
@@ -2,14 +2,14 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import {
|
5
|
+
import { type Extension, Prec } from '@codemirror/state';
|
6
6
|
import { EditorView, keymap } from '@codemirror/view';
|
7
7
|
|
8
8
|
import { isNonNullable } from '@dxos/util';
|
9
9
|
|
10
10
|
import { closeEffect, commandKeyBindings } from './action';
|
11
|
-
import {
|
12
|
-
import { commandConfig, commandState
|
11
|
+
import { type HintOptions, hint } from './hint';
|
12
|
+
import { type PopupOptions, commandConfig, commandState } from './state';
|
13
13
|
|
14
14
|
// TODO(burdon): Create knowledge base for CM notes and ideas.
|
15
15
|
// https://discuss.codemirror.net/t/inline-code-hints-like-vscode/5533/4
|
@@ -34,12 +34,10 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
34
34
|
{
|
35
35
|
const icon = document.createElement('dx-icon');
|
36
36
|
icon.setAttribute('icon', options.icon ?? 'ph--dots-three-vertical--regular');
|
37
|
-
const button = document.createElement('button');
|
38
|
-
button.appendChild(icon);
|
39
37
|
|
40
|
-
this.tag = document.createElement('dx-
|
41
|
-
this.tag.classList.add('cm-
|
42
|
-
this.tag.appendChild(
|
38
|
+
this.tag = document.createElement('dx-anchor');
|
39
|
+
this.tag.classList.add('cm-floating-menu-trigger');
|
40
|
+
this.tag.appendChild(icon);
|
43
41
|
}
|
44
42
|
|
45
43
|
container.appendChild(this.tag);
|
@@ -69,7 +67,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
69
67
|
this.tag.style.display = 'none';
|
70
68
|
this.tag.classList.add('opacity-10');
|
71
69
|
} else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
|
72
|
-
this.tag.style.display = '
|
70
|
+
this.tag.style.display = '';
|
73
71
|
} else if (
|
74
72
|
update.docChanged ||
|
75
73
|
update.focusChanged ||
|
@@ -99,7 +97,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
99
97
|
|
100
98
|
this.tag.style.top = `${offsetTop}px`;
|
101
99
|
this.tag.style.left = `${offsetLeft}px`;
|
102
|
-
this.tag.style.display = '
|
100
|
+
this.tag.style.display = '';
|
103
101
|
}
|
104
102
|
|
105
103
|
scheduleUpdate() {
|
@@ -113,21 +111,18 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
113
111
|
),
|
114
112
|
|
115
113
|
EditorView.theme({
|
116
|
-
'.cm-
|
114
|
+
'.cm-floating-menu-trigger': {
|
117
115
|
position: 'fixed',
|
118
116
|
padding: '0',
|
119
117
|
border: 'none',
|
120
118
|
opacity: '0',
|
121
|
-
},
|
122
|
-
'[data-has-focus] & .cm-ref-tag': {
|
123
|
-
opacity: '1',
|
124
|
-
},
|
125
|
-
'.cm-ref-tag button': {
|
126
119
|
display: 'grid',
|
127
|
-
|
128
|
-
justifyContent: 'center',
|
120
|
+
placeContent: 'center',
|
129
121
|
width: '2rem',
|
130
122
|
height: '2rem',
|
131
123
|
},
|
124
|
+
'&:focus-within .cm-floating-menu-trigger': {
|
125
|
+
opacity: '1',
|
126
|
+
},
|
132
127
|
}),
|
133
128
|
];
|
@@ -6,9 +6,10 @@
|
|
6
6
|
import { RangeSetBuilder } from '@codemirror/state';
|
7
7
|
import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
|
8
8
|
|
9
|
-
import { commandState } from './state';
|
10
9
|
import { clientRectsFor, flattenRect } from '../../util';
|
11
10
|
|
11
|
+
import { commandState } from './state';
|
12
|
+
|
12
13
|
export type HintOptions = {
|
13
14
|
delay?: number;
|
14
15
|
onHint?: () => string | undefined;
|
@@ -4,7 +4,7 @@
|
|
4
4
|
//
|
5
5
|
|
6
6
|
import { type Extension } from '@codemirror/state';
|
7
|
-
import { Decoration, EditorView,
|
7
|
+
import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
|
8
8
|
|
9
9
|
import { clientRectsFor, flattenRect } from '../../util';
|
10
10
|
|
@@ -3,13 +3,14 @@
|
|
3
3
|
//
|
4
4
|
|
5
5
|
import { StateField } from '@codemirror/state';
|
6
|
-
import {
|
6
|
+
import { type EditorView, type Tooltip, type TooltipView, showTooltip } from '@codemirror/view';
|
7
7
|
|
8
|
-
import { closeEffect, type Action, openEffect } from './action';
|
9
|
-
import { type CommandOptions } from './command';
|
10
8
|
import { type RenderCallback } from '../../types';
|
11
9
|
import { singleValueFacet } from '../../util';
|
12
10
|
|
11
|
+
import { type Action, closeEffect, openEffect } from './action';
|
12
|
+
import { type CommandOptions } from './command';
|
13
|
+
|
13
14
|
export const commandConfig = singleValueFacet<CommandOptions>();
|
14
15
|
|
15
16
|
export type PopupOptions = {
|
@@ -2,15 +2,15 @@
|
|
2
2
|
// Copyright 2025 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { EditorSelection, Prec, RangeSetBuilder
|
5
|
+
import { EditorSelection, type Extension, Prec, RangeSetBuilder } from '@codemirror/state';
|
6
6
|
import {
|
7
7
|
type Command,
|
8
8
|
Decoration,
|
9
9
|
type DecorationSet,
|
10
10
|
type EditorView,
|
11
|
-
keymap,
|
12
11
|
ViewPlugin,
|
13
12
|
type ViewUpdate,
|
13
|
+
keymap,
|
14
14
|
} from '@codemirror/view';
|
15
15
|
|
16
16
|
import { Hint } from './hint';
|
@@ -5,12 +5,13 @@
|
|
5
5
|
import { type EditorView } from '@codemirror/view';
|
6
6
|
import { type RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
7
7
|
|
8
|
-
import { type
|
8
|
+
import { type DxAnchorActivate } from '@dxos/react-ui';
|
9
9
|
import { type MaybePromise } from '@dxos/util';
|
10
10
|
|
11
|
+
import { type CommandMenuGroup, type CommandMenuItem, getItem, getNextItem, getPreviousItem } from '../../components';
|
12
|
+
|
11
13
|
import { commandMenu, commandRangeEffect } from './command-menu';
|
12
14
|
import { type PlaceholderOptions } from './placeholder';
|
13
|
-
import { getItem, getNextItem, getPreviousItem, type CommandMenuGroup, type CommandMenuItem } from '../../components';
|
14
15
|
|
15
16
|
export type UseCommandMenuOptions = {
|
16
17
|
viewRef: RefObject<EditorView | undefined>;
|
@@ -20,7 +21,6 @@ export type UseCommandMenuOptions = {
|
|
20
21
|
};
|
21
22
|
|
22
23
|
export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCommandMenuOptions) => {
|
23
|
-
const triggerRef = useRef<DxRefTag | null>(null);
|
24
24
|
const currentRef = useRef<CommandMenuItem | null>(null);
|
25
25
|
const groupsRef = useRef<CommandMenuGroup[]>([]);
|
26
26
|
const [currentItem, setCurrentItem] = useState<string>();
|
@@ -34,7 +34,6 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
|
|
34
34
|
}
|
35
35
|
setOpen(open);
|
36
36
|
if (!open) {
|
37
|
-
triggerRef.current = null;
|
38
37
|
setCurrentItem(undefined);
|
39
38
|
viewRef.current?.dispatch({ effects: [commandRangeEffect.of(null)] });
|
40
39
|
}
|
@@ -43,13 +42,12 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
|
|
43
42
|
);
|
44
43
|
|
45
44
|
const handleActivate = useCallback(
|
46
|
-
async (event:
|
45
|
+
async (event: DxAnchorActivate) => {
|
47
46
|
const item = getItem(groupsRef.current, currentItem);
|
48
47
|
if (item) {
|
49
48
|
currentRef.current = item;
|
50
49
|
}
|
51
50
|
|
52
|
-
triggerRef.current = event.trigger;
|
53
51
|
const triggerKey = event.trigger.getAttribute('data-trigger');
|
54
52
|
if (!open && triggerKey) {
|
55
53
|
await handleOpenChange(true, triggerKey);
|
@@ -69,7 +67,7 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
|
|
69
67
|
}, []);
|
70
68
|
|
71
69
|
const serializedTrigger = Array.isArray(trigger) ? trigger.join(',') : trigger;
|
72
|
-
const
|
70
|
+
const memoizedCommandMenu = useMemo(() => {
|
73
71
|
return commandMenu({
|
74
72
|
trigger,
|
75
73
|
placeholder,
|
@@ -106,10 +104,9 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
|
|
106
104
|
}, [handleOpenChange, getMenu, serializedTrigger, placeholder]);
|
107
105
|
|
108
106
|
return {
|
109
|
-
commandMenu:
|
107
|
+
commandMenu: memoizedCommandMenu,
|
110
108
|
currentItem,
|
111
109
|
groupsRef,
|
112
|
-
ref: triggerRef,
|
113
110
|
open,
|
114
111
|
onActivate: handleActivate,
|
115
112
|
onOpenChange: setOpen,
|
@@ -5,25 +5,26 @@
|
|
5
5
|
import { invertedEffects } from '@codemirror/commands';
|
6
6
|
import { type ChangeDesc, type Extension, StateEffect, StateField, type Text } from '@codemirror/state';
|
7
7
|
import {
|
8
|
-
hoverTooltip,
|
9
|
-
keymap,
|
10
8
|
type Command,
|
11
9
|
Decoration,
|
12
10
|
EditorView,
|
13
|
-
type Rect,
|
14
11
|
type PluginValue,
|
12
|
+
type Rect,
|
15
13
|
ViewPlugin,
|
14
|
+
hoverTooltip,
|
15
|
+
keymap,
|
16
16
|
} from '@codemirror/view';
|
17
17
|
import sortBy from 'lodash.sortby';
|
18
18
|
import { useEffect } from 'react';
|
19
19
|
|
20
|
-
import {
|
20
|
+
import { type CleanupFn, debounce } from '@dxos/async';
|
21
21
|
import { log } from '@dxos/log';
|
22
22
|
import { isNonNullable } from '@dxos/util';
|
23
23
|
|
24
|
+
import { type Comment, type Range, type RenderCallback } from '../types';
|
25
|
+
import { Cursor, callbackWrapper, singleValueFacet } from '../util';
|
26
|
+
|
24
27
|
import { documentId } from './selection';
|
25
|
-
import { type RenderCallback, type Comment, type Range } from '../types';
|
26
|
-
import { Cursor, singleValueFacet, callbackWrapper } from '../util';
|
27
28
|
|
28
29
|
//
|
29
30
|
// State management.
|
@@ -57,7 +58,11 @@ const setCommentState = StateEffect.define<CommentsState>();
|
|
57
58
|
* The ranges are tracked as Automerge cursors from which the absolute indexed ranges can be computed.
|
58
59
|
*/
|
59
60
|
export const commentsState = StateField.define<CommentsState>({
|
60
|
-
create: (state) => ({
|
61
|
+
create: (state) => ({
|
62
|
+
id: state.facet(documentId),
|
63
|
+
comments: [],
|
64
|
+
selection: {},
|
65
|
+
}),
|
61
66
|
update: (value, tr) => {
|
62
67
|
for (const effect of tr.effects) {
|
63
68
|
// Update selection.
|
@@ -98,16 +103,16 @@ export const commentsState = StateField.define<CommentsState>({
|
|
98
103
|
*/
|
99
104
|
const styles = EditorView.theme({
|
100
105
|
'.cm-comment, .cm-comment-current': {
|
101
|
-
|
102
|
-
|
103
|
-
|
106
|
+
padding: '3px 0',
|
107
|
+
backgroundColor: 'var(--dx-cmCommentSurface)',
|
108
|
+
},
|
109
|
+
'.cm-comment > span, .cm-comment-current > span': {
|
110
|
+
boxDecorationBreak: 'clone',
|
111
|
+
boxShadow: '0 0 1px 3px var(--dx-cmCommentSurface)',
|
104
112
|
backgroundColor: 'var(--dx-cmCommentSurface)',
|
105
113
|
color: 'var(--dx-cmComment)',
|
106
114
|
cursor: 'pointer',
|
107
115
|
},
|
108
|
-
'.cm-comment:hover, .cm-comment-current': {
|
109
|
-
textDecoration: 'underline',
|
110
|
-
},
|
111
116
|
});
|
112
117
|
|
113
118
|
const createCommentMark = (id: string, isCurrent: boolean) =>
|
package/src/extensions/dnd.ts
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
//
|
4
4
|
|
5
5
|
import type { Extension } from '@codemirror/state';
|
6
|
-
import {
|
6
|
+
import { EditorView, dropCursor } from '@codemirror/view';
|
7
7
|
|
8
8
|
export type DNDOptions = { onDrop?: (view: EditorView, event: { files: FileList }) => void };
|
9
9
|
|
@@ -31,11 +31,12 @@ import { type ThemeMode } from '@dxos/react-ui';
|
|
31
31
|
import { type HuePalette } from '@dxos/react-ui-theme';
|
32
32
|
import { hexToHue, isNotFalsy } from '@dxos/util';
|
33
33
|
|
34
|
+
import { editorGutter, editorMonospace } from '../defaults';
|
35
|
+
import { type ThemeStyles, defaultTheme } from '../styles';
|
36
|
+
|
34
37
|
import { automerge } from './automerge';
|
35
38
|
import { SpaceAwarenessProvider, awareness } from './awareness';
|
36
39
|
import { focus } from './focus';
|
37
|
-
import { editorGutter, editorMonospace } from '../defaults';
|
38
|
-
import { type ThemeStyles, defaultTheme } from '../styles';
|
39
40
|
|
40
41
|
//
|
41
42
|
// Basic
|
@@ -47,6 +48,7 @@ export const preventNewline = EditorState.transactionFilter.of((tr) => (tr.newDo
|
|
47
48
|
* https://codemirror.net/docs/extensions
|
48
49
|
* https://github.com/codemirror/basic-setup
|
49
50
|
* https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
|
51
|
+
* https://github.com/codemirror/theme-one-dark
|
50
52
|
*/
|
51
53
|
export type BasicExtensionsOptions = {
|
52
54
|
allowMultipleSelections?: boolean;
|
@@ -82,7 +84,7 @@ const defaultBasicOptions: BasicExtensionsOptions = {
|
|
82
84
|
history: true,
|
83
85
|
keymap: 'standard',
|
84
86
|
lineWrapping: true,
|
85
|
-
search:
|
87
|
+
search: false,
|
86
88
|
} as const;
|
87
89
|
|
88
90
|
const keymaps: { [key: string]: readonly KeyBinding[] } = {
|
@@ -157,9 +159,6 @@ export type ThemeExtensionsOptions = {
|
|
157
159
|
scroll?: {
|
158
160
|
className?: string;
|
159
161
|
};
|
160
|
-
scroller?: {
|
161
|
-
className?: string;
|
162
|
-
};
|
163
162
|
content?: {
|
164
163
|
className?: string;
|
165
164
|
};
|
@@ -186,31 +185,21 @@ export const defaultThemeSlots = grow;
|
|
186
185
|
export const createThemeExtensions = ({
|
187
186
|
themeMode,
|
188
187
|
styles,
|
189
|
-
syntaxHighlighting:
|
188
|
+
syntaxHighlighting: syntaxHighlightingProps,
|
190
189
|
slots: _slots,
|
191
190
|
}: ThemeExtensionsOptions = {}): Extension => {
|
192
191
|
const slots = defaultsDeep({}, _slots, defaultThemeSlots);
|
193
192
|
return [
|
194
193
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
195
194
|
EditorView.baseTheme(styles ? merge({}, defaultTheme, styles) : defaultTheme),
|
196
|
-
|
197
|
-
_syntaxHighlighting &&
|
198
|
-
(themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
|
195
|
+
syntaxHighlightingProps && syntaxHighlighting(themeMode === 'dark' ? oneDarkHighlightStyle : defaultHighlightStyle),
|
199
196
|
slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
|
200
197
|
slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
|
201
198
|
slots.scroll?.className &&
|
202
199
|
ViewPlugin.fromClass(
|
203
200
|
class {
|
204
201
|
constructor(view: EditorView) {
|
205
|
-
view.scrollDOM.classList.add(slots.scroll.className);
|
206
|
-
}
|
207
|
-
},
|
208
|
-
),
|
209
|
-
slots.scroller?.className &&
|
210
|
-
ViewPlugin.fromClass(
|
211
|
-
class {
|
212
|
-
constructor(view: EditorView) {
|
213
|
-
view.dom.querySelector('.cm-scroller')?.classList.add(...slots.scroller.className.split(' '));
|
202
|
+
view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
|
214
203
|
}
|
215
204
|
},
|
216
205
|
),
|
@@ -238,7 +227,6 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
|
|
238
227
|
if (space && identity) {
|
239
228
|
const peerId = identity?.identityKey.toHex();
|
240
229
|
const hue = (identity?.profile?.data?.hue as HuePalette | undefined) ?? hexToHue(peerId ?? '0');
|
241
|
-
|
242
230
|
extensions.push(
|
243
231
|
awareness(
|
244
232
|
new SpaceAwarenessProvider({
|
@@ -246,9 +234,9 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
|
|
246
234
|
channel: `awareness.${id}`,
|
247
235
|
peerId: identity.identityKey.toHex(),
|
248
236
|
info: {
|
249
|
-
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
250
237
|
darkColor: `var(--dx-${hue}Cursor)`,
|
251
238
|
lightColor: `var(--dx-${hue}Cursor)`,
|
239
|
+
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
252
240
|
},
|
253
241
|
}),
|
254
242
|
),
|
@@ -9,7 +9,7 @@ import React from 'react';
|
|
9
9
|
|
10
10
|
import { Icon } from '@dxos/react-ui';
|
11
11
|
|
12
|
-
import {
|
12
|
+
import { Domino, renderRoot } from '../util';
|
13
13
|
|
14
14
|
export type FoldingOptions = {};
|
15
15
|
|
@@ -26,7 +26,7 @@ export const folding = (_props: FoldingOptions = {}): Extension => [
|
|
26
26
|
foldGutter({
|
27
27
|
markerDOM: (open) => {
|
28
28
|
// TODO(burdon): Use sprite directly.
|
29
|
-
const el =
|
29
|
+
const el = Domino.of('div').classNames('flex h-full items-center').build();
|
30
30
|
return renderRoot(
|
31
31
|
el,
|
32
32
|
<Icon icon='ph--caret-right--bold' size={3} classNames={['mx-3 cursor-pointer', open && 'rotate-90']} />,
|
package/src/extensions/index.ts
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
export * from './annotations';
|
6
6
|
export * from './autocomplete';
|
7
|
+
export * from './autoscroll';
|
7
8
|
export * from './automerge';
|
8
9
|
export * from './awareness';
|
9
10
|
export * from './blast';
|
@@ -23,4 +24,5 @@ export * from './modes';
|
|
23
24
|
export * from './outliner';
|
24
25
|
export * from './preview';
|
25
26
|
export * from './selection';
|
27
|
+
export * from './tags';
|
26
28
|
export * from './typewriter';
|
@@ -7,6 +7,8 @@ import { type EditorView } from '@codemirror/view';
|
|
7
7
|
import { type Action } from '@dxos/app-graph';
|
8
8
|
import { type MenuActionProperties } from '@dxos/react-ui-menu';
|
9
9
|
|
10
|
+
import { createComment } from '../comments';
|
11
|
+
|
10
12
|
import {
|
11
13
|
Inline,
|
12
14
|
List,
|
@@ -25,7 +27,6 @@ import {
|
|
25
27
|
toggleList,
|
26
28
|
toggleStyle,
|
27
29
|
} from './formatting';
|
28
|
-
import { createComment } from '../comments';
|
29
30
|
|
30
31
|
export type PayloadType =
|
31
32
|
| 'view-mode'
|