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