@dxos/react-ui-editor 0.3.11-main.0e40963 → 0.3.11-main.121f5a8
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 +1997 -1487
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +16 -13
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -1
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
- package/dist/types/src/{hooks/yjs → components/TextEditor}/yjs.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +24 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +3 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts +14 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness.d.ts +48 -0
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts +4 -5
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/code.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +32 -17
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor.d.ts +25 -0
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -1
- package/dist/types/src/extensions/image.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +5 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/link.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +2 -5
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/mention.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts +4 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/index.d.ts +2 -0
- package/dist/types/src/extensions/yjs/index.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts +4 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge.d.ts +4 -0
- package/dist/types/src/hooks/automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/defs.d.ts +28 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +2 -31
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/{yjs/space-provider.d.ts → yjs.d.ts} +9 -8
- package/dist/types/src/hooks/yjs.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/cursors.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts +2 -2
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +7 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +32 -30
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +77 -80
- package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
- package/src/components/TextEditor/TextEditor.tsx +124 -92
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
- package/src/components/TextEditor/codemirror.stories.ts +3 -2
- package/src/components/TextEditor/comments.stories.tsx +357 -0
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/extensions/autocomplete.ts +23 -19
- package/src/extensions/automerge/automerge.ts +88 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +41 -0
- package/src/extensions/automerge/index.ts +6 -0
- package/src/{hooks → extensions}/automerge/semaphore.ts +34 -29
- package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
- package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
- package/src/extensions/awareness.ts +327 -0
- package/src/extensions/basic.ts +22 -17
- package/src/extensions/code.ts +28 -23
- package/src/extensions/comments.ts +252 -215
- package/src/extensions/cursor.ts +50 -0
- package/src/extensions/hr.ts +50 -52
- package/src/extensions/image.ts +40 -22
- package/src/extensions/index.ts +5 -1
- package/src/extensions/link.ts +86 -88
- package/src/extensions/markdown/bundle.ts +18 -24
- package/src/extensions/markdown/highlight.ts +3 -12
- package/src/extensions/mention.ts +9 -2
- package/src/extensions/outliner.ts +12 -0
- package/src/extensions/table.ts +10 -4
- package/src/extensions/tasklist.ts +78 -88
- package/src/extensions/yjs/cursor.ts +22 -0
- package/src/extensions/yjs/index.ts +5 -0
- package/src/{hooks → extensions}/yjs/yjs.test.ts +2 -26
- package/src/extensions/yjs/yjs.ts +16 -0
- package/src/hooks/automerge.ts +53 -0
- package/src/hooks/awareness-provider.ts +120 -0
- package/src/hooks/defs.ts +47 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/useTextModel.ts +9 -129
- package/src/hooks/{yjs/space-provider.ts → yjs.ts} +50 -19
- package/src/index.ts +3 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/markdown.ts +0 -2
- package/src/styles/tokens.ts +3 -0
- package/src/testing/replicator.ts +10 -15
- package/src/themes/default.ts +51 -78
- package/src/util.ts +41 -0
- package/dist/types/src/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/extensions/util.d.ts +0 -5
- package/dist/types/src/extensions/util.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/semaphore.d.ts +0 -19
- package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/index.d.ts +0 -3
- package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
- package/src/extensions/mermaid.ts +0 -11
- package/src/extensions/util.ts +0 -18
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/automerge/plugin.ts +0 -134
- package/src/hooks/yjs/index.ts +0 -6
- /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
- /package/dist/types/src/{hooks → extensions}/yjs/yjs.test.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
|
@@ -7,783 +7,976 @@ import { tags as tags2 } from "@lezer/highlight";
|
|
|
7
7
|
|
|
8
8
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
9
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
-
import { EditorView as
|
|
10
|
+
import { EditorView as EditorView13 } from "@codemirror/view";
|
|
11
11
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
12
12
|
import { vim } from "@replit/codemirror-vim";
|
|
13
13
|
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
-
import React, { forwardRef, useCallback, useEffect as
|
|
15
|
-
import {
|
|
16
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
-
import {
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect as useEffect3, useImperativeHandle, useRef, useState } from "react";
|
|
15
|
+
import { log as log2 } from "@dxos/log";
|
|
16
|
+
import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
|
|
17
|
+
import { inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
18
18
|
|
|
19
19
|
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
20
20
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
21
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
21
22
|
import { keymap } from "@codemirror/view";
|
|
22
23
|
var autocomplete = ({ onSearch }) => {
|
|
23
24
|
return [
|
|
24
25
|
// https://codemirror.net/docs/ref/#view.keymap
|
|
25
26
|
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
27
|
+
// TODO(burdon): Set custom keymap.
|
|
26
28
|
keymap.of(completionKeymap),
|
|
27
29
|
// https://codemirror.net/examples/autocompletion
|
|
28
30
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
29
31
|
autocompletion({
|
|
30
|
-
|
|
32
|
+
activateOnTyping: true,
|
|
33
|
+
// closeOnBlur: false,
|
|
31
34
|
// defaultKeymap: false,
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
from: match.from,
|
|
44
|
-
options: onSearch(match.text.toLowerCase())
|
|
45
|
-
};
|
|
35
|
+
// TODO(burdon): Styles/fragments.
|
|
36
|
+
tooltipClass: () => "shadow rounded"
|
|
37
|
+
}),
|
|
38
|
+
// TODO(burdon): Option to create new page?
|
|
39
|
+
// TODO(burdon): Optional decoration via addToOptions
|
|
40
|
+
markdownLanguage.data.of({
|
|
41
|
+
autocomplete: (context) => {
|
|
42
|
+
const match = context.matchBefore(/\w*/);
|
|
43
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
44
|
+
return null;
|
|
46
45
|
}
|
|
47
|
-
|
|
46
|
+
return {
|
|
47
|
+
from: match.from,
|
|
48
|
+
options: onSearch(match.text.toLowerCase())
|
|
49
|
+
};
|
|
50
|
+
}
|
|
48
51
|
})
|
|
49
52
|
];
|
|
50
53
|
};
|
|
51
54
|
|
|
52
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import { drawSelection, EditorView, placeholder } from "@codemirror/view";
|
|
57
|
-
var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
|
|
58
|
-
EditorView.lineWrapping,
|
|
59
|
-
// TODO(burdon): Compare with minimalSetup.
|
|
60
|
-
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
61
|
-
bracketMatching(),
|
|
62
|
-
closeBrackets(),
|
|
63
|
-
drawSelection(),
|
|
64
|
-
_placeholder && placeholder(_placeholder),
|
|
65
|
-
// TODO(burdon): Is this required?
|
|
66
|
-
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
67
|
-
].filter(Boolean);
|
|
68
|
-
|
|
69
|
-
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
70
|
-
import { EditorView as EditorView2, keymap as keymap2 } from "@codemirror/view";
|
|
71
|
-
import defaultsDeep from "lodash.defaultsdeep";
|
|
55
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
56
|
+
import { StateField } from "@codemirror/state";
|
|
57
|
+
import { EditorView, ViewPlugin } from "@codemirror/view";
|
|
58
|
+
import { next as A } from "@dxos/automerge/automerge";
|
|
72
59
|
import { invariant } from "@dxos/invariant";
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-1,
|
|
90
|
-
1
|
|
91
|
-
],
|
|
92
|
-
y: [
|
|
93
|
-
-3.5,
|
|
94
|
-
-1.5
|
|
95
|
-
]
|
|
60
|
+
|
|
61
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
|
|
62
|
+
import get from "lodash.get";
|
|
63
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
64
|
+
var cursorConverter = (handle, path) => ({
|
|
65
|
+
// TODO(burdon): Handle assoc to associate with a previous character.
|
|
66
|
+
toCursor: (pos) => {
|
|
67
|
+
const doc = handle.docSync();
|
|
68
|
+
if (!doc) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
const value = get(doc, path);
|
|
72
|
+
if (typeof value === "string" && value.length <= pos) {
|
|
73
|
+
return "end";
|
|
74
|
+
}
|
|
75
|
+
return automerge.getCursor(doc, path.slice(), pos);
|
|
96
76
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
y: y - top
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
element,
|
|
113
|
-
point
|
|
114
|
-
};
|
|
115
|
-
};
|
|
116
|
-
return [
|
|
117
|
-
// Cursor moved.
|
|
118
|
-
EditorView2.updateListener.of((update4) => {
|
|
119
|
-
if (blaster?.node !== update4.view.scrollDOM) {
|
|
120
|
-
if (blaster) {
|
|
121
|
-
blaster.destroy();
|
|
122
|
-
}
|
|
123
|
-
blaster = new Blaster(update4.view.scrollDOM, defaultsDeep({
|
|
124
|
-
particleGravity: 0.2,
|
|
125
|
-
particleShrinkRate: 0.995,
|
|
126
|
-
color: () => [
|
|
127
|
-
100 + Math.random() * 100,
|
|
128
|
-
0,
|
|
129
|
-
0
|
|
130
|
-
]
|
|
131
|
-
}, options, defaultOptions));
|
|
132
|
-
blaster.initialize();
|
|
133
|
-
blaster.start();
|
|
77
|
+
fromCursor: (cursor) => {
|
|
78
|
+
if (cursor === "") {
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
const doc = handle.docSync();
|
|
82
|
+
if (!doc) {
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
if (cursor === "end") {
|
|
86
|
+
const value = get(doc, path);
|
|
87
|
+
if (typeof value === "string") {
|
|
88
|
+
return value.length;
|
|
134
89
|
} else {
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
const current = update4.state.selection.main.head;
|
|
138
|
-
if (current !== last) {
|
|
139
|
-
last = current;
|
|
140
|
-
const { element, point } = getPoint(update4.view, current - 1);
|
|
141
|
-
if (element && point) {
|
|
142
|
-
blaster.spawn({
|
|
143
|
-
element,
|
|
144
|
-
point
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}),
|
|
149
|
-
keymap2.of([
|
|
150
|
-
{
|
|
151
|
-
any: (_, event) => {
|
|
152
|
-
if (blaster) {
|
|
153
|
-
if (event.key === "Enter" && event.shiftKey) {
|
|
154
|
-
blaster.shake({
|
|
155
|
-
time: 0.8
|
|
156
|
-
});
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
])
|
|
164
|
-
];
|
|
165
|
-
};
|
|
166
|
-
var Blaster = class {
|
|
167
|
-
constructor(_node, _options) {
|
|
168
|
-
this._node = _node;
|
|
169
|
-
this._options = _options;
|
|
170
|
-
this._running = false;
|
|
171
|
-
this._shakeTime = 0;
|
|
172
|
-
this._shakeTimeMax = 0;
|
|
173
|
-
this._particles = [];
|
|
174
|
-
this._particlePointer = 0;
|
|
175
|
-
this._lastPoint = {
|
|
176
|
-
x: 0,
|
|
177
|
-
y: 0
|
|
178
|
-
};
|
|
179
|
-
this.shake = throttle(({ time }) => {
|
|
180
|
-
this._shakeTime = this._shakeTimeMax || time;
|
|
181
|
-
this._shakeTimeMax = time;
|
|
182
|
-
}, 100);
|
|
183
|
-
this.spawn = throttle(({ element, point }) => {
|
|
184
|
-
const color = getRGBComponents(element, this._options.color);
|
|
185
|
-
const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
|
|
186
|
-
const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
|
|
187
|
-
this._lastPoint = point;
|
|
188
|
-
for (let i = numParticles; i--; i > 0) {
|
|
189
|
-
this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);
|
|
190
|
-
this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;
|
|
90
|
+
return 0;
|
|
191
91
|
}
|
|
192
|
-
}, 100);
|
|
193
|
-
this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
|
|
194
|
-
}
|
|
195
|
-
get node() {
|
|
196
|
-
return this._node;
|
|
197
|
-
}
|
|
198
|
-
initialize() {
|
|
199
|
-
invariant(!this._canvas && !this._ctx, void 0, {
|
|
200
|
-
F: __dxlog_file,
|
|
201
|
-
L: 138,
|
|
202
|
-
S: this,
|
|
203
|
-
A: [
|
|
204
|
-
"!this._canvas && !this._ctx",
|
|
205
|
-
""
|
|
206
|
-
]
|
|
207
|
-
});
|
|
208
|
-
this._canvas = document.createElement("canvas");
|
|
209
|
-
this._canvas.id = "code-blast-canvas";
|
|
210
|
-
this._canvas.style.position = "absolute";
|
|
211
|
-
this._canvas.style.zIndex = "0";
|
|
212
|
-
this._canvas.style.pointerEvents = "none";
|
|
213
|
-
this._ctx = this._canvas.getContext("2d");
|
|
214
|
-
const parent = this._node.parentElement?.parentElement;
|
|
215
|
-
parent?.appendChild(this._canvas);
|
|
216
|
-
this.resize();
|
|
217
|
-
}
|
|
218
|
-
destroy() {
|
|
219
|
-
this.stop();
|
|
220
|
-
if (this._canvas) {
|
|
221
|
-
this._canvas.remove();
|
|
222
|
-
this._canvas = void 0;
|
|
223
|
-
this._ctx = void 0;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
resize() {
|
|
227
|
-
if (this._node.parentElement && this._canvas) {
|
|
228
|
-
const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
|
|
229
|
-
this._canvas.style.top = `${y}px`;
|
|
230
|
-
this._canvas.style.left = `${x}px`;
|
|
231
|
-
this._canvas.width = width;
|
|
232
|
-
this._canvas.height = height;
|
|
233
92
|
}
|
|
93
|
+
return automerge.getCursorPosition(doc, path.slice(), cursor);
|
|
234
94
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
98
|
+
import { Annotation, StateEffect } from "@codemirror/state";
|
|
99
|
+
var effectType = StateEffect.define({});
|
|
100
|
+
var reconcileAnnotationType = Annotation.define();
|
|
101
|
+
var getPath = (state, field) => state.field(field).path;
|
|
102
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
103
|
+
var updateHeads = (newHeads) => effectType.of({
|
|
104
|
+
newHeads
|
|
105
|
+
});
|
|
106
|
+
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
107
|
+
|
|
108
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
109
|
+
import { next as automerge2 } from "@dxos/automerge/automerge";
|
|
110
|
+
|
|
111
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
112
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
113
|
+
var updateAutomerge = (field, handle, transactions, state) => {
|
|
114
|
+
const { lastHeads, path } = state.field(field);
|
|
115
|
+
let hasChanges = false;
|
|
116
|
+
for (const tr of transactions) {
|
|
117
|
+
tr.changes.iterChanges(() => {
|
|
118
|
+
hasChanges = true;
|
|
244
119
|
});
|
|
245
|
-
this._running = true;
|
|
246
|
-
this.loop();
|
|
247
120
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this._node.style.transform = "translate(0px, 0px)";
|
|
121
|
+
if (!hasChanges) {
|
|
122
|
+
return void 0;
|
|
251
123
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
124
|
+
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
125
|
+
for (const tr of transactions) {
|
|
126
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
127
|
+
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
128
|
+
});
|
|
255
129
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
for (let i = this._particles.length; i--; i > 0) {
|
|
273
|
-
const particle = this._particles[i];
|
|
274
|
-
if (!particle) {
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
if (particle.alpha < 0.01 || particle.size <= 0.5) {
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
this._effect.update(this._ctx, particle);
|
|
130
|
+
});
|
|
131
|
+
return newHeads ?? void 0;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-codemirror.ts
|
|
135
|
+
import { ChangeSet } from "@codemirror/state";
|
|
136
|
+
var updateCodeMirror = (view, selection, target, patches) => {
|
|
137
|
+
for (const patch of patches) {
|
|
138
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
139
|
+
if (changeSpec != null) {
|
|
140
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
141
|
+
selection = selection.map(changeSet, 1);
|
|
142
|
+
view.dispatch({
|
|
143
|
+
changes: changeSet,
|
|
144
|
+
annotations: reconcileAnnotationType.of({})
|
|
145
|
+
});
|
|
281
146
|
}
|
|
282
147
|
}
|
|
148
|
+
view.dispatch({
|
|
149
|
+
selection,
|
|
150
|
+
annotations: reconcileAnnotationType.of({})
|
|
151
|
+
});
|
|
283
152
|
};
|
|
284
|
-
var
|
|
285
|
-
|
|
286
|
-
|
|
153
|
+
var handlePatch = (patch, target, state) => {
|
|
154
|
+
if (patch.action === "insert") {
|
|
155
|
+
return handleInsert(target, patch);
|
|
156
|
+
} else if (patch.action === "splice") {
|
|
157
|
+
return handleSplice(target, patch);
|
|
158
|
+
} else if (patch.action === "del") {
|
|
159
|
+
return handleDel(target, patch);
|
|
160
|
+
} else if (patch.action === "put") {
|
|
161
|
+
return handlePut(target, patch, state);
|
|
162
|
+
} else {
|
|
163
|
+
return null;
|
|
287
164
|
}
|
|
288
165
|
};
|
|
289
|
-
var
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
y: y + 10,
|
|
294
|
-
vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
|
|
295
|
-
vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
|
|
296
|
-
size: random(this._options.particleSize.min, this._options.particleSize.max),
|
|
297
|
-
color,
|
|
298
|
-
alpha: 1
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
update(ctx, particle) {
|
|
302
|
-
particle.vy += this._options.particleGravity;
|
|
303
|
-
particle.x += particle.vx;
|
|
304
|
-
particle.y += particle.vy;
|
|
305
|
-
particle.alpha *= this._options.particleAlphaFadeout;
|
|
306
|
-
ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
|
|
307
|
-
ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);
|
|
166
|
+
var handleInsert = (target, patch) => {
|
|
167
|
+
const index = charPath(target, patch.path);
|
|
168
|
+
if (index == null) {
|
|
169
|
+
return [];
|
|
308
170
|
}
|
|
171
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
172
|
+
return [
|
|
173
|
+
{
|
|
174
|
+
from: index,
|
|
175
|
+
to: index,
|
|
176
|
+
insert: text
|
|
177
|
+
}
|
|
178
|
+
];
|
|
309
179
|
};
|
|
310
|
-
var
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
y: y + 10,
|
|
315
|
-
vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
|
|
316
|
-
vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
|
|
317
|
-
size: random(this._options.particleSize.min, this._options.particleSize.max),
|
|
318
|
-
color,
|
|
319
|
-
alpha: 1,
|
|
320
|
-
theta: random(0, 360) * Math.PI / 180,
|
|
321
|
-
drag: 0.92,
|
|
322
|
-
wander: 0.15
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
update(ctx, particle) {
|
|
326
|
-
particle.vy += this._options.particleGravity;
|
|
327
|
-
particle.x += particle.vx;
|
|
328
|
-
particle.y += particle.vy;
|
|
329
|
-
particle.vx *= particle.drag;
|
|
330
|
-
particle.vy *= particle.drag;
|
|
331
|
-
particle.theta += random(-0.5, 0.5);
|
|
332
|
-
particle.vx += Math.sin(particle.theta) * 0.1;
|
|
333
|
-
particle.vy += Math.cos(particle.theta) * 0.1;
|
|
334
|
-
particle.size *= this._options.particleShrinkRate;
|
|
335
|
-
particle.alpha *= this._options.particleAlphaFadeout;
|
|
336
|
-
ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
|
|
337
|
-
ctx.beginPath();
|
|
338
|
-
ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);
|
|
339
|
-
ctx.fill();
|
|
180
|
+
var handleSplice = (target, patch) => {
|
|
181
|
+
const index = charPath(target, patch.path);
|
|
182
|
+
if (index == null) {
|
|
183
|
+
return [];
|
|
340
184
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (!wait) {
|
|
346
|
-
callback.apply(this, args);
|
|
347
|
-
wait = true;
|
|
348
|
-
setTimeout(() => {
|
|
349
|
-
wait = false;
|
|
350
|
-
}, limit);
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
from: index,
|
|
188
|
+
insert: patch.value
|
|
351
189
|
}
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
var
|
|
355
|
-
|
|
356
|
-
|
|
190
|
+
];
|
|
191
|
+
};
|
|
192
|
+
var handleDel = (target, patch) => {
|
|
193
|
+
const index = charPath(target, patch.path);
|
|
194
|
+
if (index == null) {
|
|
195
|
+
return [];
|
|
357
196
|
}
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
197
|
+
const length = patch.length || 1;
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
from: index,
|
|
201
|
+
to: index + length
|
|
363
202
|
}
|
|
203
|
+
];
|
|
204
|
+
};
|
|
205
|
+
var handlePut = (target, patch, state) => {
|
|
206
|
+
const index = charPath(target, [
|
|
207
|
+
...patch.path,
|
|
208
|
+
0
|
|
209
|
+
]);
|
|
210
|
+
if (index == null) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
const length = state.doc.length;
|
|
214
|
+
if (typeof patch.value !== "string") {
|
|
215
|
+
return [];
|
|
364
216
|
}
|
|
365
217
|
return [
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
218
|
+
{
|
|
219
|
+
from: 0,
|
|
220
|
+
to: length,
|
|
221
|
+
insert: patch.value
|
|
222
|
+
}
|
|
369
223
|
];
|
|
370
224
|
};
|
|
371
|
-
var
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
min = 0;
|
|
225
|
+
var charPath = (textPath, candidatePath) => {
|
|
226
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
227
|
+
return null;
|
|
375
228
|
}
|
|
376
|
-
|
|
229
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
230
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
235
|
+
if (typeof index === "number") {
|
|
236
|
+
return index;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
377
239
|
};
|
|
378
240
|
|
|
379
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
241
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
242
|
+
var PatchSemaphore = class {
|
|
243
|
+
// prettier-ignore
|
|
244
|
+
constructor(_handle, _state) {
|
|
245
|
+
this._handle = _handle;
|
|
246
|
+
this._state = _state;
|
|
247
|
+
this._inReconcile = false;
|
|
248
|
+
}
|
|
249
|
+
// NOTE: Cannot destruct view.state.
|
|
250
|
+
reconcile(view) {
|
|
251
|
+
if (this._inReconcile) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this._inReconcile = true;
|
|
255
|
+
const path = getPath(view.state, this._state);
|
|
256
|
+
const oldHeads = getLastHeads(view.state, this._state);
|
|
257
|
+
let selection = view.state.selection;
|
|
258
|
+
const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
259
|
+
const toInvert = transactions.slice().reverse();
|
|
260
|
+
for (const tx of toInvert) {
|
|
261
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
262
|
+
selection = selection.map(inverted);
|
|
263
|
+
view.dispatch({
|
|
264
|
+
changes: inverted,
|
|
265
|
+
annotations: reconcileAnnotationType.of(true)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
|
|
269
|
+
if (newHeads === null || newHeads === void 0) {
|
|
270
|
+
newHeads = automerge2.getHeads(this._handle.docSync());
|
|
271
|
+
}
|
|
272
|
+
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
|
|
273
|
+
updateCodeMirror(view, selection, path, diff);
|
|
274
|
+
view.dispatch({
|
|
275
|
+
effects: updateHeads(newHeads),
|
|
276
|
+
annotations: reconcileAnnotationType.of({})
|
|
277
|
+
});
|
|
278
|
+
this._inReconcile = false;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
383
281
|
|
|
384
|
-
// packages/ui/react-ui-editor/src/
|
|
385
|
-
import {
|
|
386
|
-
var
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
"-ml-[10px]"
|
|
390
|
-
],
|
|
391
|
-
2: [
|
|
392
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
|
|
393
|
-
"-ml-[8px]"
|
|
394
|
-
],
|
|
395
|
-
3: [
|
|
396
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
397
|
-
"-ml-[7px]"
|
|
398
|
-
],
|
|
399
|
-
4: [
|
|
400
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
401
|
-
"-ml-[6px]"
|
|
402
|
-
],
|
|
403
|
-
5: [
|
|
404
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
405
|
-
"-ml-[5px]"
|
|
406
|
-
],
|
|
407
|
-
6: [
|
|
408
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline",
|
|
409
|
-
"-ml-[4px]"
|
|
410
|
-
]
|
|
282
|
+
// packages/ui/react-ui-editor/src/extensions/cursor.ts
|
|
283
|
+
import { Facet } from "@codemirror/state";
|
|
284
|
+
var defaultCursorConverter = {
|
|
285
|
+
toCursor: (position) => position.toString(),
|
|
286
|
+
fromCursor: (cursor) => parseInt(cursor)
|
|
411
287
|
};
|
|
412
|
-
var
|
|
413
|
-
|
|
414
|
-
|
|
288
|
+
var Cursor = class _Cursor {
|
|
289
|
+
static {
|
|
290
|
+
this.converter = Facet.define({
|
|
291
|
+
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
static {
|
|
295
|
+
this.getCursorFromRange = (state, range) => {
|
|
296
|
+
const cursorConverter3 = state.facet(_Cursor.converter);
|
|
297
|
+
const from = cursorConverter3.toCursor(range.from);
|
|
298
|
+
const to = cursorConverter3.toCursor(range.to, -1);
|
|
299
|
+
return [
|
|
300
|
+
from,
|
|
301
|
+
to
|
|
302
|
+
].join(":");
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
static {
|
|
306
|
+
this.getRangeFromCursor = (state, cursor) => {
|
|
307
|
+
const cursorConverter3 = state.facet(_Cursor.converter);
|
|
308
|
+
const parts = cursor.split(":");
|
|
309
|
+
const from = cursorConverter3.fromCursor(parts[0]);
|
|
310
|
+
const to = cursorConverter3.fromCursor(parts[1]);
|
|
311
|
+
return from !== void 0 && to !== void 0 ? {
|
|
312
|
+
from,
|
|
313
|
+
to
|
|
314
|
+
} : void 0;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
415
317
|
};
|
|
416
|
-
var light = "text-neutral-200 dark:text-neutral-800";
|
|
417
|
-
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
418
|
-
var bold = "font-bold";
|
|
419
|
-
var italic = "italic";
|
|
420
|
-
var strikethrough = "line-through";
|
|
421
|
-
var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
|
|
422
|
-
var codeMark = "font-mono text-primary-500";
|
|
423
|
-
var inlineUrl = mx(code, "px-1");
|
|
424
|
-
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
425
|
-
|
|
426
|
-
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
427
|
-
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
428
|
-
var tokens = tailwindConfig({}).theme;
|
|
429
318
|
|
|
430
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
431
|
-
var
|
|
432
|
-
var
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
var getLineRange = (lines, from, to) => {
|
|
458
|
-
const start = lines.findIndex((line) => line.from >= from);
|
|
459
|
-
const end = lines.findIndex((line) => line.to >= to);
|
|
460
|
-
return [
|
|
461
|
-
start,
|
|
462
|
-
end
|
|
463
|
-
];
|
|
464
|
-
};
|
|
465
|
-
var code2 = () => {
|
|
466
|
-
const getDecorations = (view) => {
|
|
467
|
-
const decorations = [];
|
|
468
|
-
if (view.state.readOnly) {
|
|
469
|
-
const text = view.state.doc.sliceString(0);
|
|
470
|
-
const matches = text.matchAll(CODE_REGEX);
|
|
471
|
-
const blocks = view.viewportLineBlocks;
|
|
472
|
-
for (const match of matches) {
|
|
473
|
-
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
474
|
-
for (let i = range[0]; i <= range[1]; i++) {
|
|
475
|
-
const block = blocks[i];
|
|
476
|
-
decorations.push(Decoration.line({
|
|
477
|
-
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
478
|
-
}).range(block.from));
|
|
319
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
320
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
|
|
321
|
+
var automerge3 = ({ handle, path }) => {
|
|
322
|
+
const state = StateField.define({
|
|
323
|
+
create: () => ({
|
|
324
|
+
path: path.slice(),
|
|
325
|
+
lastHeads: A.getHeads(handle.docSync()),
|
|
326
|
+
unreconciledTransactions: []
|
|
327
|
+
}),
|
|
328
|
+
update: (value, tr) => {
|
|
329
|
+
const result = {
|
|
330
|
+
path: path.slice(),
|
|
331
|
+
lastHeads: value.lastHeads,
|
|
332
|
+
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
333
|
+
};
|
|
334
|
+
let clearUnreconciled = false;
|
|
335
|
+
for (const effect of tr.effects) {
|
|
336
|
+
if (effect.is(effectType)) {
|
|
337
|
+
result.lastHeads = effect.value.newHeads;
|
|
338
|
+
clearUnreconciled = true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (clearUnreconciled) {
|
|
342
|
+
result.unreconciledTransactions = [];
|
|
343
|
+
} else {
|
|
344
|
+
if (!isReconcileTx(tr)) {
|
|
345
|
+
result.unreconciledTransactions.push(tr);
|
|
479
346
|
}
|
|
480
347
|
}
|
|
348
|
+
return result;
|
|
481
349
|
}
|
|
482
|
-
|
|
483
|
-
|
|
350
|
+
});
|
|
351
|
+
const semaphore = new PatchSemaphore(handle, state);
|
|
484
352
|
return [
|
|
353
|
+
Cursor.converter.of(cursorConverter(handle, path)),
|
|
354
|
+
// Reconcile external updates.
|
|
485
355
|
ViewPlugin.fromClass(class {
|
|
486
|
-
constructor(
|
|
487
|
-
this.
|
|
356
|
+
constructor(_view) {
|
|
357
|
+
this._view = _view;
|
|
358
|
+
invariant(this._view, void 0, {
|
|
359
|
+
F: __dxlog_file,
|
|
360
|
+
L: 66,
|
|
361
|
+
S: this,
|
|
362
|
+
A: [
|
|
363
|
+
"this._view",
|
|
364
|
+
""
|
|
365
|
+
]
|
|
366
|
+
});
|
|
367
|
+
handle.addListener("change", this._handleChange.bind(this));
|
|
488
368
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
369
|
+
destroy() {
|
|
370
|
+
handle.removeListener("change", this._handleChange.bind(this));
|
|
371
|
+
}
|
|
372
|
+
_handleChange() {
|
|
373
|
+
semaphore.reconcile(this._view);
|
|
493
374
|
}
|
|
494
|
-
}, {
|
|
495
|
-
decorations: (value) => value.decorations
|
|
496
375
|
}),
|
|
497
|
-
|
|
376
|
+
// Reconcile local updates.
|
|
377
|
+
EditorView.updateListener.of(({ view, changes }) => {
|
|
378
|
+
if (!changes.empty) {
|
|
379
|
+
semaphore.reconcile(view);
|
|
380
|
+
}
|
|
381
|
+
}),
|
|
382
|
+
state
|
|
498
383
|
];
|
|
499
384
|
};
|
|
500
385
|
|
|
501
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
502
|
-
import {
|
|
503
|
-
import {
|
|
504
|
-
import
|
|
505
|
-
import {
|
|
506
|
-
import {
|
|
507
|
-
import {
|
|
508
|
-
import {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
L: 16,
|
|
520
|
-
S: void 0,
|
|
521
|
-
C: (f, a) => f(...a)
|
|
522
|
-
});
|
|
523
|
-
}
|
|
386
|
+
// packages/ui/react-ui-editor/src/extensions/awareness.ts
|
|
387
|
+
import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
|
|
388
|
+
import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
389
|
+
import { useEffect } from "react";
|
|
390
|
+
import { Event } from "@dxos/async";
|
|
391
|
+
import { Context } from "@dxos/context";
|
|
392
|
+
import { generateName } from "@dxos/display-name";
|
|
393
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
394
|
+
import { getColorForValue } from "@dxos/react-ui-theme";
|
|
395
|
+
var dummyProvider = {
|
|
396
|
+
remoteStateChange: new Event(),
|
|
397
|
+
open: () => {
|
|
398
|
+
},
|
|
399
|
+
close: () => {
|
|
400
|
+
},
|
|
401
|
+
update: () => {
|
|
402
|
+
},
|
|
403
|
+
getRemoteStates: () => []
|
|
524
404
|
};
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
if (!hasChanges) {
|
|
555
|
-
return void 0;
|
|
556
|
-
}
|
|
557
|
-
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
558
|
-
for (const tr of transactions) {
|
|
559
|
-
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
560
|
-
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
405
|
+
var AwarenessProvider = Facet2.define({
|
|
406
|
+
combine: (providers) => providers[0] ?? dummyProvider
|
|
407
|
+
});
|
|
408
|
+
var RemoteSelectionChangedAnnotation = Annotation2.define();
|
|
409
|
+
var awareness = (provider = dummyProvider) => {
|
|
410
|
+
return [
|
|
411
|
+
AwarenessProvider.of(provider),
|
|
412
|
+
ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
|
|
413
|
+
decorations: (value) => value.decorations
|
|
414
|
+
}),
|
|
415
|
+
styles
|
|
416
|
+
];
|
|
417
|
+
};
|
|
418
|
+
var useAwareness = ({ awareness: awareness2, peer }) => {
|
|
419
|
+
const { themeMode } = useThemeContext();
|
|
420
|
+
useEffect(() => {
|
|
421
|
+
if (awareness2 && peer) {
|
|
422
|
+
awareness2.setLocalStateField("user", {
|
|
423
|
+
name: peer.name ?? generateName(peer.id),
|
|
424
|
+
color: getColorForValue({
|
|
425
|
+
value: peer.id,
|
|
426
|
+
type: "color"
|
|
427
|
+
}),
|
|
428
|
+
colorLight: getColorForValue({
|
|
429
|
+
value: peer.id,
|
|
430
|
+
themeMode,
|
|
431
|
+
type: "highlight"
|
|
432
|
+
})
|
|
561
433
|
});
|
|
562
434
|
}
|
|
563
|
-
}
|
|
564
|
-
|
|
435
|
+
}, [
|
|
436
|
+
awareness2,
|
|
437
|
+
peer,
|
|
438
|
+
themeMode
|
|
439
|
+
]);
|
|
565
440
|
};
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
441
|
+
var RemoteSelectionsDecorator = class {
|
|
442
|
+
constructor(view) {
|
|
443
|
+
this.decorations = RangeSet.of([]);
|
|
444
|
+
this._ctx = new Context();
|
|
445
|
+
this._lastAnchor = void 0;
|
|
446
|
+
this._lastHead = void 0;
|
|
447
|
+
this._cursorConverter = view.state.facet(Cursor.converter);
|
|
448
|
+
this._provider = view.state.facet(AwarenessProvider);
|
|
449
|
+
this._provider.open();
|
|
450
|
+
this._provider.remoteStateChange.on(this._ctx, () => {
|
|
575
451
|
view.dispatch({
|
|
576
|
-
|
|
577
|
-
|
|
452
|
+
annotations: [
|
|
453
|
+
RemoteSelectionChangedAnnotation.of([])
|
|
454
|
+
]
|
|
578
455
|
});
|
|
579
|
-
}
|
|
456
|
+
});
|
|
580
457
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
});
|
|
585
|
-
};
|
|
586
|
-
var handlePatch = (patch, target, state) => {
|
|
587
|
-
if (patch.action === "insert") {
|
|
588
|
-
return handleInsert(target, patch);
|
|
589
|
-
} else if (patch.action === "splice") {
|
|
590
|
-
return handleSplice(target, patch);
|
|
591
|
-
} else if (patch.action === "del") {
|
|
592
|
-
return handleDel(target, patch);
|
|
593
|
-
} else if (patch.action === "put") {
|
|
594
|
-
return handlePut(target, patch, state);
|
|
595
|
-
} else {
|
|
596
|
-
return null;
|
|
458
|
+
destroy() {
|
|
459
|
+
void this._ctx.dispose();
|
|
460
|
+
this._provider.close();
|
|
597
461
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (index == null) {
|
|
602
|
-
return [];
|
|
462
|
+
update(update2) {
|
|
463
|
+
this._updateLocalSelection(update2);
|
|
464
|
+
this._updateRemoteSelections(update2);
|
|
603
465
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
{
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
insert: text
|
|
466
|
+
_updateLocalSelection(update2) {
|
|
467
|
+
const hasFocus = update2.view.hasFocus && update2.view.dom.ownerDocument.hasFocus();
|
|
468
|
+
const { anchor = void 0, head = void 0 } = hasFocus ? update2.state.selection.main : {};
|
|
469
|
+
if (this._lastAnchor === anchor && this._lastHead === head) {
|
|
470
|
+
return;
|
|
610
471
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
472
|
+
this._lastAnchor = anchor;
|
|
473
|
+
this._lastHead = head;
|
|
474
|
+
this._provider.update(anchor !== void 0 && head !== void 0 ? {
|
|
475
|
+
anchor: this._cursorConverter.toCursor(anchor),
|
|
476
|
+
head: this._cursorConverter.toCursor(head)
|
|
477
|
+
} : void 0);
|
|
478
|
+
}
|
|
479
|
+
_updateRemoteSelections(update2) {
|
|
480
|
+
const decorations = [];
|
|
481
|
+
const awarenessStates = this._provider.getRemoteStates();
|
|
482
|
+
for (const state of awarenessStates) {
|
|
483
|
+
const anchor = state.position?.anchor ? this._cursorConverter.fromCursor(state.position.anchor) : null;
|
|
484
|
+
const head = state.position?.head ? this._cursorConverter.fromCursor(state.position.head) : null;
|
|
485
|
+
if (anchor == null || head == null) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const start = Math.min(Math.min(anchor, head), update2.view.state.doc.length);
|
|
489
|
+
const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
|
|
490
|
+
const startLine = update2.view.state.doc.lineAt(start);
|
|
491
|
+
const endLine = update2.view.state.doc.lineAt(end);
|
|
492
|
+
const color = state.info.color ?? "#30bced";
|
|
493
|
+
const lightColor = state.info.lightColor ?? color + "33";
|
|
494
|
+
if (startLine.number === endLine.number) {
|
|
495
|
+
decorations.push({
|
|
496
|
+
from: start,
|
|
497
|
+
to: end,
|
|
498
|
+
value: Decoration.mark({
|
|
499
|
+
attributes: {
|
|
500
|
+
style: `background-color: ${lightColor}`
|
|
501
|
+
},
|
|
502
|
+
class: "cm-collab-selection"
|
|
503
|
+
})
|
|
504
|
+
});
|
|
505
|
+
} else {
|
|
506
|
+
decorations.push({
|
|
507
|
+
from: start,
|
|
508
|
+
to: startLine.from + startLine.length,
|
|
509
|
+
value: Decoration.mark({
|
|
510
|
+
attributes: {
|
|
511
|
+
style: `background-color: ${color}`
|
|
512
|
+
},
|
|
513
|
+
class: "cm-collab-selection"
|
|
514
|
+
})
|
|
515
|
+
});
|
|
516
|
+
decorations.push({
|
|
517
|
+
from: endLine.from,
|
|
518
|
+
to: end,
|
|
519
|
+
value: Decoration.mark({
|
|
520
|
+
attributes: {
|
|
521
|
+
style: `background-color: ${color}`
|
|
522
|
+
},
|
|
523
|
+
class: "cm-collab-selection"
|
|
524
|
+
})
|
|
525
|
+
});
|
|
526
|
+
for (let i = startLine.number + 1; i < endLine.number; i++) {
|
|
527
|
+
const linePos = update2.view.state.doc.line(i).from;
|
|
528
|
+
decorations.push({
|
|
529
|
+
from: linePos,
|
|
530
|
+
to: linePos,
|
|
531
|
+
value: Decoration.line({
|
|
532
|
+
attributes: {
|
|
533
|
+
style: `background-color: ${color}`,
|
|
534
|
+
class: "cm-collab-selectionLine"
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
decorations.push({
|
|
541
|
+
from: head,
|
|
542
|
+
to: head,
|
|
543
|
+
value: Decoration.widget({
|
|
544
|
+
side: head - anchor > 0 ? -1 : 1,
|
|
545
|
+
block: false,
|
|
546
|
+
widget: new RemoteCaretWidget(state.info.displayName ?? "Anonymous", color)
|
|
547
|
+
})
|
|
548
|
+
});
|
|
622
549
|
}
|
|
623
|
-
|
|
624
|
-
};
|
|
625
|
-
var handleDel = (target, patch) => {
|
|
626
|
-
const index = charPath(target, patch.path);
|
|
627
|
-
if (index == null) {
|
|
628
|
-
return [];
|
|
550
|
+
this.decorations = Decoration.set(decorations, true);
|
|
629
551
|
}
|
|
630
|
-
const length = patch.length || 1;
|
|
631
|
-
return [
|
|
632
|
-
{
|
|
633
|
-
from: index,
|
|
634
|
-
to: index + length
|
|
635
|
-
}
|
|
636
|
-
];
|
|
637
552
|
};
|
|
638
|
-
var
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
if (index == null) {
|
|
644
|
-
return [];
|
|
553
|
+
var RemoteCaretWidget = class extends WidgetType {
|
|
554
|
+
constructor(_name, _color) {
|
|
555
|
+
super();
|
|
556
|
+
this._name = _name;
|
|
557
|
+
this._color = _color;
|
|
645
558
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
559
|
+
toDOM() {
|
|
560
|
+
const span = document.createElement("span");
|
|
561
|
+
span.className = "cm-collab-selectionCaret";
|
|
562
|
+
span.style.backgroundColor = this._color;
|
|
563
|
+
span.style.borderColor = this._color;
|
|
564
|
+
const dot = document.createElement("div");
|
|
565
|
+
dot.className = "cm-collab-selectionCaretDot";
|
|
566
|
+
const info = document.createElement("div");
|
|
567
|
+
info.className = "cm-collab-selectionInfo";
|
|
568
|
+
info.innerText = this._name;
|
|
569
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
570
|
+
span.appendChild(dot);
|
|
571
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
572
|
+
span.appendChild(info);
|
|
573
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
574
|
+
return span;
|
|
649
575
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
from: 0,
|
|
653
|
-
to: length,
|
|
654
|
-
insert: patch.value
|
|
655
|
-
}
|
|
656
|
-
];
|
|
657
|
-
};
|
|
658
|
-
var charPath = (textPath, candidatePath) => {
|
|
659
|
-
if (candidatePath.length !== textPath.length + 1) {
|
|
660
|
-
return null;
|
|
576
|
+
updateDOM() {
|
|
577
|
+
return false;
|
|
661
578
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
return null;
|
|
665
|
-
}
|
|
579
|
+
eq(widget) {
|
|
580
|
+
return widget._color === this._color;
|
|
666
581
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
return index;
|
|
582
|
+
get estimatedHeight() {
|
|
583
|
+
return -1;
|
|
670
584
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// packages/ui/react-ui-editor/src/hooks/automerge/semaphore.ts
|
|
675
|
-
var PatchSemaphore = class {
|
|
676
|
-
constructor(field) {
|
|
677
|
-
this._inReconcile = false;
|
|
678
|
-
this._queue = [];
|
|
679
|
-
this.reconcile = (handle, view) => {
|
|
680
|
-
if (this._inReconcile) {
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
this._inReconcile = true;
|
|
684
|
-
const path = getPath(view.state, this._field);
|
|
685
|
-
const oldHeads = getLastHeads(view.state, this._field);
|
|
686
|
-
let selection = view.state.selection;
|
|
687
|
-
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
688
|
-
const toInvert = transactions.slice().reverse();
|
|
689
|
-
for (const tx of toInvert) {
|
|
690
|
-
const inverted = tx.changes.invert(tx.startState.doc);
|
|
691
|
-
selection = selection.map(inverted);
|
|
692
|
-
view.dispatch({
|
|
693
|
-
changes: inverted,
|
|
694
|
-
annotations: reconcileAnnotationType.of(true)
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
698
|
-
if (newHeads === null || newHeads === void 0) {
|
|
699
|
-
newHeads = automerge.getHeads(handle.docSync());
|
|
700
|
-
}
|
|
701
|
-
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
702
|
-
updateCodeMirror(view, selection, path, diff);
|
|
703
|
-
view.dispatch({
|
|
704
|
-
effects: updateHeads(newHeads),
|
|
705
|
-
annotations: reconcileAnnotationType.of({})
|
|
706
|
-
});
|
|
707
|
-
this._inReconcile = false;
|
|
708
|
-
};
|
|
709
|
-
if (field !== void 0) {
|
|
710
|
-
this._field = field;
|
|
711
|
-
}
|
|
585
|
+
ignoreEvent() {
|
|
586
|
+
return true;
|
|
712
587
|
}
|
|
713
588
|
};
|
|
589
|
+
var styles = EditorView2.baseTheme({
|
|
590
|
+
".cm-collab-selection": {},
|
|
591
|
+
".cm-collab-selectionLine": {
|
|
592
|
+
padding: 0,
|
|
593
|
+
margin: "0px 2px 0px 4px"
|
|
594
|
+
},
|
|
595
|
+
".cm-collab-selectionCaret": {
|
|
596
|
+
position: "relative",
|
|
597
|
+
borderLeft: "1px solid black",
|
|
598
|
+
borderRight: "1px solid black",
|
|
599
|
+
marginLeft: "-1px",
|
|
600
|
+
marginRight: "-1px",
|
|
601
|
+
boxSizing: "border-box",
|
|
602
|
+
display: "inline"
|
|
603
|
+
},
|
|
604
|
+
".cm-collab-selectionCaretDot": {
|
|
605
|
+
borderRadius: "50%",
|
|
606
|
+
position: "absolute",
|
|
607
|
+
width: ".4em",
|
|
608
|
+
height: ".4em",
|
|
609
|
+
top: "-.2em",
|
|
610
|
+
left: "-.2em",
|
|
611
|
+
backgroundColor: "inherit",
|
|
612
|
+
transition: "transform .3s ease-in-out",
|
|
613
|
+
boxSizing: "border-box"
|
|
614
|
+
},
|
|
615
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
|
|
616
|
+
transformOrigin: "bottom center",
|
|
617
|
+
transform: "scale(0)"
|
|
618
|
+
},
|
|
619
|
+
".cm-collab-selectionInfo": {
|
|
620
|
+
position: "absolute",
|
|
621
|
+
top: "-1.05em",
|
|
622
|
+
left: "-1px",
|
|
623
|
+
fontSize: ".75em",
|
|
624
|
+
fontFamily: "serif",
|
|
625
|
+
fontStyle: "normal",
|
|
626
|
+
fontWeight: "normal",
|
|
627
|
+
lineHeight: "normal",
|
|
628
|
+
userSelect: "none",
|
|
629
|
+
color: "white",
|
|
630
|
+
paddingLeft: "2px",
|
|
631
|
+
paddingRight: "2px",
|
|
632
|
+
zIndex: 101,
|
|
633
|
+
transition: "opacity .3s ease-in-out",
|
|
634
|
+
backgroundColor: "inherit",
|
|
635
|
+
// These should be separate.
|
|
636
|
+
opacity: 0,
|
|
637
|
+
transitionDelay: "0s",
|
|
638
|
+
whiteSpace: "nowrap"
|
|
639
|
+
},
|
|
640
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionInfo": {
|
|
641
|
+
opacity: 1,
|
|
642
|
+
transitionDelay: "0s"
|
|
643
|
+
}
|
|
644
|
+
});
|
|
714
645
|
|
|
715
|
-
// packages/ui/react-ui-editor/src/
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
var
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
646
|
+
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
647
|
+
import { closeBrackets } from "@codemirror/autocomplete";
|
|
648
|
+
import { history } from "@codemirror/commands";
|
|
649
|
+
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
650
|
+
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
651
|
+
import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
|
|
652
|
+
var basicBundle = ({ themeMode, placeholder: _placeholder, multiline = true }) => [
|
|
653
|
+
multiline ? EditorView3.lineWrapping : [],
|
|
654
|
+
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
655
|
+
bracketMatching(),
|
|
656
|
+
closeBrackets(),
|
|
657
|
+
drawSelection(),
|
|
658
|
+
highlightActiveLine(),
|
|
659
|
+
history(),
|
|
660
|
+
_placeholder ? placeholder(_placeholder) : [],
|
|
661
|
+
// https://github.com/codemirror/theme-one-dark
|
|
662
|
+
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
666
|
+
import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
|
|
667
|
+
import defaultsDeep from "lodash.defaultsdeep";
|
|
668
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
669
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
670
|
+
var defaultOptions = {
|
|
671
|
+
effect: 2,
|
|
672
|
+
maxParticles: 200,
|
|
673
|
+
particleGravity: 0.08,
|
|
674
|
+
particleAlphaFadeout: 0.996,
|
|
675
|
+
particleSize: {
|
|
676
|
+
min: 2,
|
|
677
|
+
max: 8
|
|
678
|
+
},
|
|
679
|
+
particleNumRange: {
|
|
680
|
+
min: 5,
|
|
681
|
+
max: 10
|
|
682
|
+
},
|
|
683
|
+
particleVelocityRange: {
|
|
684
|
+
x: [
|
|
685
|
+
-1,
|
|
686
|
+
1
|
|
687
|
+
],
|
|
688
|
+
y: [
|
|
689
|
+
-3.5,
|
|
690
|
+
-1.5
|
|
691
|
+
]
|
|
692
|
+
},
|
|
693
|
+
particleShrinkRate: 0.95,
|
|
694
|
+
shakeIntensity: 5
|
|
695
|
+
};
|
|
696
|
+
var blast = (options = defaultOptions) => {
|
|
697
|
+
let blaster;
|
|
698
|
+
let last = 0;
|
|
699
|
+
const getPoint = (view, pos) => {
|
|
700
|
+
const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
|
|
701
|
+
const element = document.elementFromPoint(x, y);
|
|
702
|
+
const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
|
|
703
|
+
const point = {
|
|
704
|
+
x: x - left,
|
|
705
|
+
y: y - top
|
|
706
|
+
};
|
|
707
|
+
return {
|
|
708
|
+
element,
|
|
709
|
+
point
|
|
710
|
+
};
|
|
711
|
+
};
|
|
712
|
+
return [
|
|
713
|
+
// Cursor moved.
|
|
714
|
+
EditorView4.updateListener.of((update2) => {
|
|
715
|
+
if (blaster?.node !== update2.view.scrollDOM) {
|
|
716
|
+
if (blaster) {
|
|
717
|
+
blaster.destroy();
|
|
743
718
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
719
|
+
blaster = new Blaster(update2.view.scrollDOM, defaultsDeep({
|
|
720
|
+
particleGravity: 0.2,
|
|
721
|
+
particleShrinkRate: 0.995,
|
|
722
|
+
color: () => [
|
|
723
|
+
100 + Math.random() * 100,
|
|
724
|
+
0,
|
|
725
|
+
0
|
|
726
|
+
]
|
|
727
|
+
}, options, defaultOptions));
|
|
728
|
+
blaster.initialize();
|
|
729
|
+
blaster.start();
|
|
747
730
|
} else {
|
|
748
|
-
|
|
749
|
-
|
|
731
|
+
blaster.resize();
|
|
732
|
+
}
|
|
733
|
+
const current = update2.state.selection.main.head;
|
|
734
|
+
if (current !== last) {
|
|
735
|
+
last = current;
|
|
736
|
+
const { element, point } = getPoint(update2.view, current - 1);
|
|
737
|
+
if (element && point) {
|
|
738
|
+
blaster.spawn({
|
|
739
|
+
element,
|
|
740
|
+
point
|
|
741
|
+
});
|
|
750
742
|
}
|
|
751
743
|
}
|
|
752
|
-
|
|
744
|
+
}),
|
|
745
|
+
keymap2.of([
|
|
746
|
+
{
|
|
747
|
+
any: (_, event) => {
|
|
748
|
+
if (blaster) {
|
|
749
|
+
if (event.key === "Enter" && event.shiftKey) {
|
|
750
|
+
blaster.shake({
|
|
751
|
+
time: 0.8
|
|
752
|
+
});
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
])
|
|
760
|
+
];
|
|
761
|
+
};
|
|
762
|
+
var Blaster = class {
|
|
763
|
+
constructor(_node, _options) {
|
|
764
|
+
this._node = _node;
|
|
765
|
+
this._options = _options;
|
|
766
|
+
this._running = false;
|
|
767
|
+
this._shakeTime = 0;
|
|
768
|
+
this._shakeTimeMax = 0;
|
|
769
|
+
this._particles = [];
|
|
770
|
+
this._particlePointer = 0;
|
|
771
|
+
this._lastPoint = {
|
|
772
|
+
x: 0,
|
|
773
|
+
y: 0
|
|
774
|
+
};
|
|
775
|
+
this.shake = throttle(({ time }) => {
|
|
776
|
+
this._shakeTime = this._shakeTimeMax || time;
|
|
777
|
+
this._shakeTimeMax = time;
|
|
778
|
+
}, 100);
|
|
779
|
+
this.spawn = throttle(({ element, point }) => {
|
|
780
|
+
const color = getRGBComponents(element, this._options.color);
|
|
781
|
+
const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
|
|
782
|
+
const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
|
|
783
|
+
this._lastPoint = point;
|
|
784
|
+
for (let i = numParticles; i--; i > 0) {
|
|
785
|
+
this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);
|
|
786
|
+
this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;
|
|
787
|
+
}
|
|
788
|
+
}, 100);
|
|
789
|
+
this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
|
|
790
|
+
}
|
|
791
|
+
get node() {
|
|
792
|
+
return this._node;
|
|
793
|
+
}
|
|
794
|
+
initialize() {
|
|
795
|
+
invariant2(!this._canvas && !this._ctx, void 0, {
|
|
796
|
+
F: __dxlog_file2,
|
|
797
|
+
L: 138,
|
|
798
|
+
S: this,
|
|
799
|
+
A: [
|
|
800
|
+
"!this._canvas && !this._ctx",
|
|
801
|
+
""
|
|
802
|
+
]
|
|
803
|
+
});
|
|
804
|
+
this._canvas = document.createElement("canvas");
|
|
805
|
+
this._canvas.id = "code-blast-canvas";
|
|
806
|
+
this._canvas.style.position = "absolute";
|
|
807
|
+
this._canvas.style.zIndex = "0";
|
|
808
|
+
this._canvas.style.pointerEvents = "none";
|
|
809
|
+
this._ctx = this._canvas.getContext("2d");
|
|
810
|
+
const parent = this._node.parentElement?.parentElement;
|
|
811
|
+
parent?.appendChild(this._canvas);
|
|
812
|
+
this.resize();
|
|
813
|
+
}
|
|
814
|
+
destroy() {
|
|
815
|
+
this.stop();
|
|
816
|
+
if (this._canvas) {
|
|
817
|
+
this._canvas.remove();
|
|
818
|
+
this._canvas = void 0;
|
|
819
|
+
this._ctx = void 0;
|
|
753
820
|
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
this.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
this.
|
|
762
|
-
handle.addListener("change", this._handleChange);
|
|
821
|
+
}
|
|
822
|
+
resize() {
|
|
823
|
+
if (this._node.parentElement && this._canvas) {
|
|
824
|
+
const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
|
|
825
|
+
this._canvas.style.top = `${y}px`;
|
|
826
|
+
this._canvas.style.left = `${x}px`;
|
|
827
|
+
this._canvas.width = width;
|
|
828
|
+
this._canvas.height = height;
|
|
763
829
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
830
|
+
}
|
|
831
|
+
start() {
|
|
832
|
+
invariant2(this._canvas && this._ctx, void 0, {
|
|
833
|
+
F: __dxlog_file2,
|
|
834
|
+
L: 177,
|
|
835
|
+
S: this,
|
|
836
|
+
A: [
|
|
837
|
+
"this._canvas && this._ctx",
|
|
838
|
+
""
|
|
839
|
+
]
|
|
840
|
+
});
|
|
841
|
+
this._running = true;
|
|
842
|
+
this.loop();
|
|
843
|
+
}
|
|
844
|
+
stop() {
|
|
845
|
+
this._running = false;
|
|
846
|
+
this._node.style.transform = "translate(0px, 0px)";
|
|
847
|
+
}
|
|
848
|
+
loop() {
|
|
849
|
+
if (!this._running || !this._canvas || !this._ctx) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);
|
|
853
|
+
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
854
|
+
this._lastTime ??= now;
|
|
855
|
+
const dt = (now - this._lastTime) / 1e3;
|
|
856
|
+
this._lastTime = now;
|
|
857
|
+
if (this._shakeTime > 0) {
|
|
858
|
+
this._shakeTime -= dt;
|
|
859
|
+
const magnitude = this._shakeTime / this._shakeTimeMax * this._options.shakeIntensity;
|
|
860
|
+
const shakeX = random(-magnitude, magnitude);
|
|
861
|
+
const shakeY = random(-magnitude, magnitude);
|
|
862
|
+
this._node.style.transform = `translate(${shakeX}px,${shakeY}px)`;
|
|
863
|
+
}
|
|
864
|
+
this.drawParticles();
|
|
865
|
+
requestAnimationFrame(this.loop.bind(this));
|
|
866
|
+
}
|
|
867
|
+
drawParticles() {
|
|
868
|
+
for (let i = this._particles.length; i--; i > 0) {
|
|
869
|
+
const particle = this._particles[i];
|
|
870
|
+
if (!particle) {
|
|
871
|
+
continue;
|
|
769
872
|
}
|
|
873
|
+
if (particle.alpha < 0.01 || particle.size <= 0.5) {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
this._effect.update(this._ctx, particle);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
var Effect = class {
|
|
881
|
+
constructor(_options) {
|
|
882
|
+
this._options = _options;
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
var Effect1 = class extends Effect {
|
|
886
|
+
create(x, y, color) {
|
|
887
|
+
return {
|
|
888
|
+
x,
|
|
889
|
+
y: y + 10,
|
|
890
|
+
vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
|
|
891
|
+
vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
|
|
892
|
+
size: random(this._options.particleSize.min, this._options.particleSize.max),
|
|
893
|
+
color,
|
|
894
|
+
alpha: 1
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
update(ctx, particle) {
|
|
898
|
+
particle.vy += this._options.particleGravity;
|
|
899
|
+
particle.x += particle.vx;
|
|
900
|
+
particle.y += particle.vy;
|
|
901
|
+
particle.alpha *= this._options.particleAlphaFadeout;
|
|
902
|
+
ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
|
|
903
|
+
ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
var Effect2 = class extends Effect {
|
|
907
|
+
create(x, y, color) {
|
|
908
|
+
return {
|
|
909
|
+
x,
|
|
910
|
+
y: y + 10,
|
|
911
|
+
vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
|
|
912
|
+
vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
|
|
913
|
+
size: random(this._options.particleSize.min, this._options.particleSize.max),
|
|
914
|
+
color,
|
|
915
|
+
alpha: 1,
|
|
916
|
+
theta: random(0, 360) * Math.PI / 180,
|
|
917
|
+
drag: 0.92,
|
|
918
|
+
wander: 0.15
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
update(ctx, particle) {
|
|
922
|
+
particle.vy += this._options.particleGravity;
|
|
923
|
+
particle.x += particle.vx;
|
|
924
|
+
particle.y += particle.vy;
|
|
925
|
+
particle.vx *= particle.drag;
|
|
926
|
+
particle.vy *= particle.drag;
|
|
927
|
+
particle.theta += random(-0.5, 0.5);
|
|
928
|
+
particle.vx += Math.sin(particle.theta) * 0.1;
|
|
929
|
+
particle.vy += Math.cos(particle.theta) * 0.1;
|
|
930
|
+
particle.size *= this._options.particleShrinkRate;
|
|
931
|
+
particle.alpha *= this._options.particleAlphaFadeout;
|
|
932
|
+
ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
|
|
933
|
+
ctx.beginPath();
|
|
934
|
+
ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);
|
|
935
|
+
ctx.fill();
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
function throttle(callback, limit) {
|
|
939
|
+
let wait = false;
|
|
940
|
+
return function(...args) {
|
|
941
|
+
if (!wait) {
|
|
942
|
+
callback.apply(this, args);
|
|
943
|
+
wait = true;
|
|
944
|
+
setTimeout(() => {
|
|
945
|
+
wait = false;
|
|
946
|
+
}, limit);
|
|
770
947
|
}
|
|
771
|
-
|
|
772
|
-
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
var getRGBComponents = (node, color) => {
|
|
951
|
+
if (typeof color === "function") {
|
|
952
|
+
return color();
|
|
953
|
+
}
|
|
954
|
+
const bgColor = getComputedStyle(node).color;
|
|
955
|
+
if (bgColor) {
|
|
956
|
+
const x = bgColor.match(/(\d+), (\d+), (\d+)/)?.slice(1);
|
|
957
|
+
if (x) {
|
|
958
|
+
return x;
|
|
773
959
|
}
|
|
774
|
-
}
|
|
775
|
-
return
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
960
|
+
}
|
|
961
|
+
return [
|
|
962
|
+
50,
|
|
963
|
+
50,
|
|
964
|
+
50
|
|
965
|
+
];
|
|
966
|
+
};
|
|
967
|
+
var random = (min, max) => {
|
|
968
|
+
if (!max) {
|
|
969
|
+
max = min;
|
|
970
|
+
min = 0;
|
|
971
|
+
}
|
|
972
|
+
return min + ~~(Math.random() * (max - min + 1));
|
|
782
973
|
};
|
|
783
|
-
var reconcileAnnotationType = Annotation.define();
|
|
784
|
-
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
785
974
|
|
|
786
|
-
// packages/ui/react-ui-editor/src/
|
|
975
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
976
|
+
import { ViewPlugin as ViewPlugin3, EditorView as EditorView5, Decoration as Decoration2 } from "@codemirror/view";
|
|
977
|
+
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
978
|
+
|
|
979
|
+
// packages/ui/react-ui-editor/src/styles/cursors.ts
|
|
787
980
|
import * as random2 from "lib0/random";
|
|
788
981
|
var cursorColors = [
|
|
789
982
|
{
|
|
@@ -821,267 +1014,243 @@ var cursorColors = [
|
|
|
821
1014
|
];
|
|
822
1015
|
var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
823
1016
|
|
|
824
|
-
// packages/ui/react-ui-editor/src/
|
|
825
|
-
import
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1017
|
+
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
1018
|
+
import { mx } from "@dxos/react-ui-theme";
|
|
1019
|
+
var headings = {
|
|
1020
|
+
1: [
|
|
1021
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
|
|
1022
|
+
"-ml-[10px]"
|
|
1023
|
+
],
|
|
1024
|
+
2: [
|
|
1025
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
|
|
1026
|
+
"-ml-[8px]"
|
|
1027
|
+
],
|
|
1028
|
+
3: [
|
|
1029
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
1030
|
+
"-ml-[7px]"
|
|
1031
|
+
],
|
|
1032
|
+
4: [
|
|
1033
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
1034
|
+
"-ml-[6px]"
|
|
1035
|
+
],
|
|
1036
|
+
5: [
|
|
1037
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
1038
|
+
"-ml-[5px]"
|
|
1039
|
+
],
|
|
1040
|
+
6: [
|
|
1041
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline",
|
|
1042
|
+
"-ml-[4px]"
|
|
1043
|
+
]
|
|
1044
|
+
};
|
|
1045
|
+
var heading = (level, readonly) => {
|
|
1046
|
+
const [style, offset] = headings[level];
|
|
1047
|
+
return mx(style, readonly && offset);
|
|
1048
|
+
};
|
|
1049
|
+
var light = "text-neutral-200 dark:text-neutral-800";
|
|
1050
|
+
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
1051
|
+
var bold = "font-bold";
|
|
1052
|
+
var italic = "italic";
|
|
1053
|
+
var strikethrough = "line-through";
|
|
1054
|
+
var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
|
|
1055
|
+
var codeMark = "font-mono text-primary-500";
|
|
1056
|
+
var inlineUrl = mx(code, "px-1");
|
|
1057
|
+
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
1058
|
+
|
|
1059
|
+
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
1060
|
+
import get2 from "lodash.get";
|
|
1061
|
+
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
1062
|
+
var tokens = tailwindConfig({}).theme;
|
|
1063
|
+
var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
|
|
1064
|
+
|
|
1065
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
1066
|
+
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
1067
|
+
var styles2 = EditorView5.baseTheme({
|
|
1068
|
+
"& .cm-codeblock": {
|
|
1069
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1070
|
+
},
|
|
1071
|
+
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1072
|
+
background: getToken("extend.colors.neutral.50")
|
|
1073
|
+
},
|
|
1074
|
+
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1075
|
+
background: getToken("extend.colors.neutral.850")
|
|
1076
|
+
},
|
|
1077
|
+
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1078
|
+
display: "block",
|
|
1079
|
+
paddingInline: "4px !important"
|
|
1080
|
+
},
|
|
1081
|
+
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
1082
|
+
paddingTop: "4px !important",
|
|
1083
|
+
borderTopLeftRadius: "4px",
|
|
1084
|
+
borderTopRightRadius: "4px"
|
|
1085
|
+
},
|
|
1086
|
+
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
1087
|
+
paddingBottom: "4px !important",
|
|
1088
|
+
borderBottomLeftRadius: "4px",
|
|
1089
|
+
borderBottomRightRadius: "4px"
|
|
890
1090
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1091
|
+
});
|
|
1092
|
+
var getLineRange = (lines, from, to) => {
|
|
1093
|
+
const start = lines.findIndex((line) => line.from >= from);
|
|
1094
|
+
const end = lines.findIndex((line) => line.to >= to);
|
|
1095
|
+
return [
|
|
1096
|
+
start,
|
|
1097
|
+
end
|
|
1098
|
+
];
|
|
1099
|
+
};
|
|
1100
|
+
var code2 = () => {
|
|
1101
|
+
const buildDecorations5 = (view) => {
|
|
1102
|
+
const decorations = [];
|
|
1103
|
+
const text = view.state.doc.sliceString(0);
|
|
1104
|
+
const matches = text.matchAll(CODE_REGEX);
|
|
1105
|
+
const blocks = view.viewportLineBlocks;
|
|
1106
|
+
for (const match of matches) {
|
|
1107
|
+
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1108
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1109
|
+
const block = blocks[i];
|
|
1110
|
+
decorations.push(Decoration2.line({
|
|
1111
|
+
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1112
|
+
}).range(block.from));
|
|
900
1113
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1114
|
+
}
|
|
1115
|
+
return Decoration2.set(decorations);
|
|
1116
|
+
};
|
|
1117
|
+
return [
|
|
1118
|
+
ViewPlugin3.fromClass(class {
|
|
1119
|
+
constructor(view) {
|
|
1120
|
+
this.hasFocus = false;
|
|
1121
|
+
this.decorations = buildDecorations5(view);
|
|
906
1122
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1123
|
+
update(update2) {
|
|
1124
|
+
if (
|
|
1125
|
+
// TODO(burdon): Generalize for other extensions.
|
|
1126
|
+
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1127
|
+
) {
|
|
1128
|
+
this.hasFocus = update2.view.hasFocus;
|
|
1129
|
+
this.decorations = buildDecorations5(update2.view);
|
|
1130
|
+
}
|
|
910
1131
|
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
}
|
|
917
|
-
_handleBeforeUnload() {
|
|
918
|
-
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
919
|
-
this._clientId
|
|
920
|
-
], "window unload");
|
|
921
|
-
}
|
|
1132
|
+
}, {
|
|
1133
|
+
decorations: (value) => value.decorations
|
|
1134
|
+
}),
|
|
1135
|
+
styles2
|
|
1136
|
+
];
|
|
922
1137
|
};
|
|
923
1138
|
|
|
924
|
-
// packages/ui/react-ui-editor/src/
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
return void 0;
|
|
947
|
-
}
|
|
948
|
-
return createYjsModel(props);
|
|
1139
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1140
|
+
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1141
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView6 } from "@codemirror/view";
|
|
1142
|
+
import sortBy from "lodash.sortby";
|
|
1143
|
+
import { useEffect as useEffect2 } from "react";
|
|
1144
|
+
import { debounce } from "@dxos/async";
|
|
1145
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
1146
|
+
import { nonNullable } from "@dxos/util";
|
|
1147
|
+
|
|
1148
|
+
// packages/ui/react-ui-editor/src/util.ts
|
|
1149
|
+
import { log } from "@dxos/log";
|
|
1150
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1151
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
1152
|
+
try {
|
|
1153
|
+
return fn(...args);
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
log.catch(err, void 0, {
|
|
1156
|
+
F: __dxlog_file3,
|
|
1157
|
+
L: 18,
|
|
1158
|
+
S: void 0,
|
|
1159
|
+
C: (f, a) => f(...a)
|
|
1160
|
+
});
|
|
949
1161
|
}
|
|
950
1162
|
};
|
|
951
|
-
var
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
})
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
text: () => text.content.toString(),
|
|
970
|
-
// https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
971
|
-
getCursorFromRange: (range) => {
|
|
972
|
-
const from = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content, range.from));
|
|
973
|
-
const to = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content, range.to, -1));
|
|
974
|
-
return [
|
|
975
|
-
arrayToString(from),
|
|
976
|
-
arrayToString(to)
|
|
977
|
-
].join(":");
|
|
978
|
-
},
|
|
979
|
-
getRangeFromCursor: (cursor) => {
|
|
980
|
-
invariant2(text.doc, void 0, {
|
|
981
|
-
F: __dxlog_file4,
|
|
982
|
-
L: 113,
|
|
983
|
-
S: void 0,
|
|
984
|
-
A: [
|
|
985
|
-
"text.doc",
|
|
986
|
-
""
|
|
987
|
-
]
|
|
988
|
-
});
|
|
989
|
-
const parts = cursor.split(":");
|
|
990
|
-
const from = Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(parts[0])), text.doc);
|
|
991
|
-
const to = Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(parts[1])), text.doc);
|
|
992
|
-
return from && to ? {
|
|
993
|
-
from: from.index,
|
|
994
|
-
to: to.index
|
|
995
|
-
} : void 0;
|
|
996
|
-
},
|
|
997
|
-
extension: [
|
|
998
|
-
yCollab(text.content, provider?.awareness),
|
|
999
|
-
modelState.init(() => model)
|
|
1000
|
-
],
|
|
1001
|
-
awareness: provider?.awareness,
|
|
1002
|
-
peer: identity ? {
|
|
1003
|
-
id: identity.identityKey.toHex(),
|
|
1004
|
-
name: identity.profile?.displayName
|
|
1005
|
-
} : void 0
|
|
1006
|
-
};
|
|
1007
|
-
return model;
|
|
1008
|
-
};
|
|
1009
|
-
var createAutomergeModel = ({ identity, text }) => {
|
|
1010
|
-
const obj = text;
|
|
1011
|
-
const doc = getRawDoc(obj, [
|
|
1012
|
-
obj.field
|
|
1013
|
-
]);
|
|
1014
|
-
const model = {
|
|
1015
|
-
id: obj.id,
|
|
1016
|
-
content: doc,
|
|
1017
|
-
text: () => get2(doc.handle.docSync(), doc.path),
|
|
1018
|
-
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
1019
|
-
extension: [
|
|
1020
|
-
automergePlugin(doc.handle, doc.path),
|
|
1021
|
-
modelState.init(() => model)
|
|
1022
|
-
],
|
|
1023
|
-
peer: identity ? {
|
|
1024
|
-
id: identity.identityKey.toHex(),
|
|
1025
|
-
name: identity.profile?.displayName
|
|
1026
|
-
} : void 0
|
|
1027
|
-
};
|
|
1028
|
-
return model;
|
|
1163
|
+
var logChanges = (trs) => {
|
|
1164
|
+
const changes = trs.flatMap((tr) => {
|
|
1165
|
+
if (tr.changes.empty) {
|
|
1166
|
+
return void 0;
|
|
1167
|
+
}
|
|
1168
|
+
const changes2 = [];
|
|
1169
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
|
|
1170
|
+
fromA,
|
|
1171
|
+
toA,
|
|
1172
|
+
fromB,
|
|
1173
|
+
toB,
|
|
1174
|
+
inserted: inserted.toString()
|
|
1175
|
+
})));
|
|
1176
|
+
return changes2;
|
|
1177
|
+
}).filter(Boolean);
|
|
1178
|
+
if (changes.length) {
|
|
1179
|
+
console.log("changes", changes);
|
|
1180
|
+
}
|
|
1029
1181
|
};
|
|
1030
1182
|
|
|
1031
1183
|
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1032
|
-
var
|
|
1033
|
-
var
|
|
1034
|
-
"& .cm-bookmark": {
|
|
1035
|
-
cursor: "pointer",
|
|
1036
|
-
margin: "4px",
|
|
1037
|
-
padding: "4px",
|
|
1038
|
-
backgroundColor: "yellow"
|
|
1039
|
-
},
|
|
1040
|
-
"& .cm-bookmark-selected": {
|
|
1041
|
-
backgroundColor: "orange"
|
|
1042
|
-
},
|
|
1184
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
1185
|
+
var styles3 = EditorView6.baseTheme({
|
|
1043
1186
|
"& .cm-comment": {
|
|
1044
|
-
backgroundColor: "yellow"
|
|
1187
|
+
backgroundColor: getToken("extend.colors.yellow.50")
|
|
1045
1188
|
},
|
|
1046
1189
|
"& .cm-comment-active": {
|
|
1047
|
-
backgroundColor: "
|
|
1190
|
+
backgroundColor: getToken("extend.colors.yellow.100")
|
|
1048
1191
|
}
|
|
1049
1192
|
});
|
|
1050
1193
|
var marks = {
|
|
1051
|
-
highlight:
|
|
1194
|
+
highlight: Decoration3.mark({
|
|
1052
1195
|
class: "cm-comment"
|
|
1053
1196
|
}),
|
|
1054
|
-
highlightActive:
|
|
1197
|
+
highlightActive: Decoration3.mark({
|
|
1055
1198
|
class: "cm-comment-active"
|
|
1056
1199
|
})
|
|
1057
1200
|
};
|
|
1058
|
-
var
|
|
1201
|
+
var setFocus = (view, id, center = true) => {
|
|
1202
|
+
const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
|
|
1203
|
+
if (!comment?.comment.cursor) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
|
|
1207
|
+
if (range) {
|
|
1208
|
+
view.dispatch({
|
|
1209
|
+
selection: {
|
|
1210
|
+
anchor: range.from
|
|
1211
|
+
},
|
|
1212
|
+
effects: [
|
|
1213
|
+
//
|
|
1214
|
+
EditorView6.scrollIntoView(range.from, center ? {
|
|
1215
|
+
y: "center"
|
|
1216
|
+
} : void 0),
|
|
1217
|
+
setSelection.of({
|
|
1218
|
+
active: id
|
|
1219
|
+
})
|
|
1220
|
+
]
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
var setComments = StateEffect2.define();
|
|
1059
1225
|
var setSelection = StateEffect2.define();
|
|
1060
1226
|
var setCommentState = StateEffect2.define();
|
|
1061
|
-
var
|
|
1227
|
+
var commentsState = StateField2.define({
|
|
1062
1228
|
create: () => ({
|
|
1063
|
-
|
|
1229
|
+
comments: [],
|
|
1230
|
+
selection: {}
|
|
1064
1231
|
}),
|
|
1065
1232
|
update: (value, tr) => {
|
|
1066
1233
|
for (const effect of tr.effects) {
|
|
1067
1234
|
if (effect.is(setSelection)) {
|
|
1068
1235
|
return {
|
|
1069
1236
|
...value,
|
|
1070
|
-
|
|
1237
|
+
selection: effect.value
|
|
1071
1238
|
};
|
|
1072
1239
|
}
|
|
1073
|
-
if (effect.is(
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1240
|
+
if (effect.is(setComments)) {
|
|
1241
|
+
const comments2 = effect.value.map((comment) => {
|
|
1242
|
+
if (!comment.cursor) {
|
|
1243
|
+
return void 0;
|
|
1244
|
+
}
|
|
1245
|
+
const range = Cursor.getRangeFromCursor(tr.state, comment.cursor);
|
|
1077
1246
|
return range && {
|
|
1078
|
-
|
|
1079
|
-
|
|
1247
|
+
comment,
|
|
1248
|
+
range
|
|
1080
1249
|
};
|
|
1081
1250
|
}).filter(nonNullable);
|
|
1082
1251
|
return {
|
|
1083
1252
|
...value,
|
|
1084
|
-
|
|
1253
|
+
comments: comments2
|
|
1085
1254
|
};
|
|
1086
1255
|
}
|
|
1087
1256
|
if (effect.is(setCommentState)) {
|
|
@@ -1091,78 +1260,109 @@ var commentsStateField = StateField3.define({
|
|
|
1091
1260
|
return value;
|
|
1092
1261
|
}
|
|
1093
1262
|
});
|
|
1094
|
-
var highlightDecorations =
|
|
1095
|
-
|
|
1263
|
+
var highlightDecorations = EditorView6.decorations.compute([
|
|
1264
|
+
commentsState
|
|
1096
1265
|
], (state) => {
|
|
1097
|
-
const { active,
|
|
1098
|
-
const decorations = sortBy(
|
|
1099
|
-
|
|
1266
|
+
const { selection: { active }, comments: comments2 } = state.field(commentsState);
|
|
1267
|
+
const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
|
|
1268
|
+
const range = comment.range;
|
|
1269
|
+
if (!range || range.from === range.to) {
|
|
1270
|
+
console.warn("Invalid range:", range);
|
|
1100
1271
|
return void 0;
|
|
1101
1272
|
}
|
|
1102
|
-
|
|
1103
|
-
from: selection.from,
|
|
1104
|
-
to: selection.to
|
|
1105
|
-
};
|
|
1106
|
-
if (selection.id === active) {
|
|
1273
|
+
if (comment.comment.id === active) {
|
|
1107
1274
|
return marks.highlightActive.range(range.from, range.to);
|
|
1108
1275
|
} else {
|
|
1109
1276
|
return marks.highlight.range(range.from, range.to);
|
|
1110
1277
|
}
|
|
1111
|
-
}).filter(nonNullable)
|
|
1112
|
-
return
|
|
1278
|
+
}).filter(nonNullable);
|
|
1279
|
+
return Decoration3.set(decorations);
|
|
1113
1280
|
});
|
|
1114
|
-
var
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
return this._anchor === other._anchor && this._id === other._id;
|
|
1132
|
-
}
|
|
1133
|
-
toDOM() {
|
|
1134
|
-
const span = document.createElement("span");
|
|
1135
|
-
span.className = "cm-bookmark";
|
|
1136
|
-
span.textContent = "\u203B";
|
|
1137
|
-
if (this._handleClick) {
|
|
1138
|
-
span.onclick = () => this._handleClick();
|
|
1281
|
+
var trackPastedComments = (onUpdate) => {
|
|
1282
|
+
let tracked = null;
|
|
1283
|
+
const handleTrack = (event, view) => {
|
|
1284
|
+
const comments2 = view.state.field(commentsState);
|
|
1285
|
+
const { main } = view.state.selection;
|
|
1286
|
+
const selectedRanges = comments2.comments.filter(({ range }) => range.from >= main.from && range.to <= main.to && range.from < range.to);
|
|
1287
|
+
if (!selectedRanges.length) {
|
|
1288
|
+
tracked = null;
|
|
1289
|
+
} else {
|
|
1290
|
+
tracked = {
|
|
1291
|
+
text: view.state.doc.slice(main.from, main.to),
|
|
1292
|
+
comments: selectedRanges.map(({ comment, range }) => ({
|
|
1293
|
+
id: comment.id,
|
|
1294
|
+
from: range.from - main.from,
|
|
1295
|
+
to: range.to - main.from
|
|
1296
|
+
}))
|
|
1297
|
+
};
|
|
1139
1298
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1299
|
+
};
|
|
1300
|
+
return [
|
|
1301
|
+
EditorView6.domEventHandlers({
|
|
1302
|
+
cut: handleTrack,
|
|
1303
|
+
copy: handleTrack
|
|
1304
|
+
}),
|
|
1305
|
+
// Handle paste.
|
|
1306
|
+
EditorView6.updateListener.of(({ view, state, transactions }) => {
|
|
1307
|
+
if (tracked) {
|
|
1308
|
+
const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
|
|
1309
|
+
if (paste) {
|
|
1310
|
+
let found = -1;
|
|
1311
|
+
paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
1312
|
+
if (text.eq(tracked.text)) {
|
|
1313
|
+
for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
|
|
1314
|
+
fromB = transactions[i].changes.mapPos(fromB);
|
|
1315
|
+
}
|
|
1316
|
+
found = fromB;
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
if (found > -1) {
|
|
1320
|
+
for (const moved of tracked.comments) {
|
|
1321
|
+
const range = {
|
|
1322
|
+
from: found + moved.from,
|
|
1323
|
+
to: found + moved.to
|
|
1324
|
+
};
|
|
1325
|
+
const cursor = Cursor.getCursorFromRange(state, range);
|
|
1326
|
+
onUpdate(moved.id, cursor);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
tracked = null;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
})
|
|
1333
|
+
];
|
|
1142
1334
|
};
|
|
1143
1335
|
var comments = (options = {}) => {
|
|
1336
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1144
1337
|
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1145
1338
|
const createCommentThread = (view) => {
|
|
1146
1339
|
invariant3(options.onCreate, void 0, {
|
|
1147
|
-
F:
|
|
1148
|
-
L:
|
|
1340
|
+
F: __dxlog_file4,
|
|
1341
|
+
L: 259,
|
|
1149
1342
|
S: void 0,
|
|
1150
1343
|
A: [
|
|
1151
1344
|
"options.onCreate",
|
|
1152
1345
|
""
|
|
1153
1346
|
]
|
|
1154
1347
|
});
|
|
1155
|
-
const {
|
|
1348
|
+
const { from, to } = view.state.selection.main;
|
|
1156
1349
|
if (from === to) {
|
|
1157
1350
|
return false;
|
|
1158
1351
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1352
|
+
if (to === view.state.doc.length) {
|
|
1353
|
+
view.dispatch({
|
|
1354
|
+
changes: {
|
|
1355
|
+
from: to,
|
|
1356
|
+
insert: "\n"
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
const cursor = Cursor.getCursorFromRange(view.state, {
|
|
1161
1361
|
from,
|
|
1162
1362
|
to
|
|
1163
1363
|
});
|
|
1164
|
-
if (
|
|
1165
|
-
const id =
|
|
1364
|
+
if (cursor) {
|
|
1365
|
+
const id = options.onCreate?.(cursor, view.coordsAtPos(from));
|
|
1166
1366
|
if (id) {
|
|
1167
1367
|
view.dispatch({
|
|
1168
1368
|
effects: setSelection.of({
|
|
@@ -1172,61 +1372,21 @@ var comments = (options = {}) => {
|
|
|
1172
1372
|
anchor: from
|
|
1173
1373
|
}
|
|
1174
1374
|
});
|
|
1175
|
-
if (options.footnote) {
|
|
1176
|
-
const tag = `[^${id}]`;
|
|
1177
|
-
view.dispatch({
|
|
1178
|
-
changes: {
|
|
1179
|
-
from: head,
|
|
1180
|
-
insert: tag
|
|
1181
|
-
},
|
|
1182
|
-
selection: {
|
|
1183
|
-
anchor: head + tag.length
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
1375
|
return true;
|
|
1188
1376
|
}
|
|
1189
1377
|
}
|
|
1190
1378
|
return false;
|
|
1191
1379
|
};
|
|
1192
|
-
const bookmarksViewPlugin = ViewPlugin3.fromClass(class BookmarkViewPlugin {
|
|
1193
|
-
static {
|
|
1194
|
-
// Match markdown footnotes (option).
|
|
1195
|
-
// https://www.markdownguide.org/extended-syntax/#footnotes
|
|
1196
|
-
this.bookmarkMatcher = new MatchDecorator({
|
|
1197
|
-
regexp: /\[\^(\w+)\]/g,
|
|
1198
|
-
decoration: (match, view, pos) => {
|
|
1199
|
-
const id = match[1];
|
|
1200
|
-
return Decoration2.replace({
|
|
1201
|
-
id,
|
|
1202
|
-
widget: new BookmarkWidget(pos, id)
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
constructor(view) {
|
|
1208
|
-
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.createDeco(view);
|
|
1209
|
-
}
|
|
1210
|
-
update(update4) {
|
|
1211
|
-
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.updateDeco(update4, this.bookmarks);
|
|
1212
|
-
}
|
|
1213
|
-
}, {
|
|
1214
|
-
decorations: (instance) => instance.bookmarks,
|
|
1215
|
-
provide: (plugin) => EditorView4.atomicRanges.of((view) => {
|
|
1216
|
-
return view.plugin(plugin)?.bookmarks || Decoration2.none;
|
|
1217
|
-
})
|
|
1218
|
-
});
|
|
1219
1380
|
return [
|
|
1220
|
-
|
|
1221
|
-
commentsStateField,
|
|
1381
|
+
commentsState,
|
|
1222
1382
|
highlightDecorations,
|
|
1223
|
-
|
|
1383
|
+
styles3,
|
|
1224
1384
|
//
|
|
1225
1385
|
// Keymap.
|
|
1226
1386
|
//
|
|
1227
1387
|
options.onCreate ? keymap3.of([
|
|
1228
1388
|
{
|
|
1229
|
-
key:
|
|
1389
|
+
key: shortcut,
|
|
1230
1390
|
run: callbackWrapper(createCommentThread)
|
|
1231
1391
|
}
|
|
1232
1392
|
]) : [],
|
|
@@ -1243,7 +1403,7 @@ var comments = (options = {}) => {
|
|
|
1243
1403
|
above: true,
|
|
1244
1404
|
create: () => {
|
|
1245
1405
|
const el = document.createElement("div");
|
|
1246
|
-
options.onHover
|
|
1406
|
+
options.onHover(el, shortcut);
|
|
1247
1407
|
return {
|
|
1248
1408
|
dom: el,
|
|
1249
1409
|
offset: {
|
|
@@ -1256,178 +1416,206 @@ var comments = (options = {}) => {
|
|
|
1256
1416
|
}
|
|
1257
1417
|
return null;
|
|
1258
1418
|
}, {
|
|
1259
|
-
|
|
1419
|
+
// TODO(burdon): Hide on change triggered immediately?
|
|
1420
|
+
// hideOnChange: true,
|
|
1260
1421
|
hoverTime: 1e3
|
|
1261
1422
|
}) : [],
|
|
1262
1423
|
//
|
|
1263
|
-
//
|
|
1264
|
-
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1424
|
+
// Track deleted ranges and update ranges for decorations.
|
|
1265
1425
|
//
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
S: void 0,
|
|
1276
|
-
A: [
|
|
1277
|
-
"model",
|
|
1278
|
-
""
|
|
1279
|
-
]
|
|
1280
|
-
});
|
|
1281
|
-
ranges.forEach((range) => {
|
|
1282
|
-
if (from2 === to2) {
|
|
1283
|
-
const newRange = model.getRangeFromCursor(range.cursor);
|
|
1284
|
-
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1285
|
-
log3.info("deleted comment", {
|
|
1286
|
-
thread: range.id
|
|
1287
|
-
}, {
|
|
1288
|
-
F: __dxlog_file5,
|
|
1289
|
-
L: 342,
|
|
1290
|
-
S: void 0,
|
|
1291
|
-
C: (f, a) => f(...a)
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
if (from <= range.to) {
|
|
1296
|
-
const newRange = model.getRangeFromCursor(range.cursor);
|
|
1297
|
-
Object.assign(range, newRange);
|
|
1298
|
-
mod = true;
|
|
1426
|
+
EditorView6.updateListener.of(({ view, state, changes }) => {
|
|
1427
|
+
let mod = false;
|
|
1428
|
+
const { comments: comments2, ...value } = state.field(commentsState);
|
|
1429
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1430
|
+
comments2.forEach(({ comment, range }) => {
|
|
1431
|
+
if (from2 === to2) {
|
|
1432
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1433
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1434
|
+
options.onDelete?.(comment.id);
|
|
1299
1435
|
}
|
|
1300
|
-
}
|
|
1436
|
+
}
|
|
1437
|
+
if (from <= range.to) {
|
|
1438
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1439
|
+
Object.assign(range, newRange);
|
|
1440
|
+
mod = true;
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
});
|
|
1444
|
+
if (mod) {
|
|
1445
|
+
view.dispatch({
|
|
1446
|
+
effects: setCommentState.of({
|
|
1447
|
+
comments: comments2,
|
|
1448
|
+
...value
|
|
1449
|
+
})
|
|
1301
1450
|
});
|
|
1302
|
-
if (mod) {
|
|
1303
|
-
view.dispatch({
|
|
1304
|
-
effects: setCommentState.of({
|
|
1305
|
-
active,
|
|
1306
|
-
ranges
|
|
1307
|
-
})
|
|
1308
|
-
});
|
|
1309
|
-
}
|
|
1310
1451
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1452
|
+
}),
|
|
1453
|
+
//
|
|
1454
|
+
// Track selection/proximity.
|
|
1455
|
+
//
|
|
1456
|
+
EditorView6.updateListener.of(({ view, state, changes }) => {
|
|
1457
|
+
let min = Infinity;
|
|
1458
|
+
const { selection: { active, closest }, comments: comments2 } = state.field(commentsState);
|
|
1459
|
+
const { head } = state.selection.main;
|
|
1460
|
+
const selection = {};
|
|
1461
|
+
comments2.forEach(({ comment, range }) => {
|
|
1462
|
+
if (head >= range.from && head <= range.to) {
|
|
1463
|
+
selection.active = comment.id;
|
|
1464
|
+
selection.closest = void 0;
|
|
1465
|
+
}
|
|
1466
|
+
if (!selection.active) {
|
|
1467
|
+
const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
|
|
1324
1468
|
if (d < min) {
|
|
1325
|
-
|
|
1469
|
+
selection.closest = comment.id;
|
|
1326
1470
|
min = d;
|
|
1327
1471
|
}
|
|
1328
|
-
});
|
|
1329
|
-
if (selected.active !== active || selected.closest !== closest) {
|
|
1330
|
-
view.dispatch({
|
|
1331
|
-
effects: setSelection.of(selected)
|
|
1332
|
-
});
|
|
1333
|
-
handleSelect({
|
|
1334
|
-
...selected,
|
|
1335
|
-
ranges: ranges.map((range) => ({
|
|
1336
|
-
...range,
|
|
1337
|
-
location: view.coordsAtPos(range.from)
|
|
1338
|
-
}))
|
|
1339
|
-
});
|
|
1340
1472
|
}
|
|
1473
|
+
});
|
|
1474
|
+
if (selection.active !== active || selection.closest !== closest) {
|
|
1475
|
+
view.dispatch({
|
|
1476
|
+
effects: setSelection.of(selection)
|
|
1477
|
+
});
|
|
1478
|
+
handleSelect({
|
|
1479
|
+
selection,
|
|
1480
|
+
comments: comments2.map(({ comment, range }) => ({
|
|
1481
|
+
comment,
|
|
1482
|
+
range,
|
|
1483
|
+
location: view.coordsAtPos(range.from)
|
|
1484
|
+
}))
|
|
1485
|
+
});
|
|
1341
1486
|
}
|
|
1342
|
-
})
|
|
1487
|
+
}),
|
|
1488
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : []
|
|
1343
1489
|
];
|
|
1344
1490
|
};
|
|
1491
|
+
var useComments = (view, comments2) => {
|
|
1492
|
+
useEffect2(() => {
|
|
1493
|
+
if (view && comments2) {
|
|
1494
|
+
view.dispatch({
|
|
1495
|
+
effects: setComments.of(comments2)
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
}, [
|
|
1499
|
+
view,
|
|
1500
|
+
comments2
|
|
1501
|
+
]);
|
|
1502
|
+
};
|
|
1345
1503
|
|
|
1346
1504
|
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1347
|
-
import {
|
|
1348
|
-
import
|
|
1349
|
-
|
|
1505
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1506
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1507
|
+
import { Decoration as Decoration4, EditorView as EditorView7, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1508
|
+
var styles4 = EditorView7.baseTheme({
|
|
1350
1509
|
"& .cm-hr": {
|
|
1351
|
-
// TODO(burdon): ???
|
|
1352
1510
|
// Note that block-level decorations should not have vertical margins,
|
|
1353
|
-
|
|
1511
|
+
borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
1354
1512
|
}
|
|
1355
1513
|
});
|
|
1356
1514
|
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1357
|
-
|
|
1358
|
-
super();
|
|
1359
|
-
this._pos = _pos;
|
|
1360
|
-
}
|
|
1361
|
-
eq(other) {
|
|
1362
|
-
return this._pos === other._pos;
|
|
1363
|
-
}
|
|
1364
|
-
toDOM(view) {
|
|
1515
|
+
toDOM() {
|
|
1365
1516
|
const el = document.createElement("div");
|
|
1366
1517
|
el.className = "cm-hr";
|
|
1367
1518
|
return el;
|
|
1368
1519
|
}
|
|
1369
1520
|
};
|
|
1370
|
-
var
|
|
1371
|
-
|
|
1372
|
-
decoration: (match, view, pos) => Decoration3.replace({
|
|
1373
|
-
widget: new HorizontalRuleWidget(pos)
|
|
1374
|
-
})
|
|
1521
|
+
var decoration = Decoration4.replace({
|
|
1522
|
+
widget: new HorizontalRuleWidget()
|
|
1375
1523
|
});
|
|
1376
|
-
var
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1524
|
+
var buildDecorations = (view) => {
|
|
1525
|
+
const builder = new RangeSetBuilder();
|
|
1526
|
+
const { state } = view;
|
|
1527
|
+
const cursor = state.selection.main.head;
|
|
1528
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1529
|
+
syntaxTree(state).iterate({
|
|
1530
|
+
enter: (node) => {
|
|
1531
|
+
if (node.name === "HorizontalRule") {
|
|
1532
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
1533
|
+
builder.add(node.from, node.to, decoration);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
from,
|
|
1538
|
+
to
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
return builder.finish();
|
|
1542
|
+
};
|
|
1543
|
+
var hr = () => {
|
|
1544
|
+
return [
|
|
1545
|
+
styles4,
|
|
1546
|
+
ViewPlugin4.fromClass(class {
|
|
1547
|
+
constructor(view) {
|
|
1548
|
+
this.decorations = buildDecorations(view);
|
|
1549
|
+
}
|
|
1550
|
+
update(update2) {
|
|
1551
|
+
this.decorations = buildDecorations(update2.view);
|
|
1552
|
+
}
|
|
1553
|
+
}, {
|
|
1554
|
+
decorations: (v) => v.decorations
|
|
1390
1555
|
})
|
|
1391
|
-
|
|
1392
|
-
|
|
1556
|
+
];
|
|
1557
|
+
};
|
|
1393
1558
|
|
|
1394
1559
|
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1395
|
-
import { syntaxTree } from "@codemirror/language";
|
|
1396
|
-
import {
|
|
1397
|
-
import { Decoration as
|
|
1560
|
+
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1561
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
1562
|
+
import { Decoration as Decoration5, EditorView as EditorView8, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1398
1563
|
var image = (options = {}) => {
|
|
1399
|
-
return
|
|
1400
|
-
create: (state) =>
|
|
1401
|
-
|
|
1402
|
-
|
|
1564
|
+
return StateField3.define({
|
|
1565
|
+
create: (state) => {
|
|
1566
|
+
return Decoration5.set(buildDecorations2(0, state.doc.length, state));
|
|
1567
|
+
},
|
|
1568
|
+
update: (value, tr) => {
|
|
1569
|
+
if (!tr.docChanged && !tr.selection) {
|
|
1570
|
+
return value;
|
|
1571
|
+
}
|
|
1572
|
+
const cursor = tr.state.selection.main.head;
|
|
1573
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1574
|
+
let from = Math.min(cursor, oldCursor);
|
|
1575
|
+
let to = Math.max(cursor, oldCursor);
|
|
1576
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1577
|
+
from = Math.min(from, fromB);
|
|
1578
|
+
to = Math.max(to, toB);
|
|
1579
|
+
});
|
|
1580
|
+
from = tr.state.doc.lineAt(from).from;
|
|
1581
|
+
to = tr.state.doc.lineAt(to).to;
|
|
1582
|
+
return value.map(tr.changes).update({
|
|
1583
|
+
filterFrom: from,
|
|
1584
|
+
filterTo: to,
|
|
1585
|
+
filter: () => false,
|
|
1586
|
+
add: buildDecorations2(from, to, tr.state)
|
|
1587
|
+
});
|
|
1588
|
+
},
|
|
1589
|
+
provide: (field) => EditorView8.decorations.from(field)
|
|
1403
1590
|
});
|
|
1404
1591
|
};
|
|
1405
|
-
var
|
|
1406
|
-
const
|
|
1592
|
+
var buildDecorations2 = (from, to, state) => {
|
|
1593
|
+
const decorations = [];
|
|
1407
1594
|
const cursor = state.selection.main.head;
|
|
1408
|
-
|
|
1595
|
+
syntaxTree2(state).iterate({
|
|
1409
1596
|
enter: (node) => {
|
|
1410
1597
|
if (node.name === "Image") {
|
|
1411
1598
|
const urlNode = node.node.getChild("URL");
|
|
1412
1599
|
if (urlNode) {
|
|
1413
1600
|
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1414
1601
|
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1415
|
-
|
|
1602
|
+
decorations.push(Decoration5.replace({
|
|
1416
1603
|
block: true,
|
|
1417
1604
|
widget: new ImageWidget(url)
|
|
1418
|
-
}));
|
|
1605
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
1419
1606
|
}
|
|
1420
1607
|
}
|
|
1421
|
-
}
|
|
1608
|
+
},
|
|
1609
|
+
from,
|
|
1610
|
+
to
|
|
1422
1611
|
});
|
|
1423
|
-
return
|
|
1612
|
+
return decorations;
|
|
1424
1613
|
};
|
|
1425
1614
|
var ImageWidget = class extends WidgetType3 {
|
|
1426
1615
|
constructor(_url) {
|
|
1427
1616
|
super();
|
|
1428
1617
|
this._url = _url;
|
|
1429
1618
|
}
|
|
1430
|
-
// TODO(burdon): Does this need to be unique for each position?
|
|
1431
1619
|
eq(other) {
|
|
1432
1620
|
return this._url === other._url;
|
|
1433
1621
|
}
|
|
@@ -1435,22 +1623,29 @@ var ImageWidget = class extends WidgetType3 {
|
|
|
1435
1623
|
const img = document.createElement("img");
|
|
1436
1624
|
img.setAttribute("src", this._url);
|
|
1437
1625
|
img.setAttribute("class", "cm-image");
|
|
1626
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
1438
1627
|
return img;
|
|
1439
1628
|
}
|
|
1440
1629
|
};
|
|
1441
1630
|
|
|
1442
1631
|
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1443
|
-
import { syntaxTree as
|
|
1444
|
-
import { RangeSetBuilder as RangeSetBuilder2
|
|
1445
|
-
import {
|
|
1632
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1633
|
+
import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
|
|
1634
|
+
import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin5, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1446
1635
|
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1447
|
-
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1448
1636
|
var link = (options = {}) => {
|
|
1449
1637
|
const extensions = [
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1638
|
+
ViewPlugin5.fromClass(class {
|
|
1639
|
+
constructor(view) {
|
|
1640
|
+
this.decorations = buildDecorations3(view, options);
|
|
1641
|
+
}
|
|
1642
|
+
update(update2) {
|
|
1643
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1644
|
+
this.decorations = buildDecorations3(update2.view, options);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}, {
|
|
1648
|
+
decorations: (v) => v.decorations
|
|
1454
1649
|
})
|
|
1455
1650
|
];
|
|
1456
1651
|
if (options.onHover) {
|
|
@@ -1458,35 +1653,25 @@ var link = (options = {}) => {
|
|
|
1458
1653
|
// https://codemirror.net/examples/tooltip
|
|
1459
1654
|
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1460
1655
|
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1461
|
-
hoverTooltip2((view, pos) => {
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
let
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
match = void 0;
|
|
1470
|
-
break;
|
|
1471
|
-
}
|
|
1472
|
-
idx = text.indexOf(match[0]);
|
|
1473
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1474
|
-
break;
|
|
1475
|
-
}
|
|
1476
|
-
idx += match[0].length;
|
|
1477
|
-
} while (true);
|
|
1478
|
-
if (!match) {
|
|
1656
|
+
hoverTooltip2((view, pos, side) => {
|
|
1657
|
+
const syntax = syntaxTree3(view.state).resolveInner(pos, side);
|
|
1658
|
+
let link2 = null;
|
|
1659
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1660
|
+
link2 = node.name === "Link" ? node : null;
|
|
1661
|
+
}
|
|
1662
|
+
const url = link2 && link2.getChild("URL");
|
|
1663
|
+
if (!url || !link2) {
|
|
1479
1664
|
return null;
|
|
1480
1665
|
}
|
|
1481
|
-
const
|
|
1666
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1482
1667
|
return {
|
|
1483
|
-
pos:
|
|
1484
|
-
end:
|
|
1668
|
+
pos: link2.from,
|
|
1669
|
+
end: link2.to,
|
|
1485
1670
|
above: true,
|
|
1486
1671
|
create: () => {
|
|
1487
1672
|
const el = document.createElement("div");
|
|
1488
1673
|
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1489
|
-
options.onHover(el,
|
|
1674
|
+
options.onHover(el, urlText);
|
|
1490
1675
|
return {
|
|
1491
1676
|
dom: el,
|
|
1492
1677
|
offset: {
|
|
@@ -1502,14 +1687,13 @@ var link = (options = {}) => {
|
|
|
1502
1687
|
return extensions;
|
|
1503
1688
|
};
|
|
1504
1689
|
var LinkButton = class extends WidgetType4 {
|
|
1505
|
-
constructor(
|
|
1690
|
+
constructor(_url, _onAttach) {
|
|
1506
1691
|
super();
|
|
1507
|
-
this._pos = _pos;
|
|
1508
1692
|
this._url = _url;
|
|
1509
1693
|
this._onAttach = _onAttach;
|
|
1510
1694
|
}
|
|
1511
1695
|
eq(other) {
|
|
1512
|
-
return this.
|
|
1696
|
+
return this._url === other._url;
|
|
1513
1697
|
}
|
|
1514
1698
|
toDOM(view) {
|
|
1515
1699
|
const el = document.createElement("span");
|
|
@@ -1546,74 +1730,77 @@ var LinkText = class extends WidgetType4 {
|
|
|
1546
1730
|
return link2;
|
|
1547
1731
|
}
|
|
1548
1732
|
};
|
|
1549
|
-
var
|
|
1733
|
+
var buildDecorations3 = (view, options) => {
|
|
1550
1734
|
const builder = new RangeSetBuilder2();
|
|
1735
|
+
const { state } = view;
|
|
1551
1736
|
const cursor = state.selection.main.head;
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1737
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1738
|
+
syntaxTree3(state).iterate({
|
|
1739
|
+
enter: (node) => {
|
|
1740
|
+
if (node.name === "Link") {
|
|
1741
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1742
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1743
|
+
const urlNode = node.node.getChild("URL");
|
|
1744
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1745
|
+
if (!url) {
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1749
|
+
builder.add(node.from, node.to, Decoration6.replace({
|
|
1750
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
1751
|
+
}));
|
|
1752
|
+
}
|
|
1753
|
+
if (options.onRender) {
|
|
1754
|
+
builder.add(node.to, node.to, Decoration6.replace({
|
|
1755
|
+
widget: new LinkButton(url, options.onRender)
|
|
1756
|
+
}));
|
|
1757
|
+
}
|
|
1560
1758
|
return false;
|
|
1561
1759
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
if (options.onRender) {
|
|
1568
|
-
builder.add(node.to, node.to, Decoration5.replace({
|
|
1569
|
-
widget: new LinkButton(node.from, url, options.onRender)
|
|
1570
|
-
}));
|
|
1571
|
-
}
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
});
|
|
1760
|
+
},
|
|
1761
|
+
from,
|
|
1762
|
+
to
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1576
1765
|
return builder.finish();
|
|
1577
1766
|
};
|
|
1578
1767
|
|
|
1579
1768
|
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1580
|
-
import { EditorView as
|
|
1769
|
+
import { EditorView as EditorView9 } from "@codemirror/view";
|
|
1581
1770
|
var listener = ({ onFocus, onChange }) => {
|
|
1582
1771
|
const extensions = [];
|
|
1583
|
-
onFocus && extensions.push(
|
|
1772
|
+
onFocus && extensions.push(EditorView9.focusChangeEffect.of((_, focused) => {
|
|
1584
1773
|
onFocus(focused);
|
|
1585
1774
|
return null;
|
|
1586
1775
|
}));
|
|
1587
|
-
onChange && extensions.push(
|
|
1588
|
-
onChange(
|
|
1776
|
+
onChange && extensions.push(EditorView9.updateListener.of((update2) => {
|
|
1777
|
+
onChange(update2.state.doc.toString());
|
|
1589
1778
|
}));
|
|
1590
1779
|
return extensions;
|
|
1591
1780
|
};
|
|
1592
1781
|
|
|
1593
1782
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1594
1783
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1595
|
-
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1596
|
-
import { markdownLanguage as
|
|
1784
|
+
import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1785
|
+
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
1597
1786
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1598
1787
|
import { languages } from "@codemirror/language-data";
|
|
1599
1788
|
import { searchKeymap } from "@codemirror/search";
|
|
1600
1789
|
import { EditorState } from "@codemirror/state";
|
|
1601
1790
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1602
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1791
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView10, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1603
1792
|
|
|
1604
1793
|
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1605
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1794
|
+
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
1606
1795
|
import { HighlightStyle } from "@codemirror/language";
|
|
1607
1796
|
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
1608
1797
|
import { Table } from "@lezer/markdown";
|
|
1609
|
-
import get4 from "lodash.get";
|
|
1610
1798
|
var markdownTags = {
|
|
1611
1799
|
Blockquote: Tag.define(),
|
|
1612
1800
|
CodeMark: Tag.define(),
|
|
1613
1801
|
CodeText: Tag.define(),
|
|
1614
1802
|
EmphasisMark: Tag.define(),
|
|
1615
1803
|
HeaderMark: Tag.define(),
|
|
1616
|
-
// HorizontalRule: Tag.define(),
|
|
1617
1804
|
InlineCode: Tag.define(),
|
|
1618
1805
|
LinkLabel: Tag.define(),
|
|
1619
1806
|
LinkReference: Tag.define(),
|
|
@@ -1777,12 +1964,6 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1777
1964
|
],
|
|
1778
1965
|
class: blockquote
|
|
1779
1966
|
},
|
|
1780
|
-
// TODO(burdon): Replace with extension.
|
|
1781
|
-
// {
|
|
1782
|
-
// tag: [markdownTags.HorizontalRule],
|
|
1783
|
-
// class: mx(horizontalRule, readonly && '-indent-[100rem]'),
|
|
1784
|
-
// },
|
|
1785
|
-
// TODO(burdon): Only if being edited.
|
|
1786
1967
|
{
|
|
1787
1968
|
tag: [
|
|
1788
1969
|
markdownTags.TableCell
|
|
@@ -1790,9 +1971,9 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1790
1971
|
class: "font-mono"
|
|
1791
1972
|
}
|
|
1792
1973
|
], {
|
|
1793
|
-
scope:
|
|
1974
|
+
scope: markdownLanguage2,
|
|
1794
1975
|
all: {
|
|
1795
|
-
fontFamily:
|
|
1976
|
+
fontFamily: getToken("fontFamily.body", []).join(",")
|
|
1796
1977
|
}
|
|
1797
1978
|
});
|
|
1798
1979
|
};
|
|
@@ -1802,17 +1983,17 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1802
1983
|
return [
|
|
1803
1984
|
EditorState.allowMultipleSelections.of(true),
|
|
1804
1985
|
EditorState.tabSize.of(2),
|
|
1805
|
-
|
|
1806
|
-
|
|
1986
|
+
EditorView10.lineWrapping,
|
|
1987
|
+
// https://github.com/codemirror/basic-setup
|
|
1807
1988
|
bracketMatching2(),
|
|
1808
1989
|
closeBrackets2(),
|
|
1809
1990
|
crosshairCursor(),
|
|
1810
1991
|
dropCursor(),
|
|
1811
1992
|
drawSelection2(),
|
|
1812
|
-
|
|
1813
|
-
|
|
1993
|
+
highlightActiveLine2(),
|
|
1994
|
+
history2(),
|
|
1814
1995
|
indentOnInput(),
|
|
1815
|
-
_placeholder
|
|
1996
|
+
_placeholder ? placeholder2(_placeholder) : [],
|
|
1816
1997
|
// Main extension.
|
|
1817
1998
|
// https://github.com/codemirror/lang-markdown
|
|
1818
1999
|
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
@@ -1822,7 +2003,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1822
2003
|
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1823
2004
|
// https://github.github.com/gfm
|
|
1824
2005
|
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1825
|
-
base:
|
|
2006
|
+
base: markdownLanguage3,
|
|
1826
2007
|
// Languages for syntax highlighting fenced code blocks.
|
|
1827
2008
|
codeLanguages: languages,
|
|
1828
2009
|
// Parser extensions.
|
|
@@ -1834,34 +2015,40 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1834
2015
|
// https://github.com/codemirror/theme-one-dark
|
|
1835
2016
|
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1836
2017
|
// Custom styles.
|
|
1837
|
-
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2018
|
+
syntaxHighlighting2(markdownHighlightStyle(readonly)),
|
|
2019
|
+
keymap4.of([
|
|
2020
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
2021
|
+
indentWithTab,
|
|
2022
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
2023
|
+
...closeBracketsKeymap,
|
|
2024
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
2025
|
+
...historyKeymap,
|
|
2026
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
2027
|
+
...searchKeymap,
|
|
2028
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
2029
|
+
...standardKeymap
|
|
2030
|
+
])
|
|
2031
|
+
];
|
|
2032
|
+
};
|
|
1852
2033
|
|
|
1853
2034
|
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1854
2035
|
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1855
2036
|
var mention = ({ onSearch }) => {
|
|
1856
2037
|
return autocompletion2({
|
|
2038
|
+
// TODO(burdon): Not working.
|
|
2039
|
+
activateOnTyping: true,
|
|
2040
|
+
// activateOnTypingDelay: 100,
|
|
2041
|
+
// selectOnOpen: true,
|
|
1857
2042
|
closeOnBlur: false,
|
|
2043
|
+
// defaultKeymap: false,
|
|
2044
|
+
icons: false,
|
|
1858
2045
|
override: [
|
|
1859
2046
|
(context) => {
|
|
2047
|
+
console.log(context);
|
|
1860
2048
|
const match = context.matchBefore(/@(\w+)?/);
|
|
1861
2049
|
if (!match || match.from === match.to && !context.explicit) {
|
|
1862
2050
|
return null;
|
|
1863
2051
|
}
|
|
1864
|
-
console.log(match);
|
|
1865
2052
|
return {
|
|
1866
2053
|
from: match.from,
|
|
1867
2054
|
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
@@ -1873,23 +2060,23 @@ var mention = ({ onSearch }) => {
|
|
|
1873
2060
|
});
|
|
1874
2061
|
};
|
|
1875
2062
|
|
|
1876
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1877
|
-
var
|
|
2063
|
+
// packages/ui/react-ui-editor/src/extensions/outliner.ts
|
|
2064
|
+
var outliner = (options = {}) => {
|
|
1878
2065
|
return [];
|
|
1879
2066
|
};
|
|
1880
2067
|
|
|
1881
2068
|
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1882
|
-
import { syntaxTree as
|
|
1883
|
-
import { RangeSetBuilder as RangeSetBuilder3, StateField as
|
|
1884
|
-
import { Decoration as
|
|
2069
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2070
|
+
import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField4 } from "@codemirror/state";
|
|
2071
|
+
import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1885
2072
|
var table = (options = {}) => {
|
|
1886
|
-
return
|
|
1887
|
-
create: (state) =>
|
|
1888
|
-
update: (_, tr) =>
|
|
1889
|
-
provide: (field) =>
|
|
2073
|
+
return StateField4.define({
|
|
2074
|
+
create: (state) => update(state, options),
|
|
2075
|
+
update: (_, tr) => update(tr.state, options),
|
|
2076
|
+
provide: (field) => EditorView11.decorations.from(field)
|
|
1890
2077
|
});
|
|
1891
2078
|
};
|
|
1892
|
-
var
|
|
2079
|
+
var update = (state, options) => {
|
|
1893
2080
|
const builder = new RangeSetBuilder3();
|
|
1894
2081
|
const cursor = state.selection.main.head;
|
|
1895
2082
|
const tables = [];
|
|
@@ -1898,7 +2085,7 @@ var update3 = (state, options) => {
|
|
|
1898
2085
|
const table2 = getTable();
|
|
1899
2086
|
return table2.rows?.[table2.rows.length - 1];
|
|
1900
2087
|
};
|
|
1901
|
-
|
|
2088
|
+
syntaxTree4(state).iterate({
|
|
1902
2089
|
enter: (node) => {
|
|
1903
2090
|
switch (node.name) {
|
|
1904
2091
|
case "Table": {
|
|
@@ -1930,11 +2117,10 @@ var update3 = (state, options) => {
|
|
|
1930
2117
|
});
|
|
1931
2118
|
tables.forEach((table2) => {
|
|
1932
2119
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1933
|
-
hide && builder.add(table2.from, table2.to,
|
|
1934
|
-
block: true,
|
|
2120
|
+
hide && builder.add(table2.from, table2.to, Decoration7.replace({
|
|
1935
2121
|
widget: new TableWidget(table2)
|
|
1936
2122
|
}));
|
|
1937
|
-
builder.add(table2.from, table2.to,
|
|
2123
|
+
builder.add(table2.from, table2.to, Decoration7.mark({
|
|
1938
2124
|
class: "cm-table"
|
|
1939
2125
|
}));
|
|
1940
2126
|
});
|
|
@@ -1946,7 +2132,7 @@ var TableWidget = class extends WidgetType5 {
|
|
|
1946
2132
|
this._table = _table;
|
|
1947
2133
|
}
|
|
1948
2134
|
eq(other) {
|
|
1949
|
-
return this._table.
|
|
2135
|
+
return this._table.header?.join() === other._table.header?.join() && this._table.rows?.join() === other._table.rows?.join();
|
|
1950
2136
|
}
|
|
1951
2137
|
toDOM(view) {
|
|
1952
2138
|
const table2 = document.createElement("table");
|
|
@@ -1972,90 +2158,105 @@ var TableWidget = class extends WidgetType5 {
|
|
|
1972
2158
|
}
|
|
1973
2159
|
return table2;
|
|
1974
2160
|
}
|
|
2161
|
+
ignoreEvent(e) {
|
|
2162
|
+
return !/^mouse/.test(e.type);
|
|
2163
|
+
}
|
|
1975
2164
|
};
|
|
1976
2165
|
|
|
1977
2166
|
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
1978
|
-
import {
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2167
|
+
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
2168
|
+
import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
|
|
2169
|
+
import { EditorView as EditorView12, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
|
|
2170
|
+
var styles5 = EditorView12.baseTheme({
|
|
2171
|
+
"& .cm-task": {
|
|
2172
|
+
color: getToken("extend.colors.blue.500")
|
|
2173
|
+
},
|
|
2174
|
+
"& .cm-task-checkbox": {
|
|
2175
|
+
marginLeft: "4px",
|
|
2176
|
+
marginRight: "4px"
|
|
1983
2177
|
}
|
|
1984
2178
|
});
|
|
1985
2179
|
var CheckboxWidget = class extends WidgetType6 {
|
|
1986
|
-
constructor(
|
|
2180
|
+
constructor(_checked) {
|
|
1987
2181
|
super();
|
|
1988
|
-
this._pos = _pos;
|
|
1989
2182
|
this._checked = _checked;
|
|
1990
|
-
this._indent = _indent;
|
|
1991
|
-
this._onCheck = _onCheck;
|
|
1992
2183
|
}
|
|
1993
2184
|
eq(other) {
|
|
1994
|
-
return this.
|
|
2185
|
+
return this._checked === other._checked;
|
|
1995
2186
|
}
|
|
1996
2187
|
toDOM(view) {
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
2000
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2001
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
2188
|
+
const input = document.createElement("input");
|
|
2189
|
+
input.className = "cm-task-checkbox";
|
|
2002
2190
|
input.type = "checkbox";
|
|
2003
2191
|
input.checked = this._checked;
|
|
2004
|
-
if (
|
|
2192
|
+
if (view.state.readOnly) {
|
|
2005
2193
|
input.setAttribute("disabled", "true");
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2194
|
+
} else {
|
|
2195
|
+
input.onmousedown = (event) => {
|
|
2196
|
+
const pos = view.posAtDOM(input);
|
|
2197
|
+
const text = view.state.sliceDoc(pos, pos + 3);
|
|
2198
|
+
if (text === (this._checked ? "[x]" : "[ ]")) {
|
|
2199
|
+
view.dispatch({
|
|
2200
|
+
changes: {
|
|
2201
|
+
from: pos + 1,
|
|
2202
|
+
to: pos + 2,
|
|
2203
|
+
insert: this._checked ? " " : "x"
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
event.preventDefault();
|
|
2207
|
+
}
|
|
2011
2208
|
};
|
|
2012
2209
|
}
|
|
2013
|
-
return
|
|
2210
|
+
return input;
|
|
2014
2211
|
}
|
|
2015
2212
|
ignoreEvent() {
|
|
2016
2213
|
return false;
|
|
2017
2214
|
}
|
|
2018
2215
|
};
|
|
2216
|
+
var checkedDecoration = Decoration8.replace({
|
|
2217
|
+
widget: new CheckboxWidget(true)
|
|
2218
|
+
});
|
|
2219
|
+
var uncheckedDecoration = Decoration8.replace({
|
|
2220
|
+
widget: new CheckboxWidget(false)
|
|
2221
|
+
});
|
|
2222
|
+
var buildDecorations4 = (view) => {
|
|
2223
|
+
const builder = new RangeSetBuilder4();
|
|
2224
|
+
const { state } = view;
|
|
2225
|
+
const cursor = state.selection.main.head;
|
|
2226
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2227
|
+
syntaxTree5(state).iterate({
|
|
2228
|
+
enter: (node) => {
|
|
2229
|
+
if (node.name === "TaskMarker") {
|
|
2230
|
+
if (cursor < node.from || cursor > node.to) {
|
|
2231
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2232
|
+
builder.add(node.from - 2, node.from - 1, Decoration8.mark({
|
|
2233
|
+
class: "cm-task"
|
|
2234
|
+
}));
|
|
2235
|
+
builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
},
|
|
2239
|
+
from,
|
|
2240
|
+
to
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
return builder.finish();
|
|
2244
|
+
};
|
|
2019
2245
|
var tasklist = (options = {}) => {
|
|
2020
|
-
const taskMatcher = new MatchDecorator3({
|
|
2021
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
2022
|
-
decoration: (match, view, pos) => {
|
|
2023
|
-
const indent = Math.floor(match[1].length / 2);
|
|
2024
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
2025
|
-
return Decoration7.replace({
|
|
2026
|
-
widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
2027
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
2028
|
-
view.dispatch({
|
|
2029
|
-
changes: {
|
|
2030
|
-
from: idx,
|
|
2031
|
-
to: idx + 1,
|
|
2032
|
-
insert: checked2 ? "x" : " "
|
|
2033
|
-
},
|
|
2034
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
2035
|
-
selection: {
|
|
2036
|
-
anchor: idx + 3
|
|
2037
|
-
}
|
|
2038
|
-
});
|
|
2039
|
-
})
|
|
2040
|
-
});
|
|
2041
|
-
}
|
|
2042
|
-
});
|
|
2043
|
-
const tasks = ViewPlugin5.fromClass(class {
|
|
2044
|
-
constructor(view) {
|
|
2045
|
-
this.tasks = taskMatcher.createDeco(view);
|
|
2046
|
-
}
|
|
2047
|
-
update(update4) {
|
|
2048
|
-
this.tasks = taskMatcher.updateDeco(update4, this.tasks);
|
|
2049
|
-
}
|
|
2050
|
-
}, {
|
|
2051
|
-
decorations: (value) => value.tasks,
|
|
2052
|
-
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2053
|
-
return view.plugin(plugin)?.tasks || Decoration7.none;
|
|
2054
|
-
})
|
|
2055
|
-
});
|
|
2056
2246
|
return [
|
|
2057
|
-
|
|
2058
|
-
|
|
2247
|
+
ViewPlugin6.fromClass(class {
|
|
2248
|
+
constructor(view) {
|
|
2249
|
+
this.decorations = buildDecorations4(view);
|
|
2250
|
+
}
|
|
2251
|
+
update(update2) {
|
|
2252
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2253
|
+
this.decorations = buildDecorations4(update2.view);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}, {
|
|
2257
|
+
decorations: (v) => v.decorations
|
|
2258
|
+
}),
|
|
2259
|
+
styles5
|
|
2059
2260
|
];
|
|
2060
2261
|
};
|
|
2061
2262
|
|
|
@@ -2116,8 +2317,29 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
2116
2317
|
];
|
|
2117
2318
|
};
|
|
2118
2319
|
|
|
2320
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2321
|
+
import { yCollab } from "y-codemirror.next";
|
|
2322
|
+
|
|
2323
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
|
|
2324
|
+
import * as Y from "yjs";
|
|
2325
|
+
import { arrayToString, stringToArray } from "@dxos/util";
|
|
2326
|
+
var cursorConverter2 = (text) => ({
|
|
2327
|
+
toCursor: (index, assoc) => {
|
|
2328
|
+
return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
|
|
2329
|
+
},
|
|
2330
|
+
fromCursor: (cursor) => {
|
|
2331
|
+
return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2336
|
+
var yjs = (content, awareness2) => [
|
|
2337
|
+
Cursor.converter.of(cursorConverter2(content)),
|
|
2338
|
+
yCollab(content, awareness2)
|
|
2339
|
+
];
|
|
2340
|
+
|
|
2119
2341
|
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2120
|
-
import
|
|
2342
|
+
import get3 from "lodash.get";
|
|
2121
2343
|
var defaultTheme = {
|
|
2122
2344
|
//
|
|
2123
2345
|
// Main layout:
|
|
@@ -2146,21 +2368,21 @@ var defaultTheme = {
|
|
|
2146
2368
|
"&.cm-focused": {
|
|
2147
2369
|
outline: "none"
|
|
2148
2370
|
},
|
|
2149
|
-
"
|
|
2371
|
+
".cm-scroller": {
|
|
2150
2372
|
overflow: "visible",
|
|
2151
|
-
fontFamily:
|
|
2373
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2152
2374
|
},
|
|
2153
|
-
"
|
|
2375
|
+
".cm-content": {
|
|
2154
2376
|
padding: 0,
|
|
2155
2377
|
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2156
2378
|
fontSize: "16px"
|
|
2157
2379
|
},
|
|
2158
2380
|
"&light .cm-content": {
|
|
2159
|
-
color:
|
|
2381
|
+
color: get3(tokens, "extend.colors.neutral.900", "black"),
|
|
2160
2382
|
caretColor: "black"
|
|
2161
2383
|
},
|
|
2162
2384
|
"&dark .cm-content": {
|
|
2163
|
-
color:
|
|
2385
|
+
color: get3(tokens, "extend.colors.neutral.100", "white"),
|
|
2164
2386
|
caretColor: "white"
|
|
2165
2387
|
},
|
|
2166
2388
|
//
|
|
@@ -2172,158 +2394,120 @@ var defaultTheme = {
|
|
|
2172
2394
|
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
2173
2395
|
borderLeft: "2px solid white"
|
|
2174
2396
|
},
|
|
2175
|
-
"
|
|
2397
|
+
".cm-placeholder": {
|
|
2176
2398
|
fontWeight: 100
|
|
2177
2399
|
},
|
|
2178
2400
|
//
|
|
2179
2401
|
// line
|
|
2180
2402
|
//
|
|
2181
|
-
"
|
|
2403
|
+
".cm-line": {
|
|
2182
2404
|
paddingInline: 0
|
|
2183
2405
|
},
|
|
2184
|
-
"
|
|
2406
|
+
".cm-activeLine": {
|
|
2185
2407
|
background: "inherit"
|
|
2186
2408
|
},
|
|
2187
2409
|
//
|
|
2188
2410
|
// Selection
|
|
2189
2411
|
//
|
|
2190
2412
|
"&light .cm-selectionBackground": {
|
|
2191
|
-
background:
|
|
2413
|
+
background: get3(tokens, "extend.colors.primary.100")
|
|
2192
2414
|
},
|
|
2193
2415
|
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2194
|
-
background:
|
|
2416
|
+
background: get3(tokens, "extend.colors.primary.200")
|
|
2195
2417
|
},
|
|
2196
2418
|
"&dark .cm-selectionBackground": {
|
|
2197
|
-
background:
|
|
2419
|
+
background: get3(tokens, "extend.colors.primary.700")
|
|
2198
2420
|
},
|
|
2199
2421
|
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2200
|
-
background:
|
|
2422
|
+
background: get3(tokens, "extend.colors.primary.600")
|
|
2201
2423
|
},
|
|
2202
2424
|
//
|
|
2203
2425
|
// Search
|
|
2204
2426
|
//
|
|
2205
2427
|
"&light .cm-searchMatch": {
|
|
2206
|
-
backgroundColor:
|
|
2428
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.100")
|
|
2207
2429
|
},
|
|
2208
2430
|
"&dark .cm-searchMatch": {
|
|
2209
|
-
backgroundColor:
|
|
2210
|
-
},
|
|
2211
|
-
//
|
|
2212
|
-
// collaboration
|
|
2213
|
-
// TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
|
|
2214
|
-
//
|
|
2215
|
-
"&light .cm-ySelection, &light .cm-yLineSelection": {
|
|
2216
|
-
mixBlendMode: "multiply"
|
|
2217
|
-
},
|
|
2218
|
-
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
2219
|
-
mixBlendMode: "screen"
|
|
2220
|
-
},
|
|
2221
|
-
"& .cm-ySelectionInfo": {
|
|
2222
|
-
padding: "2px 4px",
|
|
2223
|
-
marginBlockStart: "-4px"
|
|
2224
|
-
},
|
|
2225
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
2226
|
-
paddingBlockStart: ".15em",
|
|
2227
|
-
paddingBlockEnd: ".15em"
|
|
2228
|
-
},
|
|
2229
|
-
"& .cm-ySelectionCaret": {
|
|
2230
|
-
display: "inline-block",
|
|
2231
|
-
insetBlockStart: ".1em",
|
|
2232
|
-
blockSize: "1.4em",
|
|
2233
|
-
verticalAlign: "top"
|
|
2234
|
-
},
|
|
2235
|
-
"& .cm-yLineSelection": {
|
|
2236
|
-
margin: 0
|
|
2431
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.700")
|
|
2237
2432
|
},
|
|
2238
2433
|
//
|
|
2239
2434
|
// link
|
|
2240
2435
|
//
|
|
2241
|
-
"
|
|
2242
|
-
color:
|
|
2436
|
+
".cm-link": {
|
|
2437
|
+
color: get3(tokens, "extend.colors.primary.500"),
|
|
2243
2438
|
textDecorationLine: "underline",
|
|
2244
2439
|
textDecorationThickness: "1px",
|
|
2245
2440
|
textUnderlineOffset: "2px",
|
|
2246
2441
|
borderRadius: ".125rem",
|
|
2247
|
-
fontFamily:
|
|
2442
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2248
2443
|
},
|
|
2249
2444
|
//
|
|
2250
2445
|
// tooltip
|
|
2251
2446
|
//
|
|
2252
|
-
"
|
|
2447
|
+
".cm-tooltip": {
|
|
2253
2448
|
border: "none"
|
|
2254
2449
|
},
|
|
2255
|
-
"
|
|
2450
|
+
".cm-tooltip-below": {},
|
|
2256
2451
|
//
|
|
2257
2452
|
// autocomplete
|
|
2258
2453
|
//
|
|
2259
|
-
"
|
|
2454
|
+
".cm-tooltip-autocomplete": {
|
|
2260
2455
|
marginTop: "4px",
|
|
2261
2456
|
marginLeft: "-3px"
|
|
2262
2457
|
},
|
|
2263
|
-
"
|
|
2264
|
-
"
|
|
2265
|
-
"
|
|
2458
|
+
".cm-tooltip-autocomplete ul li": {},
|
|
2459
|
+
".cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2460
|
+
".cm-completionIcon": {
|
|
2266
2461
|
display: "none"
|
|
2267
2462
|
},
|
|
2268
|
-
"
|
|
2269
|
-
fontFamily:
|
|
2463
|
+
".cm-completionLabel": {
|
|
2464
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2270
2465
|
},
|
|
2271
|
-
"
|
|
2466
|
+
".cm-completionMatchedText": {
|
|
2272
2467
|
textDecoration: "none"
|
|
2273
2468
|
},
|
|
2274
2469
|
//
|
|
2275
|
-
// widgets
|
|
2276
|
-
//
|
|
2277
|
-
"& .cm-widgetBuffer": {
|
|
2278
|
-
display: "none",
|
|
2279
|
-
height: 0
|
|
2280
|
-
},
|
|
2281
|
-
//
|
|
2282
2470
|
// table
|
|
2283
2471
|
//
|
|
2284
|
-
"
|
|
2285
|
-
fontFamily: `${
|
|
2472
|
+
".cm-table *": {
|
|
2473
|
+
fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
2286
2474
|
textDecoration: "none !important"
|
|
2287
2475
|
},
|
|
2288
|
-
"
|
|
2476
|
+
".cm-table-head": {
|
|
2289
2477
|
padding: "2px 16px 2px 0px",
|
|
2290
|
-
borderBottom: `1px solid ${
|
|
2478
|
+
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
|
|
2291
2479
|
fontWeight: 100,
|
|
2292
2480
|
textAlign: "left",
|
|
2293
|
-
color:
|
|
2481
|
+
color: get3(tokens, "extend.colors.neutral.500")
|
|
2294
2482
|
},
|
|
2295
|
-
"
|
|
2483
|
+
".cm-table-cell": {
|
|
2296
2484
|
padding: "2px 16px 2px 0px"
|
|
2297
2485
|
},
|
|
2298
2486
|
//
|
|
2299
2487
|
// image
|
|
2300
2488
|
//
|
|
2301
|
-
"
|
|
2302
|
-
|
|
2489
|
+
".cm-image": {
|
|
2490
|
+
display: "block",
|
|
2491
|
+
height: "0"
|
|
2492
|
+
},
|
|
2493
|
+
".cm-image.cm-loaded-image": {
|
|
2494
|
+
height: "auto",
|
|
2495
|
+
borderTop: "0.5rem solid transparent",
|
|
2496
|
+
borderBottom: "0.5rem solid transparent"
|
|
2303
2497
|
},
|
|
2304
2498
|
//
|
|
2305
2499
|
// font size
|
|
2306
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2500
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2501
|
+
// but it's far more verbose than it needs to be.
|
|
2307
2502
|
//
|
|
2308
|
-
...Object.keys(
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
2317
|
-
height
|
|
2318
|
-
};
|
|
2319
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
2320
|
-
height
|
|
2321
|
-
};
|
|
2322
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
2323
|
-
height
|
|
2324
|
-
};
|
|
2325
|
-
return acc;
|
|
2326
|
-
}, {}),
|
|
2503
|
+
// ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
|
|
2504
|
+
// const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
|
|
2505
|
+
//
|
|
2506
|
+
// acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
|
|
2507
|
+
// acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
|
|
2508
|
+
// acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
|
|
2509
|
+
// return acc;
|
|
2510
|
+
// }, {}),
|
|
2327
2511
|
/**
|
|
2328
2512
|
* Panels
|
|
2329
2513
|
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
@@ -2340,144 +2524,141 @@ var defaultTheme = {
|
|
|
2340
2524
|
* </div>
|
|
2341
2525
|
* </div
|
|
2342
2526
|
*/
|
|
2343
|
-
"
|
|
2344
|
-
border: `1px solid ${
|
|
2527
|
+
".cm-panels": {
|
|
2528
|
+
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
2345
2529
|
},
|
|
2346
|
-
"
|
|
2347
|
-
background:
|
|
2348
|
-
fontFamily:
|
|
2530
|
+
".cm-panel": {
|
|
2531
|
+
background: get3(tokens, "extend.colors.neutral.50"),
|
|
2532
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2349
2533
|
},
|
|
2350
|
-
"
|
|
2534
|
+
".cm-button": {
|
|
2351
2535
|
margin: "4px",
|
|
2352
|
-
fontFamily:
|
|
2353
|
-
background:
|
|
2536
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(","),
|
|
2537
|
+
background: get3(tokens, "extend.colors.neutral.100"),
|
|
2354
2538
|
backgroundImage: "none",
|
|
2355
2539
|
border: "none",
|
|
2356
2540
|
"&:hover": {
|
|
2357
|
-
background:
|
|
2541
|
+
background: get3(tokens, "extend.colors.neutral.200")
|
|
2358
2542
|
},
|
|
2359
2543
|
"&:active": {
|
|
2360
|
-
background:
|
|
2544
|
+
background: get3(tokens, "extend.colors.neutral.300"),
|
|
2361
2545
|
backgroundImage: "none"
|
|
2362
2546
|
}
|
|
2363
2547
|
},
|
|
2364
|
-
"
|
|
2548
|
+
".cm-panel input[type=checkbox]": {
|
|
2365
2549
|
marginRight: "0.4rem !important"
|
|
2366
2550
|
}
|
|
2367
2551
|
};
|
|
2368
2552
|
var textTheme = {
|
|
2369
|
-
"
|
|
2370
|
-
fontFamily:
|
|
2553
|
+
".cm-scroller": {
|
|
2554
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2371
2555
|
},
|
|
2372
|
-
"
|
|
2373
|
-
fontFamily:
|
|
2556
|
+
".cm-placeholder": {
|
|
2557
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2374
2558
|
}
|
|
2375
2559
|
};
|
|
2376
2560
|
var markdownTheme = {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2561
|
+
".cm-scroller": {
|
|
2562
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2563
|
+
},
|
|
2564
|
+
".cm-placeholder": {
|
|
2565
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2380
2566
|
}
|
|
2381
2567
|
};
|
|
2382
2568
|
var codeTheme = {
|
|
2383
|
-
"
|
|
2384
|
-
fontFamily:
|
|
2569
|
+
".cm-scroller": {
|
|
2570
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2385
2571
|
},
|
|
2386
|
-
"
|
|
2387
|
-
fontFamily:
|
|
2572
|
+
".cm-placeholder": {
|
|
2573
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2388
2574
|
}
|
|
2389
2575
|
};
|
|
2390
2576
|
|
|
2391
2577
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
2578
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
|
|
2392
2579
|
var EditorModes = [
|
|
2393
2580
|
"default",
|
|
2394
2581
|
"vim"
|
|
2395
2582
|
];
|
|
2396
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly,
|
|
2397
|
-
const { themeMode } = useThemeContext();
|
|
2583
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
|
|
2398
2584
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
2399
2585
|
tabBehavior: "limited"
|
|
2400
2586
|
});
|
|
2401
|
-
const
|
|
2402
|
-
const
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
state,
|
|
2587
|
+
const { themeMode } = useThemeContext2();
|
|
2588
|
+
const rootRef = useRef(null);
|
|
2589
|
+
const [view, setView] = useState(null);
|
|
2590
|
+
useImperativeHandle(forwardedRef, () => view, [
|
|
2406
2591
|
view
|
|
2407
|
-
}), [
|
|
2408
|
-
view,
|
|
2409
|
-
state,
|
|
2410
|
-
root
|
|
2411
|
-
]);
|
|
2412
|
-
const { awareness, peer } = model;
|
|
2413
|
-
useEffect2(() => {
|
|
2414
|
-
if (awareness && peer) {
|
|
2415
|
-
awareness.setLocalStateField("user", {
|
|
2416
|
-
name: peer.name ?? generateName(peer.id),
|
|
2417
|
-
color: getColorForValue({
|
|
2418
|
-
value: peer.id,
|
|
2419
|
-
type: "color"
|
|
2420
|
-
}),
|
|
2421
|
-
colorLight: getColorForValue({
|
|
2422
|
-
value: peer.id,
|
|
2423
|
-
themeMode,
|
|
2424
|
-
type: "highlight"
|
|
2425
|
-
})
|
|
2426
|
-
});
|
|
2427
|
-
}
|
|
2428
|
-
}, [
|
|
2429
|
-
awareness,
|
|
2430
|
-
peer,
|
|
2431
|
-
themeMode
|
|
2432
2592
|
]);
|
|
2433
|
-
|
|
2434
|
-
if (
|
|
2435
|
-
view
|
|
2436
|
-
effects: setCommentRange.of({
|
|
2437
|
-
model,
|
|
2438
|
-
comments: comments2
|
|
2439
|
-
})
|
|
2440
|
-
});
|
|
2593
|
+
useEffect3(() => {
|
|
2594
|
+
if (autofocus) {
|
|
2595
|
+
view?.focus();
|
|
2441
2596
|
}
|
|
2442
2597
|
}, [
|
|
2443
2598
|
view,
|
|
2444
|
-
|
|
2599
|
+
autofocus
|
|
2445
2600
|
]);
|
|
2446
|
-
|
|
2447
|
-
|
|
2601
|
+
useAwareness(model);
|
|
2602
|
+
useEffect3(() => {
|
|
2603
|
+
if (!model || !rootRef.current) {
|
|
2448
2604
|
return;
|
|
2449
2605
|
}
|
|
2450
|
-
const
|
|
2606
|
+
const state = EditorState2.create({
|
|
2451
2607
|
doc: model.text(),
|
|
2608
|
+
selection,
|
|
2452
2609
|
extensions: [
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2610
|
+
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
2611
|
+
EditorView13.exceptionSink.of((err) => {
|
|
2612
|
+
log2.catch(err, void 0, {
|
|
2613
|
+
F: __dxlog_file5,
|
|
2614
|
+
L: 126,
|
|
2615
|
+
S: void 0,
|
|
2616
|
+
C: (f, a) => f(...a)
|
|
2617
|
+
});
|
|
2618
|
+
}),
|
|
2456
2619
|
// Theme.
|
|
2457
|
-
// TODO(burdon): Make
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2620
|
+
// TODO(burdon): Make configurable.
|
|
2621
|
+
EditorView13.baseTheme(defaultTheme),
|
|
2622
|
+
EditorView13.theme(theme ?? {}),
|
|
2623
|
+
EditorView13.darkTheme.of(themeMode === "dark"),
|
|
2624
|
+
EditorView13.editorAttributes.of({
|
|
2625
|
+
class: slots.editor?.className ?? ""
|
|
2626
|
+
}),
|
|
2627
|
+
EditorView13.contentAttributes.of({
|
|
2628
|
+
class: slots.content?.className ?? ""
|
|
2629
|
+
}),
|
|
2630
|
+
// State.
|
|
2631
|
+
EditorState2.readOnly.of(!!readonly),
|
|
2461
2632
|
// Storage and replication.
|
|
2633
|
+
// NOTE: This must come before user extensions.
|
|
2462
2634
|
model.extension,
|
|
2635
|
+
// TODO(burdon): Factor out (requires special handling for Escape/focus).
|
|
2636
|
+
editorMode === "vim" && vim(),
|
|
2463
2637
|
// Custom.
|
|
2464
2638
|
...extensions
|
|
2465
2639
|
].filter(Boolean)
|
|
2466
2640
|
});
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2641
|
+
const newView = new EditorView13({
|
|
2642
|
+
state,
|
|
2643
|
+
parent: rootRef.current,
|
|
2644
|
+
scrollTo,
|
|
2645
|
+
// NOTE: Uncomment to debug/monitor all transactions.
|
|
2646
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
2647
|
+
dispatchTransactions: (trs, view2) => {
|
|
2648
|
+
if (debug) {
|
|
2649
|
+
logChanges(trs);
|
|
2650
|
+
}
|
|
2651
|
+
view2.update(trs);
|
|
2652
|
+
}
|
|
2474
2653
|
});
|
|
2654
|
+
view?.destroy();
|
|
2655
|
+
setView(newView);
|
|
2475
2656
|
return () => {
|
|
2476
|
-
|
|
2477
|
-
|
|
2657
|
+
newView?.destroy();
|
|
2658
|
+
setView(null);
|
|
2478
2659
|
};
|
|
2479
2660
|
}, [
|
|
2480
|
-
|
|
2661
|
+
rootRef,
|
|
2481
2662
|
model,
|
|
2482
2663
|
readonly,
|
|
2483
2664
|
editorMode,
|
|
@@ -2487,11 +2668,13 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: co
|
|
|
2487
2668
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
2488
2669
|
switch (key) {
|
|
2489
2670
|
case "Enter": {
|
|
2490
|
-
view?.
|
|
2671
|
+
view?.focus();
|
|
2491
2672
|
break;
|
|
2492
2673
|
}
|
|
2493
2674
|
case "Escape": {
|
|
2494
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)
|
|
2675
|
+
if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
2676
|
+
rootRef.current?.focus();
|
|
2677
|
+
}
|
|
2495
2678
|
break;
|
|
2496
2679
|
}
|
|
2497
2680
|
}
|
|
@@ -2501,106 +2684,433 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: co
|
|
|
2501
2684
|
]);
|
|
2502
2685
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2503
2686
|
key: model.id,
|
|
2504
|
-
|
|
2687
|
+
role: "none",
|
|
2505
2688
|
tabIndex: 0,
|
|
2506
|
-
|
|
2689
|
+
onKeyUp: handleKeyUp,
|
|
2690
|
+
...slots.root,
|
|
2507
2691
|
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
2508
|
-
|
|
2692
|
+
ref: rootRef
|
|
2509
2693
|
});
|
|
2510
2694
|
});
|
|
2511
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly,
|
|
2512
|
-
const { themeMode } =
|
|
2513
|
-
const
|
|
2695
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, multiline, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2696
|
+
const { themeMode } = useThemeContext2();
|
|
2697
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
2514
2698
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2515
2699
|
ref: forwardedRef,
|
|
2516
2700
|
readonly,
|
|
2517
2701
|
extensions: [
|
|
2518
2702
|
basicBundle({
|
|
2519
|
-
readonly,
|
|
2520
2703
|
themeMode,
|
|
2521
|
-
placeholder:
|
|
2704
|
+
placeholder: placeholder3,
|
|
2705
|
+
multiline
|
|
2522
2706
|
}),
|
|
2523
2707
|
...extensions
|
|
2524
2708
|
],
|
|
2525
|
-
|
|
2709
|
+
theme,
|
|
2710
|
+
slots: updatedSlots,
|
|
2526
2711
|
...props
|
|
2527
2712
|
});
|
|
2528
2713
|
});
|
|
2529
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly,
|
|
2530
|
-
const { themeMode } =
|
|
2531
|
-
const
|
|
2714
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2715
|
+
const { themeMode } = useThemeContext2();
|
|
2716
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
2532
2717
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2533
2718
|
ref: forwardedRef,
|
|
2534
2719
|
readonly,
|
|
2535
2720
|
extensions: [
|
|
2536
2721
|
markdownBundle({
|
|
2537
|
-
readonly,
|
|
2538
2722
|
themeMode,
|
|
2539
|
-
|
|
2723
|
+
readonly,
|
|
2724
|
+
placeholder: placeholder3
|
|
2540
2725
|
}),
|
|
2541
2726
|
...extensions
|
|
2542
2727
|
],
|
|
2543
|
-
|
|
2728
|
+
theme,
|
|
2729
|
+
slots: updatedSlots,
|
|
2544
2730
|
...props
|
|
2545
2731
|
});
|
|
2546
2732
|
});
|
|
2547
2733
|
var defaultSlots = {
|
|
2548
2734
|
root: {
|
|
2549
|
-
|
|
2735
|
+
// TODO(burdon): Add focusRing by default/as property?
|
|
2736
|
+
className: mx3("flex flex-col grow overflow-y-auto", inputSurface)
|
|
2737
|
+
},
|
|
2738
|
+
editor: {
|
|
2739
|
+
className: "h-full p-2"
|
|
2550
2740
|
}
|
|
2551
2741
|
};
|
|
2552
2742
|
var defaultTextSlots = {
|
|
2553
|
-
...defaultSlots
|
|
2554
|
-
editor: {
|
|
2555
|
-
theme: textTheme
|
|
2556
|
-
}
|
|
2743
|
+
...defaultSlots
|
|
2557
2744
|
};
|
|
2558
2745
|
var defaultMarkdownSlots = {
|
|
2559
|
-
...defaultSlots
|
|
2560
|
-
|
|
2561
|
-
|
|
2746
|
+
...defaultSlots
|
|
2747
|
+
};
|
|
2748
|
+
|
|
2749
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2750
|
+
import get4 from "lodash.get";
|
|
2751
|
+
import { getRawDoc } from "@dxos/echo-schema";
|
|
2752
|
+
import { isNotNullOrUndefined } from "@dxos/util";
|
|
2753
|
+
|
|
2754
|
+
// packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
|
|
2755
|
+
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2756
|
+
import { Context as Context2 } from "@dxos/context";
|
|
2757
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2758
|
+
import { log as log3 } from "@dxos/log";
|
|
2759
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2760
|
+
var DEBOUNCE_INTERVAL = 100;
|
|
2761
|
+
var SpaceAwarenessProvider = class {
|
|
2762
|
+
constructor(params) {
|
|
2763
|
+
this.remoteStateChange = new Event2();
|
|
2764
|
+
this._remoteStates = /* @__PURE__ */ new Map();
|
|
2765
|
+
this._space = params.space;
|
|
2766
|
+
this._channel = params.channel;
|
|
2767
|
+
this._peerId = params.peerId;
|
|
2768
|
+
this._info = params.info;
|
|
2769
|
+
}
|
|
2770
|
+
open() {
|
|
2771
|
+
this._ctx = new Context2();
|
|
2772
|
+
this._postTask = new DeferredTask(this._ctx, async () => {
|
|
2773
|
+
if (this._localState) {
|
|
2774
|
+
await this._space.postMessage(this._channel, {
|
|
2775
|
+
kind: "post",
|
|
2776
|
+
state: this._localState
|
|
2777
|
+
});
|
|
2778
|
+
await sleep(DEBOUNCE_INTERVAL);
|
|
2779
|
+
}
|
|
2780
|
+
});
|
|
2781
|
+
const unsubscribe = this._space.listen(this._channel, (message) => {
|
|
2782
|
+
switch (message.payload.kind) {
|
|
2783
|
+
case "query": {
|
|
2784
|
+
this._handleQueryMessage();
|
|
2785
|
+
break;
|
|
2786
|
+
}
|
|
2787
|
+
case "post": {
|
|
2788
|
+
this._handlePostMessage(message.payload);
|
|
2789
|
+
break;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2793
|
+
this._ctx.onDispose(unsubscribe);
|
|
2794
|
+
void this._space.postMessage(this._channel, {
|
|
2795
|
+
kind: "query"
|
|
2796
|
+
}).catch((err) => {
|
|
2797
|
+
log3.debug("failed to query awareness", {
|
|
2798
|
+
err
|
|
2799
|
+
}, {
|
|
2800
|
+
F: __dxlog_file6,
|
|
2801
|
+
L: 85,
|
|
2802
|
+
S: this,
|
|
2803
|
+
C: (f, a) => f(...a)
|
|
2804
|
+
});
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
close() {
|
|
2808
|
+
void this._ctx?.dispose();
|
|
2809
|
+
this._ctx = void 0;
|
|
2810
|
+
this._postTask = void 0;
|
|
2811
|
+
}
|
|
2812
|
+
update(position) {
|
|
2813
|
+
invariant4(this._postTask, void 0, {
|
|
2814
|
+
F: __dxlog_file6,
|
|
2815
|
+
L: 96,
|
|
2816
|
+
S: this,
|
|
2817
|
+
A: [
|
|
2818
|
+
"this._postTask",
|
|
2819
|
+
""
|
|
2820
|
+
]
|
|
2821
|
+
});
|
|
2822
|
+
this._localState = {
|
|
2823
|
+
peerId: this._peerId,
|
|
2824
|
+
position,
|
|
2825
|
+
info: this._info
|
|
2826
|
+
};
|
|
2827
|
+
this._postTask.schedule();
|
|
2828
|
+
}
|
|
2829
|
+
getRemoteStates() {
|
|
2830
|
+
return Array.from(this._remoteStates.values());
|
|
2831
|
+
}
|
|
2832
|
+
_handleQueryMessage() {
|
|
2833
|
+
invariant4(this._postTask, void 0, {
|
|
2834
|
+
F: __dxlog_file6,
|
|
2835
|
+
L: 111,
|
|
2836
|
+
S: this,
|
|
2837
|
+
A: [
|
|
2838
|
+
"this._postTask",
|
|
2839
|
+
""
|
|
2840
|
+
]
|
|
2841
|
+
});
|
|
2842
|
+
this._postTask.schedule();
|
|
2843
|
+
}
|
|
2844
|
+
_handlePostMessage(message) {
|
|
2845
|
+
invariant4(message.kind === "post", void 0, {
|
|
2846
|
+
F: __dxlog_file6,
|
|
2847
|
+
L: 116,
|
|
2848
|
+
S: this,
|
|
2849
|
+
A: [
|
|
2850
|
+
"message.kind === 'post'",
|
|
2851
|
+
""
|
|
2852
|
+
]
|
|
2853
|
+
});
|
|
2854
|
+
this._remoteStates.set(message.state.peerId, message.state);
|
|
2855
|
+
this.remoteStateChange.emit();
|
|
2856
|
+
}
|
|
2857
|
+
};
|
|
2858
|
+
|
|
2859
|
+
// packages/ui/react-ui-editor/src/hooks/defs.ts
|
|
2860
|
+
import { StateField as StateField5 } from "@codemirror/state";
|
|
2861
|
+
var modelState = StateField5.define({
|
|
2862
|
+
create: () => void 0,
|
|
2863
|
+
update: (model) => model
|
|
2864
|
+
});
|
|
2865
|
+
|
|
2866
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2867
|
+
var createAutomergeModel = ({ space, identity, text }) => {
|
|
2868
|
+
const obj = text;
|
|
2869
|
+
const doc = getRawDoc(obj, [
|
|
2870
|
+
obj.field
|
|
2871
|
+
]);
|
|
2872
|
+
const awarenessProvider = space && new SpaceAwarenessProvider({
|
|
2873
|
+
space,
|
|
2874
|
+
channel: `automerge.awareness.${obj.id}`,
|
|
2875
|
+
info: {
|
|
2876
|
+
displayName: identity?.profile?.displayName ?? "",
|
|
2877
|
+
color: cursorColor.color,
|
|
2878
|
+
lightColor: cursorColor.light
|
|
2879
|
+
},
|
|
2880
|
+
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2881
|
+
});
|
|
2882
|
+
const model = {
|
|
2883
|
+
id: obj.id,
|
|
2884
|
+
content: doc,
|
|
2885
|
+
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2886
|
+
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
2887
|
+
extension: [
|
|
2888
|
+
modelState.init(() => model),
|
|
2889
|
+
automerge3({
|
|
2890
|
+
handle: doc.handle,
|
|
2891
|
+
path: doc.path
|
|
2892
|
+
}),
|
|
2893
|
+
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
2894
|
+
awareness()
|
|
2895
|
+
].filter(isNotNullOrUndefined),
|
|
2896
|
+
peer: identity ? {
|
|
2897
|
+
id: identity.identityKey.toHex(),
|
|
2898
|
+
name: identity.profile?.displayName
|
|
2899
|
+
} : void 0
|
|
2900
|
+
};
|
|
2901
|
+
return model;
|
|
2902
|
+
};
|
|
2903
|
+
|
|
2904
|
+
// packages/ui/react-ui-editor/src/hooks/yjs.ts
|
|
2905
|
+
import * as decoding from "lib0/decoding";
|
|
2906
|
+
import * as encoding from "lib0/encoding";
|
|
2907
|
+
import { Observable } from "lib0/observable";
|
|
2908
|
+
import * as YP from "y-protocols/awareness";
|
|
2909
|
+
import { invariant as invariant5 } from "@dxos/invariant";
|
|
2910
|
+
import { log as log4 } from "@dxos/log";
|
|
2911
|
+
var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2912
|
+
var createYjsModel = ({ identity, space, text }) => {
|
|
2913
|
+
invariant5(text?.doc && text?.content, void 0, {
|
|
2914
|
+
F: __dxlog_file7,
|
|
2915
|
+
L: 22,
|
|
2916
|
+
S: void 0,
|
|
2917
|
+
A: [
|
|
2918
|
+
"text?.doc && text?.content",
|
|
2919
|
+
""
|
|
2920
|
+
]
|
|
2921
|
+
});
|
|
2922
|
+
const provider = space ? new YAwarenessProvider({
|
|
2923
|
+
space,
|
|
2924
|
+
doc: text.doc,
|
|
2925
|
+
channel: `yjs.awareness.${text.id}`
|
|
2926
|
+
}) : void 0;
|
|
2927
|
+
const model = {
|
|
2928
|
+
id: text.doc.guid,
|
|
2929
|
+
content: text.content,
|
|
2930
|
+
text: () => text.content.toString(),
|
|
2931
|
+
extension: [
|
|
2932
|
+
modelState.init(() => model),
|
|
2933
|
+
yjs(text.content, provider?.awareness)
|
|
2934
|
+
],
|
|
2935
|
+
awareness: provider?.awareness,
|
|
2936
|
+
peer: identity ? {
|
|
2937
|
+
id: identity.identityKey.toHex(),
|
|
2938
|
+
name: identity.profile?.displayName
|
|
2939
|
+
} : void 0
|
|
2940
|
+
};
|
|
2941
|
+
return model;
|
|
2942
|
+
};
|
|
2943
|
+
var messageAwareness = 1;
|
|
2944
|
+
var messageQueryAwareness = 3;
|
|
2945
|
+
var YAwarenessProvider = class extends Observable {
|
|
2946
|
+
constructor({ space, doc, channel, awareness: awareness2 }) {
|
|
2947
|
+
super();
|
|
2948
|
+
this._space = space;
|
|
2949
|
+
this._awareness = awareness2 ?? new YP.Awareness(doc);
|
|
2950
|
+
this._channel = channel;
|
|
2951
|
+
this._clientId = doc.clientID;
|
|
2952
|
+
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
2953
|
+
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
2954
|
+
if (typeof window !== "undefined") {
|
|
2955
|
+
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
2956
|
+
} else if (typeof process !== "undefined") {
|
|
2957
|
+
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
2958
|
+
}
|
|
2959
|
+
const encoderAwarenessQuery = encoding.createEncoder();
|
|
2960
|
+
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
2961
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
2962
|
+
const encoderAwarenessState = encoding.createEncoder();
|
|
2963
|
+
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
2964
|
+
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
|
|
2965
|
+
this._clientId
|
|
2966
|
+
]));
|
|
2967
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
2968
|
+
}
|
|
2969
|
+
get awareness() {
|
|
2970
|
+
return this._awareness;
|
|
2971
|
+
}
|
|
2972
|
+
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
2973
|
+
log4("awareness update", {
|
|
2974
|
+
added,
|
|
2975
|
+
updated,
|
|
2976
|
+
removed,
|
|
2977
|
+
origin
|
|
2978
|
+
}, {
|
|
2979
|
+
F: __dxlog_file7,
|
|
2980
|
+
L: 101,
|
|
2981
|
+
S: this,
|
|
2982
|
+
C: (f, a) => f(...a)
|
|
2983
|
+
});
|
|
2984
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
2985
|
+
const encoderAwareness = encoding.createEncoder();
|
|
2986
|
+
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
2987
|
+
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
2988
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
2989
|
+
}
|
|
2990
|
+
_handleSpaceMessage({ payload }) {
|
|
2991
|
+
log4("space message", payload, {
|
|
2992
|
+
F: __dxlog_file7,
|
|
2993
|
+
L: 110,
|
|
2994
|
+
S: this,
|
|
2995
|
+
C: (f, a) => f(...a)
|
|
2996
|
+
});
|
|
2997
|
+
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
2998
|
+
const encoder = this._readMessage(data);
|
|
2999
|
+
if (encoder) {
|
|
3000
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
_readMessage(message) {
|
|
3004
|
+
const decoder = decoding.createDecoder(message);
|
|
3005
|
+
const encoder = encoding.createEncoder();
|
|
3006
|
+
const messageType = decoding.readVarUint(decoder);
|
|
3007
|
+
let sendReply = false;
|
|
3008
|
+
switch (messageType) {
|
|
3009
|
+
case messageAwareness: {
|
|
3010
|
+
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
3011
|
+
break;
|
|
3012
|
+
}
|
|
3013
|
+
case messageQueryAwareness: {
|
|
3014
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
3015
|
+
encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
3016
|
+
sendReply = true;
|
|
3017
|
+
break;
|
|
3018
|
+
}
|
|
3019
|
+
default: {
|
|
3020
|
+
console.error("Invalid message:", messageType);
|
|
3021
|
+
return encoder;
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
if (!sendReply) {
|
|
3025
|
+
return null;
|
|
3026
|
+
}
|
|
3027
|
+
return encoder;
|
|
3028
|
+
}
|
|
3029
|
+
_handleBeforeUnload() {
|
|
3030
|
+
YP.removeAwarenessStates(this._awareness, [
|
|
3031
|
+
this._clientId
|
|
3032
|
+
], "window unload");
|
|
3033
|
+
}
|
|
3034
|
+
};
|
|
3035
|
+
|
|
3036
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
3037
|
+
import { useEffect as useEffect4, useState as useState2 } from "react";
|
|
3038
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
3039
|
+
var useTextModel = (props) => {
|
|
3040
|
+
const { identity, space, text } = props;
|
|
3041
|
+
const [model, setModel] = useState2();
|
|
3042
|
+
useEffect4(() => setModel(createModel(props)), [
|
|
3043
|
+
identity,
|
|
3044
|
+
space,
|
|
3045
|
+
text
|
|
3046
|
+
]);
|
|
3047
|
+
return model;
|
|
3048
|
+
};
|
|
3049
|
+
var createModel = (props) => {
|
|
3050
|
+
const { text } = props;
|
|
3051
|
+
if (isAutomergeObject(text)) {
|
|
3052
|
+
return createAutomergeModel(props);
|
|
3053
|
+
} else if (text?.doc) {
|
|
3054
|
+
return createYjsModel(props);
|
|
3055
|
+
} else {
|
|
3056
|
+
return void 0;
|
|
2562
3057
|
}
|
|
2563
3058
|
};
|
|
2564
3059
|
export {
|
|
3060
|
+
AwarenessProvider,
|
|
2565
3061
|
BaseTextEditor,
|
|
3062
|
+
Cursor,
|
|
2566
3063
|
Doc,
|
|
2567
3064
|
EditorModes,
|
|
2568
3065
|
MarkdownEditor,
|
|
3066
|
+
RemoteSelectionsDecorator,
|
|
2569
3067
|
SpaceAwarenessProvider,
|
|
2570
3068
|
TextEditor,
|
|
2571
3069
|
TextKind,
|
|
3070
|
+
YAwarenessProvider,
|
|
2572
3071
|
YText,
|
|
2573
3072
|
YXmlFragment,
|
|
2574
3073
|
autocomplete,
|
|
3074
|
+
automerge3 as automerge,
|
|
3075
|
+
awareness,
|
|
2575
3076
|
basicBundle,
|
|
2576
3077
|
blast,
|
|
3078
|
+
callbackWrapper,
|
|
2577
3079
|
code2 as code,
|
|
2578
3080
|
codeTheme,
|
|
2579
3081
|
comments,
|
|
2580
|
-
|
|
3082
|
+
createAutomergeModel,
|
|
3083
|
+
createYjsModel,
|
|
2581
3084
|
defaultMarkdownSlots,
|
|
2582
3085
|
defaultOptions,
|
|
2583
3086
|
defaultSlots,
|
|
2584
3087
|
defaultTextSlots,
|
|
2585
3088
|
defaultTheme,
|
|
3089
|
+
getToken,
|
|
2586
3090
|
hr,
|
|
2587
3091
|
image,
|
|
2588
3092
|
link,
|
|
2589
3093
|
listener,
|
|
3094
|
+
logChanges,
|
|
2590
3095
|
markdownBundle,
|
|
2591
3096
|
markdownHighlightStyle,
|
|
2592
3097
|
markdownTags,
|
|
2593
3098
|
markdownTagsExtensions,
|
|
2594
3099
|
markdownTheme,
|
|
2595
3100
|
mention,
|
|
2596
|
-
mermaid,
|
|
2597
3101
|
modelState,
|
|
2598
|
-
|
|
3102
|
+
outliner,
|
|
3103
|
+
setComments,
|
|
3104
|
+
setFocus,
|
|
3105
|
+
setSelection,
|
|
2599
3106
|
table,
|
|
2600
3107
|
tags2 as tags,
|
|
2601
3108
|
tasklist,
|
|
2602
3109
|
textTheme,
|
|
2603
3110
|
typewriter,
|
|
2604
|
-
|
|
3111
|
+
useAwareness,
|
|
3112
|
+
useComments,
|
|
3113
|
+
useTextModel,
|
|
3114
|
+
yjs
|
|
2605
3115
|
};
|
|
2606
3116
|
//# sourceMappingURL=index.mjs.map
|