@dxos/react-ui-editor 0.3.10-main.ee543cf → 0.3.10-next.63eb179
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 +735 -731
- 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.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -4
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +4 -9
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/{basic.d.ts → basic/bundle.d.ts} +1 -1
- 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/{experimental.d.ts → hyperlink/hyperlinkWidget.d.ts} +2 -2
- 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 +2 -5
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts +17 -0
- 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 +0 -2
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- 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/hooks/useTextModel.d.ts +4 -4
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +10 -12
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/package.json +32 -20
- package/src/{hooks/automerge → automerge}/automerge.stories.tsx +1 -2
- package/src/components/TextEditor/TextEditor.stories.tsx +43 -46
- package/src/components/TextEditor/TextEditor.tsx +45 -59
- package/src/components/TextEditor/extensions/{basic.ts → basic/bundle.ts} +2 -4
- 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/{listener.ts → change.ts} +3 -3
- package/src/components/TextEditor/extensions/{link.ts → hyperlink/hyperlinkDecoration.ts} +31 -68
- package/src/components/TextEditor/extensions/{tooltip.tsx → hyperlink/hyperlinkTooltip.tsx} +9 -9
- package/src/components/TextEditor/extensions/{experimental.tsx → hyperlink/hyperlinkWidget.tsx} +2 -17
- package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
- package/src/components/TextEditor/extensions/index.ts +2 -5
- package/src/components/TextEditor/extensions/markdown/bundle.ts +11 -15
- package/src/components/TextEditor/extensions/markdown/index.ts +1 -1
- package/src/components/TextEditor/extensions/markdown/tags.ts +38 -0
- package/src/components/TextEditor/extensions/markdown/theme.ts +265 -0
- package/src/components/TextEditor/index.ts +0 -3
- package/src/components/TextEditor/yjs.stories.tsx +0 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useCollaboration.ts +45 -0
- package/src/hooks/useTextModel.ts +40 -50
- package/src/styles/markdown.ts +24 -26
- package/src/testing/replicator.ts +2 -2
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +0 -35
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts +0 -9
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts +0 -11
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -7
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +0 -31
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +0 -5
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts +0 -17
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/index.d.ts +0 -2
- package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/amToCodemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/codeMirrorToAm.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +0 -242
- package/src/components/TextEditor/extensions/autocomplete.ts +0 -72
- package/src/components/TextEditor/extensions/markdown/highlight.ts +0 -166
- package/src/components/TextEditor/extensions/tasklist.ts +0 -113
- package/src/components/TextEditor/themes/default.ts +0 -170
- package/src/components/TextEditor/themes/index.ts +0 -5
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/PatchSemaphore.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/amToCodemirror.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/codeMirrorToAm.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/handle.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/index.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge/automerge-plugin}/plugin.d.ts +0 -0
- /package/dist/types/src/{hooks/automerge → automerge}/automerge.stories.d.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/PatchSemaphore.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/amToCodemirror.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/codeMirrorToAm.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/handle.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/index.ts +0 -0
- /package/src/{hooks/automerge → automerge/automerge-plugin}/plugin.ts +0 -0
|
@@ -1,128 +1,149 @@
|
|
|
1
1
|
// packages/ui/react-ui-editor/src/index.ts
|
|
2
2
|
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
3
|
-
import { Doc, YText, YXmlFragment } from "@dxos/text-model";
|
|
3
|
+
import { Doc, YText as YText2, YXmlFragment } from "@dxos/text-model";
|
|
4
4
|
|
|
5
5
|
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
6
6
|
import { tags as tags2 } from "@lezer/highlight";
|
|
7
7
|
|
|
8
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/
|
|
9
|
-
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
10
|
-
import { keymap } from "@codemirror/view";
|
|
11
|
-
var autocomplete = ({ getOptions }) => {
|
|
12
|
-
return [
|
|
13
|
-
// https://codemirror.net/docs/ref/#view.keymap
|
|
14
|
-
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
15
|
-
keymap.of(completionKeymap),
|
|
16
|
-
// https://codemirror.net/examples/autocompletion
|
|
17
|
-
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
18
|
-
autocompletion({
|
|
19
|
-
// TODO(burdon): Set custom keymap.
|
|
20
|
-
// defaultKeymap: false,
|
|
21
|
-
// Don't start unless key press.
|
|
22
|
-
activateOnTyping: false,
|
|
23
|
-
// TODO(burdon): Option to create new page?
|
|
24
|
-
// TODO(burdon): Optional decoration via addToOptions
|
|
25
|
-
override: [
|
|
26
|
-
(context) => {
|
|
27
|
-
const word = context.matchBefore(/\w*/);
|
|
28
|
-
if (!word || word.from === word.to && !context.explicit) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
from: word.from,
|
|
33
|
-
options: getOptions(word.text.toLowerCase())
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
})
|
|
38
|
-
];
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic.ts
|
|
8
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/bundle.ts
|
|
42
9
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
43
10
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
44
11
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
45
12
|
import { EditorView, placeholder } from "@codemirror/view";
|
|
46
13
|
var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
|
|
47
|
-
// TODO(burdon): Compare with minimalSetup.
|
|
48
|
-
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
49
14
|
bracketMatching(),
|
|
50
15
|
closeBrackets(),
|
|
51
|
-
EditorView.lineWrapping,
|
|
52
16
|
_placeholder && placeholder(_placeholder),
|
|
17
|
+
EditorView.lineWrapping,
|
|
53
18
|
// TODO(burdon): Is this required?
|
|
54
19
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
55
20
|
].filter(Boolean);
|
|
56
21
|
|
|
57
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/
|
|
58
|
-
import
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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"
|
|
50
|
+
},
|
|
51
|
+
"& .cm-line": {
|
|
52
|
+
paddingInline: 0,
|
|
53
|
+
lineHeight: 1.6,
|
|
54
|
+
minBlockSize: "1.6em"
|
|
55
|
+
},
|
|
56
|
+
"& .cm-line *": {
|
|
57
|
+
lineHeight: 1.6
|
|
58
|
+
},
|
|
59
|
+
"& .cm-scroller": {
|
|
60
|
+
fontFamily: get(tokens, "fontFamily.body", []).join(","),
|
|
61
|
+
overflow: "visible"
|
|
62
|
+
},
|
|
63
|
+
"& .cm-content": {
|
|
64
|
+
padding: 0,
|
|
65
|
+
caretColor: "black"
|
|
66
|
+
},
|
|
67
|
+
".dark & .cm-content": {
|
|
68
|
+
caretColor: "white"
|
|
69
|
+
},
|
|
70
|
+
"& .cm-tooltip": {
|
|
71
|
+
backgroundColor: "transparent",
|
|
72
|
+
border: "none"
|
|
73
|
+
},
|
|
74
|
+
"& .cm-link": {
|
|
75
|
+
color: get(tokens, "extend.colors.primary.500"),
|
|
76
|
+
textDecoration: "underline"
|
|
75
77
|
}
|
|
76
78
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
|
|
80
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/change.ts
|
|
81
|
+
import { StateField } from "@codemirror/state";
|
|
82
|
+
var onChangeExtension = (onChange) => StateField.define({
|
|
83
|
+
create: () => null,
|
|
84
|
+
update: (_value, transaction) => {
|
|
85
|
+
if (transaction.docChanged && onChange) {
|
|
86
|
+
onChange(transaction.newDoc.toString());
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts
|
|
93
|
+
import { syntaxTree } from "@codemirror/language";
|
|
94
|
+
import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
|
|
95
|
+
import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
|
|
96
|
+
var Link = class extends WidgetType {
|
|
97
|
+
constructor(_anchor, _text, _url) {
|
|
79
98
|
super();
|
|
80
|
-
this.
|
|
99
|
+
this._anchor = _anchor;
|
|
81
100
|
this._text = _text;
|
|
82
101
|
this._url = _url;
|
|
83
102
|
}
|
|
84
103
|
eq(other) {
|
|
85
|
-
return this.
|
|
104
|
+
return this._text === other.text && this._url === other.url;
|
|
86
105
|
}
|
|
87
106
|
toDOM(view) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
const link = document.createElement("a");
|
|
108
|
+
link.setAttribute("class", "cm-link");
|
|
109
|
+
link.textContent = this._text;
|
|
91
110
|
if (this._url) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
link2.href = this._url;
|
|
111
|
+
link.setAttribute("target", "_blank");
|
|
112
|
+
link.href = this._url;
|
|
95
113
|
} else {
|
|
96
|
-
|
|
114
|
+
link.onclick = () => {
|
|
97
115
|
view.dispatch({
|
|
98
116
|
selection: {
|
|
99
|
-
anchor: this.
|
|
117
|
+
anchor: this._anchor
|
|
100
118
|
}
|
|
101
119
|
});
|
|
102
120
|
};
|
|
103
121
|
}
|
|
104
|
-
return
|
|
122
|
+
return link;
|
|
105
123
|
}
|
|
106
124
|
};
|
|
107
|
-
var
|
|
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) => {
|
|
108
133
|
const builder = new RangeSetBuilder();
|
|
109
134
|
const cursor = state.selection.main.head;
|
|
110
135
|
syntaxTree(state).iterate({
|
|
111
136
|
enter: (node) => {
|
|
112
|
-
if (node.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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) : "";
|
|
118
143
|
builder.add(node.from, node.to, Decoration.replace({
|
|
119
|
-
widget: new
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
if (options.onRender) {
|
|
123
|
-
builder.add(node.to, node.to, Decoration.replace({
|
|
124
|
-
widget: new LinkButton(node.from, url, options.onRender)
|
|
144
|
+
widget: new Link(node.from, text, link ? url : void 0)
|
|
125
145
|
}));
|
|
146
|
+
return false;
|
|
126
147
|
}
|
|
127
148
|
}
|
|
128
149
|
return true;
|
|
@@ -130,26 +151,143 @@ var update = (state, options) => {
|
|
|
130
151
|
});
|
|
131
152
|
return builder.finish();
|
|
132
153
|
};
|
|
133
|
-
var link = (options = {}) => {
|
|
134
|
-
return StateField.define({
|
|
135
|
-
create: (state) => update(state, options),
|
|
136
|
-
update: (_, tr) => update(tr.state, options),
|
|
137
|
-
provide: (field) => EditorView2.decorations.from(field)
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
154
|
|
|
141
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/
|
|
142
|
-
import {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx
|
|
156
|
+
import { hoverTooltip } from "@codemirror/view";
|
|
157
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
158
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
159
|
+
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
|
|
160
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
161
|
+
const p = pos - from;
|
|
162
|
+
let idx = 0;
|
|
163
|
+
let match;
|
|
164
|
+
do {
|
|
165
|
+
match = text.substring(idx).match(regexp);
|
|
166
|
+
if (!match) {
|
|
167
|
+
match = void 0;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
idx = text.indexOf(match[0]);
|
|
171
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
172
|
+
break;
|
|
148
173
|
}
|
|
174
|
+
idx += match[0].length;
|
|
175
|
+
} while (true);
|
|
176
|
+
if (!match) {
|
|
149
177
|
return null;
|
|
150
178
|
}
|
|
179
|
+
const [, , url] = match ?? [];
|
|
180
|
+
return {
|
|
181
|
+
pos: idx + from,
|
|
182
|
+
end: idx + from + match[0].length,
|
|
183
|
+
above: true,
|
|
184
|
+
create: () => {
|
|
185
|
+
const el = document.createElement("a");
|
|
186
|
+
el.innerText = "_";
|
|
187
|
+
el.className = tooltipContent({}, "mb-2 p-1");
|
|
188
|
+
el.setAttribute("target", "_blank");
|
|
189
|
+
el.setAttribute("href", url);
|
|
190
|
+
onHover(el, url);
|
|
191
|
+
return {
|
|
192
|
+
dom: el
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
151
196
|
});
|
|
152
197
|
|
|
198
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx
|
|
199
|
+
import { Decoration as Decoration2, EditorView as EditorView3, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
200
|
+
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
201
|
+
var linkDecorator = () => new MatchDecorator({
|
|
202
|
+
regexp: markdownLinkRegexp2,
|
|
203
|
+
decorate: (add, from, to, match, view) => {
|
|
204
|
+
const [_, label, url] = match;
|
|
205
|
+
const p0 = {
|
|
206
|
+
start: from,
|
|
207
|
+
end: from + 1
|
|
208
|
+
};
|
|
209
|
+
const p1 = {
|
|
210
|
+
start: from + 1,
|
|
211
|
+
end: from + 1 + label.length
|
|
212
|
+
};
|
|
213
|
+
const p2 = {
|
|
214
|
+
start: p1.end,
|
|
215
|
+
end: p1.end + 1
|
|
216
|
+
};
|
|
217
|
+
const p3 = {
|
|
218
|
+
start: p1.end + 1,
|
|
219
|
+
end: p1.end + 1 + url.length + 2
|
|
220
|
+
};
|
|
221
|
+
add(p0.start, p0.end, Decoration2.mark({
|
|
222
|
+
class: "cm-hyperlink-bracket",
|
|
223
|
+
_attributes: {
|
|
224
|
+
style: "display: none"
|
|
225
|
+
}
|
|
226
|
+
}));
|
|
227
|
+
add(p1.start, p1.end, Decoration2.mark({
|
|
228
|
+
class: "cm-hyperlink-label"
|
|
229
|
+
}));
|
|
230
|
+
add(p2.start, p2.end, Decoration2.mark({
|
|
231
|
+
class: "cm-hyperlink-bracket",
|
|
232
|
+
_attributes: {
|
|
233
|
+
style: "display: none"
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
add(p3.start, p3.end, Decoration2.mark({
|
|
237
|
+
class: "cm-hyperlink-url",
|
|
238
|
+
_attributes: {
|
|
239
|
+
style: "display: none"
|
|
240
|
+
}
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
function hyperLinkExtension() {
|
|
245
|
+
return ViewPlugin.fromClass(class HyperLinkView {
|
|
246
|
+
constructor(view) {
|
|
247
|
+
this.decorator = linkDecorator();
|
|
248
|
+
this.decorations = this.decorator.createDeco(view);
|
|
249
|
+
}
|
|
250
|
+
update(update2) {
|
|
251
|
+
if (update2.docChanged || update2.viewportChanged) {
|
|
252
|
+
this.decorations = this.decorator.updateDeco(update2, this.decorations);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}, {
|
|
256
|
+
decorations: (view) => view.decorations,
|
|
257
|
+
eventHandlers: {
|
|
258
|
+
// TODO(burdon): CLick to expand link.
|
|
259
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
260
|
+
mousedown: function(event, view) {
|
|
261
|
+
const pos = view.posAtDOM(event.target);
|
|
262
|
+
if (pos !== 0) {
|
|
263
|
+
console.log(this);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
267
|
+
keydown: (event, view) => {
|
|
268
|
+
view.update([]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
var hyperlinkStyle = EditorView3.baseTheme({
|
|
274
|
+
".cm-hyperlink-label": {
|
|
275
|
+
cursor: "pointer",
|
|
276
|
+
textDecoration: "underline"
|
|
277
|
+
},
|
|
278
|
+
".cm-hyperlink-bracket": {
|
|
279
|
+
opacity: 0.2
|
|
280
|
+
},
|
|
281
|
+
".cm-hyperlink-url": {
|
|
282
|
+
opacity: 0.5,
|
|
283
|
+
fontFamily: "monospace"
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
var hyperlinkWidget = [
|
|
287
|
+
hyperLinkExtension(),
|
|
288
|
+
hyperlinkStyle
|
|
289
|
+
];
|
|
290
|
+
|
|
153
291
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
154
292
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
155
293
|
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
@@ -160,59 +298,222 @@ import { lintKeymap } from "@codemirror/lint";
|
|
|
160
298
|
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
161
299
|
import { EditorState } from "@codemirror/state";
|
|
162
300
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
163
|
-
import { crosshairCursor, drawSelection, dropCursor, EditorView as
|
|
301
|
+
import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
|
|
164
302
|
|
|
165
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/
|
|
166
|
-
import {
|
|
167
|
-
import { HighlightStyle } from "@codemirror/language";
|
|
168
|
-
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
169
|
-
import get from "lodash.get";
|
|
170
|
-
|
|
171
|
-
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
172
|
-
import { mx } from "@dxos/react-ui-theme";
|
|
173
|
-
var heading = {
|
|
174
|
-
1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
|
|
175
|
-
2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
|
|
176
|
-
3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
177
|
-
4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
178
|
-
5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
179
|
-
6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
|
|
180
|
-
};
|
|
181
|
-
var light = "text-neutral-200 dark:text-neutral-800";
|
|
182
|
-
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
183
|
-
var bold = "font-bold";
|
|
184
|
-
var italic = "italic";
|
|
185
|
-
var strikethrough = "line-through";
|
|
186
|
-
var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
|
|
187
|
-
var codeMark = "font-mono text-primary-500";
|
|
188
|
-
var inlineUrl = mx(code, "px-1");
|
|
189
|
-
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
190
|
-
var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-500/50";
|
|
191
|
-
|
|
192
|
-
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
193
|
-
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
194
|
-
var tokens = tailwindConfig({}).theme;
|
|
195
|
-
|
|
196
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
|
|
303
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
|
|
304
|
+
import { styleTags, Tag } from "@lezer/highlight";
|
|
197
305
|
var markdownTags = {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
URL: Tag.define()
|
|
306
|
+
headingMark: Tag.define(),
|
|
307
|
+
quoteMark: Tag.define(),
|
|
308
|
+
listMark: Tag.define(),
|
|
309
|
+
linkMark: Tag.define(),
|
|
310
|
+
emphasisMark: Tag.define(),
|
|
311
|
+
codeMark: Tag.define(),
|
|
312
|
+
codeText: Tag.define(),
|
|
313
|
+
inlineCode: Tag.define(),
|
|
314
|
+
url: Tag.define(),
|
|
315
|
+
linkReference: Tag.define(),
|
|
316
|
+
linkLabel: Tag.define()
|
|
210
317
|
};
|
|
211
318
|
var markdownTagsExtension = {
|
|
212
319
|
props: [
|
|
213
|
-
styleTags(
|
|
320
|
+
styleTags({
|
|
321
|
+
HeaderMark: markdownTags.headingMark,
|
|
322
|
+
QuoteMark: markdownTags.quoteMark,
|
|
323
|
+
ListMark: markdownTags.listMark,
|
|
324
|
+
LinkMark: markdownTags.linkMark,
|
|
325
|
+
EmphasisMark: markdownTags.emphasisMark,
|
|
326
|
+
CodeMark: markdownTags.codeMark,
|
|
327
|
+
CodeText: markdownTags.codeText,
|
|
328
|
+
InlineCode: markdownTags.inlineCode,
|
|
329
|
+
URL: markdownTags.url,
|
|
330
|
+
LinkReference: markdownTags.linkReference,
|
|
331
|
+
LinkLabel: markdownTags.linkLabel
|
|
332
|
+
})
|
|
214
333
|
]
|
|
215
334
|
};
|
|
335
|
+
|
|
336
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
|
|
337
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
338
|
+
import { HighlightStyle } from "@codemirror/language";
|
|
339
|
+
import { tags } from "@lezer/highlight";
|
|
340
|
+
import get2 from "lodash.get";
|
|
341
|
+
var chalky = "#e5c07b";
|
|
342
|
+
var coral = "#e06c75";
|
|
343
|
+
var cyan = "#56b6c2";
|
|
344
|
+
var invalid = "#ffffff";
|
|
345
|
+
var ivory = "#abb2bf";
|
|
346
|
+
var stone = "#7d8799";
|
|
347
|
+
var malibu = "#61afef";
|
|
348
|
+
var sage = "#98c379";
|
|
349
|
+
var whiskey = "#d19a66";
|
|
350
|
+
var violet = "#c678dd";
|
|
351
|
+
var highlightBackground = "#2c313a";
|
|
352
|
+
var tooltipBackground = "#353a42";
|
|
353
|
+
var monospace = get2(tokens, "fontFamily.mono", [
|
|
354
|
+
"monospace"
|
|
355
|
+
]).join(",");
|
|
356
|
+
var markdownTheme = {
|
|
357
|
+
// TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
|
|
358
|
+
// '&': {
|
|
359
|
+
// color: ivory,
|
|
360
|
+
// backgroundColor: background
|
|
361
|
+
// },
|
|
362
|
+
// '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
|
|
363
|
+
// '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
|
|
364
|
+
// backgroundColor: selection
|
|
365
|
+
// },
|
|
366
|
+
// '.cm-panels': { backgroundColor: darkBackground, color: ivory },
|
|
367
|
+
// '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
|
368
|
+
// '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
|
369
|
+
// '.cm-searchMatch': {
|
|
370
|
+
// backgroundColor: '#72a1ff59',
|
|
371
|
+
// outline: '1px solid #457dff'
|
|
372
|
+
// },
|
|
373
|
+
// '.cm-searchMatch.cm-searchMatch-selected': {
|
|
374
|
+
// backgroundColor: '#6199ff2f'
|
|
375
|
+
// },
|
|
376
|
+
// '.cm-activeLine': { backgroundColor: '#6699ff0b' },
|
|
377
|
+
// '.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
|
378
|
+
// '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
|
379
|
+
// backgroundColor: '#bad0f847'
|
|
380
|
+
// },
|
|
381
|
+
// '.cm-gutters': {
|
|
382
|
+
// backgroundColor: background,
|
|
383
|
+
// color: stone,
|
|
384
|
+
// border: 'none'
|
|
385
|
+
// },
|
|
386
|
+
// '.cm-activeLineGutter': {
|
|
387
|
+
// backgroundColor: highlightBackground
|
|
388
|
+
// },
|
|
389
|
+
// '.cm-foldPlaceholder': {
|
|
390
|
+
// backgroundColor: 'transparent',
|
|
391
|
+
// border: 'none',
|
|
392
|
+
// color: '#ddd'
|
|
393
|
+
// },
|
|
394
|
+
"& .cm-tooltip": {
|
|
395
|
+
backgroundColor: "transparent",
|
|
396
|
+
border: "none"
|
|
397
|
+
},
|
|
398
|
+
// '.dark & .cm-tooltip': {
|
|
399
|
+
// border: 'none',
|
|
400
|
+
// backgroundColor: tooltipBackground,
|
|
401
|
+
// },
|
|
402
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
|
|
403
|
+
// borderTopColor: 'transparent',
|
|
404
|
+
// borderBottomColor: 'transparent',
|
|
405
|
+
// },
|
|
406
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
|
|
407
|
+
// borderTopColor: tooltipBackground,
|
|
408
|
+
// borderBottomColor: tooltipBackground,
|
|
409
|
+
// },
|
|
410
|
+
// '.dark & .cm-tooltip-autocomplete': {
|
|
411
|
+
// '& > ul > li[aria-selected]': {
|
|
412
|
+
// backgroundColor: highlightBackground,
|
|
413
|
+
// color: ivory,
|
|
414
|
+
// },
|
|
415
|
+
// },
|
|
416
|
+
"&.cm-focused": {
|
|
417
|
+
outline: "none"
|
|
418
|
+
},
|
|
419
|
+
"& .cm-line": {
|
|
420
|
+
paddingInline: 0,
|
|
421
|
+
lineHeight: 1.6,
|
|
422
|
+
minBlockSize: "1.6em"
|
|
423
|
+
},
|
|
424
|
+
"& .cm-line *": {
|
|
425
|
+
lineHeight: 1.6
|
|
426
|
+
},
|
|
427
|
+
"&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
|
|
428
|
+
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
429
|
+
},
|
|
430
|
+
".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
|
|
431
|
+
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
432
|
+
},
|
|
433
|
+
"& .cm-selectionMatch": {
|
|
434
|
+
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
435
|
+
},
|
|
436
|
+
".dark & .cm-selectionMatch": {
|
|
437
|
+
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
438
|
+
},
|
|
439
|
+
"& .cm-content": {
|
|
440
|
+
padding: 0,
|
|
441
|
+
caretColor: "black"
|
|
442
|
+
},
|
|
443
|
+
".dark & .cm-content": {
|
|
444
|
+
caretColor: "white"
|
|
445
|
+
},
|
|
446
|
+
"& .cm-cursor": {
|
|
447
|
+
borderLeftColor: "black"
|
|
448
|
+
},
|
|
449
|
+
".dark & .cm-cursor": {
|
|
450
|
+
borderLeftColor: "white"
|
|
451
|
+
},
|
|
452
|
+
".cm-placeholder": {
|
|
453
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
454
|
+
},
|
|
455
|
+
"& .cm-scroller": {
|
|
456
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
|
|
457
|
+
overflow: "visible"
|
|
458
|
+
},
|
|
459
|
+
"& .cm-activeLine": {
|
|
460
|
+
backgroundColor: "transparent"
|
|
461
|
+
},
|
|
462
|
+
".dark & .cm-activeLine": {
|
|
463
|
+
backgroundColor: "transparent"
|
|
464
|
+
},
|
|
465
|
+
"& .cm-ySelectionInfo": {
|
|
466
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(","),
|
|
467
|
+
padding: "2px 4px",
|
|
468
|
+
marginBlockStart: "-4px"
|
|
469
|
+
},
|
|
470
|
+
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
471
|
+
paddingBlockStart: ".15em",
|
|
472
|
+
paddingBlockEnd: ".15em"
|
|
473
|
+
},
|
|
474
|
+
"& .cm-ySelection, & .cm-yLineSelection": {
|
|
475
|
+
mixBlendMode: "multiply"
|
|
476
|
+
},
|
|
477
|
+
".dark & .cm-ySelection, .dark & .cm-yLineSelection": {
|
|
478
|
+
mixBlendMode: "screen"
|
|
479
|
+
},
|
|
480
|
+
"& .cm-ySelectionCaret": {
|
|
481
|
+
display: "inline-block",
|
|
482
|
+
insetBlockStart: ".1em",
|
|
483
|
+
blockSize: "1.4em",
|
|
484
|
+
verticalAlign: "top"
|
|
485
|
+
},
|
|
486
|
+
"& .cm-yLineSelection": {
|
|
487
|
+
margin: "0"
|
|
488
|
+
},
|
|
489
|
+
".cm-link": {
|
|
490
|
+
color: "rgb(20 89 208)",
|
|
491
|
+
textDecorationLine: "underline",
|
|
492
|
+
textDecorationThickness: "1px",
|
|
493
|
+
textUnderlineOffset: "2px",
|
|
494
|
+
borderRadius: ".125rem"
|
|
495
|
+
},
|
|
496
|
+
// TODO(burdon): Document.
|
|
497
|
+
...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
498
|
+
const height = get2(tokens, [
|
|
499
|
+
"extend",
|
|
500
|
+
"fontSize",
|
|
501
|
+
fontSize,
|
|
502
|
+
1,
|
|
503
|
+
"lineHeight"
|
|
504
|
+
]);
|
|
505
|
+
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
506
|
+
height
|
|
507
|
+
};
|
|
508
|
+
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
509
|
+
height
|
|
510
|
+
};
|
|
511
|
+
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
512
|
+
height
|
|
513
|
+
};
|
|
514
|
+
return acc;
|
|
515
|
+
}, {})
|
|
516
|
+
};
|
|
216
517
|
var markdownHighlightStyle = HighlightStyle.define([
|
|
217
518
|
{
|
|
218
519
|
tag: [
|
|
@@ -252,22 +553,48 @@ var markdownHighlightStyle = HighlightStyle.define([
|
|
|
252
553
|
],
|
|
253
554
|
color: "inherit !important"
|
|
254
555
|
},
|
|
255
|
-
// Markdown marks.
|
|
256
556
|
{
|
|
257
557
|
tag: [
|
|
558
|
+
tags.link,
|
|
559
|
+
tags.url
|
|
560
|
+
],
|
|
561
|
+
color: "inherit !important",
|
|
562
|
+
textDecoration: "none !important"
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
tag: [
|
|
566
|
+
tags.function(tags.variableName),
|
|
567
|
+
tags.labelName
|
|
568
|
+
],
|
|
569
|
+
color: malibu,
|
|
570
|
+
fontFamily: monospace
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
tag: [
|
|
574
|
+
markdownTags.codeMark,
|
|
575
|
+
markdownTags.emphasisMark,
|
|
576
|
+
markdownTags.headingMark,
|
|
577
|
+
markdownTags.linkLabel,
|
|
578
|
+
markdownTags.linkReference,
|
|
579
|
+
markdownTags.listMark,
|
|
580
|
+
markdownTags.quoteMark,
|
|
581
|
+
markdownTags.url,
|
|
258
582
|
tags.meta,
|
|
259
|
-
tags.processingInstruction
|
|
260
|
-
markdownTags.CodeMark,
|
|
261
|
-
markdownTags.EmphasisMark,
|
|
262
|
-
markdownTags.HeaderMark,
|
|
263
|
-
markdownTags.LinkLabel,
|
|
264
|
-
markdownTags.LinkReference,
|
|
265
|
-
markdownTags.ListMark,
|
|
266
|
-
markdownTags.QuoteMark
|
|
583
|
+
tags.processingInstruction
|
|
267
584
|
],
|
|
268
585
|
class: mark
|
|
269
586
|
},
|
|
270
|
-
|
|
587
|
+
{
|
|
588
|
+
tag: [
|
|
589
|
+
markdownTags.codeText,
|
|
590
|
+
markdownTags.inlineCode
|
|
591
|
+
],
|
|
592
|
+
class: "font-mono"
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
tag: tags.emphasis,
|
|
596
|
+
class: italic
|
|
597
|
+
},
|
|
271
598
|
{
|
|
272
599
|
tag: tags.heading1,
|
|
273
600
|
class: heading[1]
|
|
@@ -292,61 +619,18 @@ var markdownHighlightStyle = HighlightStyle.define([
|
|
|
292
619
|
tag: tags.heading6,
|
|
293
620
|
class: heading[6]
|
|
294
621
|
},
|
|
295
|
-
// Emphasis.
|
|
296
|
-
{
|
|
297
|
-
tag: tags.emphasis,
|
|
298
|
-
class: italic
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
tag: tags.strong,
|
|
302
|
-
class: bold
|
|
303
|
-
},
|
|
304
622
|
{
|
|
305
623
|
tag: tags.strikethrough,
|
|
306
624
|
class: strikethrough
|
|
307
625
|
},
|
|
308
|
-
// Naked URLs.
|
|
309
|
-
{
|
|
310
|
-
tag: [
|
|
311
|
-
markdownTags.URL
|
|
312
|
-
],
|
|
313
|
-
class: inlineUrl
|
|
314
|
-
},
|
|
315
|
-
// E.g., code block language (after ```).
|
|
316
|
-
{
|
|
317
|
-
tag: [
|
|
318
|
-
tags.function(tags.variableName),
|
|
319
|
-
tags.labelName
|
|
320
|
-
],
|
|
321
|
-
class: codeMark
|
|
322
|
-
},
|
|
323
|
-
// The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
324
|
-
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
325
|
-
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
326
|
-
// the code, but all other CSS properties will be inherited.
|
|
327
|
-
{
|
|
328
|
-
tag: [
|
|
329
|
-
markdownTags.CodeText,
|
|
330
|
-
markdownTags.InlineCode
|
|
331
|
-
],
|
|
332
|
-
class: code
|
|
333
|
-
},
|
|
334
626
|
{
|
|
335
|
-
tag:
|
|
336
|
-
|
|
337
|
-
],
|
|
338
|
-
class: blockquote
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
tag: [
|
|
342
|
-
markdownTags.HorizontalRule
|
|
343
|
-
],
|
|
344
|
-
class: horizontalRule
|
|
627
|
+
tag: tags.strong,
|
|
628
|
+
class: bold
|
|
345
629
|
}
|
|
346
630
|
], {
|
|
347
631
|
scope: markdownLanguage,
|
|
348
632
|
all: {
|
|
349
|
-
fontFamily:
|
|
633
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
350
634
|
}
|
|
351
635
|
});
|
|
352
636
|
|
|
@@ -357,7 +641,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
357
641
|
closeBrackets2(),
|
|
358
642
|
_placeholder && placeholder2(_placeholder),
|
|
359
643
|
EditorState.allowMultipleSelections.of(true),
|
|
360
|
-
|
|
644
|
+
EditorView4.lineWrapping,
|
|
361
645
|
// autocompletion(),
|
|
362
646
|
crosshairCursor(),
|
|
363
647
|
dropCursor(),
|
|
@@ -369,7 +653,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
369
653
|
history(),
|
|
370
654
|
indentOnInput(),
|
|
371
655
|
rectangularSelection(),
|
|
372
|
-
|
|
656
|
+
keymap.of([
|
|
373
657
|
...closeBracketsKeymap,
|
|
374
658
|
// ...completionKeymap,
|
|
375
659
|
...defaultKeymap,
|
|
@@ -381,308 +665,23 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
381
665
|
]),
|
|
382
666
|
// Main extension.
|
|
383
667
|
// https://github.com/codemirror/lang-markdown
|
|
384
|
-
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
385
668
|
markdown({
|
|
386
|
-
// GRM by default (vs strict CommonMark):
|
|
387
|
-
// Table, TaskList, Strikethrough, and Autolink.
|
|
388
|
-
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
389
|
-
// https://github.github.com/gfm
|
|
390
|
-
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
391
669
|
base: markdownLanguage2,
|
|
392
|
-
// Languages for syntax highlighting fenced code blocks.
|
|
393
670
|
codeLanguages: languages,
|
|
394
|
-
// Parser extensions.
|
|
395
671
|
extensions: [
|
|
396
|
-
//
|
|
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.
|
|
397
678
|
markdownTagsExtension
|
|
398
679
|
]
|
|
399
680
|
}),
|
|
400
|
-
//
|
|
401
|
-
syntaxHighlighting2(markdownHighlightStyle),
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
].filter(Boolean);
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
|
|
408
|
-
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
409
|
-
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
|
|
410
|
-
import { EditorView as EditorView4, Decoration as Decoration2, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
411
|
-
var CheckboxWidget = class extends WidgetType2 {
|
|
412
|
-
constructor(_pos, _getCursor, _checked) {
|
|
413
|
-
super();
|
|
414
|
-
this._pos = _pos;
|
|
415
|
-
this._getCursor = _getCursor;
|
|
416
|
-
this._checked = _checked;
|
|
417
|
-
}
|
|
418
|
-
eq(other) {
|
|
419
|
-
return this._pos === other._pos && this._checked === other._checked;
|
|
420
|
-
}
|
|
421
|
-
toDOM(view) {
|
|
422
|
-
const wrap = document.createElement("span");
|
|
423
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
424
|
-
wrap.className = "cm-task-item";
|
|
425
|
-
const box = wrap.appendChild(document.createElement("input"));
|
|
426
|
-
box.type = "checkbox";
|
|
427
|
-
box.checked = this._checked;
|
|
428
|
-
box.onclick = (event) => {
|
|
429
|
-
this._checked = !isChecked(view.state, this._pos);
|
|
430
|
-
view.dispatch({
|
|
431
|
-
changes: {
|
|
432
|
-
from: this._pos + 1,
|
|
433
|
-
to: this._pos + 2,
|
|
434
|
-
insert: this._checked ? "x" : " "
|
|
435
|
-
},
|
|
436
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
437
|
-
selection: {
|
|
438
|
-
anchor: this._pos + 4
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
return true;
|
|
442
|
-
};
|
|
443
|
-
return wrap;
|
|
444
|
-
}
|
|
445
|
-
// TODO(burdon): Remove listener?
|
|
446
|
-
// override destroy() {
|
|
447
|
-
// console.log('destroy', this._pos);
|
|
448
|
-
// }
|
|
449
|
-
ignoreEvent() {
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
var isChecked = (state, pos) => {
|
|
454
|
-
return state.sliceDoc(pos, pos + 3).toLowerCase() === "[x]";
|
|
455
|
-
};
|
|
456
|
-
var statefield = () => {
|
|
457
|
-
let lastPosition = 0;
|
|
458
|
-
const listener2 = EditorView4.updateListener.of((update2) => {
|
|
459
|
-
lastPosition = update2.startState.selection.main.head;
|
|
460
|
-
});
|
|
461
|
-
const checkbox = (state) => {
|
|
462
|
-
const builder = new RangeSetBuilder2();
|
|
463
|
-
syntaxTree2(state).iterate({
|
|
464
|
-
enter: (node) => {
|
|
465
|
-
if (node.name === "TaskMarker") {
|
|
466
|
-
builder.add(node.from, node.to, Decoration2.replace({
|
|
467
|
-
widget: new CheckboxWidget(node.from, () => lastPosition, isChecked(state, node.from))
|
|
468
|
-
}));
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
return builder.finish();
|
|
473
|
-
};
|
|
474
|
-
return [
|
|
475
|
-
listener2,
|
|
476
|
-
StateField3.define({
|
|
477
|
-
create: (state) => checkbox(state),
|
|
478
|
-
update: (_, tr) => checkbox(tr.state),
|
|
479
|
-
provide: (field) => EditorView4.decorations.from(field)
|
|
480
|
-
})
|
|
481
|
-
];
|
|
482
|
-
};
|
|
483
|
-
var styles = EditorView4.baseTheme({
|
|
484
|
-
"& .cm-task-item": {
|
|
485
|
-
padding: "0 4px"
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
var tasklist = () => [
|
|
489
|
-
statefield(),
|
|
490
|
-
styles
|
|
491
|
-
];
|
|
492
|
-
|
|
493
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
|
|
494
|
-
import { hoverTooltip } from "@codemirror/view";
|
|
495
|
-
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
496
|
-
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
497
|
-
var tooltip = ({ onHover, regexp = markdownLinkRegexp }) => hoverTooltip((view, pos) => {
|
|
498
|
-
const { from, text } = view.state.doc.lineAt(pos);
|
|
499
|
-
const p = pos - from;
|
|
500
|
-
let idx = 0;
|
|
501
|
-
let match;
|
|
502
|
-
do {
|
|
503
|
-
match = text.substring(idx).match(regexp);
|
|
504
|
-
if (!match) {
|
|
505
|
-
match = void 0;
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
idx = text.indexOf(match[0]);
|
|
509
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
idx += match[0].length;
|
|
513
|
-
} while (true);
|
|
514
|
-
if (!match) {
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
const [, , url] = match ?? [];
|
|
518
|
-
return {
|
|
519
|
-
pos: idx + from,
|
|
520
|
-
end: idx + from + match[0].length,
|
|
521
|
-
above: true,
|
|
522
|
-
create: () => {
|
|
523
|
-
const el = document.createElement("div");
|
|
524
|
-
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
525
|
-
onHover(el, url);
|
|
526
|
-
return {
|
|
527
|
-
dom: el,
|
|
528
|
-
offset: {
|
|
529
|
-
x: 0,
|
|
530
|
-
y: 12
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/themes/default.ts
|
|
538
|
-
import get2 from "lodash.get";
|
|
539
|
-
var defaultTheme = {
|
|
540
|
-
//
|
|
541
|
-
// Main layout:
|
|
542
|
-
// <div class=".cm-editor .cm-focused">
|
|
543
|
-
// <div class=".cm-scroller">
|
|
544
|
-
// <div class=".cm-content" role="textbox" contenteditable="true">
|
|
545
|
-
// <div class=".cm-line" />
|
|
546
|
-
// </div>
|
|
547
|
-
// </div>
|
|
548
|
-
// </div>
|
|
549
|
-
//
|
|
550
|
-
"&": {},
|
|
551
|
-
"&.cm-focused": {
|
|
552
|
-
outline: "none"
|
|
553
|
-
},
|
|
554
|
-
"& .cm-scroller": {
|
|
555
|
-
overflow: "visible"
|
|
556
|
-
},
|
|
557
|
-
"& .cm-content": {
|
|
558
|
-
padding: 0,
|
|
559
|
-
// Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
560
|
-
fontSize: "16px"
|
|
561
|
-
},
|
|
562
|
-
"&light .cm-content": {
|
|
563
|
-
color: get2(tokens, "extend.colors.neutral.900", "black"),
|
|
564
|
-
caretColor: "black"
|
|
565
|
-
},
|
|
566
|
-
"&dark .cm-content": {
|
|
567
|
-
color: get2(tokens, "extend.colors.neutral.100", "white"),
|
|
568
|
-
caretColor: "white"
|
|
569
|
-
},
|
|
570
|
-
//
|
|
571
|
-
// Cursor
|
|
572
|
-
//
|
|
573
|
-
"&light .cm-cursor": {
|
|
574
|
-
borderLeft: "2px solid black"
|
|
575
|
-
},
|
|
576
|
-
"&dark .cm-cursor": {
|
|
577
|
-
borderLeft: "2px solid white"
|
|
578
|
-
},
|
|
579
|
-
"& .cm-placeholder": {
|
|
580
|
-
fontWeight: 100
|
|
581
|
-
},
|
|
582
|
-
//
|
|
583
|
-
// line
|
|
584
|
-
//
|
|
585
|
-
"& .cm-line": {
|
|
586
|
-
paddingInline: 0
|
|
587
|
-
},
|
|
588
|
-
"& .cm-line *": {},
|
|
589
|
-
"& .cm-activeLine": {
|
|
590
|
-
backgroundColor: "transparent"
|
|
591
|
-
},
|
|
592
|
-
//
|
|
593
|
-
// selection
|
|
594
|
-
//
|
|
595
|
-
"&light .cm-selectionBackground, &light.cm-focused .cm-selectionBackground": {
|
|
596
|
-
background: get2(tokens, "extend.colors.primary.150", "#00ffff")
|
|
597
|
-
},
|
|
598
|
-
"&dark .cm-selectionBackground, &dark.cm-focused .cm-selectionBackground": {
|
|
599
|
-
background: get2(tokens, "extend.colors.primary.850", "#00ffff")
|
|
600
|
-
},
|
|
601
|
-
"&light .cm-selectionMatch": {
|
|
602
|
-
background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
603
|
-
},
|
|
604
|
-
"&dark .cm-selectionMatch": {
|
|
605
|
-
background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
606
|
-
},
|
|
607
|
-
//
|
|
608
|
-
// collaboration
|
|
609
|
-
// TODO(burdon): Review classnames (YJS dependent?)
|
|
610
|
-
//
|
|
611
|
-
"&light .cm-ySelection, &light .cm-yLineSelection": {
|
|
612
|
-
mixBlendMode: "multiply"
|
|
613
|
-
},
|
|
614
|
-
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
615
|
-
mixBlendMode: "screen"
|
|
616
|
-
},
|
|
617
|
-
"& .cm-ySelectionInfo": {
|
|
618
|
-
padding: "2px 4px",
|
|
619
|
-
marginBlockStart: "-4px"
|
|
620
|
-
},
|
|
621
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
622
|
-
paddingBlockStart: ".15em",
|
|
623
|
-
paddingBlockEnd: ".15em"
|
|
624
|
-
},
|
|
625
|
-
"& .cm-ySelectionCaret": {
|
|
626
|
-
display: "inline-block",
|
|
627
|
-
insetBlockStart: ".1em",
|
|
628
|
-
blockSize: "1.4em",
|
|
629
|
-
verticalAlign: "top"
|
|
630
|
-
},
|
|
631
|
-
"& .cm-yLineSelection": {
|
|
632
|
-
margin: 0
|
|
633
|
-
},
|
|
634
|
-
//
|
|
635
|
-
// link
|
|
636
|
-
//
|
|
637
|
-
"& .cm-link": {
|
|
638
|
-
color: get2(tokens, "extend.colors.primary.500"),
|
|
639
|
-
textDecorationLine: "underline",
|
|
640
|
-
textDecorationThickness: "1px",
|
|
641
|
-
textUnderlineOffset: "2px",
|
|
642
|
-
borderRadius: ".125rem",
|
|
643
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
644
|
-
},
|
|
645
|
-
//
|
|
646
|
-
// tooltip
|
|
647
|
-
//
|
|
648
|
-
"& .cm-tooltip": {
|
|
649
|
-
border: "none"
|
|
650
|
-
},
|
|
651
|
-
// '& .cm-tooltip-below': {},
|
|
652
|
-
// '& .cm-tooltip-autocomplete': {},
|
|
653
|
-
//
|
|
654
|
-
// font size
|
|
655
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
|
|
656
|
-
//
|
|
657
|
-
...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
658
|
-
const height = get2(tokens, [
|
|
659
|
-
"extend",
|
|
660
|
-
"fontSize",
|
|
661
|
-
fontSize,
|
|
662
|
-
1,
|
|
663
|
-
"lineHeight"
|
|
664
|
-
]);
|
|
665
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
666
|
-
height
|
|
667
|
-
};
|
|
668
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
669
|
-
height
|
|
670
|
-
};
|
|
671
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
672
|
-
height
|
|
673
|
-
};
|
|
674
|
-
return acc;
|
|
675
|
-
}, {})
|
|
676
|
-
};
|
|
677
|
-
var textTheme = {
|
|
678
|
-
"& .cm-scroller": {
|
|
679
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
var codeTheme = {
|
|
683
|
-
"& .cm-scroller": {
|
|
684
|
-
fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
|
|
685
|
-
}
|
|
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);
|
|
686
685
|
};
|
|
687
686
|
|
|
688
687
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
@@ -691,174 +690,28 @@ import { EditorView as EditorView5 } from "@codemirror/view";
|
|
|
691
690
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
692
691
|
import { vim } from "@replit/codemirror-vim";
|
|
693
692
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
694
|
-
import
|
|
695
|
-
import {
|
|
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
696
|
import { useThemeContext } from "@dxos/react-ui";
|
|
697
|
-
import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
|
|
698
|
-
var EditorModes = [
|
|
699
|
-
"default",
|
|
700
|
-
"vim"
|
|
701
|
-
];
|
|
702
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
|
|
703
|
-
const { themeMode } = useThemeContext();
|
|
704
|
-
const tabsterDOMAttribute = useFocusableGroup({
|
|
705
|
-
tabBehavior: "limited"
|
|
706
|
-
});
|
|
707
|
-
const { awareness, peer } = model;
|
|
708
|
-
useEffect(() => {
|
|
709
|
-
if (awareness && peer) {
|
|
710
|
-
awareness.setLocalStateField("user", {
|
|
711
|
-
name: peer.name ?? generateName(peer.id),
|
|
712
|
-
color: getColorForValue({
|
|
713
|
-
value: peer.id,
|
|
714
|
-
type: "color"
|
|
715
|
-
}),
|
|
716
|
-
colorLight: getColorForValue({
|
|
717
|
-
value: peer.id,
|
|
718
|
-
themeMode,
|
|
719
|
-
type: "highlight"
|
|
720
|
-
})
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
}, [
|
|
724
|
-
awareness,
|
|
725
|
-
peer,
|
|
726
|
-
themeMode
|
|
727
|
-
]);
|
|
728
|
-
const [root, setRoot] = useState(null);
|
|
729
|
-
const [state, setState] = useState();
|
|
730
|
-
const [view, setView] = useState();
|
|
731
|
-
useImperativeHandle(forwardedRef, () => ({
|
|
732
|
-
root,
|
|
733
|
-
state,
|
|
734
|
-
view
|
|
735
|
-
}), [
|
|
736
|
-
view,
|
|
737
|
-
state,
|
|
738
|
-
root
|
|
739
|
-
]);
|
|
740
|
-
useEffect(() => {
|
|
741
|
-
if (!root) {
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
const state2 = EditorState2.create({
|
|
745
|
-
doc: model.text(),
|
|
746
|
-
extensions: [
|
|
747
|
-
// TODO(burdon): Factor out VIM mode?
|
|
748
|
-
editorMode === "vim" && vim(),
|
|
749
|
-
// Theme.
|
|
750
|
-
EditorView5.baseTheme(defaultTheme),
|
|
751
|
-
EditorView5.theme(slots?.editor?.theme ?? {}),
|
|
752
|
-
// TODO(burdon): themeMode doesn't change in storybooks.
|
|
753
|
-
EditorView5.darkTheme.of(themeMode === "dark"),
|
|
754
|
-
// Storage and replication.
|
|
755
|
-
model.extension,
|
|
756
|
-
// Custom.
|
|
757
|
-
...extensions
|
|
758
|
-
].filter(Boolean)
|
|
759
|
-
});
|
|
760
|
-
view?.destroy();
|
|
761
|
-
setView(new EditorView5({
|
|
762
|
-
state: state2,
|
|
763
|
-
parent: root
|
|
764
|
-
}));
|
|
765
|
-
setState(state2);
|
|
766
|
-
return () => {
|
|
767
|
-
view?.destroy();
|
|
768
|
-
setView(void 0);
|
|
769
|
-
setState(void 0);
|
|
770
|
-
};
|
|
771
|
-
}, [
|
|
772
|
-
root,
|
|
773
|
-
model,
|
|
774
|
-
themeMode,
|
|
775
|
-
editorMode
|
|
776
|
-
]);
|
|
777
|
-
const handleKeyUp = useCallback((event) => {
|
|
778
|
-
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
779
|
-
switch (key) {
|
|
780
|
-
case "Enter": {
|
|
781
|
-
view?.contentDOM.focus();
|
|
782
|
-
break;
|
|
783
|
-
}
|
|
784
|
-
case "Escape": {
|
|
785
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}, [
|
|
790
|
-
view,
|
|
791
|
-
editorMode
|
|
792
|
-
]);
|
|
793
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
794
|
-
key: model.id,
|
|
795
|
-
ref: setRoot,
|
|
796
|
-
tabIndex: 0,
|
|
797
|
-
...slots?.root,
|
|
798
|
-
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
799
|
-
onKeyUp: handleKeyUp
|
|
800
|
-
});
|
|
801
|
-
});
|
|
802
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
803
|
-
const { themeMode } = useThemeContext();
|
|
804
|
-
const slots = defaultsDeep({}, _slots, defaultTextSlots);
|
|
805
|
-
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
806
|
-
ref: forwardedRef,
|
|
807
|
-
extensions: [
|
|
808
|
-
basicBundle({
|
|
809
|
-
themeMode,
|
|
810
|
-
placeholder: slots?.editor?.placeholder
|
|
811
|
-
}),
|
|
812
|
-
...extensions
|
|
813
|
-
],
|
|
814
|
-
slots,
|
|
815
|
-
...props
|
|
816
|
-
});
|
|
817
|
-
});
|
|
818
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
819
|
-
const { themeMode } = useThemeContext();
|
|
820
|
-
const slots = defaultsDeep({}, _slots, defaultSlots);
|
|
821
|
-
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
822
|
-
ref: forwardedRef,
|
|
823
|
-
extensions: [
|
|
824
|
-
markdownBundle({
|
|
825
|
-
themeMode,
|
|
826
|
-
placeholder: slots?.editor?.placeholder
|
|
827
|
-
}),
|
|
828
|
-
...extensions
|
|
829
|
-
],
|
|
830
|
-
slots,
|
|
831
|
-
...props
|
|
832
|
-
});
|
|
833
|
-
});
|
|
834
|
-
var defaultSlots = {
|
|
835
|
-
root: {
|
|
836
|
-
className: mx2("p-2", inputSurface)
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
var defaultTextSlots = {
|
|
840
|
-
...defaultSlots,
|
|
841
|
-
editor: {
|
|
842
|
-
theme: textTheme
|
|
843
|
-
}
|
|
844
|
-
};
|
|
845
697
|
|
|
846
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
847
|
-
import
|
|
848
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
698
|
+
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
699
|
+
import { useEffect, useMemo } from "react";
|
|
849
700
|
import { yCollab } from "y-codemirror.next";
|
|
850
|
-
import {
|
|
851
|
-
import {
|
|
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";
|
|
852
705
|
|
|
853
|
-
// packages/ui/react-ui-editor/src/
|
|
854
|
-
import { Annotation, Facet, StateEffect, StateField as
|
|
855
|
-
import { ViewPlugin } from "@codemirror/view";
|
|
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";
|
|
856
709
|
import * as automerge2 from "@dxos/automerge/automerge";
|
|
857
710
|
|
|
858
|
-
// packages/ui/react-ui-editor/src/
|
|
711
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
859
712
|
import { next as automerge } from "@dxos/automerge/automerge";
|
|
860
713
|
|
|
861
|
-
// packages/ui/react-ui-editor/src/
|
|
714
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
|
|
862
715
|
import { ChangeSet } from "@codemirror/state";
|
|
863
716
|
var amToCodemirror_default = (view, selection, target, patches) => {
|
|
864
717
|
for (const patch of patches) {
|
|
@@ -965,7 +818,7 @@ var charPath = (textPath, candidatePath) => {
|
|
|
965
818
|
return null;
|
|
966
819
|
};
|
|
967
820
|
|
|
968
|
-
// packages/ui/react-ui-editor/src/
|
|
821
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
|
|
969
822
|
import { next as am } from "@dxos/automerge/automerge";
|
|
970
823
|
var codeMirrorToAm_default = (field, handle, transactions, state) => {
|
|
971
824
|
const { lastHeads, path } = state.field(field);
|
|
@@ -988,7 +841,7 @@ var codeMirrorToAm_default = (field, handle, transactions, state) => {
|
|
|
988
841
|
return newHeads ?? void 0;
|
|
989
842
|
};
|
|
990
843
|
|
|
991
|
-
// packages/ui/react-ui-editor/src/
|
|
844
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
|
|
992
845
|
var PatchSemaphore = class {
|
|
993
846
|
constructor(field) {
|
|
994
847
|
this._inReconcile = false;
|
|
@@ -1029,7 +882,7 @@ var PatchSemaphore = class {
|
|
|
1029
882
|
}
|
|
1030
883
|
};
|
|
1031
884
|
|
|
1032
|
-
// packages/ui/react-ui-editor/src/
|
|
885
|
+
// packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
|
|
1033
886
|
var effectType = StateEffect.define({});
|
|
1034
887
|
var updateHeads = (newHeads) => effectType.of({
|
|
1035
888
|
newHeads
|
|
@@ -1040,7 +893,7 @@ var semaphoreFacet = Facet.define({
|
|
|
1040
893
|
combine: (values) => values.at(-1)
|
|
1041
894
|
});
|
|
1042
895
|
var automergePlugin = (handle, path) => {
|
|
1043
|
-
const stateField =
|
|
896
|
+
const stateField = StateField3.define({
|
|
1044
897
|
create: () => ({
|
|
1045
898
|
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
1046
899
|
unreconciledTransactions: [],
|
|
@@ -1070,7 +923,7 @@ var automergePlugin = (handle, path) => {
|
|
|
1070
923
|
}
|
|
1071
924
|
});
|
|
1072
925
|
const semaphore = new PatchSemaphore(stateField);
|
|
1073
|
-
const viewPlugin =
|
|
926
|
+
const viewPlugin = ViewPlugin2.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
1074
927
|
constructor(view) {
|
|
1075
928
|
this._handleChange = () => {
|
|
1076
929
|
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
@@ -1100,6 +953,45 @@ var automergePlugin = (handle, path) => {
|
|
|
1100
953
|
var reconcileAnnotationType = Annotation.define();
|
|
1101
954
|
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
1102
955
|
|
|
956
|
+
// packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
|
|
957
|
+
var useCollaboration = (model, themeMode) => {
|
|
958
|
+
const { provider, peer, content } = model;
|
|
959
|
+
const extension = useMemo(() => {
|
|
960
|
+
if (content instanceof YText) {
|
|
961
|
+
return yCollab(content, provider?.awareness);
|
|
962
|
+
} else if (isDocAccessor(content)) {
|
|
963
|
+
return automergePlugin(content.handle, content.path);
|
|
964
|
+
} else {
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
}, []);
|
|
968
|
+
useEffect(() => {
|
|
969
|
+
if (provider && peer) {
|
|
970
|
+
provider.awareness.setLocalStateField("user", {
|
|
971
|
+
name: peer.name ?? generateName(peer.id),
|
|
972
|
+
color: getColorForValue({
|
|
973
|
+
value: peer.id,
|
|
974
|
+
type: "color"
|
|
975
|
+
}),
|
|
976
|
+
colorLight: getColorForValue({
|
|
977
|
+
value: peer.id,
|
|
978
|
+
themeMode,
|
|
979
|
+
type: "highlight"
|
|
980
|
+
})
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
}, [
|
|
984
|
+
provider,
|
|
985
|
+
peer,
|
|
986
|
+
themeMode
|
|
987
|
+
]);
|
|
988
|
+
return extension;
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
992
|
+
import { useMemo as useMemo2 } from "react";
|
|
993
|
+
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
994
|
+
|
|
1103
995
|
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
1104
996
|
import * as random from "lib0/random";
|
|
1105
997
|
var cursorColors = [
|
|
@@ -1239,80 +1131,185 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1239
1131
|
};
|
|
1240
1132
|
|
|
1241
1133
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1242
|
-
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
1243
1134
|
var useTextModel = ({ identity, space, text }) => {
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
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({
|
|
1252
1143
|
space,
|
|
1253
|
-
text
|
|
1254
|
-
|
|
1144
|
+
doc: text.doc,
|
|
1145
|
+
channel: `yjs.awareness.${text.id}`
|
|
1146
|
+
});
|
|
1255
1147
|
}, [
|
|
1256
1148
|
identity,
|
|
1257
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
|
+
}, [
|
|
1258
1163
|
text
|
|
1259
1164
|
]);
|
|
1260
|
-
return model;
|
|
1261
|
-
};
|
|
1262
|
-
var createModel = (options) => {
|
|
1263
|
-
const { space, text } = options;
|
|
1264
1165
|
if (isActualAutomergeObject(text)) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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;
|
|
1271
1179
|
}
|
|
1272
|
-
};
|
|
1273
|
-
var createYjsModel = ({ identity, space, text }) => {
|
|
1274
|
-
invariant(space && text?.doc && text?.content, void 0, {
|
|
1275
|
-
F: __dxlog_file2,
|
|
1276
|
-
L: 73,
|
|
1277
|
-
S: void 0,
|
|
1278
|
-
A: [
|
|
1279
|
-
"space && text?.doc && text?.content",
|
|
1280
|
-
""
|
|
1281
|
-
]
|
|
1282
|
-
});
|
|
1283
|
-
const provider = new SpaceAwarenessProvider({
|
|
1284
|
-
space,
|
|
1285
|
-
doc: text.doc,
|
|
1286
|
-
channel: `yjs.awareness.${text.id}`
|
|
1287
|
-
});
|
|
1288
1180
|
return {
|
|
1289
1181
|
id: text.doc.guid,
|
|
1290
1182
|
content: text.content,
|
|
1291
|
-
|
|
1292
|
-
extension: yCollab(text.content, provider.awareness),
|
|
1293
|
-
awareness: provider.awareness,
|
|
1183
|
+
provider,
|
|
1294
1184
|
peer: identity ? {
|
|
1295
1185
|
id: identity.identityKey.toHex(),
|
|
1296
1186
|
name: identity.profile?.displayName
|
|
1297
1187
|
} : void 0
|
|
1298
1188
|
};
|
|
1299
1189
|
};
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
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
|
|
1304
1212
|
]);
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1213
|
+
const collaboration = useCollaboration(model, themeMode);
|
|
1214
|
+
useEffect2(() => {
|
|
1215
|
+
if (!parent) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
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,
|
|
1221
|
+
extensions: [
|
|
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)
|
|
1231
|
+
});
|
|
1232
|
+
setState(state2);
|
|
1233
|
+
view?.destroy();
|
|
1234
|
+
setView(new EditorView5({
|
|
1235
|
+
state: state2,
|
|
1236
|
+
parent
|
|
1237
|
+
}));
|
|
1238
|
+
return () => {
|
|
1239
|
+
view?.destroy();
|
|
1240
|
+
setView(void 0);
|
|
1241
|
+
setState(void 0);
|
|
1242
|
+
};
|
|
1243
|
+
}, [
|
|
1244
|
+
parent,
|
|
1245
|
+
model.content,
|
|
1246
|
+
themeMode,
|
|
1247
|
+
editorMode
|
|
1248
|
+
]);
|
|
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
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}, [
|
|
1262
|
+
view,
|
|
1263
|
+
editorMode
|
|
1264
|
+
]);
|
|
1265
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1266
|
+
key: model.id,
|
|
1267
|
+
ref: setParent,
|
|
1268
|
+
tabIndex: 0,
|
|
1269
|
+
...slots?.root,
|
|
1270
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
1271
|
+
onKeyUp: handleKeyUp
|
|
1272
|
+
});
|
|
1273
|
+
});
|
|
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
|
|
1289
|
+
}
|
|
1290
|
+
}),
|
|
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
|
|
1300
|
+
})
|
|
1301
|
+
];
|
|
1302
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1303
|
+
ref: forwardedRef,
|
|
1304
|
+
extensions,
|
|
1305
|
+
slots: defaultsDeep({}, slots, {
|
|
1306
|
+
editor: {
|
|
1307
|
+
theme: markdownTheme
|
|
1308
|
+
}
|
|
1309
|
+
}),
|
|
1310
|
+
...props
|
|
1311
|
+
});
|
|
1312
|
+
});
|
|
1316
1313
|
export {
|
|
1317
1314
|
BaseTextEditor,
|
|
1318
1315
|
Doc,
|
|
@@ -1321,27 +1318,34 @@ export {
|
|
|
1321
1318
|
SpaceAwarenessProvider,
|
|
1322
1319
|
TextEditor,
|
|
1323
1320
|
TextKind,
|
|
1324
|
-
YText,
|
|
1321
|
+
YText2 as YText,
|
|
1325
1322
|
YXmlFragment,
|
|
1326
|
-
autocomplete,
|
|
1327
1323
|
basicBundle,
|
|
1328
|
-
|
|
1324
|
+
basicTheme,
|
|
1325
|
+
chalky,
|
|
1326
|
+
coral,
|
|
1327
|
+
createHyperlinkTooltip,
|
|
1329
1328
|
cursorColor,
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1329
|
+
cyan,
|
|
1330
|
+
highlightBackground,
|
|
1331
|
+
hyperLinkExtension,
|
|
1332
|
+
hyperlinkDecoration,
|
|
1333
|
+
hyperlinkStyle,
|
|
1334
|
+
hyperlinkWidget,
|
|
1335
|
+
invalid,
|
|
1336
|
+
ivory,
|
|
1337
|
+
malibu,
|
|
1335
1338
|
markdownBundle,
|
|
1336
1339
|
markdownHighlightStyle,
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1340
|
+
markdownTheme,
|
|
1341
|
+
onChangeExtension,
|
|
1342
|
+
sage,
|
|
1343
|
+
stone,
|
|
1341
1344
|
tags2 as tags,
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1345
|
+
tooltipBackground,
|
|
1346
|
+
useCollaboration,
|
|
1347
|
+
useTextModel,
|
|
1348
|
+
violet,
|
|
1349
|
+
whiskey
|
|
1346
1350
|
};
|
|
1347
1351
|
//# sourceMappingURL=index.mjs.map
|