@dxos/react-ui-editor 0.3.9-next.7522f7a → 0.3.9
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 +930 -811
- 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 -10
- 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/{theme.d.ts → extensions/basic/theme.d.ts} +2 -1
- 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.map +1 -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.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 +4 -3
- 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 +30 -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 -47
- package/src/components/TextEditor/TextEditor.stories.tsx +10 -3
- package/src/components/TextEditor/TextEditor.tsx +113 -56
- 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/{theme.ts → extensions/basic/theme.ts} +22 -8
- package/src/components/TextEditor/extensions/change.ts +22 -0
- package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
- package/src/components/TextEditor/extensions/index.ts +4 -3
- 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} +16 -16
- 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 -14
- 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 -21
- 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 -100
- 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/hyperlinkDecoration.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts.map +0 -1
- 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 -36
- package/src/components/Editor/index.ts +0 -5
- package/src/components/Markdown/Markdown.stories.tsx +0 -70
- package/src/components/Markdown/Markdown.tsx +0 -239
- 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/dist/types/src/components/TextEditor/extensions/{hyperlinkDecoration.d.ts → hyperlink/hyperlinkDecoration.d.ts} +0 -0
- /package/dist/types/src/components/TextEditor/extensions/{hyperlinkTooltip.d.ts → hyperlink/hyperlinkTooltip.d.ts} +0 -0
- /package/dist/types/src/components/TextEditor/extensions/{hyperlinkWidget.d.ts → hyperlink/hyperlinkWidget.d.ts} +0 -0
- /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/TextEditor/extensions/{hyperlinkDecoration.ts → hyperlink/hyperlinkDecoration.ts} +0 -0
- /package/src/components/TextEditor/extensions/{hyperlinkTooltip.tsx → hyperlink/hyperlinkTooltip.tsx} +0 -0
- /package/src/components/TextEditor/extensions/{hyperlinkWidget.tsx → hyperlink/hyperlinkWidget.tsx} +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,48 +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
|
-
|
|
239
|
-
|
|
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";
|
|
345
|
+
var ivory = "#abb2bf";
|
|
346
|
+
var stone = "#7d8799";
|
|
264
347
|
var malibu = "#61afef";
|
|
265
|
-
var
|
|
266
|
-
var
|
|
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", [
|
|
267
354
|
"monospace"
|
|
268
355
|
]).join(",");
|
|
269
356
|
var markdownTheme = {
|
|
270
|
-
// TODO(thure):
|
|
357
|
+
// TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
|
|
271
358
|
// '&': {
|
|
272
359
|
// color: ivory,
|
|
273
360
|
// backgroundColor: background
|
|
@@ -304,7 +391,7 @@ var markdownTheme = {
|
|
|
304
391
|
// border: 'none',
|
|
305
392
|
// color: '#ddd'
|
|
306
393
|
// },
|
|
307
|
-
".cm-tooltip": {
|
|
394
|
+
"& .cm-tooltip": {
|
|
308
395
|
backgroundColor: "transparent",
|
|
309
396
|
border: "none"
|
|
310
397
|
},
|
|
@@ -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
|
},
|
|
@@ -404,8 +493,9 @@ var markdownTheme = {
|
|
|
404
493
|
textUnderlineOffset: "2px",
|
|
405
494
|
borderRadius: ".125rem"
|
|
406
495
|
},
|
|
407
|
-
|
|
408
|
-
|
|
496
|
+
// TODO(burdon): Document.
|
|
497
|
+
...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
498
|
+
const height = get2(tokens, [
|
|
409
499
|
"extend",
|
|
410
500
|
"fontSize",
|
|
411
501
|
fontSize,
|
|
@@ -540,40 +630,341 @@ var markdownHighlightStyle = HighlightStyle.define([
|
|
|
540
630
|
], {
|
|
541
631
|
scope: markdownLanguage,
|
|
542
632
|
all: {
|
|
543
|
-
fontFamily:
|
|
633
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
544
634
|
}
|
|
545
635
|
});
|
|
546
636
|
|
|
547
|
-
// packages/ui/react-ui-editor/src/components/
|
|
548
|
-
var
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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({})
|
|
557
731
|
});
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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]) {
|
|
572
811
|
return null;
|
|
573
812
|
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
|
|
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
|
+
}, []);
|
|
577
968
|
useEffect(() => {
|
|
578
969
|
if (provider && peer) {
|
|
579
970
|
provider.awareness.setLocalStateField("user", {
|
|
@@ -594,382 +985,253 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
|
|
|
594
985
|
peer,
|
|
595
986
|
themeMode
|
|
596
987
|
]);
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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/circleci/project/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));
|
|
600
1055
|
}
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}),
|
|
626
|
-
EditorState.allowMultipleSelections.of(true),
|
|
627
|
-
EditorView.lineWrapping,
|
|
628
|
-
keymap.of([
|
|
629
|
-
...closeBracketsKeymap,
|
|
630
|
-
...completionKeymap,
|
|
631
|
-
...defaultKeymap,
|
|
632
|
-
...foldKeymap,
|
|
633
|
-
...historyKeymap,
|
|
634
|
-
...lintKeymap,
|
|
635
|
-
...searchKeymap,
|
|
636
|
-
indentWithTab
|
|
637
|
-
]),
|
|
638
|
-
// Main extension.
|
|
639
|
-
// https://github.com/codemirror/lang-markdown
|
|
640
|
-
markdown({
|
|
641
|
-
base: markdownLanguage2,
|
|
642
|
-
codeLanguages: languages,
|
|
643
|
-
extensions: [
|
|
644
|
-
// TODO(burdon): This seems to upgrade the parser.
|
|
645
|
-
// GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
|
|
646
|
-
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
647
|
-
// https://github.github.com/gfm
|
|
648
|
-
// GFM,
|
|
649
|
-
// Custom styling.
|
|
650
|
-
markdownTagsExtension
|
|
651
|
-
]
|
|
652
|
-
}),
|
|
653
|
-
// Theme.
|
|
654
|
-
EditorView.theme({
|
|
655
|
-
...markdownTheme,
|
|
656
|
-
...slots.editor?.markdownTheme
|
|
657
|
-
}),
|
|
658
|
-
...themeMode === "dark" ? [
|
|
659
|
-
syntaxHighlighting(oneDarkHighlightStyle)
|
|
660
|
-
] : [
|
|
661
|
-
syntaxHighlighting(defaultHighlightStyle)
|
|
662
|
-
],
|
|
663
|
-
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
664
|
-
syntaxHighlighting(markdownHighlightStyle),
|
|
665
|
-
// Settings.
|
|
666
|
-
...editorMode === "vim" ? [
|
|
667
|
-
vim()
|
|
668
|
-
] : [],
|
|
669
|
-
// TODO(burdon): Move to extensions.
|
|
670
|
-
// Replication and awareness (incl. remote selection).
|
|
671
|
-
// https://codemirror.net/docs/ref/#collab
|
|
672
|
-
...content instanceof YText ? [
|
|
673
|
-
yCollab(content, provider?.awareness)
|
|
674
|
-
] : [],
|
|
675
|
-
// Custom.
|
|
676
|
-
...extensions
|
|
677
|
-
]
|
|
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)
|
|
678
1080
|
});
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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);
|
|
702
1108
|
break;
|
|
703
1109
|
}
|
|
704
|
-
case
|
|
705
|
-
|
|
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;
|
|
706
1114
|
break;
|
|
707
1115
|
}
|
|
1116
|
+
default: {
|
|
1117
|
+
console.error("Invalid message:", messageType);
|
|
1118
|
+
return encoder;
|
|
1119
|
+
}
|
|
708
1120
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
});
|
|
721
|
-
});
|
|
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
|
+
};
|
|
722
1132
|
|
|
723
|
-
// packages/ui/react-ui-editor/src/
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
import ListItem from "@tiptap/extension-list-item";
|
|
729
|
-
import Placeholder from "@tiptap/extension-placeholder";
|
|
730
|
-
import { EditorContent, useEditor as useNaturalEditor } from "@tiptap/react";
|
|
731
|
-
import StarterKit from "@tiptap/starter-kit";
|
|
732
|
-
import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
|
|
733
|
-
import { generateName as generateName2 } from "@dxos/display-name";
|
|
734
|
-
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
735
|
-
var useEditor = ({ model, placeholder: placeholder3 = "Enter text\u2026", slots = {} }) => {
|
|
736
|
-
const extensions = useMemo3(() => [
|
|
737
|
-
StarterKit.configure({
|
|
738
|
-
// Extensions
|
|
739
|
-
history: false,
|
|
740
|
-
// Nodes
|
|
741
|
-
blockquote: {
|
|
742
|
-
HTMLAttributes: {
|
|
743
|
-
class: blockquote
|
|
744
|
-
}
|
|
745
|
-
},
|
|
746
|
-
bulletList: {
|
|
747
|
-
HTMLAttributes: {
|
|
748
|
-
class: unorderedList
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
|
-
codeBlock: {
|
|
752
|
-
HTMLAttributes: {
|
|
753
|
-
class: codeBlock
|
|
754
|
-
}
|
|
755
|
-
},
|
|
756
|
-
heading: false,
|
|
757
|
-
horizontalRule: {
|
|
758
|
-
HTMLAttributes: {
|
|
759
|
-
class: horizontalRule
|
|
760
|
-
}
|
|
761
|
-
},
|
|
762
|
-
listItem: false,
|
|
763
|
-
orderedList: {
|
|
764
|
-
HTMLAttributes: {
|
|
765
|
-
class: orderedList
|
|
766
|
-
}
|
|
767
|
-
},
|
|
768
|
-
paragraph: {
|
|
769
|
-
HTMLAttributes: {
|
|
770
|
-
class: paragraph
|
|
771
|
-
}
|
|
772
|
-
},
|
|
773
|
-
// Marks
|
|
774
|
-
bold: {
|
|
775
|
-
HTMLAttributes: {
|
|
776
|
-
class: bold
|
|
777
|
-
}
|
|
778
|
-
},
|
|
779
|
-
code: {
|
|
780
|
-
HTMLAttributes: {
|
|
781
|
-
class: codeWithoutMarks
|
|
782
|
-
}
|
|
783
|
-
},
|
|
784
|
-
italic: {
|
|
785
|
-
HTMLAttributes: {
|
|
786
|
-
class: italic
|
|
787
|
-
}
|
|
788
|
-
},
|
|
789
|
-
strike: {
|
|
790
|
-
HTMLAttributes: {
|
|
791
|
-
class: strikethrough
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}),
|
|
795
|
-
Heading.extend({
|
|
796
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
797
|
-
const hasLevel = this.options.levels.includes(node.attrs.level);
|
|
798
|
-
const level = hasLevel ? node.attrs.level : this.options.levels[0];
|
|
799
|
-
return [
|
|
800
|
-
`h${level}`,
|
|
801
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
802
|
-
class: heading[level]
|
|
803
|
-
}),
|
|
804
|
-
0
|
|
805
|
-
];
|
|
806
|
-
}
|
|
807
|
-
}),
|
|
808
|
-
ListItem.extend({
|
|
809
|
-
renderHTML: ({ HTMLAttributes }) => [
|
|
810
|
-
"li",
|
|
811
|
-
mergeAttributes(HTMLAttributes, {
|
|
812
|
-
marker: "\u2022 ",
|
|
813
|
-
class: listItem
|
|
814
|
-
}),
|
|
815
|
-
[
|
|
816
|
-
"div",
|
|
817
|
-
{
|
|
818
|
-
role: "none"
|
|
819
|
-
},
|
|
820
|
-
0
|
|
821
|
-
]
|
|
822
|
-
]
|
|
823
|
-
}),
|
|
824
|
-
// https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration
|
|
825
|
-
...model && typeof model.content !== "string" ? [
|
|
826
|
-
Collaboration.configure({
|
|
827
|
-
fragment: model.content
|
|
828
|
-
})
|
|
829
|
-
] : [],
|
|
830
|
-
...model?.provider ? [
|
|
831
|
-
CollaborationCursor.configure({
|
|
832
|
-
provider: model.provider,
|
|
833
|
-
user: model.peer && {
|
|
834
|
-
name: model.peer.name ?? generateName2(model.peer.id),
|
|
835
|
-
color: cursorColor.color
|
|
836
|
-
}
|
|
837
|
-
})
|
|
838
|
-
] : [],
|
|
839
|
-
Placeholder.configure({
|
|
840
|
-
placeholder: placeholder3,
|
|
841
|
-
emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
|
|
842
|
-
})
|
|
843
|
-
], [
|
|
844
|
-
model?.id
|
|
845
|
-
]);
|
|
846
|
-
return useNaturalEditor({
|
|
847
|
-
extensions,
|
|
848
|
-
editorProps: {
|
|
849
|
-
attributes: {
|
|
850
|
-
class: mx2("focus:outline-none focus-visible:outline-none", slots.editor?.className),
|
|
851
|
-
spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
|
|
852
|
-
tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
|
|
853
|
-
}
|
|
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;
|
|
854
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
|
+
});
|
|
855
1147
|
}, [
|
|
856
|
-
|
|
1148
|
+
identity,
|
|
1149
|
+
space,
|
|
1150
|
+
text,
|
|
1151
|
+
text?.doc
|
|
857
1152
|
]);
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|
|
863
1164
|
]);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
if (
|
|
877
|
-
return
|
|
878
|
-
ref: forwardedRef,
|
|
879
|
-
model,
|
|
880
|
-
slots
|
|
881
|
-
});
|
|
882
|
-
} else {
|
|
883
|
-
return /* @__PURE__ */ React3.createElement(MarkdownEditor, {
|
|
884
|
-
ref: forwardedRef,
|
|
885
|
-
model,
|
|
886
|
-
slots
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
}));
|
|
890
|
-
|
|
891
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
892
|
-
import { tags as tags2 } from "@lezer/highlight";
|
|
893
|
-
|
|
894
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
895
|
-
import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
|
|
896
|
-
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
897
|
-
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
898
|
-
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
899
|
-
import { EditorView as EditorView2, placeholder as placeholder2 } from "@codemirror/view";
|
|
900
|
-
import React4, { forwardRef as forwardRef4, useEffect as useEffect2, useImperativeHandle as useImperativeHandle3, useState as useState2, useCallback as useCallback2 } from "react";
|
|
901
|
-
import { yCollab as yCollab2 } from "y-codemirror.next";
|
|
902
|
-
import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
|
|
903
|
-
import { YText as YText2 } from "@dxos/text-model";
|
|
904
|
-
|
|
905
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/theme.ts
|
|
906
|
-
import get2 from "lodash.get";
|
|
907
|
-
import { tailwindConfig as tailwindConfig2 } from "@dxos/react-ui-theme";
|
|
908
|
-
var tokens2 = tailwindConfig2({}).theme;
|
|
909
|
-
var defaultStyles = {
|
|
910
|
-
"&.cm-focused": {
|
|
911
|
-
outline: "none"
|
|
912
|
-
},
|
|
913
|
-
".cm-scroller": {
|
|
914
|
-
fontFamily: get2(tokens2, "fontFamily.body", []).join(","),
|
|
915
|
-
overflow: "visible"
|
|
916
|
-
},
|
|
917
|
-
".cm-tooltip": {
|
|
918
|
-
backgroundColor: "transparent",
|
|
919
|
-
border: "none"
|
|
920
|
-
},
|
|
921
|
-
".cm-link": {
|
|
922
|
-
color: get2(tokens2, "extend.colors.primary.500"),
|
|
923
|
-
textDecoration: "underline"
|
|
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;
|
|
924
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
|
+
};
|
|
925
1189
|
};
|
|
926
1190
|
|
|
927
1191
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
928
|
-
var
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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,
|
|
941
1207
|
view
|
|
1208
|
+
}), [
|
|
1209
|
+
view,
|
|
1210
|
+
state,
|
|
1211
|
+
parent
|
|
942
1212
|
]);
|
|
1213
|
+
const collaboration = useCollaboration(model, themeMode);
|
|
943
1214
|
useEffect2(() => {
|
|
944
1215
|
if (!parent) {
|
|
945
1216
|
return;
|
|
946
1217
|
}
|
|
947
|
-
|
|
1218
|
+
const initialText = isDocAccessor2(model.content) ? get3(model.content.handle.docSync(), model.content.path) : model.content?.toString();
|
|
948
1219
|
const state2 = EditorState2.create({
|
|
949
|
-
doc:
|
|
1220
|
+
doc: initialText,
|
|
950
1221
|
extensions: [
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
placeholder2(slots.editor?.placeholder ?? ""),
|
|
954
|
-
EditorView2.lineWrapping,
|
|
955
|
-
// Themes.
|
|
956
|
-
EditorView2.theme(theme),
|
|
957
|
-
...themeMode === "dark" ? [
|
|
958
|
-
syntaxHighlighting2(oneDarkHighlightStyle2)
|
|
959
|
-
] : [
|
|
960
|
-
syntaxHighlighting2(defaultHighlightStyle2)
|
|
961
|
-
],
|
|
1222
|
+
// TODO(burdon): Factor out VIM mode?
|
|
1223
|
+
editorMode === "vim" && vim(),
|
|
962
1224
|
// Replication.
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
] : [],
|
|
1225
|
+
collaboration,
|
|
1226
|
+
// Theme.
|
|
1227
|
+
slots?.editor?.theme && EditorView5.theme(slots?.editor?.theme),
|
|
967
1228
|
// Custom.
|
|
968
1229
|
...extensions
|
|
969
|
-
]
|
|
1230
|
+
].filter(Boolean)
|
|
970
1231
|
});
|
|
971
1232
|
setState(state2);
|
|
972
|
-
|
|
1233
|
+
view?.destroy();
|
|
1234
|
+
setView(new EditorView5({
|
|
973
1235
|
state: state2,
|
|
974
1236
|
parent
|
|
975
1237
|
}));
|
|
@@ -980,253 +1242,110 @@ var TextEditor = /* @__PURE__ */ forwardRef4(({ model, extensions = [], theme =
|
|
|
980
1242
|
};
|
|
981
1243
|
}, [
|
|
982
1244
|
parent,
|
|
983
|
-
content,
|
|
984
|
-
themeMode
|
|
1245
|
+
model.content,
|
|
1246
|
+
themeMode,
|
|
1247
|
+
editorMode
|
|
985
1248
|
]);
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
});
|
|
1249
|
+
const handleKeyUp = useCallback((event) => {
|
|
1250
|
+
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
1251
|
+
switch (key) {
|
|
1252
|
+
case "Enter": {
|
|
1253
|
+
view?.contentDOM.focus();
|
|
1254
|
+
break;
|
|
1255
|
+
}
|
|
1256
|
+
case "Escape": {
|
|
1257
|
+
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
998
1260
|
}
|
|
999
1261
|
}, [
|
|
1000
|
-
view
|
|
1262
|
+
view,
|
|
1263
|
+
editorMode
|
|
1001
1264
|
]);
|
|
1002
|
-
return /* @__PURE__ */
|
|
1003
|
-
key: id,
|
|
1265
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1266
|
+
key: model.id,
|
|
1004
1267
|
ref: setParent,
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
...
|
|
1268
|
+
tabIndex: 0,
|
|
1269
|
+
...slots?.root,
|
|
1270
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
1271
|
+
onKeyUp: handleKeyUp
|
|
1008
1272
|
});
|
|
1009
1273
|
});
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
toDOM(view) {
|
|
1026
|
-
const link = document.createElement("a");
|
|
1027
|
-
link.setAttribute("class", "cm-link");
|
|
1028
|
-
link.textContent = this._text;
|
|
1029
|
-
if (this._url) {
|
|
1030
|
-
link.setAttribute("target", "_blank");
|
|
1031
|
-
link.href = this._url;
|
|
1032
|
-
} else {
|
|
1033
|
-
link.onclick = () => {
|
|
1034
|
-
view.dispatch({
|
|
1035
|
-
selection: {
|
|
1036
|
-
anchor: this._anchor
|
|
1037
|
-
}
|
|
1038
|
-
});
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
return link;
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
var hyperlinkDecoration = ({ link } = {}) => {
|
|
1045
|
-
return StateField2.define({
|
|
1046
|
-
create: (state) => update(state, link),
|
|
1047
|
-
update: (_, tr) => update(tr.state, link),
|
|
1048
|
-
provide: (field) => EditorView3.decorations.from(field)
|
|
1049
|
-
});
|
|
1050
|
-
};
|
|
1051
|
-
var update = (state, link) => {
|
|
1052
|
-
const builder = new RangeSetBuilder();
|
|
1053
|
-
const cursor2 = state.selection.main.head;
|
|
1054
|
-
syntaxTree(state).iterate({
|
|
1055
|
-
enter: (node) => {
|
|
1056
|
-
if (cursor2 < node.from || cursor2 > node.to) {
|
|
1057
|
-
if (node.name === "Link") {
|
|
1058
|
-
const marks = node.node.getChildren("LinkMark");
|
|
1059
|
-
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
1060
|
-
const urlNode = node.node.getChild("URL");
|
|
1061
|
-
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1062
|
-
builder.add(node.from, node.to, Decoration.replace({
|
|
1063
|
-
widget: new Link(node.from, text, link ? url : void 0)
|
|
1064
|
-
}));
|
|
1065
|
-
return false;
|
|
1066
|
-
}
|
|
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
|
|
1067
1289
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1290
|
+
}),
|
|
1291
|
+
...props
|
|
1070
1292
|
});
|
|
1071
|
-
return builder.finish();
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkTooltip.tsx
|
|
1075
|
-
import { hoverTooltip } from "@codemirror/view";
|
|
1076
|
-
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1077
|
-
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1078
|
-
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
|
|
1079
|
-
const { from, text } = view.state.doc.lineAt(pos);
|
|
1080
|
-
const p = pos - from;
|
|
1081
|
-
let idx = 0;
|
|
1082
|
-
let match;
|
|
1083
|
-
do {
|
|
1084
|
-
match = text.substring(idx).match(regexp);
|
|
1085
|
-
if (!match) {
|
|
1086
|
-
match = void 0;
|
|
1087
|
-
break;
|
|
1088
|
-
}
|
|
1089
|
-
idx = text.indexOf(match[0]);
|
|
1090
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1091
|
-
break;
|
|
1092
|
-
}
|
|
1093
|
-
idx += match[0].length;
|
|
1094
|
-
} while (true);
|
|
1095
|
-
if (!match) {
|
|
1096
|
-
return null;
|
|
1097
|
-
}
|
|
1098
|
-
const [, , url] = match ?? [];
|
|
1099
|
-
return {
|
|
1100
|
-
pos: idx + from,
|
|
1101
|
-
end: idx + from + match[0].length,
|
|
1102
|
-
above: true,
|
|
1103
|
-
create: () => {
|
|
1104
|
-
const el = document.createElement("a");
|
|
1105
|
-
el.innerText = "_";
|
|
1106
|
-
el.className = tooltipContent({}, "mb-2 p-1");
|
|
1107
|
-
el.setAttribute("target", "_blank");
|
|
1108
|
-
el.setAttribute("href", url);
|
|
1109
|
-
onHover(el, url);
|
|
1110
|
-
return {
|
|
1111
|
-
dom: el
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
});
|
|
1116
|
-
|
|
1117
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkWidget.tsx
|
|
1118
|
-
import { Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
1119
|
-
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
1120
|
-
var linkDecorator = () => new MatchDecorator({
|
|
1121
|
-
regexp: markdownLinkRegexp2,
|
|
1122
|
-
decorate: (add, from, to, match, view) => {
|
|
1123
|
-
const [_, label, url] = match;
|
|
1124
|
-
const p0 = {
|
|
1125
|
-
start: from,
|
|
1126
|
-
end: from + 1
|
|
1127
|
-
};
|
|
1128
|
-
const p1 = {
|
|
1129
|
-
start: from + 1,
|
|
1130
|
-
end: from + 1 + label.length
|
|
1131
|
-
};
|
|
1132
|
-
const p2 = {
|
|
1133
|
-
start: p1.end,
|
|
1134
|
-
end: p1.end + 1
|
|
1135
|
-
};
|
|
1136
|
-
const p3 = {
|
|
1137
|
-
start: p1.end + 1,
|
|
1138
|
-
end: p1.end + 1 + url.length + 2
|
|
1139
|
-
};
|
|
1140
|
-
add(p0.start, p0.end, Decoration2.mark({
|
|
1141
|
-
class: "cm-hyperlink-bracket",
|
|
1142
|
-
_attributes: {
|
|
1143
|
-
style: "display: none"
|
|
1144
|
-
}
|
|
1145
|
-
}));
|
|
1146
|
-
add(p1.start, p1.end, Decoration2.mark({
|
|
1147
|
-
class: "cm-hyperlink-label"
|
|
1148
|
-
}));
|
|
1149
|
-
add(p2.start, p2.end, Decoration2.mark({
|
|
1150
|
-
class: "cm-hyperlink-bracket",
|
|
1151
|
-
_attributes: {
|
|
1152
|
-
style: "display: none"
|
|
1153
|
-
}
|
|
1154
|
-
}));
|
|
1155
|
-
add(p3.start, p3.end, Decoration2.mark({
|
|
1156
|
-
class: "cm-hyperlink-url",
|
|
1157
|
-
_attributes: {
|
|
1158
|
-
style: "display: none"
|
|
1159
|
-
}
|
|
1160
|
-
}));
|
|
1161
|
-
}
|
|
1162
1293
|
});
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
// TODO(burdon): CLick to expand link.
|
|
1178
|
-
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
1179
|
-
mousedown: function(event, view) {
|
|
1180
|
-
const pos = view.posAtDOM(event.target);
|
|
1181
|
-
if (pos !== 0) {
|
|
1182
|
-
console.log(this);
|
|
1183
|
-
}
|
|
1184
|
-
},
|
|
1185
|
-
// TODO(burdon): Hide when cursor moves away.
|
|
1186
|
-
keydown: (event, view) => {
|
|
1187
|
-
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
|
|
1188
1308
|
}
|
|
1189
|
-
}
|
|
1309
|
+
}),
|
|
1310
|
+
...props
|
|
1190
1311
|
});
|
|
1191
|
-
}
|
|
1192
|
-
var hyperlinkStyle = EditorView4.baseTheme({
|
|
1193
|
-
".cm-hyperlink-label": {
|
|
1194
|
-
cursor: "pointer",
|
|
1195
|
-
textDecoration: "underline"
|
|
1196
|
-
},
|
|
1197
|
-
".cm-hyperlink-bracket": {
|
|
1198
|
-
opacity: 0.2
|
|
1199
|
-
},
|
|
1200
|
-
".cm-hyperlink-url": {
|
|
1201
|
-
opacity: 0.5,
|
|
1202
|
-
fontFamily: "monospace"
|
|
1203
|
-
}
|
|
1204
1312
|
});
|
|
1205
|
-
var hyperlinkWidget = [
|
|
1206
|
-
hyperLinkExtension(),
|
|
1207
|
-
hyperlinkStyle
|
|
1208
|
-
];
|
|
1209
|
-
|
|
1210
|
-
// packages/ui/react-ui-editor/src/index.ts
|
|
1211
|
-
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
1212
|
-
import { Doc, YText as YText3, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
|
|
1213
1313
|
export {
|
|
1314
|
+
BaseTextEditor,
|
|
1214
1315
|
Doc,
|
|
1215
|
-
Editor,
|
|
1216
1316
|
EditorModes,
|
|
1217
1317
|
MarkdownEditor,
|
|
1218
|
-
|
|
1318
|
+
SpaceAwarenessProvider,
|
|
1219
1319
|
TextEditor,
|
|
1220
1320
|
TextKind,
|
|
1221
|
-
|
|
1222
|
-
|
|
1321
|
+
YText2 as YText,
|
|
1322
|
+
YXmlFragment,
|
|
1323
|
+
basicBundle,
|
|
1324
|
+
basicTheme,
|
|
1325
|
+
chalky,
|
|
1326
|
+
coral,
|
|
1223
1327
|
createHyperlinkTooltip,
|
|
1224
|
-
|
|
1328
|
+
cursorColor,
|
|
1329
|
+
cyan,
|
|
1330
|
+
highlightBackground,
|
|
1225
1331
|
hyperLinkExtension,
|
|
1226
1332
|
hyperlinkDecoration,
|
|
1227
1333
|
hyperlinkStyle,
|
|
1228
1334
|
hyperlinkWidget,
|
|
1335
|
+
invalid,
|
|
1336
|
+
ivory,
|
|
1337
|
+
malibu,
|
|
1338
|
+
markdownBundle,
|
|
1339
|
+
markdownHighlightStyle,
|
|
1340
|
+
markdownTheme,
|
|
1341
|
+
onChangeExtension,
|
|
1342
|
+
sage,
|
|
1343
|
+
stone,
|
|
1229
1344
|
tags2 as tags,
|
|
1230
|
-
|
|
1345
|
+
tooltipBackground,
|
|
1346
|
+
useCollaboration,
|
|
1347
|
+
useTextModel,
|
|
1348
|
+
violet,
|
|
1349
|
+
whiskey
|
|
1231
1350
|
};
|
|
1232
1351
|
//# sourceMappingURL=index.mjs.map
|