@dxos/react-ui-editor 0.8.4-main.b97322e → 0.8.4-main.dedc0f3
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 -888
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +2074 -888
- 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.map +2 -2
- 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/Popover/CommandMenu.d.ts.map +1 -1
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +2 -9
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts +20 -17
- package/dist/types/src/components/Popover/RefPopover.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/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 +4 -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/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 -5
- 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/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/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/Popover/CommandMenu.tsx +3 -3
- package/src/components/Popover/RefDropdownMenu.tsx +20 -16
- package/src/components/Popover/RefPopover.tsx +63 -45
- 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 +151 -0
- package/src/extensions/awareness/awareness.ts +2 -2
- package/src/extensions/command/action.ts +1 -1
- package/src/extensions/command/command-menu.ts +6 -5
- package/src/extensions/command/command.ts +3 -3
- package/src/extensions/command/floating-menu.ts +1 -1
- 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 +5 -4
- 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/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 +5 -4
- 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 -59
- 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 -3
- package/src/stories/Command.stories.tsx +24 -31
- package/src/stories/CommandMenu.stories.tsx +23 -22
- 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 +17 -14
- package/src/stories/Preview.stories.tsx +27 -26
- 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/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
@@ -0,0 +1,151 @@
|
|
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 lastScrollTop = 0;
|
27
|
+
let timeout: NodeJS.Timeout | undefined;
|
28
|
+
let buttonContainer: HTMLDivElement;
|
29
|
+
|
30
|
+
const hideScrollbar = (view: EditorView) => {
|
31
|
+
view.scrollDOM.classList.add('cm-hide-scrollbar');
|
32
|
+
clearTimeout(timeout);
|
33
|
+
timeout = setTimeout(() => {
|
34
|
+
view.scrollDOM.classList.remove('cm-hide-scrollbar');
|
35
|
+
}, 1_000);
|
36
|
+
};
|
37
|
+
|
38
|
+
const scrollToBottom = (view: EditorView) => {
|
39
|
+
isPinned = true;
|
40
|
+
buttonContainer?.classList.add('opacity-0');
|
41
|
+
requestAnimationFrame(() => {
|
42
|
+
hideScrollbar(view);
|
43
|
+
view.scrollDOM.scrollTo({
|
44
|
+
top: view.scrollDOM.scrollHeight,
|
45
|
+
behavior: 'smooth',
|
46
|
+
});
|
47
|
+
});
|
48
|
+
};
|
49
|
+
|
50
|
+
return [
|
51
|
+
// Scroll button.
|
52
|
+
ViewPlugin.fromClass(
|
53
|
+
class {
|
54
|
+
constructor(view: EditorView) {
|
55
|
+
const scroller = view.scrollDOM.parentElement;
|
56
|
+
buttonContainer = Domino.of('div')
|
57
|
+
.classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
|
58
|
+
.child(
|
59
|
+
Domino.of('button')
|
60
|
+
.classNames('dx-button bg-accentSurface')
|
61
|
+
.data('density', 'fine')
|
62
|
+
.child(Domino.of<any>('dx-icon').attr('icon', 'ph--arrow-down--regular'))
|
63
|
+
.on('click', () => {
|
64
|
+
scrollToBottom(view);
|
65
|
+
}),
|
66
|
+
)
|
67
|
+
.build();
|
68
|
+
scroller?.appendChild(buttonContainer);
|
69
|
+
}
|
70
|
+
},
|
71
|
+
),
|
72
|
+
|
73
|
+
// Update listener for logging when scrolling is needed.
|
74
|
+
EditorView.updateListener.of((update) => {
|
75
|
+
// Listen for effects.
|
76
|
+
update.transactions.forEach((transaction) => {
|
77
|
+
for (const effect of transaction.effects) {
|
78
|
+
if (effect.is(scrollToBottomEffect)) {
|
79
|
+
scrollToBottom(update.view);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
if (update.docChanged && isPinned && !isThrottled) {
|
85
|
+
const distanceFromBottom = calcDistance(update.view.scrollDOM);
|
86
|
+
|
87
|
+
// Hide scrollbar even if not scrolling to bottom.
|
88
|
+
// hideScrollbar(update.view);
|
89
|
+
|
90
|
+
// Keep pinned.
|
91
|
+
if (distanceFromBottom > overscroll) {
|
92
|
+
isThrottled = true;
|
93
|
+
requestAnimationFrame(() => {
|
94
|
+
scrollToBottom(update.view);
|
95
|
+
});
|
96
|
+
|
97
|
+
// Reset throttle.
|
98
|
+
setTimeout(() => {
|
99
|
+
isThrottled = false;
|
100
|
+
}, throttle);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}),
|
104
|
+
|
105
|
+
EditorView.domEventHandlers({
|
106
|
+
scroll: (event, view) => {
|
107
|
+
const scroller = view.scrollDOM;
|
108
|
+
const distanceFromBottom = calcDistance(scroller);
|
109
|
+
if (distanceFromBottom === 0) {
|
110
|
+
// Pin to bottom.
|
111
|
+
isPinned = true;
|
112
|
+
buttonContainer?.classList.add('opacity-0');
|
113
|
+
} else if (scroller.scrollTop < lastScrollTop) {
|
114
|
+
// Break pin if user scrolls up.
|
115
|
+
isPinned = false;
|
116
|
+
buttonContainer?.classList.remove('opacity-0');
|
117
|
+
}
|
118
|
+
|
119
|
+
lastScrollTop = scroller.scrollTop;
|
120
|
+
},
|
121
|
+
}),
|
122
|
+
|
123
|
+
EditorView.theme({
|
124
|
+
'.cm-scroller': {
|
125
|
+
paddingBottom: `${overscroll}px`,
|
126
|
+
scrollbarWidth: 'thin',
|
127
|
+
},
|
128
|
+
'.cm-scroller.cm-hide-scrollbar': {
|
129
|
+
scrollbarWidth: 'none',
|
130
|
+
},
|
131
|
+
'.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
|
132
|
+
display: 'none',
|
133
|
+
},
|
134
|
+
|
135
|
+
// TODO(burdon): IconButton.
|
136
|
+
'.cm-scroll-button': {
|
137
|
+
position: 'absolute',
|
138
|
+
bottom: '0.5rem',
|
139
|
+
right: '1rem',
|
140
|
+
},
|
141
|
+
}),
|
142
|
+
];
|
143
|
+
};
|
144
|
+
|
145
|
+
const calcDistance = (scroller: HTMLElement) => {
|
146
|
+
const scrollTop = scroller.scrollTop;
|
147
|
+
const scrollHeight = scroller.scrollHeight;
|
148
|
+
const clientHeight = scroller.clientHeight;
|
149
|
+
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
|
150
|
+
return distanceFromBottom;
|
151
|
+
};
|
@@ -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
|
|
@@ -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,12 +38,12 @@ 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
|
+
tagName: 'dx-anchor',
|
46
47
|
class: 'cm-ref-tag',
|
47
48
|
attributes: {
|
48
49
|
'data-auto-trigger': 'true',
|
@@ -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
|
@@ -37,7 +37,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
37
37
|
const button = document.createElement('button');
|
38
38
|
button.appendChild(icon);
|
39
39
|
|
40
|
-
this.tag = document.createElement('dx-
|
40
|
+
this.tag = document.createElement('dx-anchor');
|
41
41
|
this.tag.classList.add('cm-ref-tag');
|
42
42
|
this.tag.appendChild(button);
|
43
43
|
}
|
@@ -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 DxAnchor, type DxAnchorActivate } from '@dxos/lit-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,7 @@ export type UseCommandMenuOptions = {
|
|
20
21
|
};
|
21
22
|
|
22
23
|
export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCommandMenuOptions) => {
|
23
|
-
const triggerRef = useRef<
|
24
|
+
const triggerRef = useRef<DxAnchor | null>(null);
|
24
25
|
const currentRef = useRef<CommandMenuItem | null>(null);
|
25
26
|
const groupsRef = useRef<CommandMenuGroup[]>([]);
|
26
27
|
const [currentItem, setCurrentItem] = useState<string>();
|
@@ -43,7 +44,7 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
|
|
43
44
|
);
|
44
45
|
|
45
46
|
const handleActivate = useCallback(
|
46
|
-
async (event:
|
47
|
+
async (event: DxAnchorActivate) => {
|
47
48
|
const item = getItem(groupsRef.current, currentItem);
|
48
49
|
if (item) {
|
49
50
|
currentRef.current = item;
|
@@ -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'
|
@@ -4,20 +4,21 @@
|
|
4
4
|
|
5
5
|
import { completionKeymap } from '@codemirror/autocomplete';
|
6
6
|
import { defaultKeymap, indentWithTab } from '@codemirror/commands';
|
7
|
-
import {
|
7
|
+
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
8
8
|
import { syntaxHighlighting } from '@codemirror/language';
|
9
9
|
import { languages } from '@codemirror/language-data';
|
10
10
|
import { type Extension } from '@codemirror/state';
|
11
11
|
import { keymap } from '@codemirror/view';
|
12
|
+
import { type MarkdownConfig } from '@lezer/markdown';
|
12
13
|
|
13
|
-
import { type ThemeMode } from '@dxos/react-ui';
|
14
14
|
import { isNotFalsy } from '@dxos/util';
|
15
15
|
|
16
16
|
import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
|
17
17
|
|
18
18
|
export type MarkdownBundleOptions = {
|
19
|
-
|
19
|
+
extensions?: MarkdownConfig[];
|
20
20
|
indentWithTab?: boolean;
|
21
|
+
setextHeading?: boolean;
|
21
22
|
};
|
22
23
|
|
23
24
|
/**
|
@@ -51,6 +52,7 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
|
|
51
52
|
extensions: [
|
52
53
|
// GFM provided by default.
|
53
54
|
markdownTagsExtensions,
|
55
|
+
...(options.extensions ?? defaultExtensions()),
|
54
56
|
],
|
55
57
|
}),
|
56
58
|
|
@@ -69,3 +71,23 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
|
|
69
71
|
),
|
70
72
|
];
|
71
73
|
};
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Default customizations.
|
77
|
+
* https://github.com/lezer-parser/markdown/blob/main/src/markdown.ts
|
78
|
+
*/
|
79
|
+
export const defaultExtensions = (): MarkdownConfig[] => [noSetExtHeading, noHtml];
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Remove SetextHeading (e.g., headings created from "---").
|
83
|
+
*/
|
84
|
+
const noSetExtHeading: MarkdownConfig = {
|
85
|
+
remove: ['SetextHeading'],
|
86
|
+
};
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Remove HTML and XML parsing.
|
90
|
+
*/
|
91
|
+
const noHtml: MarkdownConfig = {
|
92
|
+
remove: ['HTMLBlock', 'HTMLTag'],
|
93
|
+
};
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
import { syntaxTree } from '@codemirror/language';
|
6
6
|
import { type ChangeSpec, Transaction } from '@codemirror/state';
|
7
|
-
import { ViewPlugin, type ViewUpdate
|
7
|
+
import { type PluginValue, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
8
8
|
|
9
9
|
/**
|
10
10
|
* Monitors and augments changes.
|