@dxos/react-ui-editor 0.3.9-main.84a0b01 → 0.3.9-main.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 +974 -499
- 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 +43 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +15 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
- 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/hyperlink/hyperlinkTooltip.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts +10 -0
- 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 +5 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -0
- 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 +4 -0
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
- 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 +1 -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 +34 -27
- 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 -48
- package/src/components/TextEditor/TextEditor.stories.tsx +79 -0
- package/src/components/TextEditor/TextEditor.tsx +188 -0
- 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/hyperlink/hyperlinkTooltip.tsx +61 -0
- package/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx +119 -0
- package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
- package/src/components/TextEditor/extensions/index.ts +8 -0
- 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} +42 -27
- package/src/components/TextEditor/index.ts +8 -0
- package/src/components/TextEditor/yjs.stories.tsx +55 -0
- package/src/components/index.ts +1 -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 -3
- 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 -28
- 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 -19
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +0 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +0 -19
- 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 -107
- 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 -26
- 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/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 -80
- package/src/components/Editor/Editor.tsx +0 -33
- package/src/components/Editor/index.ts +0 -5
- package/src/components/Markdown/Markdown.stories.tsx +0 -56
- package/src/components/Markdown/Markdown.tsx +0 -206
- package/src/components/Markdown/index.ts +0 -5
- package/src/components/RichText/RichText.stories.tsx +0 -68
- package/src/components/RichText/RichText.tsx +0 -181
- package/src/components/RichText/index.ts +0 -5
- /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,199 +1,306 @@
|
|
|
1
|
-
// packages/ui/react-ui-editor/src/
|
|
2
|
-
import
|
|
3
|
-
import { YXmlFragment } from "@dxos/text-model";
|
|
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";
|
|
4
4
|
|
|
5
|
-
// packages/ui/react-ui-editor/src/
|
|
6
|
-
import {
|
|
5
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
6
|
+
import { tags as tags2 } from "@lezer/highlight";
|
|
7
7
|
|
|
8
|
-
// packages/ui/react-ui-editor/src/
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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";
|
|
11
|
+
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
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);
|
|
21
|
+
|
|
22
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
23
|
+
import get from "lodash.get";
|
|
24
|
+
|
|
25
|
+
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
26
|
+
import { mx } from "@dxos/react-ui-theme";
|
|
27
|
+
var heading = {
|
|
28
|
+
1: "mbs-4 mbe-2 text-4xl font-semibold text-inherit no-underline",
|
|
29
|
+
2: "mbs-4 mbe-2 text-3xl font-bold text-inherit no-underline",
|
|
30
|
+
3: "mbs-4 mbe-2 text-2xl font-bold text-inherit no-underline",
|
|
31
|
+
4: "mbs-4 mbe-2 text-xl font-extrabold text-inherit no-underline",
|
|
32
|
+
5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
|
|
33
|
+
6: "mbs-4 mbe-2 font-black text-inherit no-underline"
|
|
34
|
+
};
|
|
35
|
+
var bold = "font-bold";
|
|
36
|
+
var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
|
|
37
|
+
var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
|
|
38
|
+
var italic = "italic";
|
|
39
|
+
var strikethrough = "line-through";
|
|
40
|
+
var mark = "!font-normal !no-underline !text-inherit opacity-40";
|
|
41
|
+
|
|
42
|
+
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
43
|
+
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
44
|
+
var tokens = tailwindConfig({}).theme;
|
|
45
|
+
|
|
46
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
|
|
47
|
+
var basicTheme = {
|
|
48
|
+
"&.cm-focused": {
|
|
49
|
+
outline: "none"
|
|
14
50
|
},
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
51
|
+
"& .cm-line": {
|
|
52
|
+
paddingInline: 0,
|
|
53
|
+
lineHeight: 1.6,
|
|
54
|
+
minBlockSize: "1.6em"
|
|
18
55
|
},
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
light: "#ffbc4233"
|
|
56
|
+
"& .cm-line *": {
|
|
57
|
+
lineHeight: 1.6
|
|
22
58
|
},
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
"& .cm-scroller": {
|
|
60
|
+
fontFamily: get(tokens, "fontFamily.body", []).join(","),
|
|
61
|
+
overflow: "visible"
|
|
26
62
|
},
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
63
|
+
"& .cm-content": {
|
|
64
|
+
padding: 0,
|
|
65
|
+
caretColor: "black"
|
|
30
66
|
},
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
light: "#9ac2c933"
|
|
67
|
+
".dark & .cm-content": {
|
|
68
|
+
caretColor: "white"
|
|
34
69
|
},
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
"& .cm-tooltip": {
|
|
71
|
+
backgroundColor: "transparent",
|
|
72
|
+
border: "none"
|
|
38
73
|
},
|
|
39
|
-
{
|
|
40
|
-
color: "
|
|
41
|
-
|
|
74
|
+
"& .cm-link": {
|
|
75
|
+
color: get(tokens, "extend.colors.primary.500"),
|
|
76
|
+
textDecoration: "underline"
|
|
42
77
|
}
|
|
43
|
-
|
|
44
|
-
var cursorColor = cursorColors[random.uint32() % cursorColors.length];
|
|
78
|
+
};
|
|
45
79
|
|
|
46
|
-
// packages/ui/react-ui-editor/src/
|
|
47
|
-
import
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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));
|
|
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());
|
|
68
87
|
}
|
|
69
|
-
|
|
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));
|
|
88
|
+
return null;
|
|
78
89
|
}
|
|
79
|
-
|
|
80
|
-
|
|
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;
|
|
81
102
|
}
|
|
82
|
-
|
|
83
|
-
|
|
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));
|
|
103
|
+
eq(other) {
|
|
104
|
+
return this._text === other.text && this._url === other.url;
|
|
99
105
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
};
|
|
111
121
|
}
|
|
122
|
+
return link;
|
|
112
123
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
+
}
|
|
132
148
|
}
|
|
149
|
+
return true;
|
|
133
150
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
return encoder;
|
|
138
|
-
}
|
|
139
|
-
_handleBeforeUnload() {
|
|
140
|
-
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
141
|
-
this._clientId
|
|
142
|
-
], "window unload");
|
|
143
|
-
}
|
|
151
|
+
});
|
|
152
|
+
return builder.finish();
|
|
144
153
|
};
|
|
145
154
|
|
|
146
|
-
// packages/ui/react-ui-editor/src/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
151
169
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
text?.doc
|
|
161
|
-
]);
|
|
162
|
-
if (!text?.doc || !text?.content) {
|
|
163
|
-
return void 0;
|
|
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;
|
|
164
178
|
}
|
|
179
|
+
const [, , url] = match ?? [];
|
|
165
180
|
return {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
+
}
|
|
173
195
|
};
|
|
174
|
-
};
|
|
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
|
+
];
|
|
175
290
|
|
|
176
|
-
// packages/ui/react-ui-editor/src/components/
|
|
177
|
-
import {
|
|
291
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
292
|
+
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
178
293
|
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
179
|
-
import {
|
|
180
|
-
import { bracketMatching, defaultHighlightStyle, foldKeymap, indentOnInput, syntaxHighlighting } from "@codemirror/language";
|
|
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";
|
|
181
296
|
import { languages } from "@codemirror/language-data";
|
|
182
297
|
import { lintKeymap } from "@codemirror/lint";
|
|
183
|
-
import {
|
|
184
|
-
import { EditorState
|
|
185
|
-
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
186
|
-
import {
|
|
187
|
-
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
188
|
-
import { vim } from "@replit/codemirror-vim";
|
|
189
|
-
import React, { forwardRef, useEffect, useImperativeHandle, useState, useMemo as useMemo2, useCallback } from "react";
|
|
190
|
-
import { yCollab } from "y-codemirror.next";
|
|
191
|
-
import { generateName } from "@dxos/display-name";
|
|
192
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
193
|
-
import { getColorForValue } from "@dxos/react-ui-theme";
|
|
194
|
-
import { YText } from "@dxos/text-model";
|
|
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";
|
|
195
302
|
|
|
196
|
-
// packages/ui/react-ui-editor/src/components/
|
|
303
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
|
|
197
304
|
import { styleTags, Tag } from "@lezer/highlight";
|
|
198
305
|
var markdownTags = {
|
|
199
306
|
headingMark: Tag.define(),
|
|
@@ -226,51 +333,28 @@ var markdownTagsExtension = {
|
|
|
226
333
|
]
|
|
227
334
|
};
|
|
228
335
|
|
|
229
|
-
// packages/ui/react-ui-editor/src/components/
|
|
336
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
|
|
230
337
|
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
231
338
|
import { HighlightStyle } from "@codemirror/language";
|
|
232
339
|
import { tags } from "@lezer/highlight";
|
|
233
|
-
import
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
var
|
|
238
|
-
1: "mbs-4 mbe-2 text-4xl font-semibold text-inherit no-underline",
|
|
239
|
-
2: "mbs-4 mbe-2 text-3xl font-bold text-inherit no-underline",
|
|
240
|
-
3: "mbs-4 mbe-2 text-2xl font-bold text-inherit no-underline",
|
|
241
|
-
4: "mbs-4 mbe-2 text-xl font-extrabold text-inherit no-underline",
|
|
242
|
-
5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
|
|
243
|
-
6: "mbs-4 mbe-2 font-black text-inherit no-underline"
|
|
244
|
-
};
|
|
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
|
-
var bold = "font-bold";
|
|
253
|
-
var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
|
|
254
|
-
var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
|
|
255
|
-
var italic = "italic";
|
|
256
|
-
var strikethrough = "line-through";
|
|
257
|
-
var mark = "!font-normal !no-underline !text-inherit opacity-40";
|
|
258
|
-
|
|
259
|
-
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
260
|
-
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
261
|
-
var tokens = tailwindConfig({}).theme;
|
|
262
|
-
|
|
263
|
-
// packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
|
|
340
|
+
import get2 from "lodash.get";
|
|
341
|
+
var chalky = "#e5c07b";
|
|
342
|
+
var coral = "#e06c75";
|
|
343
|
+
var cyan = "#56b6c2";
|
|
344
|
+
var invalid = "#ffffff";
|
|
264
345
|
var ivory = "#abb2bf";
|
|
346
|
+
var stone = "#7d8799";
|
|
265
347
|
var malibu = "#61afef";
|
|
348
|
+
var sage = "#98c379";
|
|
349
|
+
var whiskey = "#d19a66";
|
|
350
|
+
var violet = "#c678dd";
|
|
266
351
|
var highlightBackground = "#2c313a";
|
|
267
352
|
var tooltipBackground = "#353a42";
|
|
268
|
-
var
|
|
269
|
-
var monospace = get(tokens, "fontFamily.mono", [
|
|
353
|
+
var monospace = get2(tokens, "fontFamily.mono", [
|
|
270
354
|
"monospace"
|
|
271
355
|
]).join(",");
|
|
272
356
|
var markdownTheme = {
|
|
273
|
-
// TODO(thure):
|
|
357
|
+
// TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
|
|
274
358
|
// '&': {
|
|
275
359
|
// color: ivory,
|
|
276
360
|
// backgroundColor: background
|
|
@@ -307,63 +391,69 @@ var markdownTheme = {
|
|
|
307
391
|
// border: 'none',
|
|
308
392
|
// color: '#ddd'
|
|
309
393
|
// },
|
|
310
|
-
"
|
|
311
|
-
|
|
312
|
-
|
|
394
|
+
"& .cm-tooltip": {
|
|
395
|
+
backgroundColor: "transparent",
|
|
396
|
+
border: "none"
|
|
313
397
|
},
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
},
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
398
|
+
// '.dark & .cm-tooltip': {
|
|
399
|
+
// border: 'none',
|
|
400
|
+
// backgroundColor: tooltipBackground,
|
|
401
|
+
// },
|
|
402
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
|
|
403
|
+
// borderTopColor: 'transparent',
|
|
404
|
+
// borderBottomColor: 'transparent',
|
|
405
|
+
// },
|
|
406
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
|
|
407
|
+
// borderTopColor: tooltipBackground,
|
|
408
|
+
// borderBottomColor: tooltipBackground,
|
|
409
|
+
// },
|
|
410
|
+
// '.dark & .cm-tooltip-autocomplete': {
|
|
411
|
+
// '& > ul > li[aria-selected]': {
|
|
412
|
+
// backgroundColor: highlightBackground,
|
|
413
|
+
// color: ivory,
|
|
414
|
+
// },
|
|
415
|
+
// },
|
|
416
|
+
"&.cm-focused": {
|
|
417
|
+
outline: "none"
|
|
330
418
|
},
|
|
331
419
|
"& .cm-line": {
|
|
332
420
|
paddingInline: 0,
|
|
421
|
+
lineHeight: 1.6,
|
|
333
422
|
minBlockSize: "1.6em"
|
|
334
423
|
},
|
|
335
424
|
"& .cm-line *": {
|
|
336
425
|
lineHeight: 1.6
|
|
337
426
|
},
|
|
338
427
|
"&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
|
|
339
|
-
background:
|
|
428
|
+
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
340
429
|
},
|
|
341
430
|
".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
|
|
342
|
-
background:
|
|
431
|
+
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
343
432
|
},
|
|
344
433
|
"& .cm-selectionMatch": {
|
|
345
|
-
background:
|
|
434
|
+
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
346
435
|
},
|
|
347
436
|
".dark & .cm-selectionMatch": {
|
|
348
|
-
background:
|
|
437
|
+
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
349
438
|
},
|
|
350
439
|
"& .cm-content": {
|
|
440
|
+
padding: 0,
|
|
351
441
|
caretColor: "black"
|
|
352
442
|
},
|
|
353
443
|
".dark & .cm-content": {
|
|
354
|
-
caretColor:
|
|
444
|
+
caretColor: "white"
|
|
355
445
|
},
|
|
356
446
|
"& .cm-cursor": {
|
|
357
447
|
borderLeftColor: "black"
|
|
358
448
|
},
|
|
359
449
|
".dark & .cm-cursor": {
|
|
360
|
-
borderLeftColor:
|
|
450
|
+
borderLeftColor: "white"
|
|
361
451
|
},
|
|
362
452
|
".cm-placeholder": {
|
|
363
|
-
fontFamily:
|
|
453
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
364
454
|
},
|
|
365
455
|
"& .cm-scroller": {
|
|
366
|
-
fontFamily:
|
|
456
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
|
|
367
457
|
overflow: "visible"
|
|
368
458
|
},
|
|
369
459
|
"& .cm-activeLine": {
|
|
@@ -373,7 +463,7 @@ var markdownTheme = {
|
|
|
373
463
|
backgroundColor: "transparent"
|
|
374
464
|
},
|
|
375
465
|
"& .cm-ySelectionInfo": {
|
|
376
|
-
fontFamily:
|
|
466
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(","),
|
|
377
467
|
padding: "2px 4px",
|
|
378
468
|
marginBlockStart: "-4px"
|
|
379
469
|
},
|
|
@@ -396,8 +486,16 @@ var markdownTheme = {
|
|
|
396
486
|
"& .cm-yLineSelection": {
|
|
397
487
|
margin: "0"
|
|
398
488
|
},
|
|
399
|
-
|
|
400
|
-
|
|
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, [
|
|
401
499
|
"extend",
|
|
402
500
|
"fontSize",
|
|
403
501
|
fontSize,
|
|
@@ -416,7 +514,7 @@ var markdownTheme = {
|
|
|
416
514
|
return acc;
|
|
417
515
|
}, {})
|
|
418
516
|
};
|
|
419
|
-
var
|
|
517
|
+
var markdownHighlightStyle = HighlightStyle.define([
|
|
420
518
|
{
|
|
421
519
|
tag: [
|
|
422
520
|
tags.keyword,
|
|
@@ -532,40 +630,341 @@ var markdownDarkHighlighting = HighlightStyle.define([
|
|
|
532
630
|
], {
|
|
533
631
|
scope: markdownLanguage,
|
|
534
632
|
all: {
|
|
535
|
-
fontFamily:
|
|
633
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
536
634
|
}
|
|
537
635
|
});
|
|
538
636
|
|
|
539
|
-
// packages/ui/react-ui-editor/src/components/
|
|
540
|
-
var
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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({})
|
|
549
731
|
});
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
|
565
757
|
}
|
|
566
|
-
|
|
567
|
-
|
|
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
|
|
568
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
|
+
}, []);
|
|
569
968
|
useEffect(() => {
|
|
570
969
|
if (provider && peer) {
|
|
571
970
|
provider.awareness.setLocalStateField("user", {
|
|
@@ -586,76 +985,253 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
586
985
|
peer,
|
|
587
986
|
themeMode
|
|
588
987
|
]);
|
|
589
|
-
|
|
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
|
|
1192
|
+
var EditorModes = [
|
|
1193
|
+
"default",
|
|
1194
|
+
"vim"
|
|
1195
|
+
];
|
|
1196
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots, editorMode }, forwardedRef) => {
|
|
1197
|
+
const { themeMode } = useThemeContext();
|
|
1198
|
+
const tabsterDOMAttribute = useFocusableGroup({
|
|
1199
|
+
tabBehavior: "limited"
|
|
1200
|
+
});
|
|
1201
|
+
const [parent, setParent] = useState(null);
|
|
1202
|
+
const [state, setState] = useState();
|
|
1203
|
+
const [view, setView] = useState();
|
|
1204
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
1205
|
+
editor: parent,
|
|
1206
|
+
state,
|
|
1207
|
+
view
|
|
1208
|
+
}), [
|
|
1209
|
+
view,
|
|
1210
|
+
state,
|
|
1211
|
+
parent
|
|
1212
|
+
]);
|
|
1213
|
+
const collaboration = useCollaboration(model, themeMode);
|
|
1214
|
+
useEffect2(() => {
|
|
590
1215
|
if (!parent) {
|
|
591
1216
|
return;
|
|
592
1217
|
}
|
|
593
|
-
const
|
|
594
|
-
|
|
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,
|
|
595
1221
|
extensions: [
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
drawSelection(),
|
|
606
|
-
dropCursor(),
|
|
607
|
-
EditorState.allowMultipleSelections.of(true),
|
|
608
|
-
indentOnInput(),
|
|
609
|
-
syntaxHighlighting(defaultHighlightStyle, {
|
|
610
|
-
fallback: true
|
|
611
|
-
}),
|
|
612
|
-
bracketMatching(),
|
|
613
|
-
closeBrackets(),
|
|
614
|
-
autocompletion(),
|
|
615
|
-
rectangularSelection(),
|
|
616
|
-
crosshairCursor(),
|
|
617
|
-
highlightActiveLine(),
|
|
618
|
-
highlightSelectionMatches(),
|
|
619
|
-
placeholder(slots.editor?.placeholder ?? ""),
|
|
620
|
-
keymap.of([
|
|
621
|
-
...closeBracketsKeymap,
|
|
622
|
-
...defaultKeymap,
|
|
623
|
-
...searchKeymap,
|
|
624
|
-
...historyKeymap,
|
|
625
|
-
...foldKeymap,
|
|
626
|
-
...completionKeymap,
|
|
627
|
-
...lintKeymap,
|
|
628
|
-
indentWithTab
|
|
629
|
-
]),
|
|
630
|
-
EditorView.lineWrapping,
|
|
631
|
-
// Theme
|
|
632
|
-
markdown({
|
|
633
|
-
base: markdownLanguage2,
|
|
634
|
-
codeLanguages: languages,
|
|
635
|
-
extensions: [
|
|
636
|
-
markdownTagsExtension
|
|
637
|
-
]
|
|
638
|
-
}),
|
|
639
|
-
EditorView.theme({
|
|
640
|
-
...markdownTheme,
|
|
641
|
-
...slots.editor?.markdownTheme
|
|
642
|
-
}),
|
|
643
|
-
...themeMode === "dark" ? [
|
|
644
|
-
syntaxHighlighting(oneDarkHighlightStyle)
|
|
645
|
-
] : [
|
|
646
|
-
syntaxHighlighting(defaultHighlightStyle)
|
|
647
|
-
],
|
|
648
|
-
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
649
|
-
syntaxHighlighting(markdownDarkHighlighting),
|
|
650
|
-
// Collaboration
|
|
651
|
-
...content instanceof YText ? [
|
|
652
|
-
yCollab(content, provider?.awareness)
|
|
653
|
-
] : []
|
|
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),
|
|
1228
|
+
// Custom.
|
|
1229
|
+
...extensions
|
|
1230
|
+
].filter(Boolean)
|
|
655
1231
|
});
|
|
656
1232
|
setState(state2);
|
|
657
1233
|
view?.destroy();
|
|
658
|
-
setView(new
|
|
1234
|
+
setView(new EditorView5({
|
|
659
1235
|
state: state2,
|
|
660
1236
|
parent
|
|
661
1237
|
}));
|
|
@@ -666,211 +1242,110 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
666
1242
|
};
|
|
667
1243
|
}, [
|
|
668
1244
|
parent,
|
|
669
|
-
content,
|
|
670
|
-
provider?.awareness,
|
|
1245
|
+
model.content,
|
|
671
1246
|
themeMode,
|
|
672
1247
|
editorMode
|
|
673
1248
|
]);
|
|
674
|
-
const handleKeyUp = useCallback((
|
|
1249
|
+
const handleKeyUp = useCallback((event) => {
|
|
1250
|
+
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
675
1251
|
switch (key) {
|
|
676
|
-
case "Enter":
|
|
1252
|
+
case "Enter": {
|
|
677
1253
|
view?.contentDOM.focus();
|
|
678
1254
|
break;
|
|
679
|
-
|
|
1255
|
+
}
|
|
1256
|
+
case "Escape": {
|
|
680
1257
|
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
|
|
681
1258
|
break;
|
|
1259
|
+
}
|
|
682
1260
|
}
|
|
683
1261
|
}, [
|
|
684
1262
|
view,
|
|
685
1263
|
editorMode
|
|
686
1264
|
]);
|
|
687
1265
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1266
|
+
key: model.id,
|
|
1267
|
+
ref: setParent,
|
|
688
1268
|
tabIndex: 0,
|
|
689
|
-
|
|
690
|
-
...
|
|
691
|
-
onKeyUp: handleKeyUp
|
|
692
|
-
...editorMode !== "vim" ? tabsterDOMAttribute : {},
|
|
693
|
-
ref: setParent
|
|
1269
|
+
...slots?.root,
|
|
1270
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
1271
|
+
onKeyUp: handleKeyUp
|
|
694
1272
|
});
|
|
695
1273
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
StarterKit.configure({
|
|
712
|
-
// Extensions
|
|
713
|
-
history: false,
|
|
714
|
-
// Nodes
|
|
715
|
-
blockquote: {
|
|
716
|
-
HTMLAttributes: {
|
|
717
|
-
class: blockquote
|
|
718
|
-
}
|
|
719
|
-
},
|
|
720
|
-
bulletList: {
|
|
721
|
-
HTMLAttributes: {
|
|
722
|
-
class: unorderedList
|
|
723
|
-
}
|
|
724
|
-
},
|
|
725
|
-
codeBlock: {
|
|
726
|
-
HTMLAttributes: {
|
|
727
|
-
class: codeBlock
|
|
728
|
-
}
|
|
729
|
-
},
|
|
730
|
-
heading: false,
|
|
731
|
-
horizontalRule: {
|
|
732
|
-
HTMLAttributes: {
|
|
733
|
-
class: horizontalRule
|
|
734
|
-
}
|
|
735
|
-
},
|
|
736
|
-
listItem: false,
|
|
737
|
-
orderedList: {
|
|
738
|
-
HTMLAttributes: {
|
|
739
|
-
class: orderedList
|
|
740
|
-
}
|
|
741
|
-
},
|
|
742
|
-
paragraph: {
|
|
743
|
-
HTMLAttributes: {
|
|
744
|
-
class: paragraph
|
|
745
|
-
}
|
|
746
|
-
},
|
|
747
|
-
// Marks
|
|
748
|
-
bold: {
|
|
749
|
-
HTMLAttributes: {
|
|
750
|
-
class: bold
|
|
751
|
-
}
|
|
752
|
-
},
|
|
753
|
-
code: {
|
|
754
|
-
HTMLAttributes: {
|
|
755
|
-
class: codeWithoutMarks
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
italic: {
|
|
759
|
-
HTMLAttributes: {
|
|
760
|
-
class: italic
|
|
761
|
-
}
|
|
762
|
-
},
|
|
763
|
-
strike: {
|
|
764
|
-
HTMLAttributes: {
|
|
765
|
-
class: strikethrough
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}),
|
|
769
|
-
Heading.extend({
|
|
770
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
771
|
-
const hasLevel = this.options.levels.includes(node.attrs.level);
|
|
772
|
-
const level = hasLevel ? node.attrs.level : this.options.levels[0];
|
|
773
|
-
return [
|
|
774
|
-
`h${level}`,
|
|
775
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
776
|
-
class: heading[level]
|
|
777
|
-
}),
|
|
778
|
-
0
|
|
779
|
-
];
|
|
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
|
|
1281
|
+
})
|
|
1282
|
+
];
|
|
1283
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1284
|
+
ref: forwardedRef,
|
|
1285
|
+
extensions,
|
|
1286
|
+
slots: defaultsDeep({}, slots, {
|
|
1287
|
+
editor: {
|
|
1288
|
+
theme: basicTheme
|
|
780
1289
|
}
|
|
781
1290
|
}),
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
{
|
|
792
|
-
role: "none"
|
|
793
|
-
},
|
|
794
|
-
0
|
|
795
|
-
]
|
|
796
|
-
]
|
|
797
|
-
}),
|
|
798
|
-
// https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration
|
|
799
|
-
...model && typeof model.content !== "string" ? [
|
|
800
|
-
Collaboration.configure({
|
|
801
|
-
fragment: model.content
|
|
802
|
-
})
|
|
803
|
-
] : [],
|
|
804
|
-
...model?.provider ? [
|
|
805
|
-
CollaborationCursor.configure({
|
|
806
|
-
provider: model.provider,
|
|
807
|
-
user: model.peer && {
|
|
808
|
-
name: model.peer.name ?? generateName2(model.peer.id),
|
|
809
|
-
color: cursorColor.color
|
|
810
|
-
}
|
|
811
|
-
})
|
|
812
|
-
] : [],
|
|
813
|
-
Placeholder.configure({
|
|
814
|
-
placeholder: placeholder2,
|
|
815
|
-
emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
|
|
1291
|
+
...props
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1294
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
|
|
1295
|
+
const { themeMode } = useThemeContext();
|
|
1296
|
+
const extensions = [
|
|
1297
|
+
..._extensions ?? [],
|
|
1298
|
+
markdownBundle({
|
|
1299
|
+
themeMode
|
|
816
1300
|
})
|
|
817
|
-
]
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
return useNaturalEditor({
|
|
1301
|
+
];
|
|
1302
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1303
|
+
ref: forwardedRef,
|
|
821
1304
|
extensions,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
|
|
826
|
-
tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
|
|
1305
|
+
slots: defaultsDeep({}, slots, {
|
|
1306
|
+
editor: {
|
|
1307
|
+
theme: markdownTheme
|
|
827
1308
|
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
extensions
|
|
831
|
-
]);
|
|
832
|
-
};
|
|
833
|
-
var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
|
|
834
|
-
const editor = useEditor(props);
|
|
835
|
-
useImperativeHandle2(ref, () => editor, [
|
|
836
|
-
editor
|
|
837
|
-
]);
|
|
838
|
-
return /* @__PURE__ */ React2.createElement(EditorContent, {
|
|
839
|
-
...props.slots?.root,
|
|
840
|
-
editor
|
|
1309
|
+
}),
|
|
1310
|
+
...props
|
|
841
1311
|
});
|
|
842
1312
|
});
|
|
843
|
-
|
|
844
|
-
// packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
|
|
845
|
-
var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...options }, forwardedRef) => {
|
|
846
|
-
const model = useTextModel(options);
|
|
847
|
-
if (model?.content instanceof YXmlFragment) {
|
|
848
|
-
return /* @__PURE__ */ React3.createElement(RichTextEditor, {
|
|
849
|
-
ref: forwardedRef,
|
|
850
|
-
model,
|
|
851
|
-
slots
|
|
852
|
-
});
|
|
853
|
-
} else {
|
|
854
|
-
return /* @__PURE__ */ React3.createElement(MarkdownEditor, {
|
|
855
|
-
ref: forwardedRef,
|
|
856
|
-
model,
|
|
857
|
-
slots
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
}));
|
|
861
|
-
|
|
862
|
-
// packages/ui/react-ui-editor/src/index.ts
|
|
863
|
-
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
864
|
-
import { Doc, YText as YText2, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
|
|
865
1313
|
export {
|
|
1314
|
+
BaseTextEditor,
|
|
866
1315
|
Doc,
|
|
867
|
-
Editor,
|
|
868
1316
|
EditorModes,
|
|
869
1317
|
MarkdownEditor,
|
|
870
|
-
|
|
1318
|
+
SpaceAwarenessProvider,
|
|
1319
|
+
TextEditor,
|
|
871
1320
|
TextKind,
|
|
872
1321
|
YText2 as YText,
|
|
873
|
-
|
|
874
|
-
|
|
1322
|
+
YXmlFragment,
|
|
1323
|
+
basicBundle,
|
|
1324
|
+
basicTheme,
|
|
1325
|
+
chalky,
|
|
1326
|
+
coral,
|
|
1327
|
+
createHyperlinkTooltip,
|
|
1328
|
+
cursorColor,
|
|
1329
|
+
cyan,
|
|
1330
|
+
highlightBackground,
|
|
1331
|
+
hyperLinkExtension,
|
|
1332
|
+
hyperlinkDecoration,
|
|
1333
|
+
hyperlinkStyle,
|
|
1334
|
+
hyperlinkWidget,
|
|
1335
|
+
invalid,
|
|
1336
|
+
ivory,
|
|
1337
|
+
malibu,
|
|
1338
|
+
markdownBundle,
|
|
1339
|
+
markdownHighlightStyle,
|
|
1340
|
+
markdownTheme,
|
|
1341
|
+
onChangeExtension,
|
|
1342
|
+
sage,
|
|
1343
|
+
stone,
|
|
1344
|
+
tags2 as tags,
|
|
1345
|
+
tooltipBackground,
|
|
1346
|
+
useCollaboration,
|
|
1347
|
+
useTextModel,
|
|
1348
|
+
violet,
|
|
1349
|
+
whiskey
|
|
875
1350
|
};
|
|
876
1351
|
//# sourceMappingURL=index.mjs.map
|