@dxos/react-ui-editor 0.3.9-main.2dc53cf → 0.3.9-main.318af16
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 +1026 -827
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts +9 -0
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/index.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts +6 -5
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge.stories.d.ts +2 -1
- package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +24 -20
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +7 -24
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/{stories/html/code-mirror.stories.d.ts → src/components/TextEditor/codemirror.stories.d.ts} +2 -2
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts +3 -0
- package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts +10 -0
- package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/change.d.ts +7 -0
- package/dist/types/src/components/TextEditor/extensions/change.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts +11 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/{hyperlinkTooltip.d.ts → hyperlink/hyperlinkTooltip.d.ts} +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/{hyperlink.d.ts → hyperlink/hyperlinkWidget.d.ts} +3 -3
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts +4 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +3 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +3 -0
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/components/{Markdown/markdownTags.d.ts → TextEditor/extensions/markdown/tags.d.ts} +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts +21 -0
- package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +1 -3
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts +11 -0
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +0 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +4 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useCollaboration.d.ts +9 -0
- package/dist/types/src/hooks/useCollaboration.d.ts.map +1 -0
- package/dist/types/src/{model.d.ts → hooks/useTextModel.d.ts} +3 -15
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -0
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +1 -0
- package/dist/types/src/hooks/yjs/index.d.ts.map +1 -0
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +4 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/package.json +31 -28
- package/src/automerge/automerge-plugin/PatchSemaphore.ts +5 -5
- package/src/automerge/automerge-plugin/codeMirrorToAm.ts +10 -7
- package/src/automerge/automerge-plugin/handle.ts +14 -0
- package/src/automerge/automerge-plugin/index.ts +2 -2
- package/src/automerge/automerge-plugin/plugin.ts +39 -10
- package/src/automerge/automerge.stories.tsx +32 -47
- package/src/components/TextEditor/TextEditor.stories.tsx +51 -55
- package/src/components/TextEditor/TextEditor.tsx +113 -55
- package/src/components/TextEditor/codemirror.stories.ts +114 -0
- package/src/components/TextEditor/extensions/basic/bundle.ts +26 -0
- package/src/components/TextEditor/extensions/basic/index.ts +6 -0
- package/src/components/TextEditor/extensions/basic/theme.ts +49 -0
- package/src/components/TextEditor/extensions/change.ts +22 -0
- package/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts +99 -0
- package/src/components/TextEditor/extensions/{hyperlinkTooltip.tsx → hyperlink/hyperlinkTooltip.tsx} +2 -17
- package/src/components/TextEditor/extensions/{hyperlink.tsx → hyperlink/hyperlinkWidget.tsx} +21 -10
- package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
- package/src/components/TextEditor/extensions/index.ts +3 -1
- package/src/components/TextEditor/extensions/markdown/bundle.ts +99 -0
- package/src/components/TextEditor/extensions/markdown/index.ts +6 -0
- package/src/components/{Markdown/markdownTheme.ts → TextEditor/extensions/markdown/theme.ts} +23 -12
- package/src/components/TextEditor/index.ts +1 -4
- package/src/components/TextEditor/yjs.stories.tsx +55 -0
- package/src/components/index.ts +0 -3
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useCollaboration.ts +45 -0
- package/src/{model.ts → hooks/useTextModel.ts} +42 -17
- package/src/index.ts +3 -3
- package/src/styles/tokens.ts +6 -0
- package/src/testing/replicator.ts +1 -2
- package/dist/types/src/automerge/demo.d.ts +0 -25
- package/dist/types/src/automerge/demo.d.ts.map +0 -1
- package/dist/types/src/components/Editor/Editor.d.ts +0 -15
- package/dist/types/src/components/Editor/Editor.d.ts.map +0 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +0 -29
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +0 -1
- package/dist/types/src/components/Editor/index.d.ts +0 -2
- package/dist/types/src/components/Editor/index.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts +0 -20
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +0 -20
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/index.d.ts +0 -2
- package/dist/types/src/components/Markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/markdownTags.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/markdownTheme.d.ts +0 -93
- package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +0 -1
- package/dist/types/src/components/RichText/RichText.d.ts +0 -20
- package/dist/types/src/components/RichText/RichText.d.ts.map +0 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts +0 -27
- package/dist/types/src/components/RichText/RichText.stories.d.ts.map +0 -1
- package/dist/types/src/components/RichText/index.d.ts +0 -2
- package/dist/types/src/components/RichText/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlink.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/theme.d.ts +0 -8
- package/dist/types/src/components/TextEditor/theme.d.ts.map +0 -1
- package/dist/types/src/model.d.ts.map +0 -1
- package/dist/types/src/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/yjs/index.d.ts.map +0 -1
- package/dist/types/src/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/stories/html/code-mirror.stories.d.ts.map +0 -1
- package/src/automerge/demo.ts +0 -106
- package/src/components/Editor/Editor.stories.tsx +0 -82
- package/src/components/Editor/Editor.tsx +0 -33
- package/src/components/Editor/index.ts +0 -5
- package/src/components/Markdown/Markdown.stories.tsx +0 -59
- package/src/components/Markdown/Markdown.tsx +0 -214
- package/src/components/Markdown/index.ts +0 -5
- package/src/components/RichText/RichText.stories.tsx +0 -70
- package/src/components/RichText/RichText.tsx +0 -181
- package/src/components/RichText/index.ts +0 -5
- package/src/components/TextEditor/theme.ts +0 -33
- /package/dist/types/src/{yjs → hooks/yjs}/cursors.d.ts +0 -0
- /package/dist/types/src/{yjs → hooks/yjs}/index.d.ts +0 -0
- /package/dist/types/src/{yjs → hooks/yjs}/space-provider.d.ts +0 -0
- /package/src/components/{Markdown/markdownTags.ts → TextEditor/extensions/markdown/tags.ts} +0 -0
- /package/src/{yjs → hooks/yjs}/cursors.ts +0 -0
- /package/src/{yjs → hooks/yjs}/index.ts +0 -0
- /package/src/{yjs → hooks/yjs}/space-provider.ts +0 -0
|
@@ -1,235 +1,24 @@
|
|
|
1
|
-
// packages/ui/react-ui-editor/src/
|
|
2
|
-
import
|
|
3
|
-
import { YXmlFragment } from "@dxos/text-model";
|
|
4
|
-
|
|
5
|
-
// packages/ui/react-ui-editor/src/model.ts
|
|
6
|
-
import { useMemo } from "react";
|
|
7
|
-
|
|
8
|
-
// packages/ui/react-ui-editor/src/yjs/cursors.ts
|
|
9
|
-
import * as random from "lib0/random";
|
|
10
|
-
var cursorColors = [
|
|
11
|
-
{
|
|
12
|
-
color: "#30bced",
|
|
13
|
-
light: "#30bced33"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
color: "#6eeb83",
|
|
17
|
-
light: "#6eeb8333"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
color: "#ffbc42",
|
|
21
|
-
light: "#ffbc4233"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
color: "#ecd444",
|
|
25
|
-
light: "#ecd44433"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
color: "#ee6352",
|
|
29
|
-
light: "#ee635233"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
color: "#9ac2c9",
|
|
33
|
-
light: "#9ac2c933"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
color: "#8acb88",
|
|
37
|
-
light: "#8acb8833"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
color: "#1be7ff",
|
|
41
|
-
light: "#1be7ff33"
|
|
42
|
-
}
|
|
43
|
-
];
|
|
44
|
-
var cursorColor = cursorColors[random.uint32() % cursorColors.length];
|
|
45
|
-
|
|
46
|
-
// packages/ui/react-ui-editor/src/yjs/space-provider.ts
|
|
47
|
-
import * as decoding from "lib0/decoding";
|
|
48
|
-
import * as encoding from "lib0/encoding";
|
|
49
|
-
import { Observable } from "lib0/observable";
|
|
50
|
-
import * as awarenessProtocol from "y-protocols/awareness";
|
|
51
|
-
import { log } from "@dxos/log";
|
|
52
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/yjs/space-provider.ts";
|
|
53
|
-
var messageAwareness = 1;
|
|
54
|
-
var messageQueryAwareness = 3;
|
|
55
|
-
var SpaceAwarenessProvider = class extends Observable {
|
|
56
|
-
constructor({ space, doc, channel, awareness }) {
|
|
57
|
-
super();
|
|
58
|
-
this._space = space;
|
|
59
|
-
this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
|
|
60
|
-
this._channel = channel;
|
|
61
|
-
this._clientId = doc.clientID;
|
|
62
|
-
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
63
|
-
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
64
|
-
if (typeof window !== "undefined") {
|
|
65
|
-
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
66
|
-
} else if (typeof process !== "undefined") {
|
|
67
|
-
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
68
|
-
}
|
|
69
|
-
const encoderAwarenessQuery = encoding.createEncoder();
|
|
70
|
-
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
71
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
72
|
-
const encoderAwarenessState = encoding.createEncoder();
|
|
73
|
-
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
74
|
-
encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
|
|
75
|
-
this._clientId
|
|
76
|
-
]));
|
|
77
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
78
|
-
}
|
|
79
|
-
get awareness() {
|
|
80
|
-
return this._awareness;
|
|
81
|
-
}
|
|
82
|
-
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
83
|
-
log("awareness update", {
|
|
84
|
-
added,
|
|
85
|
-
updated,
|
|
86
|
-
removed,
|
|
87
|
-
origin
|
|
88
|
-
}, {
|
|
89
|
-
F: __dxlog_file,
|
|
90
|
-
L: 67,
|
|
91
|
-
S: this,
|
|
92
|
-
C: (f, a) => f(...a)
|
|
93
|
-
});
|
|
94
|
-
const changedClients = added.concat(updated).concat(removed);
|
|
95
|
-
const encoderAwareness = encoding.createEncoder();
|
|
96
|
-
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
97
|
-
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
98
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
99
|
-
}
|
|
100
|
-
_handleSpaceMessage({ payload }) {
|
|
101
|
-
log("space message", payload, {
|
|
102
|
-
F: __dxlog_file,
|
|
103
|
-
L: 79,
|
|
104
|
-
S: this,
|
|
105
|
-
C: (f, a) => f(...a)
|
|
106
|
-
});
|
|
107
|
-
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
108
|
-
const encoder = this._readMessage(data);
|
|
109
|
-
if (encoder) {
|
|
110
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
_readMessage(message) {
|
|
114
|
-
const decoder = decoding.createDecoder(message);
|
|
115
|
-
const encoder = encoding.createEncoder();
|
|
116
|
-
const messageType = decoding.readVarUint(decoder);
|
|
117
|
-
let sendReply = false;
|
|
118
|
-
switch (messageType) {
|
|
119
|
-
case messageAwareness: {
|
|
120
|
-
awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
case messageQueryAwareness: {
|
|
124
|
-
encoding.writeVarUint(encoder, messageAwareness);
|
|
125
|
-
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
126
|
-
sendReply = true;
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
default: {
|
|
130
|
-
console.error("Invalid message:", messageType);
|
|
131
|
-
return encoder;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (!sendReply) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
return encoder;
|
|
138
|
-
}
|
|
139
|
-
_handleBeforeUnload() {
|
|
140
|
-
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
141
|
-
this._clientId
|
|
142
|
-
], "window unload");
|
|
143
|
-
}
|
|
144
|
-
};
|
|
1
|
+
// packages/ui/react-ui-editor/src/index.ts
|
|
2
|
+
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
3
|
+
import { Doc, YText as YText2, YXmlFragment } from "@dxos/text-model";
|
|
145
4
|
|
|
146
|
-
// packages/ui/react-ui-editor/src/
|
|
147
|
-
|
|
148
|
-
const provider = useMemo(() => {
|
|
149
|
-
if (!space || !text?.doc) {
|
|
150
|
-
return void 0;
|
|
151
|
-
}
|
|
152
|
-
return new SpaceAwarenessProvider({
|
|
153
|
-
space,
|
|
154
|
-
doc: text.doc,
|
|
155
|
-
channel: `yjs.awareness.${text.id}`
|
|
156
|
-
});
|
|
157
|
-
}, [
|
|
158
|
-
identity,
|
|
159
|
-
space,
|
|
160
|
-
text?.doc
|
|
161
|
-
]);
|
|
162
|
-
if (!text?.doc || !text?.content) {
|
|
163
|
-
return void 0;
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
id: text.doc.guid,
|
|
167
|
-
content: text.content,
|
|
168
|
-
provider,
|
|
169
|
-
peer: identity ? {
|
|
170
|
-
id: identity.identityKey.toHex(),
|
|
171
|
-
name: identity.profile?.displayName
|
|
172
|
-
} : void 0
|
|
173
|
-
};
|
|
174
|
-
};
|
|
5
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
6
|
+
import { tags as tags2 } from "@lezer/highlight";
|
|
175
7
|
|
|
176
|
-
// packages/ui/react-ui-editor/src/components/
|
|
177
|
-
import {
|
|
178
|
-
import {
|
|
179
|
-
import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
180
|
-
import { bracketMatching, defaultHighlightStyle, foldKeymap, indentOnInput, syntaxHighlighting } from "@codemirror/language";
|
|
181
|
-
import { languages } from "@codemirror/language-data";
|
|
182
|
-
import { lintKeymap } from "@codemirror/lint";
|
|
183
|
-
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
184
|
-
import { EditorState, StateField } from "@codemirror/state";
|
|
8
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/bundle.ts
|
|
9
|
+
import { closeBrackets } from "@codemirror/autocomplete";
|
|
10
|
+
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
185
11
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
186
|
-
import {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
import { YText } from "@dxos/text-model";
|
|
195
|
-
|
|
196
|
-
// packages/ui/react-ui-editor/src/components/Markdown/markdownTags.ts
|
|
197
|
-
import { styleTags, Tag } from "@lezer/highlight";
|
|
198
|
-
var markdownTags = {
|
|
199
|
-
headingMark: Tag.define(),
|
|
200
|
-
quoteMark: Tag.define(),
|
|
201
|
-
listMark: Tag.define(),
|
|
202
|
-
linkMark: Tag.define(),
|
|
203
|
-
emphasisMark: Tag.define(),
|
|
204
|
-
codeMark: Tag.define(),
|
|
205
|
-
codeText: Tag.define(),
|
|
206
|
-
inlineCode: Tag.define(),
|
|
207
|
-
url: Tag.define(),
|
|
208
|
-
linkReference: Tag.define(),
|
|
209
|
-
linkLabel: Tag.define()
|
|
210
|
-
};
|
|
211
|
-
var markdownTagsExtension = {
|
|
212
|
-
props: [
|
|
213
|
-
styleTags({
|
|
214
|
-
HeaderMark: markdownTags.headingMark,
|
|
215
|
-
QuoteMark: markdownTags.quoteMark,
|
|
216
|
-
ListMark: markdownTags.listMark,
|
|
217
|
-
LinkMark: markdownTags.linkMark,
|
|
218
|
-
EmphasisMark: markdownTags.emphasisMark,
|
|
219
|
-
CodeMark: markdownTags.codeMark,
|
|
220
|
-
CodeText: markdownTags.codeText,
|
|
221
|
-
InlineCode: markdownTags.inlineCode,
|
|
222
|
-
URL: markdownTags.url,
|
|
223
|
-
LinkReference: markdownTags.linkReference,
|
|
224
|
-
LinkLabel: markdownTags.linkLabel
|
|
225
|
-
})
|
|
226
|
-
]
|
|
227
|
-
};
|
|
12
|
+
import { placeholder } from "@codemirror/view";
|
|
13
|
+
var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
|
|
14
|
+
bracketMatching(),
|
|
15
|
+
closeBrackets(),
|
|
16
|
+
_placeholder && placeholder(_placeholder),
|
|
17
|
+
// TODO(burdon): Is this required?
|
|
18
|
+
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
19
|
+
].filter(Boolean);
|
|
228
20
|
|
|
229
|
-
// packages/ui/react-ui-editor/src/components/
|
|
230
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
231
|
-
import { HighlightStyle } from "@codemirror/language";
|
|
232
|
-
import { tags } from "@lezer/highlight";
|
|
21
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
233
22
|
import get from "lodash.get";
|
|
234
23
|
|
|
235
24
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
@@ -242,13 +31,6 @@ var heading = {
|
|
|
242
31
|
5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
|
|
243
32
|
6: "mbs-4 mbe-2 font-black text-inherit no-underline"
|
|
244
33
|
};
|
|
245
|
-
var blockquote = "mlb-2 border-is-4 border-neutral-500/50 pis-5";
|
|
246
|
-
var unorderedList = "mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[attr(marker)] [&>li:before]:mlb-1 [&>li:before]:mie-2";
|
|
247
|
-
var orderedList = 'mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[counters(section,_".")_"._"] [counter-reset:section] [&>li:before]:mlb-1';
|
|
248
|
-
var listItem = "contents before:[counter-increment:section]";
|
|
249
|
-
var codeBlock = "mlb-2 font-mono bg-neutral-500/10 p-3 rounded";
|
|
250
|
-
var horizontalRule = "mlb-4 border-neutral-500/50";
|
|
251
|
-
var paragraph = "mlb-1";
|
|
252
34
|
var bold = "font-bold";
|
|
253
35
|
var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
|
|
254
36
|
var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
|
|
@@ -260,53 +42,357 @@ var mark = "!font-normal !no-underline !text-inherit opacity-40";
|
|
|
260
42
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
261
43
|
var tokens = tailwindConfig({}).theme;
|
|
262
44
|
|
|
263
|
-
// packages/ui/react-ui-editor/src/components/
|
|
264
|
-
var
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
45
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
46
|
+
var basicTheme = {
|
|
47
|
+
"&.cm-focused": {
|
|
48
|
+
outline: "none"
|
|
49
|
+
},
|
|
50
|
+
"& .cm-line": {
|
|
51
|
+
paddingInline: 0,
|
|
52
|
+
lineHeight: 1.6,
|
|
53
|
+
minBlockSize: "1.6em"
|
|
54
|
+
},
|
|
55
|
+
"& .cm-line *": {
|
|
56
|
+
lineHeight: 1.6
|
|
57
|
+
},
|
|
58
|
+
"& .cm-scroller": {
|
|
59
|
+
fontFamily: get(tokens, "fontFamily.body", []).join(","),
|
|
60
|
+
overflow: "visible"
|
|
61
|
+
},
|
|
62
|
+
"& .cm-content": {
|
|
63
|
+
padding: 0,
|
|
64
|
+
caretColor: "black"
|
|
65
|
+
},
|
|
66
|
+
".dark & .cm-content": {
|
|
67
|
+
caretColor: "white"
|
|
68
|
+
},
|
|
69
|
+
"& .cm-tooltip": {
|
|
70
|
+
backgroundColor: "transparent",
|
|
71
|
+
border: "none"
|
|
72
|
+
},
|
|
73
|
+
"& .cm-link": {
|
|
74
|
+
color: get(tokens, "extend.colors.primary.500"),
|
|
75
|
+
textDecoration: "underline"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/change.ts
|
|
80
|
+
import { StateField } from "@codemirror/state";
|
|
81
|
+
var onChangeExtension = (onChange) => StateField.define({
|
|
82
|
+
create: () => null,
|
|
83
|
+
update: (_value, transaction) => {
|
|
84
|
+
if (transaction.docChanged && onChange) {
|
|
85
|
+
onChange(transaction.newDoc.toString());
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts
|
|
92
|
+
import { syntaxTree } from "@codemirror/language";
|
|
93
|
+
import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
|
|
94
|
+
import { Decoration, EditorView, WidgetType } from "@codemirror/view";
|
|
95
|
+
var Link = class extends WidgetType {
|
|
96
|
+
constructor(_anchor, _text, _url) {
|
|
97
|
+
super();
|
|
98
|
+
this._anchor = _anchor;
|
|
99
|
+
this._text = _text;
|
|
100
|
+
this._url = _url;
|
|
101
|
+
}
|
|
102
|
+
eq(other) {
|
|
103
|
+
return this._text === other.text && this._url === other.url;
|
|
104
|
+
}
|
|
105
|
+
toDOM(view) {
|
|
106
|
+
const link = document.createElement("a");
|
|
107
|
+
link.setAttribute("class", "cm-link");
|
|
108
|
+
link.textContent = this._text;
|
|
109
|
+
if (this._url) {
|
|
110
|
+
link.setAttribute("target", "_blank");
|
|
111
|
+
link.href = this._url;
|
|
112
|
+
} else {
|
|
113
|
+
link.onclick = () => {
|
|
114
|
+
view.dispatch({
|
|
115
|
+
selection: {
|
|
116
|
+
anchor: this._anchor
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return link;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var hyperlinkDecoration = ({ link } = {}) => {
|
|
125
|
+
return StateField2.define({
|
|
126
|
+
create: (state) => update(state, link),
|
|
127
|
+
update: (_, tr) => update(tr.state, link),
|
|
128
|
+
provide: (field) => EditorView.decorations.from(field)
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
var update = (state, link) => {
|
|
132
|
+
const builder = new RangeSetBuilder();
|
|
133
|
+
const cursor = state.selection.main.head;
|
|
134
|
+
syntaxTree(state).iterate({
|
|
135
|
+
enter: (node) => {
|
|
136
|
+
if (cursor < node.from || cursor > node.to) {
|
|
137
|
+
if (node.name === "Link") {
|
|
138
|
+
const marks = node.node.getChildren("LinkMark");
|
|
139
|
+
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
140
|
+
const urlNode = node.node.getChild("URL");
|
|
141
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
142
|
+
builder.add(node.from, node.to, Decoration.replace({
|
|
143
|
+
widget: new Link(node.from, text, link ? url : void 0)
|
|
144
|
+
}));
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return builder.finish();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx
|
|
155
|
+
import { hoverTooltip } from "@codemirror/view";
|
|
156
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
157
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
158
|
+
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
|
|
159
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
160
|
+
const p = pos - from;
|
|
161
|
+
let idx = 0;
|
|
162
|
+
let match;
|
|
163
|
+
do {
|
|
164
|
+
match = text.substring(idx).match(regexp);
|
|
165
|
+
if (!match) {
|
|
166
|
+
match = void 0;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
idx = text.indexOf(match[0]);
|
|
170
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
idx += match[0].length;
|
|
174
|
+
} while (true);
|
|
175
|
+
if (!match) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const [, , url] = match ?? [];
|
|
179
|
+
return {
|
|
180
|
+
pos: idx + from,
|
|
181
|
+
end: idx + from + match[0].length,
|
|
182
|
+
above: true,
|
|
183
|
+
create: () => {
|
|
184
|
+
const el = document.createElement("a");
|
|
185
|
+
el.innerText = "_";
|
|
186
|
+
el.className = tooltipContent({}, "mb-2 p-1");
|
|
187
|
+
el.setAttribute("target", "_blank");
|
|
188
|
+
el.setAttribute("href", url);
|
|
189
|
+
onHover(el, url);
|
|
190
|
+
return {
|
|
191
|
+
dom: el
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx
|
|
198
|
+
import { Decoration as Decoration2, EditorView as EditorView2, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
199
|
+
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
200
|
+
var linkDecorator = () => new MatchDecorator({
|
|
201
|
+
regexp: markdownLinkRegexp2,
|
|
202
|
+
decorate: (add, from, to, match, view) => {
|
|
203
|
+
const [_, label, url] = match;
|
|
204
|
+
const p0 = {
|
|
205
|
+
start: from,
|
|
206
|
+
end: from + 1
|
|
207
|
+
};
|
|
208
|
+
const p1 = {
|
|
209
|
+
start: from + 1,
|
|
210
|
+
end: from + 1 + label.length
|
|
211
|
+
};
|
|
212
|
+
const p2 = {
|
|
213
|
+
start: p1.end,
|
|
214
|
+
end: p1.end + 1
|
|
215
|
+
};
|
|
216
|
+
const p3 = {
|
|
217
|
+
start: p1.end + 1,
|
|
218
|
+
end: p1.end + 1 + url.length + 2
|
|
219
|
+
};
|
|
220
|
+
add(p0.start, p0.end, Decoration2.mark({
|
|
221
|
+
class: "cm-hyperlink-bracket",
|
|
222
|
+
_attributes: {
|
|
223
|
+
style: "display: none"
|
|
224
|
+
}
|
|
225
|
+
}));
|
|
226
|
+
add(p1.start, p1.end, Decoration2.mark({
|
|
227
|
+
class: "cm-hyperlink-label"
|
|
228
|
+
}));
|
|
229
|
+
add(p2.start, p2.end, Decoration2.mark({
|
|
230
|
+
class: "cm-hyperlink-bracket",
|
|
231
|
+
_attributes: {
|
|
232
|
+
style: "display: none"
|
|
233
|
+
}
|
|
234
|
+
}));
|
|
235
|
+
add(p3.start, p3.end, Decoration2.mark({
|
|
236
|
+
class: "cm-hyperlink-url",
|
|
237
|
+
_attributes: {
|
|
238
|
+
style: "display: none"
|
|
239
|
+
}
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
function hyperLinkExtension() {
|
|
244
|
+
return ViewPlugin.fromClass(class HyperLinkView {
|
|
245
|
+
constructor(view) {
|
|
246
|
+
this.decorator = linkDecorator();
|
|
247
|
+
this.decorations = this.decorator.createDeco(view);
|
|
248
|
+
}
|
|
249
|
+
update(update2) {
|
|
250
|
+
if (update2.docChanged || update2.viewportChanged) {
|
|
251
|
+
this.decorations = this.decorator.updateDeco(update2, this.decorations);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}, {
|
|
255
|
+
decorations: (view) => view.decorations,
|
|
256
|
+
eventHandlers: {
|
|
257
|
+
// TODO(burdon): CLick to expand link.
|
|
258
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
259
|
+
mousedown: function(event, view) {
|
|
260
|
+
const pos = view.posAtDOM(event.target);
|
|
261
|
+
if (pos !== 0) {
|
|
262
|
+
console.log(this);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
266
|
+
keydown: (event, view) => {
|
|
267
|
+
view.update([]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
var hyperlinkStyle = EditorView2.baseTheme({
|
|
273
|
+
".cm-hyperlink-label": {
|
|
274
|
+
cursor: "pointer",
|
|
275
|
+
textDecoration: "underline"
|
|
276
|
+
},
|
|
277
|
+
".cm-hyperlink-bracket": {
|
|
278
|
+
opacity: 0.2
|
|
279
|
+
},
|
|
280
|
+
".cm-hyperlink-url": {
|
|
281
|
+
opacity: 0.5,
|
|
282
|
+
fontFamily: "monospace"
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
var hyperlinkWidget = [
|
|
286
|
+
hyperLinkExtension(),
|
|
287
|
+
hyperlinkStyle
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
291
|
+
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
292
|
+
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
293
|
+
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
294
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, foldKeymap, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
295
|
+
import { languages } from "@codemirror/language-data";
|
|
296
|
+
import { lintKeymap } from "@codemirror/lint";
|
|
297
|
+
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
298
|
+
import { EditorState } from "@codemirror/state";
|
|
299
|
+
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
300
|
+
import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView3, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
|
|
301
|
+
|
|
302
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
|
|
303
|
+
import { styleTags, Tag } from "@lezer/highlight";
|
|
304
|
+
var markdownTags = {
|
|
305
|
+
headingMark: Tag.define(),
|
|
306
|
+
quoteMark: Tag.define(),
|
|
307
|
+
listMark: Tag.define(),
|
|
308
|
+
linkMark: Tag.define(),
|
|
309
|
+
emphasisMark: Tag.define(),
|
|
310
|
+
codeMark: Tag.define(),
|
|
311
|
+
codeText: Tag.define(),
|
|
312
|
+
inlineCode: Tag.define(),
|
|
313
|
+
url: Tag.define(),
|
|
314
|
+
linkReference: Tag.define(),
|
|
315
|
+
linkLabel: Tag.define()
|
|
316
|
+
};
|
|
317
|
+
var markdownTagsExtension = {
|
|
318
|
+
props: [
|
|
319
|
+
styleTags({
|
|
320
|
+
HeaderMark: markdownTags.headingMark,
|
|
321
|
+
QuoteMark: markdownTags.quoteMark,
|
|
322
|
+
ListMark: markdownTags.listMark,
|
|
323
|
+
LinkMark: markdownTags.linkMark,
|
|
324
|
+
EmphasisMark: markdownTags.emphasisMark,
|
|
325
|
+
CodeMark: markdownTags.codeMark,
|
|
326
|
+
CodeText: markdownTags.codeText,
|
|
327
|
+
InlineCode: markdownTags.inlineCode,
|
|
328
|
+
URL: markdownTags.url,
|
|
329
|
+
LinkReference: markdownTags.linkReference,
|
|
330
|
+
LinkLabel: markdownTags.linkLabel
|
|
331
|
+
})
|
|
332
|
+
]
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
|
|
336
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
337
|
+
import { HighlightStyle } from "@codemirror/language";
|
|
338
|
+
import { tags } from "@lezer/highlight";
|
|
339
|
+
import get2 from "lodash.get";
|
|
340
|
+
var chalky = "#e5c07b";
|
|
341
|
+
var coral = "#e06c75";
|
|
342
|
+
var cyan = "#56b6c2";
|
|
343
|
+
var invalid = "#ffffff";
|
|
344
|
+
var ivory = "#abb2bf";
|
|
345
|
+
var stone = "#7d8799";
|
|
346
|
+
var malibu = "#61afef";
|
|
347
|
+
var sage = "#98c379";
|
|
348
|
+
var whiskey = "#d19a66";
|
|
349
|
+
var violet = "#c678dd";
|
|
350
|
+
var highlightBackground = "#2c313a";
|
|
351
|
+
var tooltipBackground = "#353a42";
|
|
352
|
+
var monospace = get2(tokens, "fontFamily.mono", [
|
|
353
|
+
"monospace"
|
|
354
|
+
]).join(",");
|
|
355
|
+
var markdownTheme = {
|
|
356
|
+
// TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
|
|
357
|
+
// '&': {
|
|
358
|
+
// color: ivory,
|
|
359
|
+
// backgroundColor: background
|
|
360
|
+
// },
|
|
361
|
+
// '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
|
|
362
|
+
// '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
|
|
363
|
+
// backgroundColor: selection
|
|
364
|
+
// },
|
|
365
|
+
// '.cm-panels': { backgroundColor: darkBackground, color: ivory },
|
|
366
|
+
// '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
|
367
|
+
// '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
|
368
|
+
// '.cm-searchMatch': {
|
|
369
|
+
// backgroundColor: '#72a1ff59',
|
|
370
|
+
// outline: '1px solid #457dff'
|
|
371
|
+
// },
|
|
372
|
+
// '.cm-searchMatch.cm-searchMatch-selected': {
|
|
373
|
+
// backgroundColor: '#6199ff2f'
|
|
374
|
+
// },
|
|
375
|
+
// '.cm-activeLine': { backgroundColor: '#6699ff0b' },
|
|
376
|
+
// '.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
|
377
|
+
// '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
|
378
|
+
// backgroundColor: '#bad0f847'
|
|
379
|
+
// },
|
|
380
|
+
// '.cm-gutters': {
|
|
381
|
+
// backgroundColor: background,
|
|
382
|
+
// color: stone,
|
|
383
|
+
// border: 'none'
|
|
384
|
+
// },
|
|
385
|
+
// '.cm-activeLineGutter': {
|
|
386
|
+
// backgroundColor: highlightBackground
|
|
387
|
+
// },
|
|
388
|
+
// '.cm-foldPlaceholder': {
|
|
389
|
+
// backgroundColor: 'transparent',
|
|
390
|
+
// border: 'none',
|
|
391
|
+
// color: '#ddd'
|
|
392
|
+
// },
|
|
393
|
+
"& .cm-tooltip": {
|
|
394
|
+
backgroundColor: "transparent",
|
|
395
|
+
border: "none"
|
|
310
396
|
},
|
|
311
397
|
// '.dark & .cm-tooltip': {
|
|
312
398
|
// border: 'none',
|
|
@@ -331,40 +417,42 @@ var markdownTheme = {
|
|
|
331
417
|
},
|
|
332
418
|
"& .cm-line": {
|
|
333
419
|
paddingInline: 0,
|
|
420
|
+
lineHeight: 1.6,
|
|
334
421
|
minBlockSize: "1.6em"
|
|
335
422
|
},
|
|
336
423
|
"& .cm-line *": {
|
|
337
424
|
lineHeight: 1.6
|
|
338
425
|
},
|
|
339
426
|
"&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
|
|
340
|
-
background:
|
|
427
|
+
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
341
428
|
},
|
|
342
429
|
".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
|
|
343
|
-
background:
|
|
430
|
+
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
344
431
|
},
|
|
345
432
|
"& .cm-selectionMatch": {
|
|
346
|
-
background:
|
|
433
|
+
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
347
434
|
},
|
|
348
435
|
".dark & .cm-selectionMatch": {
|
|
349
|
-
background:
|
|
436
|
+
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
350
437
|
},
|
|
351
438
|
"& .cm-content": {
|
|
439
|
+
padding: 0,
|
|
352
440
|
caretColor: "black"
|
|
353
441
|
},
|
|
354
442
|
".dark & .cm-content": {
|
|
355
|
-
caretColor:
|
|
443
|
+
caretColor: "white"
|
|
356
444
|
},
|
|
357
445
|
"& .cm-cursor": {
|
|
358
446
|
borderLeftColor: "black"
|
|
359
447
|
},
|
|
360
448
|
".dark & .cm-cursor": {
|
|
361
|
-
borderLeftColor:
|
|
449
|
+
borderLeftColor: "white"
|
|
362
450
|
},
|
|
363
451
|
".cm-placeholder": {
|
|
364
|
-
fontFamily:
|
|
452
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
365
453
|
},
|
|
366
454
|
"& .cm-scroller": {
|
|
367
|
-
fontFamily:
|
|
455
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
|
|
368
456
|
overflow: "visible"
|
|
369
457
|
},
|
|
370
458
|
"& .cm-activeLine": {
|
|
@@ -374,7 +462,7 @@ var markdownTheme = {
|
|
|
374
462
|
backgroundColor: "transparent"
|
|
375
463
|
},
|
|
376
464
|
"& .cm-ySelectionInfo": {
|
|
377
|
-
fontFamily:
|
|
465
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(","),
|
|
378
466
|
padding: "2px 4px",
|
|
379
467
|
marginBlockStart: "-4px"
|
|
380
468
|
},
|
|
@@ -397,8 +485,16 @@ var markdownTheme = {
|
|
|
397
485
|
"& .cm-yLineSelection": {
|
|
398
486
|
margin: "0"
|
|
399
487
|
},
|
|
400
|
-
|
|
401
|
-
|
|
488
|
+
".cm-link": {
|
|
489
|
+
color: "rgb(20 89 208)",
|
|
490
|
+
textDecorationLine: "underline",
|
|
491
|
+
textDecorationThickness: "1px",
|
|
492
|
+
textUnderlineOffset: "2px",
|
|
493
|
+
borderRadius: ".125rem"
|
|
494
|
+
},
|
|
495
|
+
// TODO(burdon): Document.
|
|
496
|
+
...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
497
|
+
const height = get2(tokens, [
|
|
402
498
|
"extend",
|
|
403
499
|
"fontSize",
|
|
404
500
|
fontSize,
|
|
@@ -417,7 +513,7 @@ var markdownTheme = {
|
|
|
417
513
|
return acc;
|
|
418
514
|
}, {})
|
|
419
515
|
};
|
|
420
|
-
var
|
|
516
|
+
var markdownHighlightStyle = HighlightStyle.define([
|
|
421
517
|
{
|
|
422
518
|
tag: [
|
|
423
519
|
tags.keyword,
|
|
@@ -533,17 +629,570 @@ var markdownDarkHighlighting = HighlightStyle.define([
|
|
|
533
629
|
], {
|
|
534
630
|
scope: markdownLanguage,
|
|
535
631
|
all: {
|
|
536
|
-
fontFamily:
|
|
632
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
537
633
|
}
|
|
538
634
|
});
|
|
539
635
|
|
|
540
|
-
// packages/ui/react-ui-editor/src/components/
|
|
636
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
637
|
+
var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
638
|
+
return [
|
|
639
|
+
bracketMatching2(),
|
|
640
|
+
closeBrackets2(),
|
|
641
|
+
_placeholder && placeholder2(_placeholder),
|
|
642
|
+
EditorState.allowMultipleSelections.of(true),
|
|
643
|
+
EditorView3.lineWrapping,
|
|
644
|
+
// autocompletion(),
|
|
645
|
+
crosshairCursor(),
|
|
646
|
+
dropCursor(),
|
|
647
|
+
drawSelection(),
|
|
648
|
+
highlightActiveLine(),
|
|
649
|
+
highlightActiveLineGutter(),
|
|
650
|
+
highlightSelectionMatches(),
|
|
651
|
+
highlightSpecialChars(),
|
|
652
|
+
history(),
|
|
653
|
+
indentOnInput(),
|
|
654
|
+
rectangularSelection(),
|
|
655
|
+
keymap.of([
|
|
656
|
+
...closeBracketsKeymap,
|
|
657
|
+
// ...completionKeymap,
|
|
658
|
+
...defaultKeymap,
|
|
659
|
+
...foldKeymap,
|
|
660
|
+
...historyKeymap,
|
|
661
|
+
...lintKeymap,
|
|
662
|
+
...searchKeymap,
|
|
663
|
+
indentWithTab
|
|
664
|
+
]),
|
|
665
|
+
// Main extension.
|
|
666
|
+
// https://github.com/codemirror/lang-markdown
|
|
667
|
+
markdown({
|
|
668
|
+
base: markdownLanguage2,
|
|
669
|
+
codeLanguages: languages,
|
|
670
|
+
extensions: [
|
|
671
|
+
// TODO(burdon): This seems to upgrade the parser.
|
|
672
|
+
// GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
|
|
673
|
+
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
674
|
+
// https://github.github.com/gfm
|
|
675
|
+
// GFM,
|
|
676
|
+
// Custom styling.
|
|
677
|
+
markdownTagsExtension
|
|
678
|
+
]
|
|
679
|
+
}),
|
|
680
|
+
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
681
|
+
syntaxHighlighting2(markdownHighlightStyle),
|
|
682
|
+
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
|
|
683
|
+
].filter(Boolean);
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
687
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
688
|
+
import { EditorView as EditorView4 } from "@codemirror/view";
|
|
689
|
+
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
690
|
+
import { vim } from "@replit/codemirror-vim";
|
|
691
|
+
import defaultsDeep from "lodash.defaultsdeep";
|
|
692
|
+
import get3 from "lodash.get";
|
|
693
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
|
|
694
|
+
import { isDocAccessor as isDocAccessor2 } from "@dxos/echo-schema";
|
|
695
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
696
|
+
|
|
697
|
+
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
698
|
+
import { useEffect, useMemo } from "react";
|
|
699
|
+
import { yCollab } from "y-codemirror.next";
|
|
700
|
+
import { generateName } from "@dxos/display-name";
|
|
701
|
+
import { isDocAccessor } from "@dxos/echo-schema";
|
|
702
|
+
import { getColorForValue } from "@dxos/react-ui-theme";
|
|
703
|
+
import { YText } from "@dxos/text-model";
|
|
704
|
+
|
|
705
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
706
|
+
import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
|
|
707
|
+
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
708
|
+
import * as automerge2 from "@dxos/automerge/automerge";
|
|
709
|
+
|
|
710
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
711
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
712
|
+
|
|
713
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
|
|
714
|
+
import { ChangeSet } from "@codemirror/state";
|
|
715
|
+
var amToCodemirror_default = (view, selection, target, patches) => {
|
|
716
|
+
for (const patch of patches) {
|
|
717
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
718
|
+
if (changeSpec != null) {
|
|
719
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
720
|
+
selection = selection.map(changeSet, 1);
|
|
721
|
+
view.dispatch({
|
|
722
|
+
changes: changeSet,
|
|
723
|
+
annotations: reconcileAnnotationType.of({})
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
view.dispatch({
|
|
728
|
+
selection,
|
|
729
|
+
annotations: reconcileAnnotationType.of({})
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
var handlePatch = (patch, target, state) => {
|
|
733
|
+
if (patch.action === "insert") {
|
|
734
|
+
return handleInsert(target, patch);
|
|
735
|
+
} else if (patch.action === "splice") {
|
|
736
|
+
return handleSplice(target, patch);
|
|
737
|
+
} else if (patch.action === "del") {
|
|
738
|
+
return handleDel(target, patch);
|
|
739
|
+
} else if (patch.action === "put") {
|
|
740
|
+
return handlePut(target, patch, state);
|
|
741
|
+
} else {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
var handleInsert = (target, patch) => {
|
|
746
|
+
const index = charPath(target, patch.path);
|
|
747
|
+
if (index == null) {
|
|
748
|
+
return [];
|
|
749
|
+
}
|
|
750
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
751
|
+
return [
|
|
752
|
+
{
|
|
753
|
+
from: index,
|
|
754
|
+
to: index,
|
|
755
|
+
insert: text
|
|
756
|
+
}
|
|
757
|
+
];
|
|
758
|
+
};
|
|
759
|
+
var handleSplice = (target, patch) => {
|
|
760
|
+
const index = charPath(target, patch.path);
|
|
761
|
+
if (index == null) {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
return [
|
|
765
|
+
{
|
|
766
|
+
from: index,
|
|
767
|
+
insert: patch.value
|
|
768
|
+
}
|
|
769
|
+
];
|
|
770
|
+
};
|
|
771
|
+
var handleDel = (target, patch) => {
|
|
772
|
+
const index = charPath(target, patch.path);
|
|
773
|
+
if (index == null) {
|
|
774
|
+
return [];
|
|
775
|
+
}
|
|
776
|
+
const length = patch.length || 1;
|
|
777
|
+
return [
|
|
778
|
+
{
|
|
779
|
+
from: index,
|
|
780
|
+
to: index + length
|
|
781
|
+
}
|
|
782
|
+
];
|
|
783
|
+
};
|
|
784
|
+
var handlePut = (target, patch, state) => {
|
|
785
|
+
const index = charPath(target, [
|
|
786
|
+
...patch.path,
|
|
787
|
+
0
|
|
788
|
+
]);
|
|
789
|
+
if (index == null) {
|
|
790
|
+
return [];
|
|
791
|
+
}
|
|
792
|
+
const length = state.doc.length;
|
|
793
|
+
if (typeof patch.value !== "string") {
|
|
794
|
+
return [];
|
|
795
|
+
}
|
|
796
|
+
return [
|
|
797
|
+
{
|
|
798
|
+
from: 0,
|
|
799
|
+
to: length,
|
|
800
|
+
insert: patch.value
|
|
801
|
+
}
|
|
802
|
+
];
|
|
803
|
+
};
|
|
804
|
+
var charPath = (textPath, candidatePath) => {
|
|
805
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
809
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
814
|
+
if (typeof index === "number") {
|
|
815
|
+
return index;
|
|
816
|
+
}
|
|
817
|
+
return null;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
|
|
821
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
822
|
+
var codeMirrorToAm_default = (field, handle, transactions, state) => {
|
|
823
|
+
const { lastHeads, path } = state.field(field);
|
|
824
|
+
let hasChanges = false;
|
|
825
|
+
for (const tr of transactions) {
|
|
826
|
+
tr.changes.iterChanges(() => {
|
|
827
|
+
hasChanges = true;
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
if (!hasChanges) {
|
|
831
|
+
return void 0;
|
|
832
|
+
}
|
|
833
|
+
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
834
|
+
for (const tr of transactions) {
|
|
835
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
836
|
+
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
return newHeads ?? void 0;
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
844
|
+
var PatchSemaphore = class {
|
|
845
|
+
constructor(field) {
|
|
846
|
+
this._inReconcile = false;
|
|
847
|
+
this._queue = [];
|
|
848
|
+
this.reconcile = (handle, view) => {
|
|
849
|
+
if (this._inReconcile) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
this._inReconcile = true;
|
|
853
|
+
const path = getPath(view.state, this._field);
|
|
854
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
855
|
+
let selection = view.state.selection;
|
|
856
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
857
|
+
const toInvert = transactions.slice().reverse();
|
|
858
|
+
for (const tx of toInvert) {
|
|
859
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
860
|
+
selection = selection.map(inverted);
|
|
861
|
+
view.dispatch({
|
|
862
|
+
changes: inverted,
|
|
863
|
+
annotations: reconcileAnnotationType.of(true)
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
|
|
867
|
+
if (newHeads === null || newHeads === void 0) {
|
|
868
|
+
newHeads = automerge.getHeads(handle.docSync());
|
|
869
|
+
}
|
|
870
|
+
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
871
|
+
amToCodemirror_default(view, selection, path, diff);
|
|
872
|
+
view.dispatch({
|
|
873
|
+
effects: updateHeads(newHeads),
|
|
874
|
+
annotations: reconcileAnnotationType.of({})
|
|
875
|
+
});
|
|
876
|
+
this._inReconcile = false;
|
|
877
|
+
};
|
|
878
|
+
if (field !== void 0) {
|
|
879
|
+
this._field = field;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
885
|
+
var effectType = StateEffect.define({});
|
|
886
|
+
var updateHeads = (newHeads) => effectType.of({
|
|
887
|
+
newHeads
|
|
888
|
+
});
|
|
889
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
890
|
+
var getPath = (state, field) => state.field(field).path;
|
|
891
|
+
var semaphoreFacet = Facet.define({
|
|
892
|
+
combine: (values) => values.at(-1)
|
|
893
|
+
});
|
|
894
|
+
var automergePlugin = (handle, path) => {
|
|
895
|
+
const stateField = StateField3.define({
|
|
896
|
+
create: () => ({
|
|
897
|
+
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
898
|
+
unreconciledTransactions: [],
|
|
899
|
+
path: path.slice()
|
|
900
|
+
}),
|
|
901
|
+
update: (value, tr) => {
|
|
902
|
+
const result = {
|
|
903
|
+
lastHeads: value.lastHeads,
|
|
904
|
+
unreconciledTransactions: value.unreconciledTransactions.slice(),
|
|
905
|
+
path: path.slice()
|
|
906
|
+
};
|
|
907
|
+
let clearUnreconciled = false;
|
|
908
|
+
for (const effect of tr.effects) {
|
|
909
|
+
if (effect.is(effectType)) {
|
|
910
|
+
result.lastHeads = effect.value.newHeads;
|
|
911
|
+
clearUnreconciled = true;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (clearUnreconciled) {
|
|
915
|
+
result.unreconciledTransactions = [];
|
|
916
|
+
} else {
|
|
917
|
+
if (!isReconcileTx(tr)) {
|
|
918
|
+
result.unreconciledTransactions.push(tr);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return result;
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
const semaphore = new PatchSemaphore(stateField);
|
|
925
|
+
const viewPlugin = ViewPlugin2.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
926
|
+
constructor(view) {
|
|
927
|
+
this._handleChange = () => {
|
|
928
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
929
|
+
};
|
|
930
|
+
this._view = view;
|
|
931
|
+
handle.addListener("change", this._handleChange);
|
|
932
|
+
}
|
|
933
|
+
update(update2) {
|
|
934
|
+
if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
|
|
935
|
+
queueMicrotask(() => {
|
|
936
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
destroy() {
|
|
941
|
+
handle.addListener("change", this._handleChange);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
return {
|
|
945
|
+
extension: [
|
|
946
|
+
stateField,
|
|
947
|
+
semaphoreFacet.of(semaphore),
|
|
948
|
+
viewPlugin
|
|
949
|
+
]
|
|
950
|
+
};
|
|
951
|
+
};
|
|
952
|
+
var reconcileAnnotationType = Annotation.define();
|
|
953
|
+
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
954
|
+
|
|
955
|
+
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
956
|
+
var useCollaboration = (model, themeMode) => {
|
|
957
|
+
const { provider, peer, content } = model;
|
|
958
|
+
const extension = useMemo(() => {
|
|
959
|
+
if (content instanceof YText) {
|
|
960
|
+
return yCollab(content, provider?.awareness);
|
|
961
|
+
} else if (isDocAccessor(content)) {
|
|
962
|
+
return automergePlugin(content.handle, content.path);
|
|
963
|
+
} else {
|
|
964
|
+
return void 0;
|
|
965
|
+
}
|
|
966
|
+
}, []);
|
|
967
|
+
useEffect(() => {
|
|
968
|
+
if (provider && peer) {
|
|
969
|
+
provider.awareness.setLocalStateField("user", {
|
|
970
|
+
name: peer.name ?? generateName(peer.id),
|
|
971
|
+
color: getColorForValue({
|
|
972
|
+
value: peer.id,
|
|
973
|
+
type: "color"
|
|
974
|
+
}),
|
|
975
|
+
colorLight: getColorForValue({
|
|
976
|
+
value: peer.id,
|
|
977
|
+
themeMode,
|
|
978
|
+
type: "highlight"
|
|
979
|
+
})
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
}, [
|
|
983
|
+
provider,
|
|
984
|
+
peer,
|
|
985
|
+
themeMode
|
|
986
|
+
]);
|
|
987
|
+
return extension;
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
991
|
+
import { useMemo as useMemo2 } from "react";
|
|
992
|
+
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
993
|
+
|
|
994
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
995
|
+
import * as random from "lib0/random";
|
|
996
|
+
var cursorColors = [
|
|
997
|
+
{
|
|
998
|
+
color: "#30bced",
|
|
999
|
+
light: "#30bced33"
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
color: "#6eeb83",
|
|
1003
|
+
light: "#6eeb8333"
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
color: "#ffbc42",
|
|
1007
|
+
light: "#ffbc4233"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
color: "#ecd444",
|
|
1011
|
+
light: "#ecd44433"
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
color: "#ee6352",
|
|
1015
|
+
light: "#ee635233"
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
color: "#9ac2c9",
|
|
1019
|
+
light: "#9ac2c933"
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
color: "#8acb88",
|
|
1023
|
+
light: "#8acb8833"
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
color: "#1be7ff",
|
|
1027
|
+
light: "#1be7ff33"
|
|
1028
|
+
}
|
|
1029
|
+
];
|
|
1030
|
+
var cursorColor = cursorColors[random.uint32() % cursorColors.length];
|
|
1031
|
+
|
|
1032
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
|
|
1033
|
+
import * as decoding from "lib0/decoding";
|
|
1034
|
+
import * as encoding from "lib0/encoding";
|
|
1035
|
+
import { Observable } from "lib0/observable";
|
|
1036
|
+
import * as awarenessProtocol from "y-protocols/awareness";
|
|
1037
|
+
import { log } from "@dxos/log";
|
|
1038
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
1039
|
+
var messageAwareness = 1;
|
|
1040
|
+
var messageQueryAwareness = 3;
|
|
1041
|
+
var SpaceAwarenessProvider = class extends Observable {
|
|
1042
|
+
constructor({ space, doc, channel, awareness }) {
|
|
1043
|
+
super();
|
|
1044
|
+
this._space = space;
|
|
1045
|
+
this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
|
|
1046
|
+
this._channel = channel;
|
|
1047
|
+
this._clientId = doc.clientID;
|
|
1048
|
+
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
1049
|
+
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
1050
|
+
if (typeof window !== "undefined") {
|
|
1051
|
+
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
1052
|
+
} else if (typeof process !== "undefined") {
|
|
1053
|
+
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
1054
|
+
}
|
|
1055
|
+
const encoderAwarenessQuery = encoding.createEncoder();
|
|
1056
|
+
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
1057
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
1058
|
+
const encoderAwarenessState = encoding.createEncoder();
|
|
1059
|
+
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
1060
|
+
encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
|
|
1061
|
+
this._clientId
|
|
1062
|
+
]));
|
|
1063
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
1064
|
+
}
|
|
1065
|
+
get awareness() {
|
|
1066
|
+
return this._awareness;
|
|
1067
|
+
}
|
|
1068
|
+
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
1069
|
+
log("awareness update", {
|
|
1070
|
+
added,
|
|
1071
|
+
updated,
|
|
1072
|
+
removed,
|
|
1073
|
+
origin
|
|
1074
|
+
}, {
|
|
1075
|
+
F: __dxlog_file,
|
|
1076
|
+
L: 67,
|
|
1077
|
+
S: this,
|
|
1078
|
+
C: (f, a) => f(...a)
|
|
1079
|
+
});
|
|
1080
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
1081
|
+
const encoderAwareness = encoding.createEncoder();
|
|
1082
|
+
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
1083
|
+
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
1084
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
1085
|
+
}
|
|
1086
|
+
_handleSpaceMessage({ payload }) {
|
|
1087
|
+
log("space message", payload, {
|
|
1088
|
+
F: __dxlog_file,
|
|
1089
|
+
L: 79,
|
|
1090
|
+
S: this,
|
|
1091
|
+
C: (f, a) => f(...a)
|
|
1092
|
+
});
|
|
1093
|
+
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
1094
|
+
const encoder = this._readMessage(data);
|
|
1095
|
+
if (encoder) {
|
|
1096
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
_readMessage(message) {
|
|
1100
|
+
const decoder = decoding.createDecoder(message);
|
|
1101
|
+
const encoder = encoding.createEncoder();
|
|
1102
|
+
const messageType = decoding.readVarUint(decoder);
|
|
1103
|
+
let sendReply = false;
|
|
1104
|
+
switch (messageType) {
|
|
1105
|
+
case messageAwareness: {
|
|
1106
|
+
awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
case messageQueryAwareness: {
|
|
1110
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
1111
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
1112
|
+
sendReply = true;
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
default: {
|
|
1116
|
+
console.error("Invalid message:", messageType);
|
|
1117
|
+
return encoder;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
if (!sendReply) {
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
return encoder;
|
|
1124
|
+
}
|
|
1125
|
+
_handleBeforeUnload() {
|
|
1126
|
+
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
1127
|
+
this._clientId
|
|
1128
|
+
], "window unload");
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1133
|
+
var useTextModel = ({ identity, space, text }) => {
|
|
1134
|
+
const provider = useMemo2(() => {
|
|
1135
|
+
if (isActualAutomergeObject(text)) {
|
|
1136
|
+
return void 0;
|
|
1137
|
+
}
|
|
1138
|
+
if (!space || !text?.doc) {
|
|
1139
|
+
return void 0;
|
|
1140
|
+
}
|
|
1141
|
+
return new SpaceAwarenessProvider({
|
|
1142
|
+
space,
|
|
1143
|
+
doc: text.doc,
|
|
1144
|
+
channel: `yjs.awareness.${text.id}`
|
|
1145
|
+
});
|
|
1146
|
+
}, [
|
|
1147
|
+
identity,
|
|
1148
|
+
space,
|
|
1149
|
+
text,
|
|
1150
|
+
text?.doc
|
|
1151
|
+
]);
|
|
1152
|
+
const content = useMemo2(() => {
|
|
1153
|
+
if (isActualAutomergeObject(text)) {
|
|
1154
|
+
const obj = text;
|
|
1155
|
+
return getRawDoc(obj, [
|
|
1156
|
+
obj.field
|
|
1157
|
+
]);
|
|
1158
|
+
} else {
|
|
1159
|
+
return text?.content;
|
|
1160
|
+
}
|
|
1161
|
+
}, [
|
|
1162
|
+
text
|
|
1163
|
+
]);
|
|
1164
|
+
if (isActualAutomergeObject(text)) {
|
|
1165
|
+
const obj = text;
|
|
1166
|
+
return {
|
|
1167
|
+
id: obj.id,
|
|
1168
|
+
content,
|
|
1169
|
+
provider: void 0,
|
|
1170
|
+
peer: identity ? {
|
|
1171
|
+
id: identity.identityKey.toHex(),
|
|
1172
|
+
name: identity.profile?.displayName
|
|
1173
|
+
} : void 0
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
if (!text?.doc || !text?.content) {
|
|
1177
|
+
return void 0;
|
|
1178
|
+
}
|
|
1179
|
+
return {
|
|
1180
|
+
id: text.doc.guid,
|
|
1181
|
+
content: text.content,
|
|
1182
|
+
provider,
|
|
1183
|
+
peer: identity ? {
|
|
1184
|
+
id: identity.identityKey.toHex(),
|
|
1185
|
+
name: identity.profile?.displayName
|
|
1186
|
+
} : void 0
|
|
1187
|
+
};
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
541
1191
|
var EditorModes = [
|
|
542
1192
|
"default",
|
|
543
1193
|
"vim"
|
|
544
1194
|
];
|
|
545
|
-
var
|
|
546
|
-
const { id, content, provider, peer } = model ?? {};
|
|
1195
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots, editorMode }, forwardedRef) => {
|
|
547
1196
|
const { themeMode } = useThemeContext();
|
|
548
1197
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
549
1198
|
tabBehavior: "limited"
|
|
@@ -555,110 +1204,33 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
555
1204
|
editor: parent,
|
|
556
1205
|
state,
|
|
557
1206
|
view
|
|
558
|
-
}));
|
|
559
|
-
const listenChangesExtension = useMemo2(() => StateField.define({
|
|
560
|
-
create: () => null,
|
|
561
|
-
update: (_value, transaction) => {
|
|
562
|
-
if (transaction.docChanged && onChange) {
|
|
563
|
-
onChange(transaction.newDoc);
|
|
564
|
-
}
|
|
565
|
-
return null;
|
|
566
|
-
}
|
|
567
1207
|
}), [
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (provider && peer) {
|
|
572
|
-
provider.awareness.setLocalStateField("user", {
|
|
573
|
-
name: peer.name ?? generateName(peer.id),
|
|
574
|
-
color: getColorForValue({
|
|
575
|
-
value: peer.id,
|
|
576
|
-
type: "color"
|
|
577
|
-
}),
|
|
578
|
-
colorLight: getColorForValue({
|
|
579
|
-
value: peer.id,
|
|
580
|
-
themeMode,
|
|
581
|
-
type: "highlight"
|
|
582
|
-
})
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
}, [
|
|
586
|
-
provider,
|
|
587
|
-
peer,
|
|
588
|
-
themeMode
|
|
1208
|
+
view,
|
|
1209
|
+
state,
|
|
1210
|
+
parent
|
|
589
1211
|
]);
|
|
590
|
-
|
|
1212
|
+
const collaboration = useCollaboration(model, themeMode);
|
|
1213
|
+
useEffect2(() => {
|
|
591
1214
|
if (!parent) {
|
|
592
1215
|
return;
|
|
593
1216
|
}
|
|
594
|
-
const
|
|
595
|
-
|
|
1217
|
+
const initialText = isDocAccessor2(model.content) ? get3(model.content.handle.docSync(), model.content.path) : model.content?.toString();
|
|
1218
|
+
const state2 = EditorState2.create({
|
|
1219
|
+
doc: initialText,
|
|
596
1220
|
extensions: [
|
|
597
|
-
//
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
highlightActiveLineGutter(),
|
|
604
|
-
highlightSpecialChars(),
|
|
605
|
-
history(),
|
|
606
|
-
drawSelection(),
|
|
607
|
-
dropCursor(),
|
|
608
|
-
EditorState.allowMultipleSelections.of(true),
|
|
609
|
-
indentOnInput(),
|
|
610
|
-
syntaxHighlighting(defaultHighlightStyle, {
|
|
611
|
-
fallback: true
|
|
612
|
-
}),
|
|
613
|
-
bracketMatching(),
|
|
614
|
-
closeBrackets(),
|
|
615
|
-
autocompletion(),
|
|
616
|
-
rectangularSelection(),
|
|
617
|
-
crosshairCursor(),
|
|
618
|
-
highlightActiveLine(),
|
|
619
|
-
highlightSelectionMatches(),
|
|
620
|
-
placeholder(slots.editor?.placeholder ?? ""),
|
|
621
|
-
keymap.of([
|
|
622
|
-
...closeBracketsKeymap,
|
|
623
|
-
...defaultKeymap,
|
|
624
|
-
...searchKeymap,
|
|
625
|
-
...historyKeymap,
|
|
626
|
-
...foldKeymap,
|
|
627
|
-
...completionKeymap,
|
|
628
|
-
...lintKeymap,
|
|
629
|
-
indentWithTab
|
|
630
|
-
]),
|
|
631
|
-
EditorView.lineWrapping,
|
|
632
|
-
// Themes.
|
|
633
|
-
markdown({
|
|
634
|
-
base: markdownLanguage2,
|
|
635
|
-
codeLanguages: languages,
|
|
636
|
-
extensions: [
|
|
637
|
-
markdownTagsExtension
|
|
638
|
-
]
|
|
639
|
-
}),
|
|
640
|
-
EditorView.theme({
|
|
641
|
-
...markdownTheme,
|
|
642
|
-
...slots.editor?.markdownTheme
|
|
643
|
-
}),
|
|
644
|
-
...themeMode === "dark" ? [
|
|
645
|
-
syntaxHighlighting(oneDarkHighlightStyle)
|
|
646
|
-
] : [
|
|
647
|
-
syntaxHighlighting(defaultHighlightStyle)
|
|
648
|
-
],
|
|
649
|
-
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
650
|
-
syntaxHighlighting(markdownDarkHighlighting),
|
|
651
|
-
// Replication and awareness (incl. remote selection).
|
|
652
|
-
...content instanceof YText ? [
|
|
653
|
-
yCollab(content, provider?.awareness)
|
|
654
|
-
] : [],
|
|
1221
|
+
// TODO(burdon): Factor out VIM mode?
|
|
1222
|
+
editorMode === "vim" && vim(),
|
|
1223
|
+
// Replication.
|
|
1224
|
+
collaboration,
|
|
1225
|
+
// Theme.
|
|
1226
|
+
slots?.editor?.theme && EditorView4.theme(slots?.editor?.theme),
|
|
655
1227
|
// Custom.
|
|
656
1228
|
...extensions
|
|
657
|
-
]
|
|
1229
|
+
].filter(Boolean)
|
|
658
1230
|
});
|
|
659
1231
|
setState(state2);
|
|
660
1232
|
view?.destroy();
|
|
661
|
-
setView(new
|
|
1233
|
+
setView(new EditorView4({
|
|
662
1234
|
state: state2,
|
|
663
1235
|
parent
|
|
664
1236
|
}));
|
|
@@ -669,8 +1241,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
669
1241
|
};
|
|
670
1242
|
}, [
|
|
671
1243
|
parent,
|
|
672
|
-
content,
|
|
673
|
-
provider?.awareness,
|
|
1244
|
+
model.content,
|
|
674
1245
|
themeMode,
|
|
675
1246
|
editorMode
|
|
676
1247
|
]);
|
|
@@ -691,461 +1262,89 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
691
1262
|
editorMode
|
|
692
1263
|
]);
|
|
693
1264
|
return /* @__PURE__ */ React.createElement("div", {
|
|
694
|
-
|
|
1265
|
+
key: model.id,
|
|
695
1266
|
ref: setParent,
|
|
696
|
-
|
|
697
|
-
...slots
|
|
698
|
-
...editorMode !== "vim"
|
|
1267
|
+
tabIndex: 0,
|
|
1268
|
+
...slots?.root,
|
|
1269
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
699
1270
|
onKeyUp: handleKeyUp
|
|
700
1271
|
});
|
|
701
1272
|
});
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
import Placeholder from "@tiptap/extension-placeholder";
|
|
710
|
-
import { EditorContent, useEditor as useNaturalEditor } from "@tiptap/react";
|
|
711
|
-
import StarterKit from "@tiptap/starter-kit";
|
|
712
|
-
import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
|
|
713
|
-
import { generateName as generateName2 } from "@dxos/display-name";
|
|
714
|
-
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
715
|
-
var useEditor = ({ model, placeholder: placeholder3 = "Enter text\u2026", slots = {} }) => {
|
|
716
|
-
const extensions = useMemo3(() => [
|
|
717
|
-
StarterKit.configure({
|
|
718
|
-
// Extensions
|
|
719
|
-
history: false,
|
|
720
|
-
// Nodes
|
|
721
|
-
blockquote: {
|
|
722
|
-
HTMLAttributes: {
|
|
723
|
-
class: blockquote
|
|
724
|
-
}
|
|
725
|
-
},
|
|
726
|
-
bulletList: {
|
|
727
|
-
HTMLAttributes: {
|
|
728
|
-
class: unorderedList
|
|
729
|
-
}
|
|
730
|
-
},
|
|
731
|
-
codeBlock: {
|
|
732
|
-
HTMLAttributes: {
|
|
733
|
-
class: codeBlock
|
|
734
|
-
}
|
|
735
|
-
},
|
|
736
|
-
heading: false,
|
|
737
|
-
horizontalRule: {
|
|
738
|
-
HTMLAttributes: {
|
|
739
|
-
class: horizontalRule
|
|
740
|
-
}
|
|
741
|
-
},
|
|
742
|
-
listItem: false,
|
|
743
|
-
orderedList: {
|
|
744
|
-
HTMLAttributes: {
|
|
745
|
-
class: orderedList
|
|
746
|
-
}
|
|
747
|
-
},
|
|
748
|
-
paragraph: {
|
|
749
|
-
HTMLAttributes: {
|
|
750
|
-
class: paragraph
|
|
751
|
-
}
|
|
752
|
-
},
|
|
753
|
-
// Marks
|
|
754
|
-
bold: {
|
|
755
|
-
HTMLAttributes: {
|
|
756
|
-
class: bold
|
|
757
|
-
}
|
|
758
|
-
},
|
|
759
|
-
code: {
|
|
760
|
-
HTMLAttributes: {
|
|
761
|
-
class: codeWithoutMarks
|
|
762
|
-
}
|
|
763
|
-
},
|
|
764
|
-
italic: {
|
|
765
|
-
HTMLAttributes: {
|
|
766
|
-
class: italic
|
|
767
|
-
}
|
|
768
|
-
},
|
|
769
|
-
strike: {
|
|
770
|
-
HTMLAttributes: {
|
|
771
|
-
class: strikethrough
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}),
|
|
775
|
-
Heading.extend({
|
|
776
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
777
|
-
const hasLevel = this.options.levels.includes(node.attrs.level);
|
|
778
|
-
const level = hasLevel ? node.attrs.level : this.options.levels[0];
|
|
779
|
-
return [
|
|
780
|
-
`h${level}`,
|
|
781
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
782
|
-
class: heading[level]
|
|
783
|
-
}),
|
|
784
|
-
0
|
|
785
|
-
];
|
|
786
|
-
}
|
|
787
|
-
}),
|
|
788
|
-
ListItem.extend({
|
|
789
|
-
renderHTML: ({ HTMLAttributes }) => [
|
|
790
|
-
"li",
|
|
791
|
-
mergeAttributes(HTMLAttributes, {
|
|
792
|
-
marker: "\u2022 ",
|
|
793
|
-
class: listItem
|
|
794
|
-
}),
|
|
795
|
-
[
|
|
796
|
-
"div",
|
|
797
|
-
{
|
|
798
|
-
role: "none"
|
|
799
|
-
},
|
|
800
|
-
0
|
|
801
|
-
]
|
|
802
|
-
]
|
|
803
|
-
}),
|
|
804
|
-
// https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration
|
|
805
|
-
...model && typeof model.content !== "string" ? [
|
|
806
|
-
Collaboration.configure({
|
|
807
|
-
fragment: model.content
|
|
808
|
-
})
|
|
809
|
-
] : [],
|
|
810
|
-
...model?.provider ? [
|
|
811
|
-
CollaborationCursor.configure({
|
|
812
|
-
provider: model.provider,
|
|
813
|
-
user: model.peer && {
|
|
814
|
-
name: model.peer.name ?? generateName2(model.peer.id),
|
|
815
|
-
color: cursorColor.color
|
|
816
|
-
}
|
|
817
|
-
})
|
|
818
|
-
] : [],
|
|
819
|
-
Placeholder.configure({
|
|
820
|
-
placeholder: placeholder3,
|
|
821
|
-
emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
|
|
1273
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
|
|
1274
|
+
const { themeMode } = useThemeContext();
|
|
1275
|
+
const extensions = [
|
|
1276
|
+
..._extensions ?? [],
|
|
1277
|
+
basicBundle({
|
|
1278
|
+
themeMode,
|
|
1279
|
+
placeholder: slots?.editor?.placeholder
|
|
822
1280
|
})
|
|
823
|
-
]
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return useNaturalEditor({
|
|
1281
|
+
];
|
|
1282
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1283
|
+
ref: forwardedRef,
|
|
827
1284
|
extensions,
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
|
|
832
|
-
tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
|
|
1285
|
+
slots: defaultsDeep({}, slots, {
|
|
1286
|
+
editor: {
|
|
1287
|
+
theme: basicTheme
|
|
833
1288
|
}
|
|
834
|
-
}
|
|
835
|
-
}, [
|
|
836
|
-
extensions
|
|
837
|
-
]);
|
|
838
|
-
};
|
|
839
|
-
var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
|
|
840
|
-
const editor = useEditor(props);
|
|
841
|
-
useImperativeHandle2(ref, () => editor, [
|
|
842
|
-
editor
|
|
843
|
-
]);
|
|
844
|
-
return /* @__PURE__ */ React2.createElement(EditorContent, {
|
|
845
|
-
...props.slots?.root,
|
|
846
|
-
editor
|
|
847
|
-
});
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
// packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
|
|
851
|
-
var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...params }, forwardedRef) => {
|
|
852
|
-
const model = useTextModel(params);
|
|
853
|
-
if (model?.content instanceof YXmlFragment) {
|
|
854
|
-
return /* @__PURE__ */ React3.createElement(RichTextEditor, {
|
|
855
|
-
ref: forwardedRef,
|
|
856
|
-
model,
|
|
857
|
-
slots
|
|
858
|
-
});
|
|
859
|
-
} else {
|
|
860
|
-
return /* @__PURE__ */ React3.createElement(MarkdownEditor, {
|
|
861
|
-
ref: forwardedRef,
|
|
862
|
-
model,
|
|
863
|
-
slots
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
}));
|
|
867
|
-
|
|
868
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
869
|
-
import { tags as tags2 } from "@lezer/highlight";
|
|
870
|
-
|
|
871
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
872
|
-
import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
|
|
873
|
-
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
874
|
-
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
875
|
-
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
876
|
-
import { EditorView as EditorView2, placeholder as placeholder2 } from "@codemirror/view";
|
|
877
|
-
import React4, { forwardRef as forwardRef4, useEffect as useEffect2, useImperativeHandle as useImperativeHandle3, useState as useState2, useCallback as useCallback2 } from "react";
|
|
878
|
-
import { yCollab as yCollab2 } from "y-codemirror.next";
|
|
879
|
-
import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
|
|
880
|
-
import { YText as YText2 } from "@dxos/text-model";
|
|
881
|
-
|
|
882
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/theme.ts
|
|
883
|
-
import get2 from "lodash.get";
|
|
884
|
-
import { tailwindConfig as tailwindConfig2 } from "@dxos/react-ui-theme";
|
|
885
|
-
var tokens2 = tailwindConfig2({}).theme;
|
|
886
|
-
var defaultStyles = {
|
|
887
|
-
"&.cm-focused": {
|
|
888
|
-
outline: "none"
|
|
889
|
-
},
|
|
890
|
-
".cm-placeholder": {
|
|
891
|
-
fontFamily: get2(tokens2, "fontFamily.body", []).join(",")
|
|
892
|
-
},
|
|
893
|
-
"& .cm-scroller": {
|
|
894
|
-
fontFamily: get2(tokens2, "fontFamily.body", []).join(","),
|
|
895
|
-
overflow: "visible"
|
|
896
|
-
},
|
|
897
|
-
".cm-tooltip": {
|
|
898
|
-
backgroundColor: "transparent",
|
|
899
|
-
border: "none"
|
|
900
|
-
}
|
|
901
|
-
};
|
|
902
|
-
|
|
903
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
904
|
-
var TextEditor = /* @__PURE__ */ forwardRef4(({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
|
|
905
|
-
const { id, content } = model ?? {};
|
|
906
|
-
const { themeMode } = useThemeContext2();
|
|
907
|
-
const [parent, setParent] = useState2(null);
|
|
908
|
-
const [state, setState] = useState2();
|
|
909
|
-
const [view, setView] = useState2();
|
|
910
|
-
useImperativeHandle3(forwardedRef, () => {
|
|
911
|
-
return {
|
|
912
|
-
editor: parent,
|
|
913
|
-
state,
|
|
914
|
-
view
|
|
915
|
-
};
|
|
916
|
-
}, [
|
|
917
|
-
view
|
|
918
|
-
]);
|
|
919
|
-
useEffect2(() => {
|
|
920
|
-
if (!parent) {
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
view?.destroy();
|
|
924
|
-
const state2 = EditorState2.create({
|
|
925
|
-
doc: content?.toString(),
|
|
926
|
-
extensions: [
|
|
927
|
-
bracketMatching2(),
|
|
928
|
-
closeBrackets2(),
|
|
929
|
-
placeholder2(slots.editor?.placeholder ?? ""),
|
|
930
|
-
EditorView2.lineWrapping,
|
|
931
|
-
// Themes.
|
|
932
|
-
EditorView2.theme(theme),
|
|
933
|
-
...themeMode === "dark" ? [
|
|
934
|
-
syntaxHighlighting2(oneDarkHighlightStyle2)
|
|
935
|
-
] : [
|
|
936
|
-
syntaxHighlighting2(defaultHighlightStyle2)
|
|
937
|
-
],
|
|
938
|
-
// Replication.
|
|
939
|
-
...content instanceof YText2 ? [
|
|
940
|
-
yCollab2(content, void 0)
|
|
941
|
-
] : [],
|
|
942
|
-
// Custom.
|
|
943
|
-
...extensions
|
|
944
|
-
]
|
|
945
|
-
});
|
|
946
|
-
setState(state2);
|
|
947
|
-
setView(new EditorView2({
|
|
948
|
-
state: state2,
|
|
949
|
-
parent
|
|
950
|
-
}));
|
|
951
|
-
return () => {
|
|
952
|
-
view?.destroy();
|
|
953
|
-
setView(void 0);
|
|
954
|
-
setState(void 0);
|
|
955
|
-
};
|
|
956
|
-
}, [
|
|
957
|
-
parent,
|
|
958
|
-
content,
|
|
959
|
-
themeMode
|
|
960
|
-
]);
|
|
961
|
-
const handleKeyDown = useCallback2((event) => {
|
|
962
|
-
if (view) {
|
|
963
|
-
const { head, from, to } = view.state.selection.ranges[0];
|
|
964
|
-
const { number } = view.state.doc.lineAt(head);
|
|
965
|
-
const after = view.state.sliceDoc(from);
|
|
966
|
-
onKeyDown?.(event, {
|
|
967
|
-
from,
|
|
968
|
-
to,
|
|
969
|
-
line: number,
|
|
970
|
-
lines: view.state.doc.lines,
|
|
971
|
-
after
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
}, [
|
|
975
|
-
view
|
|
976
|
-
]);
|
|
977
|
-
return /* @__PURE__ */ React4.createElement("div", {
|
|
978
|
-
key: id,
|
|
979
|
-
ref: setParent,
|
|
980
|
-
...slots.root,
|
|
981
|
-
onKeyDown: handleKeyDown,
|
|
1289
|
+
}),
|
|
982
1290
|
...props
|
|
983
1291
|
});
|
|
984
1292
|
});
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
end: from + 1 + label.length
|
|
1000
|
-
};
|
|
1001
|
-
const p2 = {
|
|
1002
|
-
start: p1.end,
|
|
1003
|
-
end: p1.end + 1
|
|
1004
|
-
};
|
|
1005
|
-
const p3 = {
|
|
1006
|
-
start: p1.end + 1,
|
|
1007
|
-
end: p1.end + 1 + url.length + 2
|
|
1008
|
-
};
|
|
1009
|
-
add(p0.start, p0.end, Decoration.mark({
|
|
1010
|
-
class: "cm-hyperlink-bracket"
|
|
1011
|
-
}));
|
|
1012
|
-
add(p1.start, p1.end, Decoration.mark({
|
|
1013
|
-
class: "cm-hyperlink-label"
|
|
1014
|
-
}));
|
|
1015
|
-
add(p2.start, p2.end, Decoration.mark({
|
|
1016
|
-
class: "cm-hyperlink-bracket"
|
|
1017
|
-
}));
|
|
1018
|
-
add(p3.start, p3.end, Decoration.mark({
|
|
1019
|
-
class: "cm-hyperlink-url",
|
|
1020
|
-
__attributes: {
|
|
1021
|
-
style: "display: none"
|
|
1022
|
-
}
|
|
1023
|
-
}));
|
|
1024
|
-
}
|
|
1025
|
-
});
|
|
1026
|
-
function hyperLinkExtension() {
|
|
1027
|
-
return ViewPlugin.fromClass(class HyperLinkView {
|
|
1028
|
-
constructor(view) {
|
|
1029
|
-
this.decorator = linkDecorator();
|
|
1030
|
-
this.decorations = this.decorator.createDeco(view);
|
|
1031
|
-
}
|
|
1032
|
-
update(update) {
|
|
1033
|
-
if (update.docChanged || update.viewportChanged) {
|
|
1034
|
-
this.decorations = this.decorator.updateDeco(update, this.decorations);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}, {
|
|
1038
|
-
decorations: (view) => view.decorations,
|
|
1039
|
-
eventHandlers: {
|
|
1040
|
-
// TODO(burdon): CLick to expand link.
|
|
1041
|
-
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
1042
|
-
mousedown: function(event, view) {
|
|
1043
|
-
const pos = view.posAtDOM(event.target);
|
|
1044
|
-
if (pos !== 0) {
|
|
1045
|
-
console.log(this);
|
|
1046
|
-
}
|
|
1047
|
-
},
|
|
1048
|
-
// TODO(burdon): Hide when cursor moves away.
|
|
1049
|
-
keydown: (event, view) => {
|
|
1050
|
-
view.update([]);
|
|
1293
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
|
|
1294
|
+
const { themeMode } = useThemeContext();
|
|
1295
|
+
const extensions = [
|
|
1296
|
+
..._extensions ?? [],
|
|
1297
|
+
markdownBundle({
|
|
1298
|
+
themeMode
|
|
1299
|
+
})
|
|
1300
|
+
];
|
|
1301
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1302
|
+
ref: forwardedRef,
|
|
1303
|
+
extensions,
|
|
1304
|
+
slots: defaultsDeep({}, slots, {
|
|
1305
|
+
editor: {
|
|
1306
|
+
theme: markdownTheme
|
|
1051
1307
|
}
|
|
1052
|
-
}
|
|
1308
|
+
}),
|
|
1309
|
+
...props
|
|
1053
1310
|
});
|
|
1054
|
-
}
|
|
1055
|
-
var hyperLinkStyle = EditorView3.baseTheme({
|
|
1056
|
-
".cm-hyperlink-label": {
|
|
1057
|
-
cursor: "pointer",
|
|
1058
|
-
textDecoration: "underline"
|
|
1059
|
-
},
|
|
1060
|
-
".cm-hyperlink-bracket": {
|
|
1061
|
-
opacity: 0.2
|
|
1062
|
-
},
|
|
1063
|
-
".cm-hyperlink-url": {
|
|
1064
|
-
opacity: 0.5,
|
|
1065
|
-
fontFamily: "monospace"
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1068
|
-
var hyperlink = [
|
|
1069
|
-
hyperLinkExtension(),
|
|
1070
|
-
hyperLinkStyle
|
|
1071
|
-
];
|
|
1072
|
-
|
|
1073
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkTooltip.tsx
|
|
1074
|
-
import { hoverTooltip } from "@codemirror/view";
|
|
1075
|
-
import { ArrowCircleUp } from "@phosphor-icons/react";
|
|
1076
|
-
import React5, { StrictMode } from "react";
|
|
1077
|
-
import { createRoot } from "react-dom/client";
|
|
1078
|
-
import { getSize, mx as mx3, tooltipContent } from "@dxos/react-ui-theme";
|
|
1079
|
-
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1080
|
-
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp2) => hoverTooltip((view, pos) => {
|
|
1081
|
-
const { from, text } = view.state.doc.lineAt(pos);
|
|
1082
|
-
const p = pos - from;
|
|
1083
|
-
let idx = 0;
|
|
1084
|
-
let match;
|
|
1085
|
-
do {
|
|
1086
|
-
match = text.substring(idx).match(regexp);
|
|
1087
|
-
if (!match) {
|
|
1088
|
-
match = void 0;
|
|
1089
|
-
break;
|
|
1090
|
-
}
|
|
1091
|
-
idx = text.indexOf(match[0]);
|
|
1092
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1093
|
-
break;
|
|
1094
|
-
}
|
|
1095
|
-
idx += match[0].length;
|
|
1096
|
-
} while (true);
|
|
1097
|
-
if (!match) {
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1100
|
-
const [, , url] = match ?? [];
|
|
1101
|
-
return {
|
|
1102
|
-
pos: idx + from,
|
|
1103
|
-
end: idx + from + match[0].length,
|
|
1104
|
-
above: true,
|
|
1105
|
-
create: () => {
|
|
1106
|
-
const el = document.createElement("a");
|
|
1107
|
-
el.innerText = "_";
|
|
1108
|
-
el.className = tooltipContent({}, "mb-2 p-1");
|
|
1109
|
-
el.setAttribute("target", "_blank");
|
|
1110
|
-
el.setAttribute("href", url);
|
|
1111
|
-
onHover(el, url);
|
|
1112
|
-
return {
|
|
1113
|
-
dom: el
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
};
|
|
1117
1311
|
});
|
|
1118
|
-
var defaultHyperLinkTooltip = createHyperlinkTooltip((el, url) => {
|
|
1119
|
-
const web = new URL(url);
|
|
1120
|
-
createRoot(el).render(/* @__PURE__ */ React5.createElement(StrictMode, null, /* @__PURE__ */ React5.createElement("div", {
|
|
1121
|
-
className: "flex gap-1 items-center"
|
|
1122
|
-
}, /* @__PURE__ */ React5.createElement(ArrowCircleUp, {
|
|
1123
|
-
className: mx3(getSize(6), "text-blue-500")
|
|
1124
|
-
}), /* @__PURE__ */ React5.createElement("p", {
|
|
1125
|
-
className: "pr-1"
|
|
1126
|
-
}, web.origin))));
|
|
1127
|
-
});
|
|
1128
|
-
|
|
1129
|
-
// packages/ui/react-ui-editor/src/index.ts
|
|
1130
|
-
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
1131
|
-
import { Doc, YText as YText3, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
|
|
1132
1312
|
export {
|
|
1313
|
+
BaseTextEditor,
|
|
1133
1314
|
Doc,
|
|
1134
|
-
Editor,
|
|
1135
1315
|
EditorModes,
|
|
1136
1316
|
MarkdownEditor,
|
|
1137
|
-
|
|
1317
|
+
SpaceAwarenessProvider,
|
|
1138
1318
|
TextEditor,
|
|
1139
1319
|
TextKind,
|
|
1140
|
-
|
|
1141
|
-
|
|
1320
|
+
YText2 as YText,
|
|
1321
|
+
YXmlFragment,
|
|
1322
|
+
basicBundle,
|
|
1323
|
+
basicTheme,
|
|
1324
|
+
chalky,
|
|
1325
|
+
coral,
|
|
1142
1326
|
createHyperlinkTooltip,
|
|
1143
|
-
|
|
1144
|
-
|
|
1327
|
+
cursorColor,
|
|
1328
|
+
cyan,
|
|
1329
|
+
highlightBackground,
|
|
1145
1330
|
hyperLinkExtension,
|
|
1146
|
-
|
|
1147
|
-
|
|
1331
|
+
hyperlinkDecoration,
|
|
1332
|
+
hyperlinkStyle,
|
|
1333
|
+
hyperlinkWidget,
|
|
1334
|
+
invalid,
|
|
1335
|
+
ivory,
|
|
1336
|
+
malibu,
|
|
1337
|
+
markdownBundle,
|
|
1338
|
+
markdownHighlightStyle,
|
|
1339
|
+
markdownTheme,
|
|
1340
|
+
onChangeExtension,
|
|
1341
|
+
sage,
|
|
1342
|
+
stone,
|
|
1148
1343
|
tags2 as tags,
|
|
1149
|
-
|
|
1344
|
+
tooltipBackground,
|
|
1345
|
+
useCollaboration,
|
|
1346
|
+
useTextModel,
|
|
1347
|
+
violet,
|
|
1348
|
+
whiskey
|
|
1150
1349
|
};
|
|
1151
1350
|
//# sourceMappingURL=index.mjs.map
|