@dxos/react-ui-editor 0.3.9-next.4584ca9 → 0.3.9-next.8ad58eb
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 +1027 -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 +28 -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,25 @@
|
|
|
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
|
-
|
|
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 { EditorView, placeholder } from "@codemirror/view";
|
|
13
|
+
var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
|
|
14
|
+
bracketMatching(),
|
|
15
|
+
closeBrackets(),
|
|
16
|
+
_placeholder && placeholder(_placeholder),
|
|
17
|
+
EditorView.lineWrapping,
|
|
18
|
+
// TODO(burdon): Is this required?
|
|
19
|
+
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
20
|
+
].filter(Boolean);
|
|
228
21
|
|
|
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";
|
|
22
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
233
23
|
import get from "lodash.get";
|
|
234
24
|
|
|
235
25
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
@@ -242,13 +32,6 @@ var heading = {
|
|
|
242
32
|
5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
|
|
243
33
|
6: "mbs-4 mbe-2 font-black text-inherit no-underline"
|
|
244
34
|
};
|
|
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
35
|
var bold = "font-bold";
|
|
253
36
|
var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
|
|
254
37
|
var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
|
|
@@ -260,53 +43,357 @@ var mark = "!font-normal !no-underline !text-inherit opacity-40";
|
|
|
260
43
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
261
44
|
var tokens = tailwindConfig({}).theme;
|
|
262
45
|
|
|
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
|
-
|
|
46
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
47
|
+
var basicTheme = {
|
|
48
|
+
"&.cm-focused": {
|
|
49
|
+
outline: "none"
|
|
50
|
+
},
|
|
51
|
+
"& .cm-line": {
|
|
52
|
+
paddingInline: 0,
|
|
53
|
+
lineHeight: 1.6,
|
|
54
|
+
minBlockSize: "1.6em"
|
|
55
|
+
},
|
|
56
|
+
"& .cm-line *": {
|
|
57
|
+
lineHeight: 1.6
|
|
58
|
+
},
|
|
59
|
+
"& .cm-scroller": {
|
|
60
|
+
fontFamily: get(tokens, "fontFamily.body", []).join(","),
|
|
61
|
+
overflow: "visible"
|
|
62
|
+
},
|
|
63
|
+
"& .cm-content": {
|
|
64
|
+
padding: 0,
|
|
65
|
+
caretColor: "black"
|
|
66
|
+
},
|
|
67
|
+
".dark & .cm-content": {
|
|
68
|
+
caretColor: "white"
|
|
69
|
+
},
|
|
70
|
+
"& .cm-tooltip": {
|
|
71
|
+
backgroundColor: "transparent",
|
|
72
|
+
border: "none"
|
|
73
|
+
},
|
|
74
|
+
"& .cm-link": {
|
|
75
|
+
color: get(tokens, "extend.colors.primary.500"),
|
|
76
|
+
textDecoration: "underline"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/change.ts
|
|
81
|
+
import { StateField } from "@codemirror/state";
|
|
82
|
+
var onChangeExtension = (onChange) => StateField.define({
|
|
83
|
+
create: () => null,
|
|
84
|
+
update: (_value, transaction) => {
|
|
85
|
+
if (transaction.docChanged && onChange) {
|
|
86
|
+
onChange(transaction.newDoc.toString());
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts
|
|
93
|
+
import { syntaxTree } from "@codemirror/language";
|
|
94
|
+
import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
|
|
95
|
+
import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
|
|
96
|
+
var Link = class extends WidgetType {
|
|
97
|
+
constructor(_anchor, _text, _url) {
|
|
98
|
+
super();
|
|
99
|
+
this._anchor = _anchor;
|
|
100
|
+
this._text = _text;
|
|
101
|
+
this._url = _url;
|
|
102
|
+
}
|
|
103
|
+
eq(other) {
|
|
104
|
+
return this._text === other.text && this._url === other.url;
|
|
105
|
+
}
|
|
106
|
+
toDOM(view) {
|
|
107
|
+
const link = document.createElement("a");
|
|
108
|
+
link.setAttribute("class", "cm-link");
|
|
109
|
+
link.textContent = this._text;
|
|
110
|
+
if (this._url) {
|
|
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
|
+
};
|
|
121
|
+
}
|
|
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
|
+
});
|
|
131
|
+
};
|
|
132
|
+
var update = (state, link) => {
|
|
133
|
+
const builder = new RangeSetBuilder();
|
|
134
|
+
const cursor = state.selection.main.head;
|
|
135
|
+
syntaxTree(state).iterate({
|
|
136
|
+
enter: (node) => {
|
|
137
|
+
if (cursor < node.from || cursor > node.to) {
|
|
138
|
+
if (node.name === "Link") {
|
|
139
|
+
const marks = node.node.getChildren("LinkMark");
|
|
140
|
+
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
141
|
+
const urlNode = node.node.getChild("URL");
|
|
142
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
143
|
+
builder.add(node.from, node.to, Decoration.replace({
|
|
144
|
+
widget: new Link(node.from, text, link ? url : void 0)
|
|
145
|
+
}));
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return builder.finish();
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx
|
|
156
|
+
import { hoverTooltip } from "@codemirror/view";
|
|
157
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
158
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
159
|
+
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
|
|
160
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
161
|
+
const p = pos - from;
|
|
162
|
+
let idx = 0;
|
|
163
|
+
let match;
|
|
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) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const [, , url] = match ?? [];
|
|
180
|
+
return {
|
|
181
|
+
pos: idx + from,
|
|
182
|
+
end: idx + from + match[0].length,
|
|
183
|
+
above: true,
|
|
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
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
function hyperLinkExtension() {
|
|
245
|
+
return ViewPlugin.fromClass(class HyperLinkView {
|
|
246
|
+
constructor(view) {
|
|
247
|
+
this.decorator = linkDecorator();
|
|
248
|
+
this.decorations = this.decorator.createDeco(view);
|
|
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"
|
|
310
397
|
},
|
|
311
398
|
// '.dark & .cm-tooltip': {
|
|
312
399
|
// border: 'none',
|
|
@@ -331,40 +418,42 @@ var markdownTheme = {
|
|
|
331
418
|
},
|
|
332
419
|
"& .cm-line": {
|
|
333
420
|
paddingInline: 0,
|
|
421
|
+
lineHeight: 1.6,
|
|
334
422
|
minBlockSize: "1.6em"
|
|
335
423
|
},
|
|
336
424
|
"& .cm-line *": {
|
|
337
425
|
lineHeight: 1.6
|
|
338
426
|
},
|
|
339
427
|
"&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
|
|
340
|
-
background:
|
|
428
|
+
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
341
429
|
},
|
|
342
430
|
".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
|
|
343
|
-
background:
|
|
431
|
+
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
344
432
|
},
|
|
345
433
|
"& .cm-selectionMatch": {
|
|
346
|
-
background:
|
|
434
|
+
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
347
435
|
},
|
|
348
436
|
".dark & .cm-selectionMatch": {
|
|
349
|
-
background:
|
|
437
|
+
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
350
438
|
},
|
|
351
439
|
"& .cm-content": {
|
|
440
|
+
padding: 0,
|
|
352
441
|
caretColor: "black"
|
|
353
442
|
},
|
|
354
443
|
".dark & .cm-content": {
|
|
355
|
-
caretColor:
|
|
444
|
+
caretColor: "white"
|
|
356
445
|
},
|
|
357
446
|
"& .cm-cursor": {
|
|
358
447
|
borderLeftColor: "black"
|
|
359
448
|
},
|
|
360
449
|
".dark & .cm-cursor": {
|
|
361
|
-
borderLeftColor:
|
|
450
|
+
borderLeftColor: "white"
|
|
362
451
|
},
|
|
363
452
|
".cm-placeholder": {
|
|
364
|
-
fontFamily:
|
|
453
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
365
454
|
},
|
|
366
455
|
"& .cm-scroller": {
|
|
367
|
-
fontFamily:
|
|
456
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
|
|
368
457
|
overflow: "visible"
|
|
369
458
|
},
|
|
370
459
|
"& .cm-activeLine": {
|
|
@@ -374,7 +463,7 @@ var markdownTheme = {
|
|
|
374
463
|
backgroundColor: "transparent"
|
|
375
464
|
},
|
|
376
465
|
"& .cm-ySelectionInfo": {
|
|
377
|
-
fontFamily:
|
|
466
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(","),
|
|
378
467
|
padding: "2px 4px",
|
|
379
468
|
marginBlockStart: "-4px"
|
|
380
469
|
},
|
|
@@ -397,8 +486,16 @@ var markdownTheme = {
|
|
|
397
486
|
"& .cm-yLineSelection": {
|
|
398
487
|
margin: "0"
|
|
399
488
|
},
|
|
400
|
-
|
|
401
|
-
|
|
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, [
|
|
402
499
|
"extend",
|
|
403
500
|
"fontSize",
|
|
404
501
|
fontSize,
|
|
@@ -417,7 +514,7 @@ var markdownTheme = {
|
|
|
417
514
|
return acc;
|
|
418
515
|
}, {})
|
|
419
516
|
};
|
|
420
|
-
var
|
|
517
|
+
var markdownHighlightStyle = HighlightStyle.define([
|
|
421
518
|
{
|
|
422
519
|
tag: [
|
|
423
520
|
tags.keyword,
|
|
@@ -533,17 +630,570 @@ var markdownDarkHighlighting = HighlightStyle.define([
|
|
|
533
630
|
], {
|
|
534
631
|
scope: markdownLanguage,
|
|
535
632
|
all: {
|
|
536
|
-
fontFamily:
|
|
633
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
537
634
|
}
|
|
538
635
|
});
|
|
539
636
|
|
|
540
|
-
// packages/ui/react-ui-editor/src/components/
|
|
637
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
638
|
+
var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
639
|
+
return [
|
|
640
|
+
bracketMatching2(),
|
|
641
|
+
closeBrackets2(),
|
|
642
|
+
_placeholder && placeholder2(_placeholder),
|
|
643
|
+
EditorState.allowMultipleSelections.of(true),
|
|
644
|
+
EditorView4.lineWrapping,
|
|
645
|
+
// autocompletion(),
|
|
646
|
+
crosshairCursor(),
|
|
647
|
+
dropCursor(),
|
|
648
|
+
drawSelection(),
|
|
649
|
+
highlightActiveLine(),
|
|
650
|
+
highlightActiveLineGutter(),
|
|
651
|
+
highlightSelectionMatches(),
|
|
652
|
+
highlightSpecialChars(),
|
|
653
|
+
history(),
|
|
654
|
+
indentOnInput(),
|
|
655
|
+
rectangularSelection(),
|
|
656
|
+
keymap.of([
|
|
657
|
+
...closeBracketsKeymap,
|
|
658
|
+
// ...completionKeymap,
|
|
659
|
+
...defaultKeymap,
|
|
660
|
+
...foldKeymap,
|
|
661
|
+
...historyKeymap,
|
|
662
|
+
...lintKeymap,
|
|
663
|
+
...searchKeymap,
|
|
664
|
+
indentWithTab
|
|
665
|
+
]),
|
|
666
|
+
// Main extension.
|
|
667
|
+
// https://github.com/codemirror/lang-markdown
|
|
668
|
+
markdown({
|
|
669
|
+
base: markdownLanguage2,
|
|
670
|
+
codeLanguages: languages,
|
|
671
|
+
extensions: [
|
|
672
|
+
// TODO(burdon): This seems to upgrade the parser.
|
|
673
|
+
// GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
|
|
674
|
+
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
675
|
+
// https://github.github.com/gfm
|
|
676
|
+
// GFM,
|
|
677
|
+
// Custom styling.
|
|
678
|
+
markdownTagsExtension
|
|
679
|
+
]
|
|
680
|
+
}),
|
|
681
|
+
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
682
|
+
syntaxHighlighting2(markdownHighlightStyle),
|
|
683
|
+
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
|
|
684
|
+
].filter(Boolean);
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
688
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
689
|
+
import { EditorView as EditorView5 } from "@codemirror/view";
|
|
690
|
+
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
691
|
+
import { vim } from "@replit/codemirror-vim";
|
|
692
|
+
import defaultsDeep from "lodash.defaultsdeep";
|
|
693
|
+
import get3 from "lodash.get";
|
|
694
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
|
|
695
|
+
import { isDocAccessor as isDocAccessor2 } from "@dxos/echo-schema";
|
|
696
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
697
|
+
|
|
698
|
+
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
699
|
+
import { useEffect, useMemo } from "react";
|
|
700
|
+
import { yCollab } from "y-codemirror.next";
|
|
701
|
+
import { generateName } from "@dxos/display-name";
|
|
702
|
+
import { isDocAccessor } from "@dxos/echo-schema";
|
|
703
|
+
import { getColorForValue } from "@dxos/react-ui-theme";
|
|
704
|
+
import { YText } from "@dxos/text-model";
|
|
705
|
+
|
|
706
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
707
|
+
import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
|
|
708
|
+
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
709
|
+
import * as automerge2 from "@dxos/automerge/automerge";
|
|
710
|
+
|
|
711
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
712
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
713
|
+
|
|
714
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
|
|
715
|
+
import { ChangeSet } from "@codemirror/state";
|
|
716
|
+
var amToCodemirror_default = (view, selection, target, patches) => {
|
|
717
|
+
for (const patch of patches) {
|
|
718
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
719
|
+
if (changeSpec != null) {
|
|
720
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
721
|
+
selection = selection.map(changeSet, 1);
|
|
722
|
+
view.dispatch({
|
|
723
|
+
changes: changeSet,
|
|
724
|
+
annotations: reconcileAnnotationType.of({})
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
view.dispatch({
|
|
729
|
+
selection,
|
|
730
|
+
annotations: reconcileAnnotationType.of({})
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
var handlePatch = (patch, target, state) => {
|
|
734
|
+
if (patch.action === "insert") {
|
|
735
|
+
return handleInsert(target, patch);
|
|
736
|
+
} else if (patch.action === "splice") {
|
|
737
|
+
return handleSplice(target, patch);
|
|
738
|
+
} else if (patch.action === "del") {
|
|
739
|
+
return handleDel(target, patch);
|
|
740
|
+
} else if (patch.action === "put") {
|
|
741
|
+
return handlePut(target, patch, state);
|
|
742
|
+
} else {
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
var handleInsert = (target, patch) => {
|
|
747
|
+
const index = charPath(target, patch.path);
|
|
748
|
+
if (index == null) {
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
752
|
+
return [
|
|
753
|
+
{
|
|
754
|
+
from: index,
|
|
755
|
+
to: index,
|
|
756
|
+
insert: text
|
|
757
|
+
}
|
|
758
|
+
];
|
|
759
|
+
};
|
|
760
|
+
var handleSplice = (target, patch) => {
|
|
761
|
+
const index = charPath(target, patch.path);
|
|
762
|
+
if (index == null) {
|
|
763
|
+
return [];
|
|
764
|
+
}
|
|
765
|
+
return [
|
|
766
|
+
{
|
|
767
|
+
from: index,
|
|
768
|
+
insert: patch.value
|
|
769
|
+
}
|
|
770
|
+
];
|
|
771
|
+
};
|
|
772
|
+
var handleDel = (target, patch) => {
|
|
773
|
+
const index = charPath(target, patch.path);
|
|
774
|
+
if (index == null) {
|
|
775
|
+
return [];
|
|
776
|
+
}
|
|
777
|
+
const length = patch.length || 1;
|
|
778
|
+
return [
|
|
779
|
+
{
|
|
780
|
+
from: index,
|
|
781
|
+
to: index + length
|
|
782
|
+
}
|
|
783
|
+
];
|
|
784
|
+
};
|
|
785
|
+
var handlePut = (target, patch, state) => {
|
|
786
|
+
const index = charPath(target, [
|
|
787
|
+
...patch.path,
|
|
788
|
+
0
|
|
789
|
+
]);
|
|
790
|
+
if (index == null) {
|
|
791
|
+
return [];
|
|
792
|
+
}
|
|
793
|
+
const length = state.doc.length;
|
|
794
|
+
if (typeof patch.value !== "string") {
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
return [
|
|
798
|
+
{
|
|
799
|
+
from: 0,
|
|
800
|
+
to: length,
|
|
801
|
+
insert: patch.value
|
|
802
|
+
}
|
|
803
|
+
];
|
|
804
|
+
};
|
|
805
|
+
var charPath = (textPath, candidatePath) => {
|
|
806
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
810
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
815
|
+
if (typeof index === "number") {
|
|
816
|
+
return index;
|
|
817
|
+
}
|
|
818
|
+
return null;
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
|
|
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
|
|
845
|
+
var PatchSemaphore = class {
|
|
846
|
+
constructor(field) {
|
|
847
|
+
this._inReconcile = false;
|
|
848
|
+
this._queue = [];
|
|
849
|
+
this.reconcile = (handle, view) => {
|
|
850
|
+
if (this._inReconcile) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
this._inReconcile = true;
|
|
854
|
+
const path = getPath(view.state, this._field);
|
|
855
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
856
|
+
let selection = view.state.selection;
|
|
857
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
858
|
+
const toInvert = transactions.slice().reverse();
|
|
859
|
+
for (const tx of toInvert) {
|
|
860
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
861
|
+
selection = selection.map(inverted);
|
|
862
|
+
view.dispatch({
|
|
863
|
+
changes: inverted,
|
|
864
|
+
annotations: reconcileAnnotationType.of(true)
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
|
|
868
|
+
if (newHeads === null || newHeads === void 0) {
|
|
869
|
+
newHeads = automerge.getHeads(handle.docSync());
|
|
870
|
+
}
|
|
871
|
+
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
872
|
+
amToCodemirror_default(view, selection, path, diff);
|
|
873
|
+
view.dispatch({
|
|
874
|
+
effects: updateHeads(newHeads),
|
|
875
|
+
annotations: reconcileAnnotationType.of({})
|
|
876
|
+
});
|
|
877
|
+
this._inReconcile = false;
|
|
878
|
+
};
|
|
879
|
+
if (field !== void 0) {
|
|
880
|
+
this._field = field;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
886
|
+
var effectType = StateEffect.define({});
|
|
887
|
+
var updateHeads = (newHeads) => effectType.of({
|
|
888
|
+
newHeads
|
|
889
|
+
});
|
|
890
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
891
|
+
var getPath = (state, field) => state.field(field).path;
|
|
892
|
+
var semaphoreFacet = Facet.define({
|
|
893
|
+
combine: (values) => values.at(-1)
|
|
894
|
+
});
|
|
895
|
+
var automergePlugin = (handle, path) => {
|
|
896
|
+
const stateField = StateField3.define({
|
|
897
|
+
create: () => ({
|
|
898
|
+
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
899
|
+
unreconciledTransactions: [],
|
|
900
|
+
path: path.slice()
|
|
901
|
+
}),
|
|
902
|
+
update: (value, tr) => {
|
|
903
|
+
const result = {
|
|
904
|
+
lastHeads: value.lastHeads,
|
|
905
|
+
unreconciledTransactions: value.unreconciledTransactions.slice(),
|
|
906
|
+
path: path.slice()
|
|
907
|
+
};
|
|
908
|
+
let clearUnreconciled = false;
|
|
909
|
+
for (const effect of tr.effects) {
|
|
910
|
+
if (effect.is(effectType)) {
|
|
911
|
+
result.lastHeads = effect.value.newHeads;
|
|
912
|
+
clearUnreconciled = true;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
if (clearUnreconciled) {
|
|
916
|
+
result.unreconciledTransactions = [];
|
|
917
|
+
} else {
|
|
918
|
+
if (!isReconcileTx(tr)) {
|
|
919
|
+
result.unreconciledTransactions.push(tr);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
const semaphore = new PatchSemaphore(stateField);
|
|
926
|
+
const viewPlugin = ViewPlugin2.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
927
|
+
constructor(view) {
|
|
928
|
+
this._handleChange = () => {
|
|
929
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
930
|
+
};
|
|
931
|
+
this._view = view;
|
|
932
|
+
handle.addListener("change", this._handleChange);
|
|
933
|
+
}
|
|
934
|
+
update(update2) {
|
|
935
|
+
if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
|
|
936
|
+
queueMicrotask(() => {
|
|
937
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
destroy() {
|
|
942
|
+
handle.addListener("change", this._handleChange);
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
return {
|
|
946
|
+
extension: [
|
|
947
|
+
stateField,
|
|
948
|
+
semaphoreFacet.of(semaphore),
|
|
949
|
+
viewPlugin
|
|
950
|
+
]
|
|
951
|
+
};
|
|
952
|
+
};
|
|
953
|
+
var reconcileAnnotationType = Annotation.define();
|
|
954
|
+
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
955
|
+
|
|
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
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
996
|
+
import * as random from "lib0/random";
|
|
997
|
+
var cursorColors = [
|
|
998
|
+
{
|
|
999
|
+
color: "#30bced",
|
|
1000
|
+
light: "#30bced33"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
color: "#6eeb83",
|
|
1004
|
+
light: "#6eeb8333"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
color: "#ffbc42",
|
|
1008
|
+
light: "#ffbc4233"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
color: "#ecd444",
|
|
1012
|
+
light: "#ecd44433"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
color: "#ee6352",
|
|
1016
|
+
light: "#ee635233"
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
color: "#9ac2c9",
|
|
1020
|
+
light: "#9ac2c933"
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
color: "#8acb88",
|
|
1024
|
+
light: "#8acb8833"
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
color: "#1be7ff",
|
|
1028
|
+
light: "#1be7ff33"
|
|
1029
|
+
}
|
|
1030
|
+
];
|
|
1031
|
+
var cursorColor = cursorColors[random.uint32() % cursorColors.length];
|
|
1032
|
+
|
|
1033
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
|
|
1034
|
+
import * as decoding from "lib0/decoding";
|
|
1035
|
+
import * as encoding from "lib0/encoding";
|
|
1036
|
+
import { Observable } from "lib0/observable";
|
|
1037
|
+
import * as awarenessProtocol from "y-protocols/awareness";
|
|
1038
|
+
import { log } from "@dxos/log";
|
|
1039
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
1040
|
+
var messageAwareness = 1;
|
|
1041
|
+
var messageQueryAwareness = 3;
|
|
1042
|
+
var SpaceAwarenessProvider = class extends Observable {
|
|
1043
|
+
constructor({ space, doc, channel, awareness }) {
|
|
1044
|
+
super();
|
|
1045
|
+
this._space = space;
|
|
1046
|
+
this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
|
|
1047
|
+
this._channel = channel;
|
|
1048
|
+
this._clientId = doc.clientID;
|
|
1049
|
+
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
1050
|
+
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
1051
|
+
if (typeof window !== "undefined") {
|
|
1052
|
+
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
1053
|
+
} else if (typeof process !== "undefined") {
|
|
1054
|
+
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
1055
|
+
}
|
|
1056
|
+
const encoderAwarenessQuery = encoding.createEncoder();
|
|
1057
|
+
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
1058
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
1059
|
+
const encoderAwarenessState = encoding.createEncoder();
|
|
1060
|
+
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
1061
|
+
encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
|
|
1062
|
+
this._clientId
|
|
1063
|
+
]));
|
|
1064
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
1065
|
+
}
|
|
1066
|
+
get awareness() {
|
|
1067
|
+
return this._awareness;
|
|
1068
|
+
}
|
|
1069
|
+
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
1070
|
+
log("awareness update", {
|
|
1071
|
+
added,
|
|
1072
|
+
updated,
|
|
1073
|
+
removed,
|
|
1074
|
+
origin
|
|
1075
|
+
}, {
|
|
1076
|
+
F: __dxlog_file,
|
|
1077
|
+
L: 67,
|
|
1078
|
+
S: this,
|
|
1079
|
+
C: (f, a) => f(...a)
|
|
1080
|
+
});
|
|
1081
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
1082
|
+
const encoderAwareness = encoding.createEncoder();
|
|
1083
|
+
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
1084
|
+
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
1085
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
1086
|
+
}
|
|
1087
|
+
_handleSpaceMessage({ payload }) {
|
|
1088
|
+
log("space message", payload, {
|
|
1089
|
+
F: __dxlog_file,
|
|
1090
|
+
L: 79,
|
|
1091
|
+
S: this,
|
|
1092
|
+
C: (f, a) => f(...a)
|
|
1093
|
+
});
|
|
1094
|
+
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
1095
|
+
const encoder = this._readMessage(data);
|
|
1096
|
+
if (encoder) {
|
|
1097
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
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
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (!sendReply) {
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
return encoder;
|
|
1125
|
+
}
|
|
1126
|
+
_handleBeforeUnload() {
|
|
1127
|
+
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
1128
|
+
this._clientId
|
|
1129
|
+
], "window unload");
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1134
|
+
var useTextModel = ({ identity, space, text }) => {
|
|
1135
|
+
const provider = useMemo2(() => {
|
|
1136
|
+
if (isActualAutomergeObject(text)) {
|
|
1137
|
+
return void 0;
|
|
1138
|
+
}
|
|
1139
|
+
if (!space || !text?.doc) {
|
|
1140
|
+
return void 0;
|
|
1141
|
+
}
|
|
1142
|
+
return new SpaceAwarenessProvider({
|
|
1143
|
+
space,
|
|
1144
|
+
doc: text.doc,
|
|
1145
|
+
channel: `yjs.awareness.${text.id}`
|
|
1146
|
+
});
|
|
1147
|
+
}, [
|
|
1148
|
+
identity,
|
|
1149
|
+
space,
|
|
1150
|
+
text,
|
|
1151
|
+
text?.doc
|
|
1152
|
+
]);
|
|
1153
|
+
const content = useMemo2(() => {
|
|
1154
|
+
if (isActualAutomergeObject(text)) {
|
|
1155
|
+
const obj = text;
|
|
1156
|
+
return getRawDoc(obj, [
|
|
1157
|
+
obj.field
|
|
1158
|
+
]);
|
|
1159
|
+
} else {
|
|
1160
|
+
return text?.content;
|
|
1161
|
+
}
|
|
1162
|
+
}, [
|
|
1163
|
+
text
|
|
1164
|
+
]);
|
|
1165
|
+
if (isActualAutomergeObject(text)) {
|
|
1166
|
+
const obj = text;
|
|
1167
|
+
return {
|
|
1168
|
+
id: obj.id,
|
|
1169
|
+
content,
|
|
1170
|
+
provider: void 0,
|
|
1171
|
+
peer: identity ? {
|
|
1172
|
+
id: identity.identityKey.toHex(),
|
|
1173
|
+
name: identity.profile?.displayName
|
|
1174
|
+
} : void 0
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
if (!text?.doc || !text?.content) {
|
|
1178
|
+
return void 0;
|
|
1179
|
+
}
|
|
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
|
+
};
|
|
1190
|
+
|
|
1191
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
541
1192
|
var EditorModes = [
|
|
542
1193
|
"default",
|
|
543
1194
|
"vim"
|
|
544
1195
|
];
|
|
545
|
-
var
|
|
546
|
-
const { id, content, provider, peer } = model ?? {};
|
|
1196
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots, editorMode }, forwardedRef) => {
|
|
547
1197
|
const { themeMode } = useThemeContext();
|
|
548
1198
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
549
1199
|
tabBehavior: "limited"
|
|
@@ -555,110 +1205,33 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
555
1205
|
editor: parent,
|
|
556
1206
|
state,
|
|
557
1207
|
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
1208
|
}), [
|
|
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
|
|
1209
|
+
view,
|
|
1210
|
+
state,
|
|
1211
|
+
parent
|
|
589
1212
|
]);
|
|
590
|
-
|
|
1213
|
+
const collaboration = useCollaboration(model, themeMode);
|
|
1214
|
+
useEffect2(() => {
|
|
591
1215
|
if (!parent) {
|
|
592
1216
|
return;
|
|
593
1217
|
}
|
|
594
|
-
const
|
|
595
|
-
|
|
1218
|
+
const initialText = isDocAccessor2(model.content) ? get3(model.content.handle.docSync(), model.content.path) : model.content?.toString();
|
|
1219
|
+
const state2 = EditorState2.create({
|
|
1220
|
+
doc: initialText,
|
|
596
1221
|
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
|
-
] : [],
|
|
1222
|
+
// TODO(burdon): Factor out VIM mode?
|
|
1223
|
+
editorMode === "vim" && vim(),
|
|
1224
|
+
// Replication.
|
|
1225
|
+
collaboration,
|
|
1226
|
+
// Theme.
|
|
1227
|
+
slots?.editor?.theme && EditorView5.theme(slots?.editor?.theme),
|
|
655
1228
|
// Custom.
|
|
656
1229
|
...extensions
|
|
657
|
-
]
|
|
1230
|
+
].filter(Boolean)
|
|
658
1231
|
});
|
|
659
1232
|
setState(state2);
|
|
660
1233
|
view?.destroy();
|
|
661
|
-
setView(new
|
|
1234
|
+
setView(new EditorView5({
|
|
662
1235
|
state: state2,
|
|
663
1236
|
parent
|
|
664
1237
|
}));
|
|
@@ -669,8 +1242,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
669
1242
|
};
|
|
670
1243
|
}, [
|
|
671
1244
|
parent,
|
|
672
|
-
content,
|
|
673
|
-
provider?.awareness,
|
|
1245
|
+
model.content,
|
|
674
1246
|
themeMode,
|
|
675
1247
|
editorMode
|
|
676
1248
|
]);
|
|
@@ -691,461 +1263,89 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
691
1263
|
editorMode
|
|
692
1264
|
]);
|
|
693
1265
|
return /* @__PURE__ */ React.createElement("div", {
|
|
694
|
-
|
|
1266
|
+
key: model.id,
|
|
695
1267
|
ref: setParent,
|
|
696
|
-
|
|
697
|
-
...slots
|
|
698
|
-
...editorMode !== "vim"
|
|
1268
|
+
tabIndex: 0,
|
|
1269
|
+
...slots?.root,
|
|
1270
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
699
1271
|
onKeyUp: handleKeyUp
|
|
700
1272
|
});
|
|
701
1273
|
});
|
|
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"
|
|
1274
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
|
|
1275
|
+
const { themeMode } = useThemeContext();
|
|
1276
|
+
const extensions = [
|
|
1277
|
+
..._extensions ?? [],
|
|
1278
|
+
basicBundle({
|
|
1279
|
+
themeMode,
|
|
1280
|
+
placeholder: slots?.editor?.placeholder
|
|
822
1281
|
})
|
|
823
|
-
]
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return useNaturalEditor({
|
|
1282
|
+
];
|
|
1283
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1284
|
+
ref: forwardedRef,
|
|
827
1285
|
extensions,
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
|
|
832
|
-
tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
|
|
1286
|
+
slots: defaultsDeep({}, slots, {
|
|
1287
|
+
editor: {
|
|
1288
|
+
theme: basicTheme
|
|
833
1289
|
}
|
|
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,
|
|
1290
|
+
}),
|
|
982
1291
|
...props
|
|
983
1292
|
});
|
|
984
1293
|
});
|
|
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([]);
|
|
1294
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
|
|
1295
|
+
const { themeMode } = useThemeContext();
|
|
1296
|
+
const extensions = [
|
|
1297
|
+
..._extensions ?? [],
|
|
1298
|
+
markdownBundle({
|
|
1299
|
+
themeMode
|
|
1300
|
+
})
|
|
1301
|
+
];
|
|
1302
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1303
|
+
ref: forwardedRef,
|
|
1304
|
+
extensions,
|
|
1305
|
+
slots: defaultsDeep({}, slots, {
|
|
1306
|
+
editor: {
|
|
1307
|
+
theme: markdownTheme
|
|
1051
1308
|
}
|
|
1052
|
-
}
|
|
1309
|
+
}),
|
|
1310
|
+
...props
|
|
1053
1311
|
});
|
|
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
1312
|
});
|
|
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
1313
|
export {
|
|
1314
|
+
BaseTextEditor,
|
|
1133
1315
|
Doc,
|
|
1134
|
-
Editor,
|
|
1135
1316
|
EditorModes,
|
|
1136
1317
|
MarkdownEditor,
|
|
1137
|
-
|
|
1318
|
+
SpaceAwarenessProvider,
|
|
1138
1319
|
TextEditor,
|
|
1139
1320
|
TextKind,
|
|
1140
|
-
|
|
1141
|
-
|
|
1321
|
+
YText2 as YText,
|
|
1322
|
+
YXmlFragment,
|
|
1323
|
+
basicBundle,
|
|
1324
|
+
basicTheme,
|
|
1325
|
+
chalky,
|
|
1326
|
+
coral,
|
|
1142
1327
|
createHyperlinkTooltip,
|
|
1143
|
-
|
|
1144
|
-
|
|
1328
|
+
cursorColor,
|
|
1329
|
+
cyan,
|
|
1330
|
+
highlightBackground,
|
|
1145
1331
|
hyperLinkExtension,
|
|
1146
|
-
|
|
1147
|
-
|
|
1332
|
+
hyperlinkDecoration,
|
|
1333
|
+
hyperlinkStyle,
|
|
1334
|
+
hyperlinkWidget,
|
|
1335
|
+
invalid,
|
|
1336
|
+
ivory,
|
|
1337
|
+
malibu,
|
|
1338
|
+
markdownBundle,
|
|
1339
|
+
markdownHighlightStyle,
|
|
1340
|
+
markdownTheme,
|
|
1341
|
+
onChangeExtension,
|
|
1342
|
+
sage,
|
|
1343
|
+
stone,
|
|
1148
1344
|
tags2 as tags,
|
|
1149
|
-
|
|
1345
|
+
tooltipBackground,
|
|
1346
|
+
useCollaboration,
|
|
1347
|
+
useTextModel,
|
|
1348
|
+
violet,
|
|
1349
|
+
whiskey
|
|
1150
1350
|
};
|
|
1151
1351
|
//# sourceMappingURL=index.mjs.map
|