@dxos/react-ui-editor 0.3.10-next.ef70620 → 0.3.11-main.0e40963
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 +2226 -971
- 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 +63 -0
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +9 -4
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +12 -4
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/index.d.ts +1 -1
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts +10 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions/basic/bundle.d.ts → extensions/basic.d.ts} +3 -2
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts +25 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/code.d.ts +2 -0
- package/dist/types/src/extensions/code.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +48 -0
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts +2 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/image.d.ts +4 -0
- package/dist/types/src/extensions/image.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +16 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/extensions/link.d.ts +12 -0
- package/dist/types/src/extensions/link.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts +10 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts +17 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts +36 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts +3 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts +6 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/mermaid.d.ts +3 -0
- package/dist/types/src/extensions/mermaid.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts +8 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts +3 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/typewriter.d.ts +10 -0
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/util.d.ts +5 -0
- package/dist/types/src/extensions/util.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts +15 -0
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
- package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +1 -1
- package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
- package/dist/types/src/{automerge/automerge-plugin/PatchSemaphore.d.ts → hooks/automerge/semaphore.d.ts} +4 -1
- package/dist/types/src/hooks/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/{automerge/automerge-plugin/codeMirrorToAm.d.ts → hooks/automerge/update-automerge.d.ts} +2 -3
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts +5 -0
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +0 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +19 -5
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/hooks/yjs/yjs.test.d.ts +2 -0
- package/dist/types/src/hooks/yjs/yjs.test.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/markdown.d.ts +14 -11
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts +17 -0
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts +2 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/package.json +28 -33
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +426 -0
- package/src/components/TextEditor/TextEditor.stories.tsx +57 -43
- package/src/components/TextEditor/TextEditor.tsx +98 -52
- package/src/components/TextEditor/index.ts +1 -1
- package/src/extensions/autocomplete.ts +59 -0
- package/src/{components/TextEditor/extensions/basic/bundle.ts → extensions/basic.ts} +8 -4
- package/src/extensions/blast.ts +373 -0
- package/src/extensions/code.ts +99 -0
- package/src/extensions/comments.ts +391 -0
- package/src/extensions/hr.ts +74 -0
- package/src/extensions/image.ts +69 -0
- package/src/extensions/index.ts +19 -0
- package/src/extensions/link.ts +192 -0
- package/src/extensions/listener.ts +35 -0
- package/src/extensions/markdown/bundle.ts +106 -0
- package/src/extensions/markdown/highlight.ts +204 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +1 -1
- package/src/extensions/mention.ts +31 -0
- package/src/extensions/mermaid.ts +11 -0
- package/src/extensions/table.ts +132 -0
- package/src/extensions/tasklist.ts +121 -0
- package/src/extensions/typewriter.ts +68 -0
- package/src/extensions/util.ts +18 -0
- package/src/{automerge → hooks/automerge}/automerge.stories.tsx +7 -5
- package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +2 -1
- package/src/{automerge/automerge-plugin/PatchSemaphore.ts → hooks/automerge/semaphore.ts} +13 -9
- package/src/{automerge/automerge-plugin/codeMirrorToAm.ts → hooks/automerge/update-automerge.ts} +5 -2
- package/src/{automerge/automerge-plugin/amToCodemirror.ts → hooks/automerge/update-codemirror.ts} +4 -1
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useTextModel.ts +102 -42
- package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -1
- package/src/hooks/yjs/yjs.test.ts +59 -0
- package/src/index.ts +3 -0
- package/src/styles/markdown.ts +33 -24
- package/src/testing/replicator.ts +6 -6
- package/src/themes/default.ts +302 -0
- package/src/themes/index.ts +5 -0
- package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts +0 -6
- package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge.stories.d.ts +0 -9
- package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts +0 -10
- package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/change.d.ts +0 -7
- package/dist/types/src/components/TextEditor/extensions/change.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts +0 -11
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts +0 -8
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts +0 -10
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts +0 -4
- package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +0 -5
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +0 -8
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts +0 -17
- package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts +0 -21
- package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
- package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/basic/index.ts +0 -6
- package/src/components/TextEditor/extensions/basic/theme.ts +0 -49
- package/src/components/TextEditor/extensions/change.ts +0 -22
- package/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts +0 -99
- package/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx +0 -61
- package/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx +0 -119
- package/src/components/TextEditor/extensions/hyperlink/index.ts +0 -7
- package/src/components/TextEditor/extensions/index.ts +0 -8
- package/src/components/TextEditor/extensions/markdown/bundle.ts +0 -99
- package/src/components/TextEditor/extensions/markdown/tags.ts +0 -38
- package/src/components/TextEditor/extensions/markdown/theme.ts +0 -265
- package/src/hooks/useCollaboration.ts +0 -45
- package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
- package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
- package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
- package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +1 -1
|
@@ -1,771 +1,624 @@
|
|
|
1
1
|
// packages/ui/react-ui-editor/src/index.ts
|
|
2
2
|
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
3
|
-
import { Doc, YText
|
|
3
|
+
import { Doc, YText, YXmlFragment } from "@dxos/text-model";
|
|
4
4
|
|
|
5
5
|
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
6
6
|
import { tags as tags2 } from "@lezer/highlight";
|
|
7
7
|
|
|
8
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/
|
|
8
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
+
import { EditorView as EditorView12 } from "@codemirror/view";
|
|
11
|
+
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
12
|
+
import { vim } from "@replit/codemirror-vim";
|
|
13
|
+
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState as useState2 } from "react";
|
|
15
|
+
import { generateName } from "@dxos/display-name";
|
|
16
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
+
import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
18
|
+
|
|
19
|
+
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
20
|
+
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
21
|
+
import { keymap } from "@codemirror/view";
|
|
22
|
+
var autocomplete = ({ onSearch }) => {
|
|
23
|
+
return [
|
|
24
|
+
// https://codemirror.net/docs/ref/#view.keymap
|
|
25
|
+
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
26
|
+
keymap.of(completionKeymap),
|
|
27
|
+
// https://codemirror.net/examples/autocompletion
|
|
28
|
+
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
29
|
+
autocompletion({
|
|
30
|
+
// TODO(burdon): Set custom keymap.
|
|
31
|
+
// defaultKeymap: false,
|
|
32
|
+
// Don't start unless key press.
|
|
33
|
+
activateOnTyping: false,
|
|
34
|
+
// TODO(burdon): Option to create new page?
|
|
35
|
+
// TODO(burdon): Optional decoration via addToOptions
|
|
36
|
+
override: [
|
|
37
|
+
(context) => {
|
|
38
|
+
const match = context.matchBefore(/\w*/);
|
|
39
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
from: match.from,
|
|
44
|
+
options: onSearch(match.text.toLowerCase())
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
})
|
|
49
|
+
];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
9
53
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
10
54
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
11
55
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
12
|
-
import { EditorView, placeholder } from "@codemirror/view";
|
|
13
|
-
var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
|
|
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
|
|
14
61
|
bracketMatching(),
|
|
15
62
|
closeBrackets(),
|
|
63
|
+
drawSelection(),
|
|
16
64
|
_placeholder && placeholder(_placeholder),
|
|
17
|
-
EditorView.lineWrapping,
|
|
18
65
|
// TODO(burdon): Is this required?
|
|
19
66
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
20
67
|
].filter(Boolean);
|
|
21
68
|
|
|
22
|
-
// packages/ui/react-ui-editor/src/
|
|
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";
|
|
72
|
+
import { invariant } from "@dxos/invariant";
|
|
73
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
74
|
+
var defaultOptions = {
|
|
75
|
+
effect: 2,
|
|
76
|
+
maxParticles: 200,
|
|
77
|
+
particleGravity: 0.08,
|
|
78
|
+
particleAlphaFadeout: 0.996,
|
|
79
|
+
particleSize: {
|
|
80
|
+
min: 2,
|
|
81
|
+
max: 8
|
|
82
|
+
},
|
|
83
|
+
particleNumRange: {
|
|
84
|
+
min: 5,
|
|
85
|
+
max: 10
|
|
86
|
+
},
|
|
87
|
+
particleVelocityRange: {
|
|
88
|
+
x: [
|
|
89
|
+
-1,
|
|
90
|
+
1
|
|
91
|
+
],
|
|
92
|
+
y: [
|
|
93
|
+
-3.5,
|
|
94
|
+
-1.5
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
particleShrinkRate: 0.95,
|
|
98
|
+
shakeIntensity: 5
|
|
99
|
+
};
|
|
100
|
+
var blast = (options = defaultOptions) => {
|
|
101
|
+
let blaster;
|
|
102
|
+
let last = 0;
|
|
103
|
+
const getPoint = (view, pos) => {
|
|
104
|
+
const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
|
|
105
|
+
const element = document.elementFromPoint(x, y);
|
|
106
|
+
const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
|
|
107
|
+
const point = {
|
|
108
|
+
x: x - left,
|
|
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();
|
|
134
|
+
} else {
|
|
135
|
+
blaster.resize();
|
|
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;
|
|
191
|
+
}
|
|
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
|
+
}
|
|
234
|
+
}
|
|
235
|
+
start() {
|
|
236
|
+
invariant(this._canvas && this._ctx, void 0, {
|
|
237
|
+
F: __dxlog_file,
|
|
238
|
+
L: 177,
|
|
239
|
+
S: this,
|
|
240
|
+
A: [
|
|
241
|
+
"this._canvas && this._ctx",
|
|
242
|
+
""
|
|
243
|
+
]
|
|
244
|
+
});
|
|
245
|
+
this._running = true;
|
|
246
|
+
this.loop();
|
|
247
|
+
}
|
|
248
|
+
stop() {
|
|
249
|
+
this._running = false;
|
|
250
|
+
this._node.style.transform = "translate(0px, 0px)";
|
|
251
|
+
}
|
|
252
|
+
loop() {
|
|
253
|
+
if (!this._running || !this._canvas || !this._ctx) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);
|
|
257
|
+
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
258
|
+
this._lastTime ??= now;
|
|
259
|
+
const dt = (now - this._lastTime) / 1e3;
|
|
260
|
+
this._lastTime = now;
|
|
261
|
+
if (this._shakeTime > 0) {
|
|
262
|
+
this._shakeTime -= dt;
|
|
263
|
+
const magnitude = this._shakeTime / this._shakeTimeMax * this._options.shakeIntensity;
|
|
264
|
+
const shakeX = random(-magnitude, magnitude);
|
|
265
|
+
const shakeY = random(-magnitude, magnitude);
|
|
266
|
+
this._node.style.transform = `translate(${shakeX}px,${shakeY}px)`;
|
|
267
|
+
}
|
|
268
|
+
this.drawParticles();
|
|
269
|
+
requestAnimationFrame(this.loop.bind(this));
|
|
270
|
+
}
|
|
271
|
+
drawParticles() {
|
|
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);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var Effect = class {
|
|
285
|
+
constructor(_options) {
|
|
286
|
+
this._options = _options;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var Effect1 = class extends Effect {
|
|
290
|
+
create(x, y, color) {
|
|
291
|
+
return {
|
|
292
|
+
x,
|
|
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);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
var Effect2 = class extends Effect {
|
|
311
|
+
create(x, y, color) {
|
|
312
|
+
return {
|
|
313
|
+
x,
|
|
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();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
function throttle(callback, limit) {
|
|
343
|
+
let wait = false;
|
|
344
|
+
return function(...args) {
|
|
345
|
+
if (!wait) {
|
|
346
|
+
callback.apply(this, args);
|
|
347
|
+
wait = true;
|
|
348
|
+
setTimeout(() => {
|
|
349
|
+
wait = false;
|
|
350
|
+
}, limit);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
var getRGBComponents = (node, color) => {
|
|
355
|
+
if (typeof color === "function") {
|
|
356
|
+
return color();
|
|
357
|
+
}
|
|
358
|
+
const bgColor = getComputedStyle(node).color;
|
|
359
|
+
if (bgColor) {
|
|
360
|
+
const x = bgColor.match(/(\d+), (\d+), (\d+)/)?.slice(1);
|
|
361
|
+
if (x) {
|
|
362
|
+
return x;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return [
|
|
366
|
+
50,
|
|
367
|
+
50,
|
|
368
|
+
50
|
|
369
|
+
];
|
|
370
|
+
};
|
|
371
|
+
var random = (min, max) => {
|
|
372
|
+
if (!max) {
|
|
373
|
+
max = min;
|
|
374
|
+
min = 0;
|
|
375
|
+
}
|
|
376
|
+
return min + ~~(Math.random() * (max - min + 1));
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
380
|
+
import { ViewPlugin, EditorView as EditorView3, Decoration } from "@codemirror/view";
|
|
23
381
|
import get from "lodash.get";
|
|
382
|
+
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
24
383
|
|
|
25
384
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
26
385
|
import { mx } from "@dxos/react-ui-theme";
|
|
27
|
-
var
|
|
28
|
-
1:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
386
|
+
var headings = {
|
|
387
|
+
1: [
|
|
388
|
+
"mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
|
|
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
|
+
]
|
|
34
411
|
};
|
|
412
|
+
var heading = (level, readonly) => {
|
|
413
|
+
const [style, offset] = headings[level];
|
|
414
|
+
return mx(style, readonly && offset);
|
|
415
|
+
};
|
|
416
|
+
var light = "text-neutral-200 dark:text-neutral-800";
|
|
417
|
+
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
35
418
|
var bold = "font-bold";
|
|
36
|
-
var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
|
|
37
|
-
var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
|
|
38
419
|
var italic = "italic";
|
|
39
420
|
var strikethrough = "line-through";
|
|
40
|
-
var
|
|
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);
|
|
41
425
|
|
|
42
426
|
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
43
427
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
44
428
|
var tokens = tailwindConfig({}).theme;
|
|
45
429
|
|
|
46
|
-
// packages/ui/react-ui-editor/src/
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"& .cm-line": {
|
|
52
|
-
paddingInline: 0,
|
|
53
|
-
lineHeight: 1.6,
|
|
54
|
-
minBlockSize: "1.6em"
|
|
55
|
-
},
|
|
56
|
-
"& .cm-line *": {
|
|
57
|
-
lineHeight: 1.6
|
|
430
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
431
|
+
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
432
|
+
var styles = EditorView3.baseTheme({
|
|
433
|
+
"& .cm-codeblock": {
|
|
434
|
+
fontFamily: get(tokens, "fontFamily.mono", []).join(",")
|
|
58
435
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
overflow: "visible"
|
|
436
|
+
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
437
|
+
background: get(tokens, "extend.colors.neutral.50")
|
|
62
438
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
caretColor: "black"
|
|
439
|
+
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
440
|
+
background: get(tokens, "extend.colors.neutral.850")
|
|
66
441
|
},
|
|
67
|
-
"
|
|
68
|
-
|
|
442
|
+
'& [aria-readonly="true"] .cm-codeblock': {
|
|
443
|
+
display: "block",
|
|
444
|
+
paddingInline: "4px !important"
|
|
69
445
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
446
|
+
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
447
|
+
paddingTop: "4px !important",
|
|
448
|
+
borderTopLeftRadius: "4px",
|
|
449
|
+
borderTopRightRadius: "4px"
|
|
73
450
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
451
|
+
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
452
|
+
paddingBottom: "4px !important",
|
|
453
|
+
borderBottomLeftRadius: "4px",
|
|
454
|
+
borderBottomRightRadius: "4px"
|
|
77
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
|
+
];
|
|
78
464
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
87
481
|
}
|
|
88
|
-
return
|
|
482
|
+
return Decoration.set(decorations);
|
|
483
|
+
};
|
|
484
|
+
return [
|
|
485
|
+
ViewPlugin.fromClass(class {
|
|
486
|
+
constructor(view) {
|
|
487
|
+
this.decorations = getDecorations(view);
|
|
488
|
+
}
|
|
489
|
+
update(update4) {
|
|
490
|
+
if (update4.docChanged || update4.viewportChanged) {
|
|
491
|
+
this.decorations = getDecorations(update4.view);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}, {
|
|
495
|
+
decorations: (value) => value.decorations
|
|
496
|
+
}),
|
|
497
|
+
styles
|
|
498
|
+
];
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
502
|
+
import { StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
|
|
503
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin3, WidgetType } from "@codemirror/view";
|
|
504
|
+
import sortBy from "lodash.sortby";
|
|
505
|
+
import { debounce } from "@dxos/async";
|
|
506
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
507
|
+
import { log as log3 } from "@dxos/log";
|
|
508
|
+
import { nonNullable } from "@dxos/util";
|
|
509
|
+
|
|
510
|
+
// packages/ui/react-ui-editor/src/extensions/util.ts
|
|
511
|
+
import { log } from "@dxos/log";
|
|
512
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/util.ts";
|
|
513
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
514
|
+
try {
|
|
515
|
+
return fn(...args);
|
|
516
|
+
} catch (error) {
|
|
517
|
+
log.catch(error, void 0, {
|
|
518
|
+
F: __dxlog_file2,
|
|
519
|
+
L: 16,
|
|
520
|
+
S: void 0,
|
|
521
|
+
C: (f, a) => f(...a)
|
|
522
|
+
});
|
|
89
523
|
}
|
|
90
|
-
}
|
|
524
|
+
};
|
|
91
525
|
|
|
92
|
-
// packages/ui/react-ui-editor/src/
|
|
93
|
-
import {
|
|
94
|
-
import
|
|
95
|
-
import {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
526
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
527
|
+
import { StateField as StateField2 } from "@codemirror/state";
|
|
528
|
+
import get2 from "lodash.get";
|
|
529
|
+
import { useEffect, useState } from "react";
|
|
530
|
+
import { yCollab } from "y-codemirror.next";
|
|
531
|
+
import * as Y from "yjs";
|
|
532
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
533
|
+
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
534
|
+
import { arrayToString, stringToArray } from "@dxos/util";
|
|
535
|
+
|
|
536
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
537
|
+
import { Annotation, Facet, StateEffect, StateField } from "@codemirror/state";
|
|
538
|
+
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
539
|
+
import * as automerge2 from "@dxos/automerge/automerge";
|
|
540
|
+
|
|
541
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/semaphore.ts
|
|
542
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
543
|
+
|
|
544
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/update-automerge.ts
|
|
545
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
546
|
+
var updateAutomerge = (field, handle, transactions, state) => {
|
|
547
|
+
const { lastHeads, path } = state.field(field);
|
|
548
|
+
let hasChanges = false;
|
|
549
|
+
for (const tr of transactions) {
|
|
550
|
+
tr.changes.iterChanges(() => {
|
|
551
|
+
hasChanges = true;
|
|
552
|
+
});
|
|
102
553
|
}
|
|
103
|
-
|
|
104
|
-
return
|
|
554
|
+
if (!hasChanges) {
|
|
555
|
+
return void 0;
|
|
105
556
|
}
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
link.setAttribute("target", "_blank");
|
|
112
|
-
link.href = this._url;
|
|
113
|
-
} else {
|
|
114
|
-
link.onclick = () => {
|
|
115
|
-
view.dispatch({
|
|
116
|
-
selection: {
|
|
117
|
-
anchor: this._anchor
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
};
|
|
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());
|
|
561
|
+
});
|
|
121
562
|
}
|
|
122
|
-
return link;
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
var hyperlinkDecoration = ({ link } = {}) => {
|
|
126
|
-
return StateField2.define({
|
|
127
|
-
create: (state) => update(state, link),
|
|
128
|
-
update: (_, tr) => update(tr.state, link),
|
|
129
|
-
provide: (field) => EditorView2.decorations.from(field)
|
|
130
563
|
});
|
|
564
|
+
return newHeads ?? void 0;
|
|
131
565
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}));
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return true;
|
|
566
|
+
|
|
567
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/update-codemirror.ts
|
|
568
|
+
import { ChangeSet } from "@codemirror/state";
|
|
569
|
+
var updateCodeMirror = (view, selection, target, patches) => {
|
|
570
|
+
for (const patch of patches) {
|
|
571
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
572
|
+
if (changeSpec != null) {
|
|
573
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
574
|
+
selection = selection.map(changeSet, 1);
|
|
575
|
+
view.dispatch({
|
|
576
|
+
changes: changeSet,
|
|
577
|
+
annotations: reconcileAnnotationType.of({})
|
|
578
|
+
});
|
|
150
579
|
}
|
|
580
|
+
}
|
|
581
|
+
view.dispatch({
|
|
582
|
+
selection,
|
|
583
|
+
annotations: reconcileAnnotationType.of({})
|
|
151
584
|
});
|
|
152
|
-
return builder.finish();
|
|
153
585
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
do {
|
|
165
|
-
match = text.substring(idx).match(regexp);
|
|
166
|
-
if (!match) {
|
|
167
|
-
match = void 0;
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
idx = text.indexOf(match[0]);
|
|
171
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
idx += match[0].length;
|
|
175
|
-
} while (true);
|
|
176
|
-
if (!match) {
|
|
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 {
|
|
177
596
|
return null;
|
|
178
597
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
create: () => {
|
|
185
|
-
const el = document.createElement("a");
|
|
186
|
-
el.innerText = "_";
|
|
187
|
-
el.className = tooltipContent({}, "mb-2 p-1");
|
|
188
|
-
el.setAttribute("target", "_blank");
|
|
189
|
-
el.setAttribute("href", url);
|
|
190
|
-
onHover(el, url);
|
|
191
|
-
return {
|
|
192
|
-
dom: el
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx
|
|
199
|
-
import { Decoration as Decoration2, EditorView as EditorView3, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
200
|
-
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
201
|
-
var linkDecorator = () => new MatchDecorator({
|
|
202
|
-
regexp: markdownLinkRegexp2,
|
|
203
|
-
decorate: (add, from, to, match, view) => {
|
|
204
|
-
const [_, label, url] = match;
|
|
205
|
-
const p0 = {
|
|
206
|
-
start: from,
|
|
207
|
-
end: from + 1
|
|
208
|
-
};
|
|
209
|
-
const p1 = {
|
|
210
|
-
start: from + 1,
|
|
211
|
-
end: from + 1 + label.length
|
|
212
|
-
};
|
|
213
|
-
const p2 = {
|
|
214
|
-
start: p1.end,
|
|
215
|
-
end: p1.end + 1
|
|
216
|
-
};
|
|
217
|
-
const p3 = {
|
|
218
|
-
start: p1.end + 1,
|
|
219
|
-
end: p1.end + 1 + url.length + 2
|
|
220
|
-
};
|
|
221
|
-
add(p0.start, p0.end, Decoration2.mark({
|
|
222
|
-
class: "cm-hyperlink-bracket",
|
|
223
|
-
_attributes: {
|
|
224
|
-
style: "display: none"
|
|
225
|
-
}
|
|
226
|
-
}));
|
|
227
|
-
add(p1.start, p1.end, Decoration2.mark({
|
|
228
|
-
class: "cm-hyperlink-label"
|
|
229
|
-
}));
|
|
230
|
-
add(p2.start, p2.end, Decoration2.mark({
|
|
231
|
-
class: "cm-hyperlink-bracket",
|
|
232
|
-
_attributes: {
|
|
233
|
-
style: "display: none"
|
|
234
|
-
}
|
|
235
|
-
}));
|
|
236
|
-
add(p3.start, p3.end, Decoration2.mark({
|
|
237
|
-
class: "cm-hyperlink-url",
|
|
238
|
-
_attributes: {
|
|
239
|
-
style: "display: none"
|
|
240
|
-
}
|
|
241
|
-
}));
|
|
598
|
+
};
|
|
599
|
+
var handleInsert = (target, patch) => {
|
|
600
|
+
const index = charPath(target, patch.path);
|
|
601
|
+
if (index == null) {
|
|
602
|
+
return [];
|
|
242
603
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
604
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
605
|
+
return [
|
|
606
|
+
{
|
|
607
|
+
from: index,
|
|
608
|
+
to: index,
|
|
609
|
+
insert: text
|
|
249
610
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const pos = view.posAtDOM(event.target);
|
|
262
|
-
if (pos !== 0) {
|
|
263
|
-
console.log(this);
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
// TODO(burdon): Hide when cursor moves away.
|
|
267
|
-
keydown: (event, view) => {
|
|
268
|
-
view.update([]);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
var hyperlinkStyle = EditorView3.baseTheme({
|
|
274
|
-
".cm-hyperlink-label": {
|
|
275
|
-
cursor: "pointer",
|
|
276
|
-
textDecoration: "underline"
|
|
277
|
-
},
|
|
278
|
-
".cm-hyperlink-bracket": {
|
|
279
|
-
opacity: 0.2
|
|
280
|
-
},
|
|
281
|
-
".cm-hyperlink-url": {
|
|
282
|
-
opacity: 0.5,
|
|
283
|
-
fontFamily: "monospace"
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
var hyperlinkWidget = [
|
|
287
|
-
hyperLinkExtension(),
|
|
288
|
-
hyperlinkStyle
|
|
289
|
-
];
|
|
290
|
-
|
|
291
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
292
|
-
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
293
|
-
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
294
|
-
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
295
|
-
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, foldKeymap, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
296
|
-
import { languages } from "@codemirror/language-data";
|
|
297
|
-
import { lintKeymap } from "@codemirror/lint";
|
|
298
|
-
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
299
|
-
import { EditorState } from "@codemirror/state";
|
|
300
|
-
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
301
|
-
import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
|
|
302
|
-
|
|
303
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
|
|
304
|
-
import { styleTags, Tag } from "@lezer/highlight";
|
|
305
|
-
var markdownTags = {
|
|
306
|
-
headingMark: Tag.define(),
|
|
307
|
-
quoteMark: Tag.define(),
|
|
308
|
-
listMark: Tag.define(),
|
|
309
|
-
linkMark: Tag.define(),
|
|
310
|
-
emphasisMark: Tag.define(),
|
|
311
|
-
codeMark: Tag.define(),
|
|
312
|
-
codeText: Tag.define(),
|
|
313
|
-
inlineCode: Tag.define(),
|
|
314
|
-
url: Tag.define(),
|
|
315
|
-
linkReference: Tag.define(),
|
|
316
|
-
linkLabel: Tag.define()
|
|
317
|
-
};
|
|
318
|
-
var markdownTagsExtension = {
|
|
319
|
-
props: [
|
|
320
|
-
styleTags({
|
|
321
|
-
HeaderMark: markdownTags.headingMark,
|
|
322
|
-
QuoteMark: markdownTags.quoteMark,
|
|
323
|
-
ListMark: markdownTags.listMark,
|
|
324
|
-
LinkMark: markdownTags.linkMark,
|
|
325
|
-
EmphasisMark: markdownTags.emphasisMark,
|
|
326
|
-
CodeMark: markdownTags.codeMark,
|
|
327
|
-
CodeText: markdownTags.codeText,
|
|
328
|
-
InlineCode: markdownTags.inlineCode,
|
|
329
|
-
URL: markdownTags.url,
|
|
330
|
-
LinkReference: markdownTags.linkReference,
|
|
331
|
-
LinkLabel: markdownTags.linkLabel
|
|
332
|
-
})
|
|
333
|
-
]
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
|
|
337
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
338
|
-
import { HighlightStyle } from "@codemirror/language";
|
|
339
|
-
import { tags } from "@lezer/highlight";
|
|
340
|
-
import get2 from "lodash.get";
|
|
341
|
-
var chalky = "#e5c07b";
|
|
342
|
-
var coral = "#e06c75";
|
|
343
|
-
var cyan = "#56b6c2";
|
|
344
|
-
var invalid = "#ffffff";
|
|
345
|
-
var ivory = "#abb2bf";
|
|
346
|
-
var stone = "#7d8799";
|
|
347
|
-
var malibu = "#61afef";
|
|
348
|
-
var sage = "#98c379";
|
|
349
|
-
var whiskey = "#d19a66";
|
|
350
|
-
var violet = "#c678dd";
|
|
351
|
-
var highlightBackground = "#2c313a";
|
|
352
|
-
var tooltipBackground = "#353a42";
|
|
353
|
-
var monospace = get2(tokens, "fontFamily.mono", [
|
|
354
|
-
"monospace"
|
|
355
|
-
]).join(",");
|
|
356
|
-
var markdownTheme = {
|
|
357
|
-
// TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
|
|
358
|
-
// '&': {
|
|
359
|
-
// color: ivory,
|
|
360
|
-
// backgroundColor: background
|
|
361
|
-
// },
|
|
362
|
-
// '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
|
|
363
|
-
// '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
|
|
364
|
-
// backgroundColor: selection
|
|
365
|
-
// },
|
|
366
|
-
// '.cm-panels': { backgroundColor: darkBackground, color: ivory },
|
|
367
|
-
// '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
|
368
|
-
// '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
|
369
|
-
// '.cm-searchMatch': {
|
|
370
|
-
// backgroundColor: '#72a1ff59',
|
|
371
|
-
// outline: '1px solid #457dff'
|
|
372
|
-
// },
|
|
373
|
-
// '.cm-searchMatch.cm-searchMatch-selected': {
|
|
374
|
-
// backgroundColor: '#6199ff2f'
|
|
375
|
-
// },
|
|
376
|
-
// '.cm-activeLine': { backgroundColor: '#6699ff0b' },
|
|
377
|
-
// '.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
|
378
|
-
// '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
|
379
|
-
// backgroundColor: '#bad0f847'
|
|
380
|
-
// },
|
|
381
|
-
// '.cm-gutters': {
|
|
382
|
-
// backgroundColor: background,
|
|
383
|
-
// color: stone,
|
|
384
|
-
// border: 'none'
|
|
385
|
-
// },
|
|
386
|
-
// '.cm-activeLineGutter': {
|
|
387
|
-
// backgroundColor: highlightBackground
|
|
388
|
-
// },
|
|
389
|
-
// '.cm-foldPlaceholder': {
|
|
390
|
-
// backgroundColor: 'transparent',
|
|
391
|
-
// border: 'none',
|
|
392
|
-
// color: '#ddd'
|
|
393
|
-
// },
|
|
394
|
-
"& .cm-tooltip": {
|
|
395
|
-
backgroundColor: "transparent",
|
|
396
|
-
border: "none"
|
|
397
|
-
},
|
|
398
|
-
// '.dark & .cm-tooltip': {
|
|
399
|
-
// border: 'none',
|
|
400
|
-
// backgroundColor: tooltipBackground,
|
|
401
|
-
// },
|
|
402
|
-
// '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
|
|
403
|
-
// borderTopColor: 'transparent',
|
|
404
|
-
// borderBottomColor: 'transparent',
|
|
405
|
-
// },
|
|
406
|
-
// '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
|
|
407
|
-
// borderTopColor: tooltipBackground,
|
|
408
|
-
// borderBottomColor: tooltipBackground,
|
|
409
|
-
// },
|
|
410
|
-
// '.dark & .cm-tooltip-autocomplete': {
|
|
411
|
-
// '& > ul > li[aria-selected]': {
|
|
412
|
-
// backgroundColor: highlightBackground,
|
|
413
|
-
// color: ivory,
|
|
414
|
-
// },
|
|
415
|
-
// },
|
|
416
|
-
"&.cm-focused": {
|
|
417
|
-
outline: "none"
|
|
418
|
-
},
|
|
419
|
-
"& .cm-line": {
|
|
420
|
-
paddingInline: 0,
|
|
421
|
-
lineHeight: 1.6,
|
|
422
|
-
minBlockSize: "1.6em"
|
|
423
|
-
},
|
|
424
|
-
"& .cm-line *": {
|
|
425
|
-
lineHeight: 1.6
|
|
426
|
-
},
|
|
427
|
-
"&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
|
|
428
|
-
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
429
|
-
},
|
|
430
|
-
".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
|
|
431
|
-
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
432
|
-
},
|
|
433
|
-
"& .cm-selectionMatch": {
|
|
434
|
-
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
435
|
-
},
|
|
436
|
-
".dark & .cm-selectionMatch": {
|
|
437
|
-
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
438
|
-
},
|
|
439
|
-
"& .cm-content": {
|
|
440
|
-
padding: 0,
|
|
441
|
-
caretColor: "black"
|
|
442
|
-
},
|
|
443
|
-
".dark & .cm-content": {
|
|
444
|
-
caretColor: "white"
|
|
445
|
-
},
|
|
446
|
-
"& .cm-cursor": {
|
|
447
|
-
borderLeftColor: "black"
|
|
448
|
-
},
|
|
449
|
-
".dark & .cm-cursor": {
|
|
450
|
-
borderLeftColor: "white"
|
|
451
|
-
},
|
|
452
|
-
".cm-placeholder": {
|
|
453
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
454
|
-
},
|
|
455
|
-
"& .cm-scroller": {
|
|
456
|
-
fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
|
|
457
|
-
overflow: "visible"
|
|
458
|
-
},
|
|
459
|
-
"& .cm-activeLine": {
|
|
460
|
-
backgroundColor: "transparent"
|
|
461
|
-
},
|
|
462
|
-
".dark & .cm-activeLine": {
|
|
463
|
-
backgroundColor: "transparent"
|
|
464
|
-
},
|
|
465
|
-
"& .cm-ySelectionInfo": {
|
|
466
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(","),
|
|
467
|
-
padding: "2px 4px",
|
|
468
|
-
marginBlockStart: "-4px"
|
|
469
|
-
},
|
|
470
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
471
|
-
paddingBlockStart: ".15em",
|
|
472
|
-
paddingBlockEnd: ".15em"
|
|
473
|
-
},
|
|
474
|
-
"& .cm-ySelection, & .cm-yLineSelection": {
|
|
475
|
-
mixBlendMode: "multiply"
|
|
476
|
-
},
|
|
477
|
-
".dark & .cm-ySelection, .dark & .cm-yLineSelection": {
|
|
478
|
-
mixBlendMode: "screen"
|
|
479
|
-
},
|
|
480
|
-
"& .cm-ySelectionCaret": {
|
|
481
|
-
display: "inline-block",
|
|
482
|
-
insetBlockStart: ".1em",
|
|
483
|
-
blockSize: "1.4em",
|
|
484
|
-
verticalAlign: "top"
|
|
485
|
-
},
|
|
486
|
-
"& .cm-yLineSelection": {
|
|
487
|
-
margin: "0"
|
|
488
|
-
},
|
|
489
|
-
".cm-link": {
|
|
490
|
-
color: "rgb(20 89 208)",
|
|
491
|
-
textDecorationLine: "underline",
|
|
492
|
-
textDecorationThickness: "1px",
|
|
493
|
-
textUnderlineOffset: "2px",
|
|
494
|
-
borderRadius: ".125rem"
|
|
495
|
-
},
|
|
496
|
-
// TODO(burdon): Document.
|
|
497
|
-
...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
498
|
-
const height = get2(tokens, [
|
|
499
|
-
"extend",
|
|
500
|
-
"fontSize",
|
|
501
|
-
fontSize,
|
|
502
|
-
1,
|
|
503
|
-
"lineHeight"
|
|
504
|
-
]);
|
|
505
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
506
|
-
height
|
|
507
|
-
};
|
|
508
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
509
|
-
height
|
|
510
|
-
};
|
|
511
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
512
|
-
height
|
|
513
|
-
};
|
|
514
|
-
return acc;
|
|
515
|
-
}, {})
|
|
516
|
-
};
|
|
517
|
-
var markdownHighlightStyle = HighlightStyle.define([
|
|
518
|
-
{
|
|
519
|
-
tag: [
|
|
520
|
-
tags.keyword,
|
|
521
|
-
tags.name,
|
|
522
|
-
tags.deleted,
|
|
523
|
-
tags.character,
|
|
524
|
-
tags.propertyName,
|
|
525
|
-
tags.macroName,
|
|
526
|
-
tags.color,
|
|
527
|
-
tags.constant(tags.name),
|
|
528
|
-
tags.standard(tags.name),
|
|
529
|
-
tags.definition(tags.name),
|
|
530
|
-
tags.separator,
|
|
531
|
-
tags.typeName,
|
|
532
|
-
tags.className,
|
|
533
|
-
tags.number,
|
|
534
|
-
tags.changed,
|
|
535
|
-
tags.annotation,
|
|
536
|
-
tags.modifier,
|
|
537
|
-
tags.self,
|
|
538
|
-
tags.namespace,
|
|
539
|
-
tags.operator,
|
|
540
|
-
tags.operatorKeyword,
|
|
541
|
-
tags.escape,
|
|
542
|
-
tags.regexp,
|
|
543
|
-
tags.special(tags.string),
|
|
544
|
-
tags.meta,
|
|
545
|
-
tags.comment,
|
|
546
|
-
tags.atom,
|
|
547
|
-
tags.bool,
|
|
548
|
-
tags.special(tags.variableName),
|
|
549
|
-
tags.processingInstruction,
|
|
550
|
-
tags.string,
|
|
551
|
-
tags.inserted,
|
|
552
|
-
tags.invalid
|
|
553
|
-
],
|
|
554
|
-
color: "inherit !important"
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
tag: [
|
|
558
|
-
tags.link,
|
|
559
|
-
tags.url
|
|
560
|
-
],
|
|
561
|
-
color: "inherit !important",
|
|
562
|
-
textDecoration: "none !important"
|
|
563
|
-
},
|
|
564
|
-
{
|
|
565
|
-
tag: [
|
|
566
|
-
tags.function(tags.variableName),
|
|
567
|
-
tags.labelName
|
|
568
|
-
],
|
|
569
|
-
color: malibu,
|
|
570
|
-
fontFamily: monospace
|
|
571
|
-
},
|
|
572
|
-
{
|
|
573
|
-
tag: [
|
|
574
|
-
markdownTags.codeMark,
|
|
575
|
-
markdownTags.emphasisMark,
|
|
576
|
-
markdownTags.headingMark,
|
|
577
|
-
markdownTags.linkLabel,
|
|
578
|
-
markdownTags.linkReference,
|
|
579
|
-
markdownTags.listMark,
|
|
580
|
-
markdownTags.quoteMark,
|
|
581
|
-
markdownTags.url,
|
|
582
|
-
tags.meta,
|
|
583
|
-
tags.processingInstruction
|
|
584
|
-
],
|
|
585
|
-
class: mark
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
tag: [
|
|
589
|
-
markdownTags.codeText,
|
|
590
|
-
markdownTags.inlineCode
|
|
591
|
-
],
|
|
592
|
-
class: "font-mono"
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
tag: tags.emphasis,
|
|
596
|
-
class: italic
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
tag: tags.heading1,
|
|
600
|
-
class: heading[1]
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
tag: tags.heading2,
|
|
604
|
-
class: heading[2]
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
tag: tags.heading3,
|
|
608
|
-
class: heading[3]
|
|
609
|
-
},
|
|
610
|
-
{
|
|
611
|
-
tag: tags.heading4,
|
|
612
|
-
class: heading[4]
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
tag: tags.heading5,
|
|
616
|
-
class: heading[5]
|
|
617
|
-
},
|
|
618
|
-
{
|
|
619
|
-
tag: tags.heading6,
|
|
620
|
-
class: heading[6]
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
tag: tags.strikethrough,
|
|
624
|
-
class: strikethrough
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
tag: tags.strong,
|
|
628
|
-
class: bold
|
|
629
|
-
}
|
|
630
|
-
], {
|
|
631
|
-
scope: markdownLanguage,
|
|
632
|
-
all: {
|
|
633
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
638
|
-
var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
639
|
-
return [
|
|
640
|
-
bracketMatching2(),
|
|
641
|
-
closeBrackets2(),
|
|
642
|
-
_placeholder && placeholder2(_placeholder),
|
|
643
|
-
EditorState.allowMultipleSelections.of(true),
|
|
644
|
-
EditorView4.lineWrapping,
|
|
645
|
-
// autocompletion(),
|
|
646
|
-
crosshairCursor(),
|
|
647
|
-
dropCursor(),
|
|
648
|
-
drawSelection(),
|
|
649
|
-
highlightActiveLine(),
|
|
650
|
-
highlightActiveLineGutter(),
|
|
651
|
-
highlightSelectionMatches(),
|
|
652
|
-
highlightSpecialChars(),
|
|
653
|
-
history(),
|
|
654
|
-
indentOnInput(),
|
|
655
|
-
rectangularSelection(),
|
|
656
|
-
keymap.of([
|
|
657
|
-
...closeBracketsKeymap,
|
|
658
|
-
// ...completionKeymap,
|
|
659
|
-
...defaultKeymap,
|
|
660
|
-
...foldKeymap,
|
|
661
|
-
...historyKeymap,
|
|
662
|
-
...lintKeymap,
|
|
663
|
-
...searchKeymap,
|
|
664
|
-
indentWithTab
|
|
665
|
-
]),
|
|
666
|
-
// Main extension.
|
|
667
|
-
// https://github.com/codemirror/lang-markdown
|
|
668
|
-
markdown({
|
|
669
|
-
base: markdownLanguage2,
|
|
670
|
-
codeLanguages: languages,
|
|
671
|
-
extensions: [
|
|
672
|
-
// TODO(burdon): This seems to upgrade the parser.
|
|
673
|
-
// GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
|
|
674
|
-
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
675
|
-
// https://github.github.com/gfm
|
|
676
|
-
// GFM,
|
|
677
|
-
// Custom styling.
|
|
678
|
-
markdownTagsExtension
|
|
679
|
-
]
|
|
680
|
-
}),
|
|
681
|
-
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
682
|
-
syntaxHighlighting2(markdownHighlightStyle),
|
|
683
|
-
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
|
|
684
|
-
].filter(Boolean);
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
688
|
-
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
689
|
-
import { EditorView as EditorView5 } from "@codemirror/view";
|
|
690
|
-
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
691
|
-
import { vim } from "@replit/codemirror-vim";
|
|
692
|
-
import defaultsDeep from "lodash.defaultsdeep";
|
|
693
|
-
import get3 from "lodash.get";
|
|
694
|
-
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
|
|
695
|
-
import { isDocAccessor as isDocAccessor2 } from "@dxos/echo-schema";
|
|
696
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
697
|
-
|
|
698
|
-
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
699
|
-
import { useEffect, useMemo } from "react";
|
|
700
|
-
import { yCollab } from "y-codemirror.next";
|
|
701
|
-
import { generateName } from "@dxos/display-name";
|
|
702
|
-
import { isDocAccessor } from "@dxos/echo-schema";
|
|
703
|
-
import { getColorForValue } from "@dxos/react-ui-theme";
|
|
704
|
-
import { YText } from "@dxos/text-model";
|
|
705
|
-
|
|
706
|
-
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
707
|
-
import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
|
|
708
|
-
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
709
|
-
import * as automerge2 from "@dxos/automerge/automerge";
|
|
710
|
-
|
|
711
|
-
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
712
|
-
import { next as automerge } from "@dxos/automerge/automerge";
|
|
713
|
-
|
|
714
|
-
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
|
|
715
|
-
import { ChangeSet } from "@codemirror/state";
|
|
716
|
-
var amToCodemirror_default = (view, selection, target, patches) => {
|
|
717
|
-
for (const patch of patches) {
|
|
718
|
-
const changeSpec = handlePatch(patch, target, view.state);
|
|
719
|
-
if (changeSpec != null) {
|
|
720
|
-
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
721
|
-
selection = selection.map(changeSet, 1);
|
|
722
|
-
view.dispatch({
|
|
723
|
-
changes: changeSet,
|
|
724
|
-
annotations: reconcileAnnotationType.of({})
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
view.dispatch({
|
|
729
|
-
selection,
|
|
730
|
-
annotations: reconcileAnnotationType.of({})
|
|
731
|
-
});
|
|
732
|
-
};
|
|
733
|
-
var handlePatch = (patch, target, state) => {
|
|
734
|
-
if (patch.action === "insert") {
|
|
735
|
-
return handleInsert(target, patch);
|
|
736
|
-
} else if (patch.action === "splice") {
|
|
737
|
-
return handleSplice(target, patch);
|
|
738
|
-
} else if (patch.action === "del") {
|
|
739
|
-
return handleDel(target, patch);
|
|
740
|
-
} else if (patch.action === "put") {
|
|
741
|
-
return handlePut(target, patch, state);
|
|
742
|
-
} else {
|
|
743
|
-
return null;
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
var handleInsert = (target, patch) => {
|
|
747
|
-
const index = charPath(target, patch.path);
|
|
748
|
-
if (index == null) {
|
|
749
|
-
return [];
|
|
750
|
-
}
|
|
751
|
-
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
752
|
-
return [
|
|
753
|
-
{
|
|
754
|
-
from: index,
|
|
755
|
-
to: index,
|
|
756
|
-
insert: text
|
|
757
|
-
}
|
|
758
|
-
];
|
|
759
|
-
};
|
|
760
|
-
var handleSplice = (target, patch) => {
|
|
761
|
-
const index = charPath(target, patch.path);
|
|
762
|
-
if (index == null) {
|
|
763
|
-
return [];
|
|
764
|
-
}
|
|
765
|
-
return [
|
|
766
|
-
{
|
|
767
|
-
from: index,
|
|
768
|
-
insert: patch.value
|
|
611
|
+
];
|
|
612
|
+
};
|
|
613
|
+
var handleSplice = (target, patch) => {
|
|
614
|
+
const index = charPath(target, patch.path);
|
|
615
|
+
if (index == null) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
618
|
+
return [
|
|
619
|
+
{
|
|
620
|
+
from: index,
|
|
621
|
+
insert: patch.value
|
|
769
622
|
}
|
|
770
623
|
];
|
|
771
624
|
};
|
|
@@ -818,30 +671,7 @@ var charPath = (textPath, candidatePath) => {
|
|
|
818
671
|
return null;
|
|
819
672
|
};
|
|
820
673
|
|
|
821
|
-
// packages/ui/react-ui-editor/src/
|
|
822
|
-
import { next as am } from "@dxos/automerge/automerge";
|
|
823
|
-
var codeMirrorToAm_default = (field, handle, transactions, state) => {
|
|
824
|
-
const { lastHeads, path } = state.field(field);
|
|
825
|
-
let hasChanges = false;
|
|
826
|
-
for (const tr of transactions) {
|
|
827
|
-
tr.changes.iterChanges(() => {
|
|
828
|
-
hasChanges = true;
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
if (!hasChanges) {
|
|
832
|
-
return void 0;
|
|
833
|
-
}
|
|
834
|
-
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
835
|
-
for (const tr of transactions) {
|
|
836
|
-
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
837
|
-
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
});
|
|
841
|
-
return newHeads ?? void 0;
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
674
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/semaphore.ts
|
|
845
675
|
var PatchSemaphore = class {
|
|
846
676
|
constructor(field) {
|
|
847
677
|
this._inReconcile = false;
|
|
@@ -864,12 +694,12 @@ var PatchSemaphore = class {
|
|
|
864
694
|
annotations: reconcileAnnotationType.of(true)
|
|
865
695
|
});
|
|
866
696
|
}
|
|
867
|
-
let newHeads =
|
|
697
|
+
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
868
698
|
if (newHeads === null || newHeads === void 0) {
|
|
869
699
|
newHeads = automerge.getHeads(handle.docSync());
|
|
870
700
|
}
|
|
871
701
|
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
872
|
-
|
|
702
|
+
updateCodeMirror(view, selection, path, diff);
|
|
873
703
|
view.dispatch({
|
|
874
704
|
effects: updateHeads(newHeads),
|
|
875
705
|
annotations: reconcileAnnotationType.of({})
|
|
@@ -882,7 +712,7 @@ var PatchSemaphore = class {
|
|
|
882
712
|
}
|
|
883
713
|
};
|
|
884
714
|
|
|
885
|
-
// packages/ui/react-ui-editor/src/
|
|
715
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
886
716
|
var effectType = StateEffect.define({});
|
|
887
717
|
var updateHeads = (newHeads) => effectType.of({
|
|
888
718
|
newHeads
|
|
@@ -893,7 +723,7 @@ var semaphoreFacet = Facet.define({
|
|
|
893
723
|
combine: (values) => values.at(-1)
|
|
894
724
|
});
|
|
895
725
|
var automergePlugin = (handle, path) => {
|
|
896
|
-
const stateField =
|
|
726
|
+
const stateField = StateField.define({
|
|
897
727
|
create: () => ({
|
|
898
728
|
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
899
729
|
unreconciledTransactions: [],
|
|
@@ -931,8 +761,8 @@ var automergePlugin = (handle, path) => {
|
|
|
931
761
|
this._view = view;
|
|
932
762
|
handle.addListener("change", this._handleChange);
|
|
933
763
|
}
|
|
934
|
-
update(
|
|
935
|
-
if (
|
|
764
|
+
update(update4) {
|
|
765
|
+
if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
|
|
936
766
|
queueMicrotask(() => {
|
|
937
767
|
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
938
768
|
});
|
|
@@ -953,47 +783,8 @@ var automergePlugin = (handle, path) => {
|
|
|
953
783
|
var reconcileAnnotationType = Annotation.define();
|
|
954
784
|
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
955
785
|
|
|
956
|
-
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
957
|
-
var useCollaboration = (model, themeMode) => {
|
|
958
|
-
const { provider, peer, content } = model;
|
|
959
|
-
const extension = useMemo(() => {
|
|
960
|
-
if (content instanceof YText) {
|
|
961
|
-
return yCollab(content, provider?.awareness);
|
|
962
|
-
} else if (isDocAccessor(content)) {
|
|
963
|
-
return automergePlugin(content.handle, content.path);
|
|
964
|
-
} else {
|
|
965
|
-
return void 0;
|
|
966
|
-
}
|
|
967
|
-
}, []);
|
|
968
|
-
useEffect(() => {
|
|
969
|
-
if (provider && peer) {
|
|
970
|
-
provider.awareness.setLocalStateField("user", {
|
|
971
|
-
name: peer.name ?? generateName(peer.id),
|
|
972
|
-
color: getColorForValue({
|
|
973
|
-
value: peer.id,
|
|
974
|
-
type: "color"
|
|
975
|
-
}),
|
|
976
|
-
colorLight: getColorForValue({
|
|
977
|
-
value: peer.id,
|
|
978
|
-
themeMode,
|
|
979
|
-
type: "highlight"
|
|
980
|
-
})
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
}, [
|
|
984
|
-
provider,
|
|
985
|
-
peer,
|
|
986
|
-
themeMode
|
|
987
|
-
]);
|
|
988
|
-
return extension;
|
|
989
|
-
};
|
|
990
|
-
|
|
991
|
-
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
992
|
-
import { useMemo as useMemo2 } from "react";
|
|
993
|
-
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
994
|
-
|
|
995
786
|
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
996
|
-
import * as
|
|
787
|
+
import * as random2 from "lib0/random";
|
|
997
788
|
var cursorColors = [
|
|
998
789
|
{
|
|
999
790
|
color: "#30bced",
|
|
@@ -1028,15 +819,15 @@ var cursorColors = [
|
|
|
1028
819
|
light: "#1be7ff33"
|
|
1029
820
|
}
|
|
1030
821
|
];
|
|
1031
|
-
var cursorColor = cursorColors[
|
|
822
|
+
var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
1032
823
|
|
|
1033
824
|
// packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
|
|
1034
825
|
import * as decoding from "lib0/decoding";
|
|
1035
826
|
import * as encoding from "lib0/encoding";
|
|
1036
827
|
import { Observable } from "lib0/observable";
|
|
1037
828
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
1038
|
-
import { log } from "@dxos/log";
|
|
1039
|
-
var
|
|
829
|
+
import { log as log2 } from "@dxos/log";
|
|
830
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
1040
831
|
var messageAwareness = 1;
|
|
1041
832
|
var messageQueryAwareness = 3;
|
|
1042
833
|
var SpaceAwarenessProvider = class extends Observable {
|
|
@@ -1066,126 +857,1535 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1066
857
|
get awareness() {
|
|
1067
858
|
return this._awareness;
|
|
1068
859
|
}
|
|
1069
|
-
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
1070
|
-
|
|
1071
|
-
added,
|
|
1072
|
-
updated,
|
|
1073
|
-
removed,
|
|
1074
|
-
origin
|
|
1075
|
-
}, {
|
|
1076
|
-
F:
|
|
1077
|
-
L: 67,
|
|
1078
|
-
S: this,
|
|
1079
|
-
C: (f, a) => f(...a)
|
|
1080
|
-
});
|
|
1081
|
-
const changedClients = added.concat(updated).concat(removed);
|
|
1082
|
-
const encoderAwareness = encoding.createEncoder();
|
|
1083
|
-
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
1084
|
-
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
1085
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
860
|
+
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
861
|
+
log2("awareness update", {
|
|
862
|
+
added,
|
|
863
|
+
updated,
|
|
864
|
+
removed,
|
|
865
|
+
origin
|
|
866
|
+
}, {
|
|
867
|
+
F: __dxlog_file3,
|
|
868
|
+
L: 67,
|
|
869
|
+
S: this,
|
|
870
|
+
C: (f, a) => f(...a)
|
|
871
|
+
});
|
|
872
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
873
|
+
const encoderAwareness = encoding.createEncoder();
|
|
874
|
+
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
875
|
+
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
876
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
877
|
+
}
|
|
878
|
+
_handleSpaceMessage({ payload }) {
|
|
879
|
+
log2("space message", payload, {
|
|
880
|
+
F: __dxlog_file3,
|
|
881
|
+
L: 79,
|
|
882
|
+
S: this,
|
|
883
|
+
C: (f, a) => f(...a)
|
|
884
|
+
});
|
|
885
|
+
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
886
|
+
const encoder = this._readMessage(data);
|
|
887
|
+
if (encoder) {
|
|
888
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
_readMessage(message) {
|
|
892
|
+
const decoder = decoding.createDecoder(message);
|
|
893
|
+
const encoder = encoding.createEncoder();
|
|
894
|
+
const messageType = decoding.readVarUint(decoder);
|
|
895
|
+
let sendReply = false;
|
|
896
|
+
switch (messageType) {
|
|
897
|
+
case messageAwareness: {
|
|
898
|
+
awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
case messageQueryAwareness: {
|
|
902
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
903
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
904
|
+
sendReply = true;
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
default: {
|
|
908
|
+
console.error("Invalid message:", messageType);
|
|
909
|
+
return encoder;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (!sendReply) {
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
return encoder;
|
|
916
|
+
}
|
|
917
|
+
_handleBeforeUnload() {
|
|
918
|
+
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
919
|
+
this._clientId
|
|
920
|
+
], "window unload");
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
925
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
926
|
+
var modelState = StateField2.define({
|
|
927
|
+
create: () => void 0,
|
|
928
|
+
update: (model) => model
|
|
929
|
+
});
|
|
930
|
+
var useTextModel = (props) => {
|
|
931
|
+
const { identity, space, text } = props;
|
|
932
|
+
const [model, setModel] = useState(() => createModel(props));
|
|
933
|
+
useEffect(() => setModel(createModel(props)), [
|
|
934
|
+
identity,
|
|
935
|
+
space,
|
|
936
|
+
text
|
|
937
|
+
]);
|
|
938
|
+
return model;
|
|
939
|
+
};
|
|
940
|
+
var createModel = (props) => {
|
|
941
|
+
const { text } = props;
|
|
942
|
+
if (isActualAutomergeObject(text)) {
|
|
943
|
+
return createAutomergeModel(props);
|
|
944
|
+
} else {
|
|
945
|
+
if (!text?.doc) {
|
|
946
|
+
return void 0;
|
|
947
|
+
}
|
|
948
|
+
return createYjsModel(props);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
var createYjsModel = ({ identity, space, text }) => {
|
|
952
|
+
invariant2(text?.doc && text?.content, void 0, {
|
|
953
|
+
F: __dxlog_file4,
|
|
954
|
+
L: 97,
|
|
955
|
+
S: void 0,
|
|
956
|
+
A: [
|
|
957
|
+
"text?.doc && text?.content",
|
|
958
|
+
""
|
|
959
|
+
]
|
|
960
|
+
});
|
|
961
|
+
const provider = space ? new SpaceAwarenessProvider({
|
|
962
|
+
space,
|
|
963
|
+
doc: text.doc,
|
|
964
|
+
channel: `yjs.awareness.${text.id}`
|
|
965
|
+
}) : void 0;
|
|
966
|
+
const model = {
|
|
967
|
+
id: text.doc.guid,
|
|
968
|
+
content: text.content,
|
|
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;
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1032
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
1033
|
+
var styles2 = EditorView4.baseTheme({
|
|
1034
|
+
"& .cm-bookmark": {
|
|
1035
|
+
cursor: "pointer",
|
|
1036
|
+
margin: "4px",
|
|
1037
|
+
padding: "4px",
|
|
1038
|
+
backgroundColor: "yellow"
|
|
1039
|
+
},
|
|
1040
|
+
"& .cm-bookmark-selected": {
|
|
1041
|
+
backgroundColor: "orange"
|
|
1042
|
+
},
|
|
1043
|
+
"& .cm-comment": {
|
|
1044
|
+
backgroundColor: "yellow"
|
|
1045
|
+
},
|
|
1046
|
+
"& .cm-comment-active": {
|
|
1047
|
+
backgroundColor: "orange"
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
var marks = {
|
|
1051
|
+
highlight: Decoration2.mark({
|
|
1052
|
+
class: "cm-comment"
|
|
1053
|
+
}),
|
|
1054
|
+
highlightActive: Decoration2.mark({
|
|
1055
|
+
class: "cm-comment-active"
|
|
1056
|
+
})
|
|
1057
|
+
};
|
|
1058
|
+
var setCommentRange = StateEffect2.define();
|
|
1059
|
+
var setSelection = StateEffect2.define();
|
|
1060
|
+
var setCommentState = StateEffect2.define();
|
|
1061
|
+
var commentsStateField = StateField3.define({
|
|
1062
|
+
create: () => ({
|
|
1063
|
+
ranges: []
|
|
1064
|
+
}),
|
|
1065
|
+
update: (value, tr) => {
|
|
1066
|
+
for (const effect of tr.effects) {
|
|
1067
|
+
if (effect.is(setSelection)) {
|
|
1068
|
+
return {
|
|
1069
|
+
...value,
|
|
1070
|
+
...effect.value
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
if (effect.is(setCommentRange)) {
|
|
1074
|
+
const { model, comments: comments2 } = effect.value;
|
|
1075
|
+
const ranges = comments2.map((comment) => {
|
|
1076
|
+
const range = model.getRangeFromCursor(comment.cursor);
|
|
1077
|
+
return range && {
|
|
1078
|
+
...comment,
|
|
1079
|
+
...range
|
|
1080
|
+
};
|
|
1081
|
+
}).filter(nonNullable);
|
|
1082
|
+
return {
|
|
1083
|
+
...value,
|
|
1084
|
+
ranges
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
if (effect.is(setCommentState)) {
|
|
1088
|
+
return effect.value;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return value;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
var highlightDecorations = EditorView4.decorations.compute([
|
|
1095
|
+
commentsStateField
|
|
1096
|
+
], (state) => {
|
|
1097
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1098
|
+
const decorations = sortBy(ranges ?? [], (range) => range.from)?.flatMap((selection) => {
|
|
1099
|
+
if (selection.from === selection.to) {
|
|
1100
|
+
return void 0;
|
|
1101
|
+
}
|
|
1102
|
+
const range = {
|
|
1103
|
+
from: selection.from,
|
|
1104
|
+
to: selection.to
|
|
1105
|
+
};
|
|
1106
|
+
if (selection.id === active) {
|
|
1107
|
+
return marks.highlightActive.range(range.from, range.to);
|
|
1108
|
+
} else {
|
|
1109
|
+
return marks.highlight.range(range.from, range.to);
|
|
1110
|
+
}
|
|
1111
|
+
}).filter(nonNullable) ?? [];
|
|
1112
|
+
return Decoration2.set(decorations);
|
|
1113
|
+
});
|
|
1114
|
+
var BookmarkWidget = class extends WidgetType {
|
|
1115
|
+
constructor(_anchor, _id, _handleClick) {
|
|
1116
|
+
super();
|
|
1117
|
+
this._anchor = _anchor;
|
|
1118
|
+
this._id = _id;
|
|
1119
|
+
this._handleClick = _handleClick;
|
|
1120
|
+
invariant3(this._id, void 0, {
|
|
1121
|
+
F: __dxlog_file5,
|
|
1122
|
+
L: 153,
|
|
1123
|
+
S: this,
|
|
1124
|
+
A: [
|
|
1125
|
+
"this._id",
|
|
1126
|
+
""
|
|
1127
|
+
]
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
eq(other) {
|
|
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();
|
|
1139
|
+
}
|
|
1140
|
+
return span;
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
var comments = (options = {}) => {
|
|
1144
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1145
|
+
const createCommentThread = (view) => {
|
|
1146
|
+
invariant3(options.onCreate, void 0, {
|
|
1147
|
+
F: __dxlog_file5,
|
|
1148
|
+
L: 207,
|
|
1149
|
+
S: void 0,
|
|
1150
|
+
A: [
|
|
1151
|
+
"options.onCreate",
|
|
1152
|
+
""
|
|
1153
|
+
]
|
|
1154
|
+
});
|
|
1155
|
+
const { head, from, to } = view.state.selection.main;
|
|
1156
|
+
if (from === to) {
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
const model = view.state.field(modelState);
|
|
1160
|
+
const relPos = model?.getCursorFromRange?.({
|
|
1161
|
+
from,
|
|
1162
|
+
to
|
|
1163
|
+
});
|
|
1164
|
+
if (relPos) {
|
|
1165
|
+
const id = callbackWrapper(options.onCreate)(relPos);
|
|
1166
|
+
if (id) {
|
|
1167
|
+
view.dispatch({
|
|
1168
|
+
effects: setSelection.of({
|
|
1169
|
+
active: id
|
|
1170
|
+
}),
|
|
1171
|
+
selection: {
|
|
1172
|
+
anchor: from
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
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
|
+
return true;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return false;
|
|
1191
|
+
};
|
|
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
|
+
return [
|
|
1220
|
+
bookmarksViewPlugin,
|
|
1221
|
+
commentsStateField,
|
|
1222
|
+
highlightDecorations,
|
|
1223
|
+
styles2,
|
|
1224
|
+
//
|
|
1225
|
+
// Keymap.
|
|
1226
|
+
//
|
|
1227
|
+
options.onCreate ? keymap3.of([
|
|
1228
|
+
{
|
|
1229
|
+
key: options?.key ?? "meta-'",
|
|
1230
|
+
run: callbackWrapper(createCommentThread)
|
|
1231
|
+
}
|
|
1232
|
+
]) : [],
|
|
1233
|
+
//
|
|
1234
|
+
// Hover tooltip (for key shortcut hints, etc.)
|
|
1235
|
+
// TODO(burdon): Factor out to generic hints extension for current selection/line.
|
|
1236
|
+
//
|
|
1237
|
+
options.onHover ? hoverTooltip((view, pos) => {
|
|
1238
|
+
const selection = view.state.selection.main;
|
|
1239
|
+
if (selection && pos >= selection.from && pos <= selection.to) {
|
|
1240
|
+
return {
|
|
1241
|
+
pos: selection.from,
|
|
1242
|
+
end: selection.to,
|
|
1243
|
+
above: true,
|
|
1244
|
+
create: () => {
|
|
1245
|
+
const el = document.createElement("div");
|
|
1246
|
+
options.onHover?.(el);
|
|
1247
|
+
return {
|
|
1248
|
+
dom: el,
|
|
1249
|
+
offset: {
|
|
1250
|
+
x: 0,
|
|
1251
|
+
y: 8
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return null;
|
|
1258
|
+
}, {
|
|
1259
|
+
hideOnChange: true,
|
|
1260
|
+
hoverTime: 1e3
|
|
1261
|
+
}) : [],
|
|
1262
|
+
//
|
|
1263
|
+
// Monitor cursor movement and text updates.
|
|
1264
|
+
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1265
|
+
//
|
|
1266
|
+
EditorView4.updateListener.of(({ view, state, startState, changes }) => {
|
|
1267
|
+
{
|
|
1268
|
+
let mod = false;
|
|
1269
|
+
const model = state.field(modelState);
|
|
1270
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1271
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1272
|
+
invariant3(model, void 0, {
|
|
1273
|
+
F: __dxlog_file5,
|
|
1274
|
+
L: 336,
|
|
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;
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
if (mod) {
|
|
1303
|
+
view.dispatch({
|
|
1304
|
+
effects: setCommentState.of({
|
|
1305
|
+
active,
|
|
1306
|
+
ranges
|
|
1307
|
+
})
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
{
|
|
1312
|
+
const { head } = state.selection.main;
|
|
1313
|
+
let min = Infinity;
|
|
1314
|
+
const { active, closest, ranges } = state.field(commentsStateField);
|
|
1315
|
+
const selected = {
|
|
1316
|
+
active: void 0,
|
|
1317
|
+
closest: void 0
|
|
1318
|
+
};
|
|
1319
|
+
ranges.forEach((comment) => {
|
|
1320
|
+
const d = Math.min(Math.abs(head - comment.from), Math.abs(head - comment.to));
|
|
1321
|
+
if (head >= comment.from && head <= comment.to) {
|
|
1322
|
+
selected.active = comment.id;
|
|
1323
|
+
}
|
|
1324
|
+
if (d < min) {
|
|
1325
|
+
selected.closest = comment.id;
|
|
1326
|
+
min = d;
|
|
1327
|
+
}
|
|
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
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
})
|
|
1343
|
+
];
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1347
|
+
import { Decoration as Decoration3, EditorView as EditorView5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1348
|
+
import get3 from "lodash.get";
|
|
1349
|
+
var styles3 = EditorView5.baseTheme({
|
|
1350
|
+
"& .cm-hr": {
|
|
1351
|
+
// TODO(burdon): ???
|
|
1352
|
+
// Note that block-level decorations should not have vertical margins,
|
|
1353
|
+
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1357
|
+
constructor(_pos) {
|
|
1358
|
+
super();
|
|
1359
|
+
this._pos = _pos;
|
|
1360
|
+
}
|
|
1361
|
+
eq(other) {
|
|
1362
|
+
return this._pos === other._pos;
|
|
1363
|
+
}
|
|
1364
|
+
toDOM(view) {
|
|
1365
|
+
const el = document.createElement("div");
|
|
1366
|
+
el.className = "cm-hr";
|
|
1367
|
+
return el;
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
var placeholderMatcher = new MatchDecorator2({
|
|
1371
|
+
regexp: /^---$/g,
|
|
1372
|
+
decoration: (match, view, pos) => Decoration3.replace({
|
|
1373
|
+
widget: new HorizontalRuleWidget(pos)
|
|
1374
|
+
})
|
|
1375
|
+
});
|
|
1376
|
+
var hr = () => [
|
|
1377
|
+
styles3,
|
|
1378
|
+
ViewPlugin4.fromClass(class {
|
|
1379
|
+
constructor(view) {
|
|
1380
|
+
this.rules = placeholderMatcher.createDeco(view);
|
|
1381
|
+
}
|
|
1382
|
+
update(update4) {
|
|
1383
|
+
this.rules = placeholderMatcher.updateDeco(update4, this.rules);
|
|
1384
|
+
}
|
|
1385
|
+
}, {
|
|
1386
|
+
decorations: (instance) => instance.rules,
|
|
1387
|
+
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1388
|
+
provide: (plugin) => EditorView5.atomicRanges.of((view) => {
|
|
1389
|
+
return view.plugin(plugin)?.rules || Decoration3.none;
|
|
1390
|
+
})
|
|
1391
|
+
})
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1395
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1396
|
+
import { RangeSetBuilder, StateField as StateField4 } from "@codemirror/state";
|
|
1397
|
+
import { Decoration as Decoration4, EditorView as EditorView6, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1398
|
+
var image = (options = {}) => {
|
|
1399
|
+
return StateField4.define({
|
|
1400
|
+
create: (state) => update(state, options),
|
|
1401
|
+
update: (_, tr) => update(tr.state, options),
|
|
1402
|
+
provide: (field) => EditorView6.decorations.from(field)
|
|
1403
|
+
});
|
|
1404
|
+
};
|
|
1405
|
+
var update = (state, options) => {
|
|
1406
|
+
const builder = new RangeSetBuilder();
|
|
1407
|
+
const cursor = state.selection.main.head;
|
|
1408
|
+
syntaxTree(state).iterate({
|
|
1409
|
+
enter: (node) => {
|
|
1410
|
+
if (node.name === "Image") {
|
|
1411
|
+
const urlNode = node.node.getChild("URL");
|
|
1412
|
+
if (urlNode) {
|
|
1413
|
+
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1414
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1415
|
+
builder.add(hide ? node.from : node.to, node.to, Decoration4.replace({
|
|
1416
|
+
block: true,
|
|
1417
|
+
widget: new ImageWidget(url)
|
|
1418
|
+
}));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return builder.finish();
|
|
1424
|
+
};
|
|
1425
|
+
var ImageWidget = class extends WidgetType3 {
|
|
1426
|
+
constructor(_url) {
|
|
1427
|
+
super();
|
|
1428
|
+
this._url = _url;
|
|
1429
|
+
}
|
|
1430
|
+
// TODO(burdon): Does this need to be unique for each position?
|
|
1431
|
+
eq(other) {
|
|
1432
|
+
return this._url === other._url;
|
|
1433
|
+
}
|
|
1434
|
+
toDOM(view) {
|
|
1435
|
+
const img = document.createElement("img");
|
|
1436
|
+
img.setAttribute("src", this._url);
|
|
1437
|
+
img.setAttribute("class", "cm-image");
|
|
1438
|
+
return img;
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1443
|
+
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1444
|
+
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField5 } from "@codemirror/state";
|
|
1445
|
+
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2 } from "@codemirror/view";
|
|
1446
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1447
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1448
|
+
var link = (options = {}) => {
|
|
1449
|
+
const extensions = [
|
|
1450
|
+
StateField5.define({
|
|
1451
|
+
create: (state) => update2(state, options),
|
|
1452
|
+
update: (_, tr) => update2(tr.state, options),
|
|
1453
|
+
provide: (field) => EditorView7.decorations.from(field)
|
|
1454
|
+
})
|
|
1455
|
+
];
|
|
1456
|
+
if (options.onHover) {
|
|
1457
|
+
extensions.push(
|
|
1458
|
+
// https://codemirror.net/examples/tooltip
|
|
1459
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1460
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1461
|
+
hoverTooltip2((view, pos) => {
|
|
1462
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
1463
|
+
const p = pos - from;
|
|
1464
|
+
let idx = 0;
|
|
1465
|
+
let match;
|
|
1466
|
+
do {
|
|
1467
|
+
match = text.substring(idx).match(markdownLinkRegexp);
|
|
1468
|
+
if (!match) {
|
|
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) {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
const [, , url] = match ?? [];
|
|
1482
|
+
return {
|
|
1483
|
+
pos: idx + from,
|
|
1484
|
+
end: idx + from + match[0].length,
|
|
1485
|
+
above: true,
|
|
1486
|
+
create: () => {
|
|
1487
|
+
const el = document.createElement("div");
|
|
1488
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1489
|
+
options.onHover(el, url);
|
|
1490
|
+
return {
|
|
1491
|
+
dom: el,
|
|
1492
|
+
offset: {
|
|
1493
|
+
x: 0,
|
|
1494
|
+
y: 4
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
})
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
return extensions;
|
|
1503
|
+
};
|
|
1504
|
+
var LinkButton = class extends WidgetType4 {
|
|
1505
|
+
constructor(_pos, _url, _onAttach) {
|
|
1506
|
+
super();
|
|
1507
|
+
this._pos = _pos;
|
|
1508
|
+
this._url = _url;
|
|
1509
|
+
this._onAttach = _onAttach;
|
|
1510
|
+
}
|
|
1511
|
+
eq(other) {
|
|
1512
|
+
return this._pos === other._pos && this._url === other._url;
|
|
1513
|
+
}
|
|
1514
|
+
toDOM(view) {
|
|
1515
|
+
const el = document.createElement("span");
|
|
1516
|
+
this._onAttach(el, this._url);
|
|
1517
|
+
return el;
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
var LinkText = class extends WidgetType4 {
|
|
1521
|
+
constructor(_text, _url) {
|
|
1522
|
+
super();
|
|
1523
|
+
this._text = _text;
|
|
1524
|
+
this._url = _url;
|
|
1525
|
+
}
|
|
1526
|
+
eq(other) {
|
|
1527
|
+
return this._url === other._url;
|
|
1528
|
+
}
|
|
1529
|
+
toDOM(view) {
|
|
1530
|
+
const link2 = document.createElement("a");
|
|
1531
|
+
link2.setAttribute("class", "cm-link");
|
|
1532
|
+
link2.textContent = this._text;
|
|
1533
|
+
if (this._url) {
|
|
1534
|
+
link2.setAttribute("rel", "noreferrer");
|
|
1535
|
+
link2.setAttribute("target", "_blank");
|
|
1536
|
+
link2.href = this._url;
|
|
1537
|
+
} else {
|
|
1538
|
+
link2.onclick = () => {
|
|
1539
|
+
view.dispatch({
|
|
1540
|
+
selection: {
|
|
1541
|
+
anchor: view.posAtDOM(link2)
|
|
1542
|
+
}
|
|
1543
|
+
});
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
return link2;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
var update2 = (state, options) => {
|
|
1550
|
+
const builder = new RangeSetBuilder2();
|
|
1551
|
+
const cursor = state.selection.main.head;
|
|
1552
|
+
syntaxTree2(state).iterate({
|
|
1553
|
+
enter: (node) => {
|
|
1554
|
+
if (node.name === "Link") {
|
|
1555
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1556
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1557
|
+
const urlNode = node.node.getChild("URL");
|
|
1558
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1559
|
+
if (!url) {
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
if (state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1563
|
+
builder.add(node.from, node.to, Decoration5.replace({
|
|
1564
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
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
|
+
});
|
|
1576
|
+
return builder.finish();
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1580
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1581
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1582
|
+
const extensions = [];
|
|
1583
|
+
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1584
|
+
onFocus(focused);
|
|
1585
|
+
return null;
|
|
1586
|
+
}));
|
|
1587
|
+
onChange && extensions.push(EditorView8.updateListener.of((update4) => {
|
|
1588
|
+
onChange(update4.state.doc.toString());
|
|
1589
|
+
}));
|
|
1590
|
+
return extensions;
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1594
|
+
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1595
|
+
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1596
|
+
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
1597
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1598
|
+
import { languages } from "@codemirror/language-data";
|
|
1599
|
+
import { searchKeymap } from "@codemirror/search";
|
|
1600
|
+
import { EditorState } from "@codemirror/state";
|
|
1601
|
+
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1602
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1603
|
+
|
|
1604
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1605
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1606
|
+
import { HighlightStyle } from "@codemirror/language";
|
|
1607
|
+
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
1608
|
+
import { Table } from "@lezer/markdown";
|
|
1609
|
+
import get4 from "lodash.get";
|
|
1610
|
+
var markdownTags = {
|
|
1611
|
+
Blockquote: Tag.define(),
|
|
1612
|
+
CodeMark: Tag.define(),
|
|
1613
|
+
CodeText: Tag.define(),
|
|
1614
|
+
EmphasisMark: Tag.define(),
|
|
1615
|
+
HeaderMark: Tag.define(),
|
|
1616
|
+
// HorizontalRule: Tag.define(),
|
|
1617
|
+
InlineCode: Tag.define(),
|
|
1618
|
+
LinkLabel: Tag.define(),
|
|
1619
|
+
LinkReference: Tag.define(),
|
|
1620
|
+
ListMark: Tag.define(),
|
|
1621
|
+
QuoteMark: Tag.define(),
|
|
1622
|
+
URL: Tag.define(),
|
|
1623
|
+
// Custom.
|
|
1624
|
+
TableCell: Tag.define()
|
|
1625
|
+
};
|
|
1626
|
+
Table.defineNodes?.forEach((node) => {
|
|
1627
|
+
switch (node?.name) {
|
|
1628
|
+
case "TableCell": {
|
|
1629
|
+
node.style = markdownTags.TableCell;
|
|
1630
|
+
break;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
var markdownTagsExtensions = [
|
|
1635
|
+
Table,
|
|
1636
|
+
{
|
|
1637
|
+
props: [
|
|
1638
|
+
styleTags(markdownTags)
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
];
|
|
1642
|
+
var markdownHighlightStyle = (readonly) => {
|
|
1643
|
+
return HighlightStyle.define([
|
|
1644
|
+
{
|
|
1645
|
+
tag: [
|
|
1646
|
+
tags.keyword,
|
|
1647
|
+
tags.name,
|
|
1648
|
+
tags.deleted,
|
|
1649
|
+
tags.character,
|
|
1650
|
+
tags.propertyName,
|
|
1651
|
+
tags.macroName,
|
|
1652
|
+
tags.color,
|
|
1653
|
+
tags.constant(tags.name),
|
|
1654
|
+
tags.standard(tags.name),
|
|
1655
|
+
tags.definition(tags.name),
|
|
1656
|
+
tags.separator,
|
|
1657
|
+
tags.typeName,
|
|
1658
|
+
tags.className,
|
|
1659
|
+
tags.number,
|
|
1660
|
+
tags.changed,
|
|
1661
|
+
tags.annotation,
|
|
1662
|
+
tags.modifier,
|
|
1663
|
+
tags.self,
|
|
1664
|
+
tags.namespace,
|
|
1665
|
+
tags.operator,
|
|
1666
|
+
tags.operatorKeyword,
|
|
1667
|
+
tags.escape,
|
|
1668
|
+
tags.regexp,
|
|
1669
|
+
tags.special(tags.string),
|
|
1670
|
+
tags.meta,
|
|
1671
|
+
tags.comment,
|
|
1672
|
+
tags.atom,
|
|
1673
|
+
tags.bool,
|
|
1674
|
+
tags.special(tags.variableName),
|
|
1675
|
+
tags.processingInstruction,
|
|
1676
|
+
tags.string,
|
|
1677
|
+
tags.inserted,
|
|
1678
|
+
tags.invalid
|
|
1679
|
+
],
|
|
1680
|
+
color: "inherit !important"
|
|
1681
|
+
},
|
|
1682
|
+
// Markdown marks.
|
|
1683
|
+
{
|
|
1684
|
+
tag: [
|
|
1685
|
+
tags.meta,
|
|
1686
|
+
tags.processingInstruction,
|
|
1687
|
+
markdownTags.LinkLabel,
|
|
1688
|
+
markdownTags.LinkReference,
|
|
1689
|
+
markdownTags.ListMark
|
|
1690
|
+
],
|
|
1691
|
+
class: mark
|
|
1692
|
+
},
|
|
1693
|
+
// Markdown marks.
|
|
1694
|
+
{
|
|
1695
|
+
tag: [
|
|
1696
|
+
markdownTags.CodeMark,
|
|
1697
|
+
markdownTags.HeaderMark,
|
|
1698
|
+
markdownTags.QuoteMark,
|
|
1699
|
+
markdownTags.EmphasisMark
|
|
1700
|
+
],
|
|
1701
|
+
class: readonly ? "hidden" : mark
|
|
1702
|
+
},
|
|
1703
|
+
// E.g., code block language (after ```).
|
|
1704
|
+
{
|
|
1705
|
+
tag: [
|
|
1706
|
+
tags.function(tags.variableName),
|
|
1707
|
+
tags.labelName
|
|
1708
|
+
],
|
|
1709
|
+
class: readonly ? "hidden" : codeMark
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
tag: [
|
|
1713
|
+
tags.monospace
|
|
1714
|
+
],
|
|
1715
|
+
class: "font-mono"
|
|
1716
|
+
},
|
|
1717
|
+
// Headings.
|
|
1718
|
+
{
|
|
1719
|
+
tag: tags.heading1,
|
|
1720
|
+
class: heading(1, readonly)
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
tag: tags.heading2,
|
|
1724
|
+
class: heading(2, readonly)
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
tag: tags.heading3,
|
|
1728
|
+
class: heading(3, readonly)
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
tag: tags.heading4,
|
|
1732
|
+
class: heading(4, readonly)
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
tag: tags.heading5,
|
|
1736
|
+
class: heading(5, readonly)
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
tag: tags.heading6,
|
|
1740
|
+
class: heading(6, readonly)
|
|
1741
|
+
},
|
|
1742
|
+
// Emphasis.
|
|
1743
|
+
{
|
|
1744
|
+
tag: tags.emphasis,
|
|
1745
|
+
class: italic
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
tag: tags.strong,
|
|
1749
|
+
class: bold
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
tag: tags.strikethrough,
|
|
1753
|
+
class: strikethrough
|
|
1754
|
+
},
|
|
1755
|
+
// Naked URLs.
|
|
1756
|
+
{
|
|
1757
|
+
tag: [
|
|
1758
|
+
markdownTags.URL
|
|
1759
|
+
],
|
|
1760
|
+
class: inlineUrl
|
|
1761
|
+
},
|
|
1762
|
+
// NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
1763
|
+
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
1764
|
+
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
1765
|
+
// the code, but all other CSS properties will be inherited.
|
|
1766
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
1767
|
+
{
|
|
1768
|
+
tag: [
|
|
1769
|
+
markdownTags.CodeText,
|
|
1770
|
+
markdownTags.InlineCode
|
|
1771
|
+
],
|
|
1772
|
+
class: code
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
tag: [
|
|
1776
|
+
markdownTags.QuoteMark
|
|
1777
|
+
],
|
|
1778
|
+
class: blockquote
|
|
1779
|
+
},
|
|
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
|
+
{
|
|
1787
|
+
tag: [
|
|
1788
|
+
markdownTags.TableCell
|
|
1789
|
+
],
|
|
1790
|
+
class: "font-mono"
|
|
1791
|
+
}
|
|
1792
|
+
], {
|
|
1793
|
+
scope: markdownLanguage,
|
|
1794
|
+
all: {
|
|
1795
|
+
fontFamily: get4(tokens, "fontFamily.body", []).join(",")
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
};
|
|
1799
|
+
|
|
1800
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1801
|
+
var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
1802
|
+
return [
|
|
1803
|
+
EditorState.allowMultipleSelections.of(true),
|
|
1804
|
+
EditorState.tabSize.of(2),
|
|
1805
|
+
EditorView9.lineWrapping,
|
|
1806
|
+
customKeymap(),
|
|
1807
|
+
bracketMatching2(),
|
|
1808
|
+
closeBrackets2(),
|
|
1809
|
+
crosshairCursor(),
|
|
1810
|
+
dropCursor(),
|
|
1811
|
+
drawSelection2(),
|
|
1812
|
+
highlightActiveLine(),
|
|
1813
|
+
history(),
|
|
1814
|
+
indentOnInput(),
|
|
1815
|
+
_placeholder && placeholder2(_placeholder),
|
|
1816
|
+
// Main extension.
|
|
1817
|
+
// https://github.com/codemirror/lang-markdown
|
|
1818
|
+
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
1819
|
+
markdown({
|
|
1820
|
+
// GRM by default (vs strict CommonMark):
|
|
1821
|
+
// Table, TaskList, Strikethrough, and Autolink.
|
|
1822
|
+
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1823
|
+
// https://github.github.com/gfm
|
|
1824
|
+
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1825
|
+
base: markdownLanguage2,
|
|
1826
|
+
// Languages for syntax highlighting fenced code blocks.
|
|
1827
|
+
codeLanguages: languages,
|
|
1828
|
+
// Parser extensions.
|
|
1829
|
+
extensions: [
|
|
1830
|
+
// GFM provided by default.
|
|
1831
|
+
markdownTagsExtensions
|
|
1832
|
+
]
|
|
1833
|
+
}),
|
|
1834
|
+
// https://github.com/codemirror/theme-one-dark
|
|
1835
|
+
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1836
|
+
// Custom styles.
|
|
1837
|
+
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1838
|
+
].filter(Boolean);
|
|
1839
|
+
};
|
|
1840
|
+
var customKeymap = () => keymap4.of([
|
|
1841
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1842
|
+
indentWithTab,
|
|
1843
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
1844
|
+
...standardKeymap,
|
|
1845
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
1846
|
+
...historyKeymap,
|
|
1847
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
1848
|
+
...closeBracketsKeymap,
|
|
1849
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
1850
|
+
...searchKeymap
|
|
1851
|
+
]);
|
|
1852
|
+
|
|
1853
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1854
|
+
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1855
|
+
var mention = ({ onSearch }) => {
|
|
1856
|
+
return autocompletion2({
|
|
1857
|
+
closeOnBlur: false,
|
|
1858
|
+
override: [
|
|
1859
|
+
(context) => {
|
|
1860
|
+
const match = context.matchBefore(/@(\w+)?/);
|
|
1861
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
1862
|
+
return null;
|
|
1863
|
+
}
|
|
1864
|
+
console.log(match);
|
|
1865
|
+
return {
|
|
1866
|
+
from: match.from,
|
|
1867
|
+
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
1868
|
+
label: `@${value}`
|
|
1869
|
+
}))
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
]
|
|
1873
|
+
});
|
|
1874
|
+
};
|
|
1875
|
+
|
|
1876
|
+
// packages/ui/react-ui-editor/src/extensions/mermaid.ts
|
|
1877
|
+
var mermaid = () => {
|
|
1878
|
+
return [];
|
|
1879
|
+
};
|
|
1880
|
+
|
|
1881
|
+
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1882
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1883
|
+
import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
|
|
1884
|
+
import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1885
|
+
var table = (options = {}) => {
|
|
1886
|
+
return StateField6.define({
|
|
1887
|
+
create: (state) => update3(state, options),
|
|
1888
|
+
update: (_, tr) => update3(tr.state, options),
|
|
1889
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
1890
|
+
});
|
|
1891
|
+
};
|
|
1892
|
+
var update3 = (state, options) => {
|
|
1893
|
+
const builder = new RangeSetBuilder3();
|
|
1894
|
+
const cursor = state.selection.main.head;
|
|
1895
|
+
const tables = [];
|
|
1896
|
+
const getTable = () => tables[tables.length - 1];
|
|
1897
|
+
const getRow = () => {
|
|
1898
|
+
const table2 = getTable();
|
|
1899
|
+
return table2.rows?.[table2.rows.length - 1];
|
|
1900
|
+
};
|
|
1901
|
+
syntaxTree3(state).iterate({
|
|
1902
|
+
enter: (node) => {
|
|
1903
|
+
switch (node.name) {
|
|
1904
|
+
case "Table": {
|
|
1905
|
+
tables.push({
|
|
1906
|
+
from: node.from,
|
|
1907
|
+
to: node.to
|
|
1908
|
+
});
|
|
1909
|
+
break;
|
|
1910
|
+
}
|
|
1911
|
+
case "TableHeader": {
|
|
1912
|
+
getTable().header = [];
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
case "TableRow": {
|
|
1916
|
+
(getTable().rows ??= []).push([]);
|
|
1917
|
+
break;
|
|
1918
|
+
}
|
|
1919
|
+
case "TableCell": {
|
|
1920
|
+
const row = getRow();
|
|
1921
|
+
if (row) {
|
|
1922
|
+
row.push(state.sliceDoc(node.from, node.to));
|
|
1923
|
+
} else {
|
|
1924
|
+
getTable().header?.push(state.sliceDoc(node.from, node.to));
|
|
1925
|
+
}
|
|
1926
|
+
break;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
tables.forEach((table2) => {
|
|
1932
|
+
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1933
|
+
hide && builder.add(table2.from, table2.to, Decoration6.replace({
|
|
1934
|
+
block: true,
|
|
1935
|
+
widget: new TableWidget(table2)
|
|
1936
|
+
}));
|
|
1937
|
+
builder.add(table2.from, table2.to, Decoration6.mark({
|
|
1938
|
+
class: "cm-table"
|
|
1939
|
+
}));
|
|
1940
|
+
});
|
|
1941
|
+
return builder.finish();
|
|
1942
|
+
};
|
|
1943
|
+
var TableWidget = class extends WidgetType5 {
|
|
1944
|
+
constructor(_table) {
|
|
1945
|
+
super();
|
|
1946
|
+
this._table = _table;
|
|
1947
|
+
}
|
|
1948
|
+
eq(other) {
|
|
1949
|
+
return this._table.from === other?._table?.from;
|
|
1086
1950
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1951
|
+
toDOM(view) {
|
|
1952
|
+
const table2 = document.createElement("table");
|
|
1953
|
+
{
|
|
1954
|
+
const header = table2.appendChild(document.createElement("thead"));
|
|
1955
|
+
const tr = header.appendChild(document.createElement("tr"));
|
|
1956
|
+
this._table.header?.forEach((cell) => {
|
|
1957
|
+
const th = document.createElement("th");
|
|
1958
|
+
th.setAttribute("class", "cm-table-head");
|
|
1959
|
+
tr.appendChild(th).textContent = cell;
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
{
|
|
1963
|
+
const body = table2.appendChild(document.createElement("tbody"));
|
|
1964
|
+
this._table.rows?.forEach((row) => {
|
|
1965
|
+
const tr = body.appendChild(document.createElement("tr"));
|
|
1966
|
+
row.forEach((cell) => {
|
|
1967
|
+
const td = document.createElement("td");
|
|
1968
|
+
td.setAttribute("class", "cm-table-cell");
|
|
1969
|
+
tr.appendChild(td).textContent = cell;
|
|
1970
|
+
});
|
|
1971
|
+
});
|
|
1098
1972
|
}
|
|
1973
|
+
return table2;
|
|
1099
1974
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
1978
|
+
import { EditorView as EditorView11, Decoration as Decoration7, WidgetType as WidgetType6, MatchDecorator as MatchDecorator3, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1979
|
+
var styles4 = EditorView11.baseTheme({
|
|
1980
|
+
"& .cm-task-item": {
|
|
1981
|
+
paddingLeft: "4px",
|
|
1982
|
+
paddingRight: "8px"
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
var CheckboxWidget = class extends WidgetType6 {
|
|
1986
|
+
constructor(_pos, _checked, _indent, _onCheck) {
|
|
1987
|
+
super();
|
|
1988
|
+
this._pos = _pos;
|
|
1989
|
+
this._checked = _checked;
|
|
1990
|
+
this._indent = _indent;
|
|
1991
|
+
this._onCheck = _onCheck;
|
|
1992
|
+
}
|
|
1993
|
+
eq(other) {
|
|
1994
|
+
return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
|
|
1995
|
+
}
|
|
1996
|
+
toDOM(view) {
|
|
1997
|
+
const wrap = document.createElement("span");
|
|
1998
|
+
wrap.className = "cm-task-item";
|
|
1999
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
2000
|
+
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2001
|
+
const input = wrap.appendChild(document.createElement("input"));
|
|
2002
|
+
input.type = "checkbox";
|
|
2003
|
+
input.checked = this._checked;
|
|
2004
|
+
if (!this._onCheck) {
|
|
2005
|
+
input.setAttribute("disabled", "true");
|
|
1120
2006
|
}
|
|
1121
|
-
if (
|
|
1122
|
-
|
|
2007
|
+
if (this._onCheck) {
|
|
2008
|
+
input.onchange = (event) => {
|
|
2009
|
+
this._onCheck?.(event.target.checked);
|
|
2010
|
+
return true;
|
|
2011
|
+
};
|
|
1123
2012
|
}
|
|
1124
|
-
return
|
|
2013
|
+
return wrap;
|
|
1125
2014
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
this._clientId
|
|
1129
|
-
], "window unload");
|
|
2015
|
+
ignoreEvent() {
|
|
2016
|
+
return false;
|
|
1130
2017
|
}
|
|
1131
2018
|
};
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
2019
|
+
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
|
+
});
|
|
1138
2041
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
2042
|
+
});
|
|
2043
|
+
const tasks = ViewPlugin5.fromClass(class {
|
|
2044
|
+
constructor(view) {
|
|
2045
|
+
this.tasks = taskMatcher.createDeco(view);
|
|
1141
2046
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
doc: text.doc,
|
|
1145
|
-
channel: `yjs.awareness.${text.id}`
|
|
1146
|
-
});
|
|
1147
|
-
}, [
|
|
1148
|
-
identity,
|
|
1149
|
-
space,
|
|
1150
|
-
text,
|
|
1151
|
-
text?.doc
|
|
1152
|
-
]);
|
|
1153
|
-
const content = useMemo2(() => {
|
|
1154
|
-
if (isActualAutomergeObject(text)) {
|
|
1155
|
-
const obj = text;
|
|
1156
|
-
return getRawDoc(obj, [
|
|
1157
|
-
obj.field
|
|
1158
|
-
]);
|
|
1159
|
-
} else {
|
|
1160
|
-
return text?.content;
|
|
2047
|
+
update(update4) {
|
|
2048
|
+
this.tasks = taskMatcher.updateDeco(update4, this.tasks);
|
|
1161
2049
|
}
|
|
1162
|
-
},
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
2050
|
+
}, {
|
|
2051
|
+
decorations: (value) => value.tasks,
|
|
2052
|
+
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2053
|
+
return view.plugin(plugin)?.tasks || Decoration7.none;
|
|
2054
|
+
})
|
|
2055
|
+
});
|
|
2056
|
+
return [
|
|
2057
|
+
tasks,
|
|
2058
|
+
styles4
|
|
2059
|
+
];
|
|
2060
|
+
};
|
|
2061
|
+
|
|
2062
|
+
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2063
|
+
import { keymap as keymap5 } from "@codemirror/view";
|
|
2064
|
+
var defaultItems = [
|
|
2065
|
+
"hello world!",
|
|
2066
|
+
"this is a test.",
|
|
2067
|
+
"this is [DXOS](https://dxos.org)"
|
|
2068
|
+
];
|
|
2069
|
+
var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
2070
|
+
let t;
|
|
2071
|
+
let idx = 0;
|
|
2072
|
+
return [
|
|
2073
|
+
keymap5.of([
|
|
2074
|
+
{
|
|
2075
|
+
// Reset.
|
|
2076
|
+
key: "alt-meta-'",
|
|
2077
|
+
run: (view) => {
|
|
2078
|
+
clearTimeout(t);
|
|
2079
|
+
idx = 0;
|
|
2080
|
+
return true;
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
{
|
|
2084
|
+
// Next prompt.
|
|
2085
|
+
// TODO(burdon): Press 1-9 to select prompt?
|
|
2086
|
+
key: "shift-meta-'",
|
|
2087
|
+
run: (view) => {
|
|
2088
|
+
clearTimeout(t);
|
|
2089
|
+
const text = items[idx++];
|
|
2090
|
+
if (idx === items?.length) {
|
|
2091
|
+
idx = 0;
|
|
2092
|
+
}
|
|
2093
|
+
let i = 0;
|
|
2094
|
+
const insert = (d = 0) => {
|
|
2095
|
+
t = setTimeout(() => {
|
|
2096
|
+
const pos = view.state.selection.main.head;
|
|
2097
|
+
view.dispatch({
|
|
2098
|
+
changes: {
|
|
2099
|
+
from: pos,
|
|
2100
|
+
insert: text[i++]
|
|
2101
|
+
},
|
|
2102
|
+
selection: {
|
|
2103
|
+
anchor: pos + 1
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
if (i < text.length) {
|
|
2107
|
+
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
2108
|
+
}
|
|
2109
|
+
}, d);
|
|
2110
|
+
};
|
|
2111
|
+
insert();
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
])
|
|
2116
|
+
];
|
|
2117
|
+
};
|
|
2118
|
+
|
|
2119
|
+
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2120
|
+
import get5 from "lodash.get";
|
|
2121
|
+
var defaultTheme = {
|
|
2122
|
+
//
|
|
2123
|
+
// Main layout:
|
|
2124
|
+
// https://codemirror.net/examples/styling
|
|
2125
|
+
//
|
|
2126
|
+
// <div class="cm-editor [cm-focused] [generated classes]">
|
|
2127
|
+
// <div class="cm-scroller">
|
|
2128
|
+
// <div class="cm-gutters">
|
|
2129
|
+
// <div class="cm-gutter [...]">
|
|
2130
|
+
// <div class="cm-gutterElement">...</div>
|
|
2131
|
+
// </div>
|
|
2132
|
+
// </div>
|
|
2133
|
+
// <div class="cm-content" role="textbox" contenteditable="true">
|
|
2134
|
+
// <div class="cm-line"></div>
|
|
2135
|
+
// </div>
|
|
2136
|
+
// <div class="cm-selectionLayer">
|
|
2137
|
+
// <div class="cm-selectionBackground"></div>
|
|
2138
|
+
// </div>
|
|
2139
|
+
// <div class="cm-cursorLayer">
|
|
2140
|
+
// <div class="cm-cursor"></div>
|
|
2141
|
+
// </div>
|
|
2142
|
+
// </div>
|
|
2143
|
+
// </div>
|
|
2144
|
+
//
|
|
2145
|
+
"&": {},
|
|
2146
|
+
"&.cm-focused": {
|
|
2147
|
+
outline: "none"
|
|
2148
|
+
},
|
|
2149
|
+
"& .cm-scroller": {
|
|
2150
|
+
overflow: "visible",
|
|
2151
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
2152
|
+
},
|
|
2153
|
+
"& .cm-content": {
|
|
2154
|
+
padding: 0,
|
|
2155
|
+
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2156
|
+
fontSize: "16px"
|
|
2157
|
+
},
|
|
2158
|
+
"&light .cm-content": {
|
|
2159
|
+
color: get5(tokens, "extend.colors.neutral.900", "black"),
|
|
2160
|
+
caretColor: "black"
|
|
2161
|
+
},
|
|
2162
|
+
"&dark .cm-content": {
|
|
2163
|
+
color: get5(tokens, "extend.colors.neutral.100", "white"),
|
|
2164
|
+
caretColor: "white"
|
|
2165
|
+
},
|
|
2166
|
+
//
|
|
2167
|
+
// Cursor
|
|
2168
|
+
//
|
|
2169
|
+
"&light .cm-cursor, &light .cm-dropCursor": {
|
|
2170
|
+
borderLeft: "2px solid black"
|
|
2171
|
+
},
|
|
2172
|
+
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
2173
|
+
borderLeft: "2px solid white"
|
|
2174
|
+
},
|
|
2175
|
+
"& .cm-placeholder": {
|
|
2176
|
+
fontWeight: 100
|
|
2177
|
+
},
|
|
2178
|
+
//
|
|
2179
|
+
// line
|
|
2180
|
+
//
|
|
2181
|
+
"& .cm-line": {
|
|
2182
|
+
paddingInline: 0
|
|
2183
|
+
},
|
|
2184
|
+
"& .cm-activeLine": {
|
|
2185
|
+
background: "inherit"
|
|
2186
|
+
},
|
|
2187
|
+
//
|
|
2188
|
+
// Selection
|
|
2189
|
+
//
|
|
2190
|
+
"&light .cm-selectionBackground": {
|
|
2191
|
+
background: get5(tokens, "extend.colors.primary.100")
|
|
2192
|
+
},
|
|
2193
|
+
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2194
|
+
background: get5(tokens, "extend.colors.primary.200")
|
|
2195
|
+
},
|
|
2196
|
+
"&dark .cm-selectionBackground": {
|
|
2197
|
+
background: get5(tokens, "extend.colors.primary.700")
|
|
2198
|
+
},
|
|
2199
|
+
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2200
|
+
background: get5(tokens, "extend.colors.primary.600")
|
|
2201
|
+
},
|
|
2202
|
+
//
|
|
2203
|
+
// Search
|
|
2204
|
+
//
|
|
2205
|
+
"&light .cm-searchMatch": {
|
|
2206
|
+
backgroundColor: get5(tokens, "extend.colors.yellow.100")
|
|
2207
|
+
},
|
|
2208
|
+
"&dark .cm-searchMatch": {
|
|
2209
|
+
backgroundColor: get5(tokens, "extend.colors.yellow.700")
|
|
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
|
|
2237
|
+
},
|
|
2238
|
+
//
|
|
2239
|
+
// link
|
|
2240
|
+
//
|
|
2241
|
+
"& .cm-link": {
|
|
2242
|
+
color: get5(tokens, "extend.colors.primary.500"),
|
|
2243
|
+
textDecorationLine: "underline",
|
|
2244
|
+
textDecorationThickness: "1px",
|
|
2245
|
+
textUnderlineOffset: "2px",
|
|
2246
|
+
borderRadius: ".125rem",
|
|
2247
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2248
|
+
},
|
|
2249
|
+
//
|
|
2250
|
+
// tooltip
|
|
2251
|
+
//
|
|
2252
|
+
"& .cm-tooltip": {
|
|
2253
|
+
border: "none"
|
|
2254
|
+
},
|
|
2255
|
+
"& .cm-tooltip-below": {},
|
|
2256
|
+
//
|
|
2257
|
+
// autocomplete
|
|
2258
|
+
//
|
|
2259
|
+
"& .cm-tooltip-autocomplete": {
|
|
2260
|
+
marginTop: "4px",
|
|
2261
|
+
marginLeft: "-3px"
|
|
2262
|
+
},
|
|
2263
|
+
"& .cm-tooltip-autocomplete ul li": {},
|
|
2264
|
+
"& .cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2265
|
+
"& .cm-completionIcon": {
|
|
2266
|
+
display: "none"
|
|
2267
|
+
},
|
|
2268
|
+
"& .cm-completionLabel": {
|
|
2269
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2270
|
+
},
|
|
2271
|
+
"& .cm-completionMatchedText": {
|
|
2272
|
+
textDecoration: "none"
|
|
2273
|
+
},
|
|
2274
|
+
//
|
|
2275
|
+
// widgets
|
|
2276
|
+
//
|
|
2277
|
+
"& .cm-widgetBuffer": {
|
|
2278
|
+
display: "none",
|
|
2279
|
+
height: 0
|
|
2280
|
+
},
|
|
2281
|
+
//
|
|
2282
|
+
// table
|
|
2283
|
+
//
|
|
2284
|
+
"& .cm-table *": {
|
|
2285
|
+
fontFamily: `${get5(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
2286
|
+
textDecoration: "none !important"
|
|
2287
|
+
},
|
|
2288
|
+
"& .cm-table-head": {
|
|
2289
|
+
padding: "2px 16px 2px 0px",
|
|
2290
|
+
borderBottom: `1px solid ${get5(tokens, "extend.colors.neutral.500")}`,
|
|
2291
|
+
fontWeight: 100,
|
|
2292
|
+
textAlign: "left",
|
|
2293
|
+
color: get5(tokens, "extend.colors.neutral.500")
|
|
2294
|
+
},
|
|
2295
|
+
"& .cm-table-cell": {
|
|
2296
|
+
padding: "2px 16px 2px 0px"
|
|
2297
|
+
},
|
|
2298
|
+
//
|
|
2299
|
+
// image
|
|
2300
|
+
//
|
|
2301
|
+
"& .cm-image": {
|
|
2302
|
+
margin: "0.5rem 0"
|
|
2303
|
+
},
|
|
2304
|
+
//
|
|
2305
|
+
// font size
|
|
2306
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
|
|
2307
|
+
//
|
|
2308
|
+
...Object.keys(get5(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
2309
|
+
const height = get5(tokens, [
|
|
2310
|
+
"extend",
|
|
2311
|
+
"fontSize",
|
|
2312
|
+
fontSize,
|
|
2313
|
+
1,
|
|
2314
|
+
"lineHeight"
|
|
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
|
|
1175
2324
|
};
|
|
2325
|
+
return acc;
|
|
2326
|
+
}, {}),
|
|
2327
|
+
/**
|
|
2328
|
+
* Panels
|
|
2329
|
+
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
2330
|
+
* https://github.com/codemirror/search/blob/main/src/search.ts#L745
|
|
2331
|
+
*
|
|
2332
|
+
* Find/replace panel.
|
|
2333
|
+
* <div class="cm-announced">...</div>
|
|
2334
|
+
* <div class="cm-scroller">...</div>
|
|
2335
|
+
* <div class="cm-panels cm-panels-bottom">
|
|
2336
|
+
* <div class="cm-search cm-panel">
|
|
2337
|
+
* <input class="cm-textfield" />
|
|
2338
|
+
* <button class="cm-button">...</button>
|
|
2339
|
+
* <label><input type="checkbox" />...</label>
|
|
2340
|
+
* </div>
|
|
2341
|
+
* </div
|
|
2342
|
+
*/
|
|
2343
|
+
"& .cm-panels": {
|
|
2344
|
+
border: `1px solid ${get5(tokens, "extend.colors.neutral.200")}`
|
|
2345
|
+
},
|
|
2346
|
+
"& .cm-panel": {
|
|
2347
|
+
background: get5(tokens, "extend.colors.neutral.50"),
|
|
2348
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2349
|
+
},
|
|
2350
|
+
"& .cm-button": {
|
|
2351
|
+
margin: "4px",
|
|
2352
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(","),
|
|
2353
|
+
background: get5(tokens, "extend.colors.neutral.100"),
|
|
2354
|
+
backgroundImage: "none",
|
|
2355
|
+
border: "none",
|
|
2356
|
+
"&:hover": {
|
|
2357
|
+
background: get5(tokens, "extend.colors.neutral.200")
|
|
2358
|
+
},
|
|
2359
|
+
"&:active": {
|
|
2360
|
+
background: get5(tokens, "extend.colors.neutral.300"),
|
|
2361
|
+
backgroundImage: "none"
|
|
2362
|
+
}
|
|
2363
|
+
},
|
|
2364
|
+
"& .cm-panel input[type=checkbox]": {
|
|
2365
|
+
marginRight: "0.4rem !important"
|
|
1176
2366
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
2367
|
+
};
|
|
2368
|
+
var textTheme = {
|
|
2369
|
+
"& .cm-scroller": {
|
|
2370
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2371
|
+
},
|
|
2372
|
+
"& .cm-placeholder": {
|
|
2373
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
var markdownTheme = {
|
|
2377
|
+
// NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
|
|
2378
|
+
"& .cm-placeholder": {
|
|
2379
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
var codeTheme = {
|
|
2383
|
+
"& .cm-scroller": {
|
|
2384
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
2385
|
+
},
|
|
2386
|
+
"& .cm-placeholder": {
|
|
2387
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
1179
2388
|
}
|
|
1180
|
-
return {
|
|
1181
|
-
id: text.doc.guid,
|
|
1182
|
-
content: text.content,
|
|
1183
|
-
provider,
|
|
1184
|
-
peer: identity ? {
|
|
1185
|
-
id: identity.identityKey.toHex(),
|
|
1186
|
-
name: identity.profile?.displayName
|
|
1187
|
-
} : void 0
|
|
1188
|
-
};
|
|
1189
2389
|
};
|
|
1190
2390
|
|
|
1191
2391
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
@@ -1193,58 +2393,95 @@ var EditorModes = [
|
|
|
1193
2393
|
"default",
|
|
1194
2394
|
"vim"
|
|
1195
2395
|
];
|
|
1196
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [],
|
|
2396
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: comments2, extensions = [], editorMode, slots = defaultSlots }, forwardedRef) => {
|
|
1197
2397
|
const { themeMode } = useThemeContext();
|
|
1198
2398
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
1199
2399
|
tabBehavior: "limited"
|
|
1200
2400
|
});
|
|
1201
|
-
const [
|
|
1202
|
-
const [state,
|
|
1203
|
-
const [view, setView] = useState();
|
|
2401
|
+
const [root, setRoot] = useState2(null);
|
|
2402
|
+
const [{ state = void 0, view = void 0 } = {}, setEditor] = useState2();
|
|
1204
2403
|
useImperativeHandle(forwardedRef, () => ({
|
|
1205
|
-
|
|
2404
|
+
root,
|
|
1206
2405
|
state,
|
|
1207
2406
|
view
|
|
1208
2407
|
}), [
|
|
1209
2408
|
view,
|
|
1210
2409
|
state,
|
|
1211
|
-
|
|
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
|
+
]);
|
|
2433
|
+
useEffect2(() => {
|
|
2434
|
+
if (view && comments2?.length) {
|
|
2435
|
+
view.dispatch({
|
|
2436
|
+
effects: setCommentRange.of({
|
|
2437
|
+
model,
|
|
2438
|
+
comments: comments2
|
|
2439
|
+
})
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
}, [
|
|
2443
|
+
view,
|
|
2444
|
+
comments2
|
|
1212
2445
|
]);
|
|
1213
|
-
const collaboration = useCollaboration(model, themeMode);
|
|
1214
2446
|
useEffect2(() => {
|
|
1215
|
-
if (!
|
|
2447
|
+
if (!root) {
|
|
1216
2448
|
return;
|
|
1217
2449
|
}
|
|
1218
|
-
const initialText = isDocAccessor2(model.content) ? get3(model.content.handle.docSync(), model.content.path) : model.content?.toString();
|
|
1219
2450
|
const state2 = EditorState2.create({
|
|
1220
|
-
doc:
|
|
2451
|
+
doc: model.text(),
|
|
1221
2452
|
extensions: [
|
|
1222
|
-
|
|
2453
|
+
readonly && EditorState2.readOnly.of(readonly),
|
|
2454
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
1223
2455
|
editorMode === "vim" && vim(),
|
|
1224
|
-
// Replication.
|
|
1225
|
-
collaboration,
|
|
1226
2456
|
// Theme.
|
|
1227
|
-
|
|
2457
|
+
// TODO(burdon): Make theme configurable.
|
|
2458
|
+
EditorView12.baseTheme(defaultTheme),
|
|
2459
|
+
EditorView12.theme(slots?.editor?.theme ?? {}),
|
|
2460
|
+
EditorView12.darkTheme.of(themeMode === "dark"),
|
|
2461
|
+
// Storage and replication.
|
|
2462
|
+
model.extension,
|
|
1228
2463
|
// Custom.
|
|
1229
2464
|
...extensions
|
|
1230
2465
|
].filter(Boolean)
|
|
1231
2466
|
});
|
|
1232
|
-
setState(state2);
|
|
1233
2467
|
view?.destroy();
|
|
1234
|
-
|
|
2468
|
+
setEditor({
|
|
1235
2469
|
state: state2,
|
|
1236
|
-
|
|
1237
|
-
|
|
2470
|
+
view: new EditorView12({
|
|
2471
|
+
state: state2,
|
|
2472
|
+
parent: root
|
|
2473
|
+
})
|
|
2474
|
+
});
|
|
1238
2475
|
return () => {
|
|
1239
2476
|
view?.destroy();
|
|
1240
|
-
|
|
1241
|
-
setState(void 0);
|
|
2477
|
+
setEditor(void 0);
|
|
1242
2478
|
};
|
|
1243
2479
|
}, [
|
|
1244
|
-
|
|
1245
|
-
model
|
|
1246
|
-
|
|
1247
|
-
editorMode
|
|
2480
|
+
root,
|
|
2481
|
+
model,
|
|
2482
|
+
readonly,
|
|
2483
|
+
editorMode,
|
|
2484
|
+
themeMode
|
|
1248
2485
|
]);
|
|
1249
2486
|
const handleKeyUp = useCallback((event) => {
|
|
1250
2487
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
@@ -1254,7 +2491,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
1254
2491
|
break;
|
|
1255
2492
|
}
|
|
1256
2493
|
case "Escape": {
|
|
1257
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) &&
|
|
2494
|
+
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
|
|
1258
2495
|
break;
|
|
1259
2496
|
}
|
|
1260
2497
|
}
|
|
@@ -1264,52 +2501,66 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
1264
2501
|
]);
|
|
1265
2502
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1266
2503
|
key: model.id,
|
|
1267
|
-
ref:
|
|
2504
|
+
ref: setRoot,
|
|
1268
2505
|
tabIndex: 0,
|
|
1269
2506
|
...slots?.root,
|
|
1270
2507
|
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
1271
2508
|
onKeyUp: handleKeyUp
|
|
1272
2509
|
});
|
|
1273
2510
|
});
|
|
1274
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ extensions
|
|
2511
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
1275
2512
|
const { themeMode } = useThemeContext();
|
|
1276
|
-
const
|
|
1277
|
-
..._extensions ?? [],
|
|
1278
|
-
basicBundle({
|
|
1279
|
-
themeMode,
|
|
1280
|
-
placeholder: slots?.editor?.placeholder
|
|
1281
|
-
})
|
|
1282
|
-
];
|
|
2513
|
+
const slots = defaultsDeep2({}, _slots, defaultTextSlots);
|
|
1283
2514
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1284
2515
|
ref: forwardedRef,
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
2516
|
+
readonly,
|
|
2517
|
+
extensions: [
|
|
2518
|
+
basicBundle({
|
|
2519
|
+
readonly,
|
|
2520
|
+
themeMode,
|
|
2521
|
+
placeholder: slots?.editor?.placeholder
|
|
2522
|
+
}),
|
|
2523
|
+
...extensions
|
|
2524
|
+
],
|
|
2525
|
+
slots,
|
|
1291
2526
|
...props
|
|
1292
2527
|
});
|
|
1293
2528
|
});
|
|
1294
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions
|
|
2529
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
1295
2530
|
const { themeMode } = useThemeContext();
|
|
1296
|
-
const
|
|
1297
|
-
..._extensions ?? [],
|
|
1298
|
-
markdownBundle({
|
|
1299
|
-
themeMode
|
|
1300
|
-
})
|
|
1301
|
-
];
|
|
2531
|
+
const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
|
|
1302
2532
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1303
2533
|
ref: forwardedRef,
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
2534
|
+
readonly,
|
|
2535
|
+
extensions: [
|
|
2536
|
+
markdownBundle({
|
|
2537
|
+
readonly,
|
|
2538
|
+
themeMode,
|
|
2539
|
+
placeholder: slots?.editor?.placeholder
|
|
2540
|
+
}),
|
|
2541
|
+
...extensions
|
|
2542
|
+
],
|
|
2543
|
+
slots,
|
|
1310
2544
|
...props
|
|
1311
2545
|
});
|
|
1312
2546
|
});
|
|
2547
|
+
var defaultSlots = {
|
|
2548
|
+
root: {
|
|
2549
|
+
className: mx3("p-2", inputSurface)
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
var defaultTextSlots = {
|
|
2553
|
+
...defaultSlots,
|
|
2554
|
+
editor: {
|
|
2555
|
+
theme: textTheme
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2558
|
+
var defaultMarkdownSlots = {
|
|
2559
|
+
...defaultSlots,
|
|
2560
|
+
editor: {
|
|
2561
|
+
theme: markdownTheme
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
1313
2564
|
export {
|
|
1314
2565
|
BaseTextEditor,
|
|
1315
2566
|
Doc,
|
|
@@ -1318,34 +2569,38 @@ export {
|
|
|
1318
2569
|
SpaceAwarenessProvider,
|
|
1319
2570
|
TextEditor,
|
|
1320
2571
|
TextKind,
|
|
1321
|
-
|
|
2572
|
+
YText,
|
|
1322
2573
|
YXmlFragment,
|
|
2574
|
+
autocomplete,
|
|
1323
2575
|
basicBundle,
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
2576
|
+
blast,
|
|
2577
|
+
code2 as code,
|
|
2578
|
+
codeTheme,
|
|
2579
|
+
comments,
|
|
1328
2580
|
cursorColor,
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
2581
|
+
defaultMarkdownSlots,
|
|
2582
|
+
defaultOptions,
|
|
2583
|
+
defaultSlots,
|
|
2584
|
+
defaultTextSlots,
|
|
2585
|
+
defaultTheme,
|
|
2586
|
+
hr,
|
|
2587
|
+
image,
|
|
2588
|
+
link,
|
|
2589
|
+
listener,
|
|
1338
2590
|
markdownBundle,
|
|
1339
2591
|
markdownHighlightStyle,
|
|
2592
|
+
markdownTags,
|
|
2593
|
+
markdownTagsExtensions,
|
|
1340
2594
|
markdownTheme,
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
2595
|
+
mention,
|
|
2596
|
+
mermaid,
|
|
2597
|
+
modelState,
|
|
2598
|
+
setCommentRange,
|
|
2599
|
+
table,
|
|
1344
2600
|
tags2 as tags,
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
whiskey
|
|
2601
|
+
tasklist,
|
|
2602
|
+
textTheme,
|
|
2603
|
+
typewriter,
|
|
2604
|
+
useTextModel
|
|
1350
2605
|
};
|
|
1351
2606
|
//# sourceMappingURL=index.mjs.map
|