@dxos/react-ui-editor 0.8.4-main.e098934 → 0.8.4-main.ead640a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-22UMM3QJ.mjs → chunk-HL3YF6WC.mjs} +2 -2
- package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3555 -3484
- 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/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
- package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +3555 -3484
- 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/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +24 -9
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +27 -0
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/util.d.ts +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/{autocomplete.d.ts → autocomplete/autocomplete.d.ts} +1 -1
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +2 -2
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/autoscroll.d.ts +2 -2
- package/dist/types/src/extensions/autoscroll.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +7 -2
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/focus.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +2 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/json.d.ts +1 -1
- package/dist/types/src/extensions/json.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- 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/popover/PopoverMenuProvider.d.ts +36 -0
- package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/index.d.ts +8 -0
- package/dist/types/src/extensions/popover/index.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/menu-presets.d.ts +4 -0
- package/dist/types/src/extensions/popover/menu-presets.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/menu.d.ts +24 -0
- package/dist/types/src/extensions/popover/menu.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/modal.d.ts +7 -0
- package/dist/types/src/extensions/popover/modal.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/popover.d.ts +47 -0
- package/dist/types/src/extensions/popover/popover.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/usePopoverMenu.d.ts +34 -0
- package/dist/types/src/extensions/popover/usePopoverMenu.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/util.d.ts +8 -0
- package/dist/types/src/extensions/popover/util.d.ts.map +1 -0
- package/dist/types/src/extensions/preview/preview.d.ts +0 -1
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- 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/tags/streamer.d.ts.map +1 -1
- package/dist/types/src/extensions/tags/xml-tags.d.ts +1 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +4 -8
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/{Command.stories.d.ts → CommandDialog.stories.d.ts} +2 -3
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Comments.stories.d.ts +3 -4
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -2
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +3 -4
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +3 -4
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +0 -1
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/{CommandMenu.stories.d.ts → Popover.stories.d.ts} +6 -6
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +3 -4
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts +0 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +3 -5
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +5 -5
- 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.map +1 -1
- package/dist/types/src/types/types.d.ts +1 -1
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -51
- package/src/components/Editor/Editor.stories.tsx +69 -0
- package/src/components/Editor/Editor.tsx +57 -14
- package/src/components/EditorToolbar/EditorToolbar.tsx +1 -0
- package/src/components/index.ts +0 -1
- package/src/extensions/{autocomplete.ts → autocomplete/autocomplete.ts} +2 -1
- package/src/extensions/autocomplete/index.ts +8 -0
- package/src/extensions/autocomplete/match.ts +46 -0
- package/src/extensions/{command → autocomplete}/placeholder.ts +21 -17
- package/src/extensions/{command → autocomplete}/typeahead.ts +6 -48
- package/src/extensions/automerge/automerge.stories.tsx +8 -8
- package/src/extensions/automerge/automerge.ts +28 -9
- package/src/extensions/automerge/sync.ts +7 -3
- package/src/extensions/autoscroll.ts +29 -29
- package/src/extensions/factories.ts +41 -12
- package/src/extensions/focus.ts +5 -4
- package/src/extensions/folding.tsx +4 -6
- package/src/extensions/hashtag.tsx +2 -2
- package/src/extensions/index.ts +2 -1
- package/src/extensions/json.ts +1 -1
- package/src/extensions/markdown/bundle.ts +16 -4
- package/src/extensions/markdown/decorate.ts +1 -0
- package/src/extensions/modes.ts +2 -2
- package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +9 -9
- package/src/extensions/outliner/outliner.ts +2 -2
- package/src/extensions/popover/PopoverMenuProvider.tsx +221 -0
- package/src/extensions/popover/index.ts +12 -0
- package/src/extensions/popover/menu-presets.ts +124 -0
- package/src/extensions/popover/menu.ts +67 -0
- package/src/extensions/popover/modal.ts +24 -0
- package/src/extensions/popover/popover.ts +291 -0
- package/src/extensions/popover/usePopoverMenu.ts +173 -0
- package/src/extensions/popover/util.ts +29 -0
- package/src/extensions/preview/index.ts +1 -1
- package/src/extensions/preview/preview.ts +0 -2
- package/src/extensions/selection.ts +2 -2
- package/src/extensions/state.ts +7 -0
- package/src/extensions/tags/streamer.ts +4 -5
- package/src/extensions/tags/xml-tags.ts +59 -1
- package/src/hooks/useTextEditor.ts +27 -27
- package/src/stories/{Command.stories.tsx → CommandDialog.stories.tsx} +10 -22
- package/src/stories/Comments.stories.tsx +5 -5
- package/src/stories/EditorToolbar.stories.tsx +6 -5
- package/src/stories/Experimental.stories.tsx +6 -6
- package/src/stories/Markdown.stories.tsx +5 -5
- package/src/stories/Outliner.stories.tsx +21 -14
- package/src/stories/Popover.stories.tsx +163 -0
- package/src/stories/Preview.stories.tsx +5 -5
- package/src/stories/Tags.stories.tsx +5 -5
- package/src/stories/TextEditor.stories.tsx +7 -32
- package/src/stories/components/EditorStory.tsx +7 -5
- package/src/styles/theme.ts +12 -10
- package/src/testing/PreviewPopover.tsx +2 -0
- package/src/types/types.ts +1 -1
- package/src/util/index.ts +0 -1
- package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YXYQPV6R.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/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/extensions/command/action.d.ts +0 -17
- package/dist/types/src/extensions/command/action.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
- package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command.d.ts +0 -6
- package/dist/types/src/extensions/command/command.d.ts.map +0 -1
- package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
- package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/hint.d.ts +0 -19
- package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
- package/dist/types/src/extensions/command/index.d.ts +0 -7
- package/dist/types/src/extensions/command/index.d.ts.map +0 -1
- package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
- package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
- package/dist/types/src/extensions/command/state.d.ts +0 -16
- package/dist/types/src/extensions/command/state.d.ts.map +0 -1
- package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
- package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
- package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -25
- package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
- package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
- package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
- package/dist/types/src/util/domino.d.ts +0 -18
- package/dist/types/src/util/domino.d.ts.map +0 -1
- package/src/components/CommandMenu/CommandMenu.tsx +0 -346
- package/src/components/CommandMenu/index.ts +0 -5
- package/src/extensions/command/action.ts +0 -55
- package/src/extensions/command/command-menu.ts +0 -211
- package/src/extensions/command/command.ts +0 -34
- package/src/extensions/command/hint.ts +0 -103
- package/src/extensions/command/index.ts +0 -10
- package/src/extensions/command/state.ts +0 -90
- package/src/extensions/command/useCommandMenu.ts +0 -115
- package/src/stories/CommandMenu.stories.tsx +0 -158
- package/src/util/domino.ts +0 -51
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
//
|
|
6
6
|
|
|
7
7
|
import { next as A } from '@automerge/automerge';
|
|
8
|
-
import { type Extension, StateField } from '@codemirror/state';
|
|
8
|
+
import { type Extension, StateField, Transaction } from '@codemirror/state';
|
|
9
9
|
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { DocAccessor } from '@dxos/react-client/echo';
|
|
12
12
|
|
|
13
13
|
import { Cursor } from '../../util';
|
|
14
|
+
import { initialSync } from '../state';
|
|
14
15
|
|
|
15
16
|
import { cursorConverter } from './cursor';
|
|
16
17
|
import { type State, isReconcile, updateHeadsEffect } from './defs';
|
|
@@ -18,11 +19,13 @@ import { Syncer } from './sync';
|
|
|
18
19
|
|
|
19
20
|
export const automerge = (accessor: DocAccessor): Extension => {
|
|
20
21
|
const syncState = StateField.define<State>({
|
|
21
|
-
create: () =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
create: () => {
|
|
23
|
+
return {
|
|
24
|
+
path: accessor.path.slice(),
|
|
25
|
+
lastHeads: A.getHeads(accessor.handle.doc()!),
|
|
26
|
+
unreconciledTransactions: [],
|
|
27
|
+
};
|
|
28
|
+
},
|
|
26
29
|
|
|
27
30
|
update: (value, tr) => {
|
|
28
31
|
const result: State = {
|
|
@@ -64,6 +67,18 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
64
67
|
class {
|
|
65
68
|
constructor(private readonly _view: EditorView) {
|
|
66
69
|
accessor.handle.addListener('change', this._handleChange);
|
|
70
|
+
|
|
71
|
+
requestAnimationFrame(() => {
|
|
72
|
+
const value = DocAccessor.getValue<string>(accessor);
|
|
73
|
+
const current = this._view.state.doc.toString();
|
|
74
|
+
if (value !== current) {
|
|
75
|
+
// TODO(burdon): This attempts to set the initial state, but creates problems.
|
|
76
|
+
// this._view.dispatch({
|
|
77
|
+
// changes: { from: 0, to: this._view.state.doc.length, insert: value },
|
|
78
|
+
// annotations: initialSync,
|
|
79
|
+
// });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
destroy() {
|
|
@@ -77,9 +92,13 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
77
92
|
),
|
|
78
93
|
|
|
79
94
|
// Reconcile local updates.
|
|
80
|
-
EditorView.updateListener.of(({ view, changes }) => {
|
|
95
|
+
EditorView.updateListener.of(({ view, changes, transactions }) => {
|
|
81
96
|
if (!changes.empty) {
|
|
82
|
-
|
|
97
|
+
// Only reconcile if it's not an initial sync (to avoid loops)
|
|
98
|
+
const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
|
|
99
|
+
if (!isInitialSync) {
|
|
100
|
+
syncer.reconcile(view, true);
|
|
101
|
+
}
|
|
83
102
|
}
|
|
84
103
|
}),
|
|
85
104
|
];
|
|
@@ -8,6 +8,7 @@ import { next as A } from '@automerge/automerge';
|
|
|
8
8
|
import { type StateField } from '@codemirror/state';
|
|
9
9
|
import { type EditorView } from '@codemirror/view';
|
|
10
10
|
|
|
11
|
+
import { log } from '@dxos/log';
|
|
11
12
|
import { type IDocHandle } from '@dxos/react-client/echo';
|
|
12
13
|
|
|
13
14
|
import { type State, getLastHeads, getPath, isReconcile, reconcileAnnotation, updateHeads } from './defs';
|
|
@@ -27,7 +28,6 @@ export class Syncer {
|
|
|
27
28
|
) {}
|
|
28
29
|
|
|
29
30
|
reconcile(view: EditorView, editor: boolean): void {
|
|
30
|
-
// TODO(burdon): Better way to do mutex?
|
|
31
31
|
if (this._pending) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
@@ -41,7 +41,9 @@ export class Syncer {
|
|
|
41
41
|
this._pending = false;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
onEditorChange(view: EditorView): void {
|
|
44
|
+
private onEditorChange(view: EditorView): void {
|
|
45
|
+
log('onEditorChange');
|
|
46
|
+
|
|
45
47
|
// Apply the unreconciled transactions to the document.
|
|
46
48
|
const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
|
|
47
49
|
const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
|
|
@@ -54,7 +56,9 @@ export class Syncer {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
onAutomergeChange(view: EditorView): void {
|
|
59
|
+
private onAutomergeChange(view: EditorView): void {
|
|
60
|
+
log('onAutomergeChange');
|
|
61
|
+
|
|
58
62
|
// Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
|
|
59
63
|
const oldHeads = getLastHeads(view.state, this._state);
|
|
60
64
|
const newHeads = A.getHeads(this._handle.doc()!);
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
import { StateEffect } from '@codemirror/state';
|
|
6
6
|
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
7
7
|
|
|
8
|
-
import { Domino } from '
|
|
8
|
+
import { Domino } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
10
|
const lineHeight = 24;
|
|
11
11
|
|
|
12
12
|
export const scrollToBottomEffect = StateEffect.define<any>();
|
|
13
13
|
|
|
14
14
|
export type AutoScrollOptions = {
|
|
15
|
-
overscroll
|
|
16
|
-
throttle
|
|
15
|
+
overscroll?: number;
|
|
16
|
+
throttle?: number;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -24,7 +24,7 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
24
24
|
let isThrottled = false;
|
|
25
25
|
let isPinned = true;
|
|
26
26
|
let timeout: NodeJS.Timeout | undefined;
|
|
27
|
-
let buttonContainer: HTMLDivElement;
|
|
27
|
+
let buttonContainer: HTMLDivElement | undefined;
|
|
28
28
|
let lastScrollTop = 0;
|
|
29
29
|
let scrollCounter = 0;
|
|
30
30
|
|
|
@@ -50,28 +50,6 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
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
53
|
// Update listener for logging when scrolling is needed.
|
|
76
54
|
EditorView.updateListener.of((update) => {
|
|
77
55
|
// Listen for effects.
|
|
@@ -83,10 +61,9 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
83
61
|
}
|
|
84
62
|
});
|
|
85
63
|
|
|
64
|
+
// Maybe scroll if doc changed and pinned.
|
|
86
65
|
if (update.docChanged && isPinned && !isThrottled) {
|
|
87
66
|
const distanceFromBottom = calcDistance(update.view.scrollDOM);
|
|
88
|
-
|
|
89
|
-
// Keep pinned.
|
|
90
67
|
if (distanceFromBottom >= overscroll) {
|
|
91
68
|
isThrottled = true;
|
|
92
69
|
requestAnimationFrame(() => {
|
|
@@ -96,6 +73,7 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
96
73
|
// Reset throttle.
|
|
97
74
|
setTimeout(() => {
|
|
98
75
|
isThrottled = false;
|
|
76
|
+
scrollToBottom(update.view);
|
|
99
77
|
}, throttle);
|
|
100
78
|
}
|
|
101
79
|
}
|
|
@@ -126,6 +104,29 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
126
104
|
},
|
|
127
105
|
}),
|
|
128
106
|
|
|
107
|
+
// Scroll button.
|
|
108
|
+
ViewPlugin.fromClass(
|
|
109
|
+
class {
|
|
110
|
+
constructor(view: EditorView) {
|
|
111
|
+
const scroller = view.scrollDOM.parentElement;
|
|
112
|
+
buttonContainer = Domino.of('div')
|
|
113
|
+
.classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
|
|
114
|
+
.children(
|
|
115
|
+
Domino.of('button')
|
|
116
|
+
.classNames('dx-button bg-accentSurface')
|
|
117
|
+
.data('density', 'fine')
|
|
118
|
+
.children(Domino.of<any>('dx-icon').attributes({ icon: 'ph--arrow-down--regular' }))
|
|
119
|
+
.on('click', () => {
|
|
120
|
+
scrollToBottom(view);
|
|
121
|
+
}),
|
|
122
|
+
)
|
|
123
|
+
.build();
|
|
124
|
+
scroller?.appendChild(buttonContainer);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
|
|
129
|
+
// Styles.
|
|
129
130
|
EditorView.theme({
|
|
130
131
|
'.cm-scroller': {
|
|
131
132
|
paddingBottom: `${overscroll}px`,
|
|
@@ -138,7 +139,6 @@ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Pa
|
|
|
138
139
|
display: 'none',
|
|
139
140
|
},
|
|
140
141
|
|
|
141
|
-
// TODO(burdon): IconButton.
|
|
142
142
|
'.cm-scroll-button': {
|
|
143
143
|
position: 'absolute',
|
|
144
144
|
bottom: '0.5rem',
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
6
|
import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
7
|
-
import {
|
|
7
|
+
import { HighlightStyle, bracketMatching, syntaxHighlighting } from '@codemirror/language';
|
|
8
8
|
import { searchKeymap } from '@codemirror/search';
|
|
9
|
-
import { EditorState, type Extension } from '@codemirror/state';
|
|
10
|
-
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
9
|
+
import { type ChangeSpec, EditorState, type Extension, type TransactionSpec } from '@codemirror/state';
|
|
11
10
|
import {
|
|
12
11
|
EditorView,
|
|
13
12
|
type KeyBinding,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
placeholder,
|
|
21
20
|
scrollPastEnd,
|
|
22
21
|
} from '@codemirror/view';
|
|
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
|
|
|
@@ -29,7 +29,7 @@ import { type DocAccessor, type Space } from '@dxos/react-client/echo';
|
|
|
29
29
|
import { type Identity } from '@dxos/react-client/halo';
|
|
30
30
|
import { type ThemeMode } from '@dxos/react-ui';
|
|
31
31
|
import { type HuePalette } from '@dxos/react-ui-theme';
|
|
32
|
-
import { hexToHue,
|
|
32
|
+
import { hexToHue, isTruthy } from '@dxos/util';
|
|
33
33
|
|
|
34
34
|
import { editorGutter, editorMonospace } from '../defaults';
|
|
35
35
|
import { type ThemeStyles, defaultTheme } from '../styles';
|
|
@@ -42,7 +42,29 @@ import { focus } from './focus';
|
|
|
42
42
|
// Basic
|
|
43
43
|
//
|
|
44
44
|
|
|
45
|
-
export const
|
|
45
|
+
export const filterChars = (chars: RegExp) => {
|
|
46
|
+
return EditorState.transactionFilter.of((transaction) => {
|
|
47
|
+
if (!transaction.docChanged) return transaction;
|
|
48
|
+
|
|
49
|
+
const changes: ChangeSpec[] = [];
|
|
50
|
+
transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
51
|
+
const inserted = text.toString();
|
|
52
|
+
const filtered = inserted.replace(chars, '');
|
|
53
|
+
if (inserted !== filtered) {
|
|
54
|
+
changes.push({
|
|
55
|
+
from: fromB,
|
|
56
|
+
to: toB,
|
|
57
|
+
insert: filtered,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (changes.length) {
|
|
63
|
+
return [transaction, { changes, sequential: true } as TransactionSpec];
|
|
64
|
+
}
|
|
65
|
+
return transaction;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
46
68
|
|
|
47
69
|
/**
|
|
48
70
|
* https://codemirror.net/docs/extensions
|
|
@@ -70,6 +92,7 @@ export type BasicExtensionsOptions = {
|
|
|
70
92
|
/** If true user cannot edit the text, but they can still select and copy it. */
|
|
71
93
|
readOnly?: boolean;
|
|
72
94
|
search?: boolean;
|
|
95
|
+
/** NOTE: Do not use with stack sections. */
|
|
73
96
|
scrollPastEnd?: boolean;
|
|
74
97
|
standardKeymap?: boolean;
|
|
75
98
|
tabSize?: number;
|
|
@@ -139,9 +162,9 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
139
162
|
preventDefault: true,
|
|
140
163
|
run: () => true,
|
|
141
164
|
},
|
|
142
|
-
].filter(
|
|
165
|
+
].filter(isTruthy),
|
|
143
166
|
),
|
|
144
|
-
].filter(
|
|
167
|
+
].filter(isTruthy);
|
|
145
168
|
};
|
|
146
169
|
|
|
147
170
|
//
|
|
@@ -179,20 +202,26 @@ export const fullWidth: ThemeExtensionsOptions['slots'] = {
|
|
|
179
202
|
|
|
180
203
|
export const defaultThemeSlots = grow;
|
|
181
204
|
|
|
205
|
+
export const defaultStyles = {
|
|
206
|
+
dark: vscodeDarkStyle,
|
|
207
|
+
light: vscodeLightStyle,
|
|
208
|
+
};
|
|
209
|
+
|
|
182
210
|
/**
|
|
183
211
|
* https://codemirror.net/examples/styling
|
|
184
212
|
*/
|
|
185
213
|
export const createThemeExtensions = ({
|
|
186
214
|
themeMode,
|
|
187
215
|
styles,
|
|
188
|
-
syntaxHighlighting:
|
|
189
|
-
slots:
|
|
216
|
+
syntaxHighlighting: syntaxHighlightingProp,
|
|
217
|
+
slots: slotsParam,
|
|
190
218
|
}: ThemeExtensionsOptions = {}): Extension => {
|
|
191
|
-
const slots = defaultsDeep({},
|
|
219
|
+
const slots = defaultsDeep({}, slotsParam, defaultThemeSlots);
|
|
192
220
|
return [
|
|
193
221
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
194
222
|
EditorView.baseTheme(styles ? merge({}, defaultTheme, styles) : defaultTheme),
|
|
195
|
-
|
|
223
|
+
syntaxHighlightingProp &&
|
|
224
|
+
syntaxHighlighting(HighlightStyle.define(themeMode === 'dark' ? defaultStyles.dark : defaultStyles.light)),
|
|
196
225
|
slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
|
|
197
226
|
slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
|
|
198
227
|
slots.scroll?.className &&
|
|
@@ -203,7 +232,7 @@ export const createThemeExtensions = ({
|
|
|
203
232
|
}
|
|
204
233
|
},
|
|
205
234
|
),
|
|
206
|
-
].filter(
|
|
235
|
+
].filter(isTruthy);
|
|
207
236
|
};
|
|
208
237
|
|
|
209
238
|
//
|
package/src/extensions/focus.ts
CHANGED
|
@@ -15,6 +15,7 @@ export const focusField = StateField.define<boolean>({
|
|
|
15
15
|
return effect.value;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
|
|
18
19
|
return value;
|
|
19
20
|
},
|
|
20
21
|
});
|
|
@@ -25,11 +26,11 @@ export const focusField = StateField.define<boolean>({
|
|
|
25
26
|
export const focus = [
|
|
26
27
|
focusField,
|
|
27
28
|
EditorView.domEventHandlers({
|
|
28
|
-
focus: (
|
|
29
|
-
|
|
29
|
+
focus: (_event, view) => {
|
|
30
|
+
requestAnimationFrame(() => view.dispatch({ effects: focusEffect.of(true) }));
|
|
30
31
|
},
|
|
31
|
-
blur: (
|
|
32
|
-
|
|
32
|
+
blur: (_event, view) => {
|
|
33
|
+
requestAnimationFrame(() => view.dispatch({ effects: focusEffect.of(false) }));
|
|
33
34
|
},
|
|
34
35
|
}),
|
|
35
36
|
];
|
|
@@ -7,16 +7,15 @@ import { type Extension } from '@codemirror/state';
|
|
|
7
7
|
import { EditorView } from '@codemirror/view';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
|
|
10
|
-
import { Icon } from '@dxos/react-ui';
|
|
10
|
+
import { Domino, Icon } from '@dxos/react-ui';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { renderRoot } from '../util';
|
|
13
13
|
|
|
14
14
|
export type FoldingOptions = {};
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* https://codemirror.net/examples/gutter
|
|
18
18
|
*/
|
|
19
|
-
// TODO(burdon): Remember folding state (to state).
|
|
20
19
|
export const folding = (_props: FoldingOptions = {}): Extension => [
|
|
21
20
|
codeFolding({
|
|
22
21
|
placeholderDOM: () => {
|
|
@@ -25,10 +24,9 @@ export const folding = (_props: FoldingOptions = {}): Extension => [
|
|
|
25
24
|
}),
|
|
26
25
|
foldGutter({
|
|
27
26
|
markerDOM: (open) => {
|
|
28
|
-
// TODO(burdon): Use sprite directly.
|
|
29
|
-
const el = Domino.of('div').classNames('flex h-full items-center').build();
|
|
30
27
|
return renderRoot(
|
|
31
|
-
|
|
28
|
+
Domino.of('div').classNames('flex h-full items-center').build(),
|
|
29
|
+
// TODO(burdon): Use sprite directly.
|
|
32
30
|
<Icon icon='ph--caret-right--bold' size={3} classNames={['mx-3 cursor-pointer', open && 'rotate-90']} />,
|
|
33
31
|
);
|
|
34
32
|
},
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
WidgetType,
|
|
14
14
|
} from '@codemirror/view';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { getHashStyles, mx } from '@dxos/react-ui-theme';
|
|
17
17
|
|
|
18
18
|
class TagWidget extends WidgetType {
|
|
19
19
|
constructor(private _text: string) {
|
|
@@ -22,7 +22,7 @@ class TagWidget extends WidgetType {
|
|
|
22
22
|
|
|
23
23
|
toDOM(): HTMLSpanElement {
|
|
24
24
|
const span = document.createElement('span');
|
|
25
|
-
span.className = mx('cm-tag',
|
|
25
|
+
span.className = mx('cm-tag', getHashStyles(this._text).surface);
|
|
26
26
|
span.textContent = this._text;
|
|
27
27
|
return span;
|
|
28
28
|
}
|
package/src/extensions/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ export * from './autoscroll';
|
|
|
8
8
|
export * from './automerge';
|
|
9
9
|
export * from './awareness';
|
|
10
10
|
export * from './blast';
|
|
11
|
-
export * from './command';
|
|
12
11
|
export * from './comments';
|
|
13
12
|
export * from './debug';
|
|
14
13
|
export * from './dnd';
|
|
@@ -22,7 +21,9 @@ export * from './markdown';
|
|
|
22
21
|
export * from './mention';
|
|
23
22
|
export * from './modes';
|
|
24
23
|
export * from './outliner';
|
|
24
|
+
export * from './popover';
|
|
25
25
|
export * from './preview';
|
|
26
26
|
export * from './selection';
|
|
27
|
+
export * from './state';
|
|
27
28
|
export * from './tags';
|
|
28
29
|
export * from './typewriter';
|
package/src/extensions/json.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { type LintSource, linter } from '@codemirror/lint';
|
|
|
7
7
|
import { type Extension } from '@codemirror/state';
|
|
8
8
|
import Ajv, { type ValidateFunction } from 'ajv';
|
|
9
9
|
|
|
10
|
-
import { type JsonSchemaType } from '@dxos/echo
|
|
10
|
+
import { type JsonSchemaType } from '@dxos/echo/internal';
|
|
11
11
|
|
|
12
12
|
export type JsonExtensionsOptions = {
|
|
13
13
|
schema?: JsonSchemaType;
|
|
@@ -4,14 +4,16 @@
|
|
|
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';
|
|
12
14
|
import { type MarkdownConfig } from '@lezer/markdown';
|
|
13
15
|
|
|
14
|
-
import {
|
|
16
|
+
import { isTruthy } from '@dxos/util';
|
|
15
17
|
|
|
16
18
|
import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
|
|
17
19
|
|
|
@@ -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
|
-
].filter(
|
|
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
|
};
|
package/src/extensions/modes.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { singleValueFacet } from '../util';
|
|
|
11
11
|
|
|
12
12
|
export type EditorInputConfig = {
|
|
13
13
|
type?: string;
|
|
14
|
-
|
|
14
|
+
ignoreEscape?: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export const editorInputMode = singleValueFacet<EditorInputConfig>({});
|
|
@@ -26,7 +26,7 @@ export const InputModeExtensions: { [mode: string]: Extension } = {
|
|
|
26
26
|
vim: [
|
|
27
27
|
// https://github.com/replit/codemirror-vim
|
|
28
28
|
vim(),
|
|
29
|
-
editorInputMode.of({ type: 'vim',
|
|
29
|
+
editorInputMode.of({ type: 'vim', ignoreEscape: true }),
|
|
30
30
|
keymap.of([
|
|
31
31
|
{
|
|
32
32
|
key: 'Alt-Escape',
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Extension } from '@codemirror/state';
|
|
5
6
|
import { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
|
6
7
|
|
|
7
8
|
import { type CleanupFn, addEventListener } from '@dxos/async';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export type FloatingMenuOptions = {
|
|
10
|
+
export type MenuOptions = {
|
|
12
11
|
icon?: string;
|
|
13
12
|
height?: number;
|
|
14
13
|
padding?: number;
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
// TODO(burdon): Replace with popover.
|
|
17
|
+
export const menu = (options: MenuOptions = {}): Extension => [
|
|
18
18
|
ViewPlugin.fromClass(
|
|
19
19
|
class {
|
|
20
20
|
view: EditorView;
|
|
@@ -36,7 +36,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
36
36
|
icon.setAttribute('icon', options.icon ?? 'ph--dots-three-vertical--regular');
|
|
37
37
|
|
|
38
38
|
this.tag = document.createElement('dx-anchor');
|
|
39
|
-
this.tag.classList.add('cm-
|
|
39
|
+
this.tag.classList.add('cm-popover-trigger');
|
|
40
40
|
this.tag.appendChild(icon);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -63,12 +63,12 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// TODO(burdon): Timer to fade in/out.
|
|
66
|
-
if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
|
|
66
|
+
/*if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
|
|
67
67
|
this.tag.style.display = 'none';
|
|
68
68
|
this.tag.classList.add('opacity-10');
|
|
69
69
|
} else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
|
|
70
70
|
this.tag.style.display = '';
|
|
71
|
-
} else if (
|
|
71
|
+
} else */ if (
|
|
72
72
|
update.docChanged ||
|
|
73
73
|
update.focusChanged ||
|
|
74
74
|
update.geometryChanged ||
|
|
@@ -111,7 +111,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
111
111
|
),
|
|
112
112
|
|
|
113
113
|
EditorView.theme({
|
|
114
|
-
'.cm-
|
|
114
|
+
'.cm-popover-trigger': {
|
|
115
115
|
position: 'fixed',
|
|
116
116
|
padding: '0',
|
|
117
117
|
border: 'none',
|
|
@@ -121,7 +121,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
|
|
|
121
121
|
width: '2rem',
|
|
122
122
|
height: '2rem',
|
|
123
123
|
},
|
|
124
|
-
'&:focus-within .cm-
|
|
124
|
+
'&:focus-within .cm-popover-trigger': {
|
|
125
125
|
opacity: '1',
|
|
126
126
|
},
|
|
127
127
|
}),
|
|
@@ -7,11 +7,11 @@ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate
|
|
|
7
7
|
|
|
8
8
|
import { mx } from '@dxos/react-ui-theme';
|
|
9
9
|
|
|
10
|
-
import { floatingMenu } from '../command';
|
|
11
10
|
import { decorateMarkdown } from '../markdown';
|
|
12
11
|
|
|
13
12
|
import { commands } from './commands';
|
|
14
13
|
import { editor } from './editor';
|
|
14
|
+
import { menu } from './menu';
|
|
15
15
|
import { selectionCompartment, selectionEquals, selectionFacet } from './selection';
|
|
16
16
|
import { outlinerTree, treeFacet } from './tree';
|
|
17
17
|
|
|
@@ -52,7 +52,7 @@ export const outliner = (_options: OutlinerProps = {}): Extension => [
|
|
|
52
52
|
editor(),
|
|
53
53
|
|
|
54
54
|
// Floating menu.
|
|
55
|
-
|
|
55
|
+
menu(),
|
|
56
56
|
|
|
57
57
|
// Line decorations.
|
|
58
58
|
decorations(),
|