@dxos/react-ui-editor 0.3.10 → 0.3.11-main.0ac3ef5
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 +1645 -1066
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +21 -13
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +15 -19
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -2
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.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/{hooks/yjs → components/TextEditor}/yjs.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +26 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +4 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/{hooks/automerge/PatchSemaphore.d.ts → extensions/automerge/semaphore.d.ts} +5 -5
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness.d.ts +46 -0
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/code.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +50 -0
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/cursor.d.ts +25 -0
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts +2 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/image.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +6 -3
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
- package/dist/types/src/extensions/link.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts +10 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions/demo.d.ts → extensions/typewriter.d.ts} +2 -3
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts +4 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/index.d.ts +2 -0
- package/dist/types/src/extensions/yjs/index.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts +4 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts +2 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge.d.ts +3 -0
- package/dist/types/src/hooks/automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +15 -11
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/{yjs/space-provider.d.ts → yjs.d.ts} +8 -8
- package/dist/types/src/hooks/yjs.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/cursors.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts +2 -2
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +5 -13
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +5 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +26 -20
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +140 -79
- package/src/components/TextEditor/TextEditor.stories.tsx +14 -35
- package/src/components/TextEditor/TextEditor.tsx +87 -79
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +67 -8
- package/src/components/TextEditor/codemirror.stories.ts +3 -2
- package/src/components/TextEditor/comments.stories.tsx +315 -0
- package/src/components/TextEditor/index.ts +0 -3
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +18 -17
- package/src/extensions/automerge/automerge.ts +94 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +62 -0
- package/src/extensions/automerge/index.ts +7 -0
- package/src/{hooks/automerge/PatchSemaphore.ts → extensions/automerge/semaphore.ts} +18 -15
- package/src/{hooks/automerge/translation/codemirror-to-automerge.ts → extensions/automerge/update-automerge.ts} +6 -7
- package/src/{hooks/automerge/translation/automerge-to-codemirror.ts → extensions/automerge/update-codemirror.ts} +14 -11
- package/src/extensions/awareness.ts +309 -0
- package/src/{components/TextEditor/extensions → extensions}/code.ts +25 -18
- package/src/extensions/comments.ts +325 -0
- package/src/extensions/cursor.ts +46 -0
- package/src/extensions/hr.ts +73 -0
- package/src/extensions/image.ts +87 -0
- package/src/{components/TextEditor/extensions → extensions}/index.ts +6 -3
- package/src/{components/TextEditor/extensions → extensions}/link.ts +116 -62
- package/src/extensions/listener.ts +35 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +4 -16
- package/src/{components/TextEditor/extensions → extensions}/table.ts +23 -4
- package/src/{components/TextEditor/extensions → extensions}/tasklist.ts +3 -5
- package/src/{components/TextEditor/extensions/demo.ts → extensions/typewriter.ts} +2 -3
- package/src/extensions/yjs/cursor.ts +22 -0
- package/src/extensions/yjs/index.ts +5 -0
- package/src/extensions/yjs/yjs.test.ts +35 -0
- package/src/extensions/yjs/yjs.ts +16 -0
- package/src/hooks/automerge.ts +52 -0
- package/src/hooks/awareness-provider.ts +120 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTextModel.ts +40 -79
- package/src/hooks/{yjs/space-provider.ts → yjs.ts} +49 -19
- package/src/index.ts +6 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/markdown.ts +0 -2
- package/src/styles/tokens.ts +3 -0
- package/src/testing/replicator.ts +10 -16
- package/src/themes/default.ts +275 -0
- package/src/util.ts +18 -0
- 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/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts +0 -18
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +0 -10
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +0 -24
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- 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.map +0 -1
- 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/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +0 -5
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/translation/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/index.d.ts +0 -3
- package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/comments.ts +0 -173
- package/src/components/TextEditor/extensions/experimental.tsx +0 -114
- package/src/components/TextEditor/extensions/highlight.ts +0 -128
- package/src/components/TextEditor/extensions/image.ts +0 -67
- package/src/components/TextEditor/extensions/listener.ts +0 -23
- package/src/components/TextEditor/extensions/tooltip.tsx +0 -61
- package/src/components/TextEditor/themes/default.ts +0 -279
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/automerge/plugin.ts +0 -134
- package/src/hooks/automerge/translation/index.ts +0 -6
- package/src/hooks/yjs/index.ts +0 -6
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/tasklist.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
- /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
|
@@ -5,46 +5,614 @@ import { Doc, YText, YXmlFragment } from "@dxos/text-model";
|
|
|
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/
|
|
8
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
+
import { EditorView as EditorView12 } from "@codemirror/view";
|
|
11
|
+
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
12
|
+
import { vim } from "@replit/codemirror-vim";
|
|
13
|
+
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState, useRef } from "react";
|
|
15
|
+
import { generateName } from "@dxos/display-name";
|
|
16
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
+
import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
18
|
+
|
|
19
|
+
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
9
20
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
21
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
10
22
|
import { keymap } from "@codemirror/view";
|
|
11
23
|
var autocomplete = ({ onSearch }) => {
|
|
12
24
|
return [
|
|
13
25
|
// https://codemirror.net/docs/ref/#view.keymap
|
|
14
26
|
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
27
|
+
// TODO(burdon): Set custom keymap.
|
|
15
28
|
keymap.of(completionKeymap),
|
|
16
29
|
// https://codemirror.net/examples/autocompletion
|
|
17
30
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
18
31
|
autocompletion({
|
|
19
|
-
|
|
20
|
-
// defaultKeymap: false,
|
|
32
|
+
defaultKeymap: false,
|
|
21
33
|
// Don't start unless key press.
|
|
22
|
-
activateOnTyping: false
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
from: match.from,
|
|
33
|
-
options: onSearch(match.text.toLowerCase())
|
|
34
|
-
};
|
|
34
|
+
activateOnTyping: false
|
|
35
|
+
}),
|
|
36
|
+
// TODO(burdon): Option to create new page?
|
|
37
|
+
// TODO(burdon): Optional decoration via addToOptions
|
|
38
|
+
markdownLanguage.data.of({
|
|
39
|
+
autocomplete: (context) => {
|
|
40
|
+
const match = context.matchBefore(/\w*/);
|
|
41
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
42
|
+
return null;
|
|
35
43
|
}
|
|
36
|
-
|
|
44
|
+
return {
|
|
45
|
+
from: match.from,
|
|
46
|
+
options: onSearch(match.text.toLowerCase())
|
|
47
|
+
};
|
|
48
|
+
}
|
|
37
49
|
})
|
|
38
50
|
];
|
|
39
51
|
};
|
|
40
52
|
|
|
41
|
-
// packages/ui/react-ui-editor/src/
|
|
53
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
54
|
+
import { Facet as Facet2, StateField } from "@codemirror/state";
|
|
55
|
+
import { ViewPlugin } from "@codemirror/view";
|
|
56
|
+
import { next as A } from "@dxos/automerge/automerge";
|
|
57
|
+
|
|
58
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
|
|
59
|
+
import get from "lodash.get";
|
|
60
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
61
|
+
var cursorConverter = (handle, path) => ({
|
|
62
|
+
// TODO(burdon): Handle assoc to associate with a previous character.
|
|
63
|
+
toCursor: (pos) => {
|
|
64
|
+
const doc = handle.docSync();
|
|
65
|
+
if (!doc) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
const value = get(doc, path);
|
|
69
|
+
if (typeof value === "string" && value.length <= pos) {
|
|
70
|
+
return "end";
|
|
71
|
+
}
|
|
72
|
+
return automerge.getCursor(doc, path.slice(), pos);
|
|
73
|
+
},
|
|
74
|
+
fromCursor: (cursor) => {
|
|
75
|
+
if (cursor === "") {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
const doc = handle.docSync();
|
|
79
|
+
if (!doc) {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
if (cursor === "end") {
|
|
83
|
+
const value = get(doc, path);
|
|
84
|
+
if (typeof value === "string") {
|
|
85
|
+
return value.length;
|
|
86
|
+
} else {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return automerge.getCursorPosition(doc, path.slice(), cursor);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
95
|
+
import { Annotation, StateEffect } from "@codemirror/state";
|
|
96
|
+
var effectType = StateEffect.define({});
|
|
97
|
+
var updateHeads = (newHeads) => effectType.of({
|
|
98
|
+
newHeads
|
|
99
|
+
});
|
|
100
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
101
|
+
var getPath = (state, field) => state.field(field).path;
|
|
102
|
+
var reconcileAnnotationType = Annotation.define();
|
|
103
|
+
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
104
|
+
|
|
105
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
106
|
+
import { next as automerge2 } from "@dxos/automerge/automerge";
|
|
107
|
+
|
|
108
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
109
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
110
|
+
var updateAutomerge = (field, handle, transactions, state) => {
|
|
111
|
+
const { lastHeads, path } = state.field(field);
|
|
112
|
+
let hasChanges = false;
|
|
113
|
+
for (const tr of transactions) {
|
|
114
|
+
tr.changes.iterChanges(() => {
|
|
115
|
+
hasChanges = true;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (!hasChanges) {
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
122
|
+
for (const tr of transactions) {
|
|
123
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
124
|
+
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return newHeads ?? void 0;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-codemirror.ts
|
|
132
|
+
import { ChangeSet } from "@codemirror/state";
|
|
133
|
+
var updateCodeMirror = (view, selection, target, patches) => {
|
|
134
|
+
for (const patch of patches) {
|
|
135
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
136
|
+
if (changeSpec != null) {
|
|
137
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
138
|
+
selection = selection.map(changeSet, 1);
|
|
139
|
+
view.dispatch({
|
|
140
|
+
changes: changeSet,
|
|
141
|
+
annotations: reconcileAnnotationType.of({})
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
view.dispatch({
|
|
146
|
+
selection,
|
|
147
|
+
annotations: reconcileAnnotationType.of({})
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
var handlePatch = (patch, target, state) => {
|
|
151
|
+
if (patch.action === "insert") {
|
|
152
|
+
return handleInsert(target, patch);
|
|
153
|
+
} else if (patch.action === "splice") {
|
|
154
|
+
return handleSplice(target, patch);
|
|
155
|
+
} else if (patch.action === "del") {
|
|
156
|
+
return handleDel(target, patch);
|
|
157
|
+
} else if (patch.action === "put") {
|
|
158
|
+
return handlePut(target, patch, state);
|
|
159
|
+
} else {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
var handleInsert = (target, patch) => {
|
|
164
|
+
const index = charPath(target, patch.path);
|
|
165
|
+
if (index == null) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
from: index,
|
|
172
|
+
to: index,
|
|
173
|
+
insert: text
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
};
|
|
177
|
+
var handleSplice = (target, patch) => {
|
|
178
|
+
const index = charPath(target, patch.path);
|
|
179
|
+
if (index == null) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
return [
|
|
183
|
+
{
|
|
184
|
+
from: index,
|
|
185
|
+
insert: patch.value
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
};
|
|
189
|
+
var handleDel = (target, patch) => {
|
|
190
|
+
const index = charPath(target, patch.path);
|
|
191
|
+
if (index == null) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const length = patch.length || 1;
|
|
195
|
+
return [
|
|
196
|
+
{
|
|
197
|
+
from: index,
|
|
198
|
+
to: index + length
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
};
|
|
202
|
+
var handlePut = (target, patch, state) => {
|
|
203
|
+
const index = charPath(target, [
|
|
204
|
+
...patch.path,
|
|
205
|
+
0
|
|
206
|
+
]);
|
|
207
|
+
if (index == null) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
const length = state.doc.length;
|
|
211
|
+
if (typeof patch.value !== "string") {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
return [
|
|
215
|
+
{
|
|
216
|
+
from: 0,
|
|
217
|
+
to: length,
|
|
218
|
+
insert: patch.value
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
};
|
|
222
|
+
var charPath = (textPath, candidatePath) => {
|
|
223
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
227
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
232
|
+
if (typeof index === "number") {
|
|
233
|
+
return index;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
239
|
+
var PatchSemaphore = class {
|
|
240
|
+
constructor(_field) {
|
|
241
|
+
this._field = _field;
|
|
242
|
+
this._inReconcile = false;
|
|
243
|
+
this._queue = [];
|
|
244
|
+
this.reconcile = (handle, view) => {
|
|
245
|
+
if (this._inReconcile) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this._inReconcile = true;
|
|
249
|
+
const path = getPath(view.state, this._field);
|
|
250
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
251
|
+
let selection = view.state.selection;
|
|
252
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
253
|
+
const toInvert = transactions.slice().reverse();
|
|
254
|
+
for (const tx of toInvert) {
|
|
255
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
256
|
+
selection = selection.map(inverted);
|
|
257
|
+
view.dispatch({
|
|
258
|
+
changes: inverted,
|
|
259
|
+
annotations: reconcileAnnotationType.of(true)
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
263
|
+
if (newHeads === null || newHeads === void 0) {
|
|
264
|
+
newHeads = automerge2.getHeads(handle.docSync());
|
|
265
|
+
}
|
|
266
|
+
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
|
|
267
|
+
updateCodeMirror(view, selection, path, diff);
|
|
268
|
+
view.dispatch({
|
|
269
|
+
effects: updateHeads(newHeads),
|
|
270
|
+
annotations: reconcileAnnotationType.of({})
|
|
271
|
+
});
|
|
272
|
+
this._inReconcile = false;
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// packages/ui/react-ui-editor/src/extensions/cursor.ts
|
|
278
|
+
import { Facet } from "@codemirror/state";
|
|
279
|
+
var defaultCursorConverter = {
|
|
280
|
+
toCursor: (position) => position.toString(),
|
|
281
|
+
fromCursor: (cursor) => parseInt(cursor)
|
|
282
|
+
};
|
|
283
|
+
var Cursor = class {
|
|
284
|
+
static {
|
|
285
|
+
this.converter = Facet.define({
|
|
286
|
+
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
static {
|
|
290
|
+
this.getCursorFromRange = (cursorConverter3, range) => {
|
|
291
|
+
const from = cursorConverter3.toCursor(range.from);
|
|
292
|
+
const to = cursorConverter3.toCursor(range.to, -1);
|
|
293
|
+
return [
|
|
294
|
+
from,
|
|
295
|
+
to
|
|
296
|
+
].join(":");
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
static {
|
|
300
|
+
this.getRangeFromCursor = (cursorConverter3, cursor) => {
|
|
301
|
+
const parts = cursor.split(":");
|
|
302
|
+
const from = cursorConverter3.fromCursor(parts[0]);
|
|
303
|
+
const to = cursorConverter3.fromCursor(parts[1]);
|
|
304
|
+
return from !== void 0 && to !== void 0 ? {
|
|
305
|
+
from,
|
|
306
|
+
to
|
|
307
|
+
} : void 0;
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
313
|
+
var semaphoreFacet = Facet2.define({
|
|
314
|
+
combine: (values) => values.at(-1)
|
|
315
|
+
});
|
|
316
|
+
var automerge3 = ({ handle, path }) => {
|
|
317
|
+
const stateField = StateField.define({
|
|
318
|
+
create: () => ({
|
|
319
|
+
lastHeads: A.getHeads(handle.docSync()),
|
|
320
|
+
unreconciledTransactions: [],
|
|
321
|
+
path: path.slice()
|
|
322
|
+
}),
|
|
323
|
+
update: (value, tr) => {
|
|
324
|
+
const result = {
|
|
325
|
+
lastHeads: value.lastHeads,
|
|
326
|
+
unreconciledTransactions: value.unreconciledTransactions.slice(),
|
|
327
|
+
path: path.slice()
|
|
328
|
+
};
|
|
329
|
+
let clearUnreconciled = false;
|
|
330
|
+
for (const effect of tr.effects) {
|
|
331
|
+
if (effect.is(effectType)) {
|
|
332
|
+
result.lastHeads = effect.value.newHeads;
|
|
333
|
+
clearUnreconciled = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (clearUnreconciled) {
|
|
337
|
+
result.unreconciledTransactions = [];
|
|
338
|
+
} else {
|
|
339
|
+
if (!isReconcileTx(tr)) {
|
|
340
|
+
result.unreconciledTransactions.push(tr);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
const semaphore = new PatchSemaphore(stateField);
|
|
347
|
+
const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
348
|
+
constructor(_view) {
|
|
349
|
+
this._view = _view;
|
|
350
|
+
this._handleChange = () => {
|
|
351
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
352
|
+
};
|
|
353
|
+
handle.addListener("change", this._handleChange);
|
|
354
|
+
}
|
|
355
|
+
update(update2) {
|
|
356
|
+
if (update2.transactions.length > 0 && update2.transactions.some((tr) => !isReconcileTx(tr))) {
|
|
357
|
+
queueMicrotask(() => {
|
|
358
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
destroy() {
|
|
363
|
+
handle.addListener("change", this._handleChange);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
extension: [
|
|
368
|
+
Cursor.converter.of(cursorConverter(handle, path)),
|
|
369
|
+
semaphoreFacet.of(semaphore),
|
|
370
|
+
stateField,
|
|
371
|
+
viewPlugin
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// packages/ui/react-ui-editor/src/extensions/awareness.ts
|
|
377
|
+
import { Annotation as Annotation2, Facet as Facet3, RangeSet } from "@codemirror/state";
|
|
378
|
+
import { Decoration, EditorView, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
379
|
+
import { Event } from "@dxos/async";
|
|
380
|
+
import { Context } from "@dxos/context";
|
|
381
|
+
var dummyProvider = {
|
|
382
|
+
remoteStateChange: new Event(),
|
|
383
|
+
open: () => {
|
|
384
|
+
},
|
|
385
|
+
close: () => {
|
|
386
|
+
},
|
|
387
|
+
update: () => {
|
|
388
|
+
},
|
|
389
|
+
getRemoteStates: () => []
|
|
390
|
+
};
|
|
391
|
+
var AwarenessProvider = Facet3.define({
|
|
392
|
+
combine: (providers) => providers[0] ?? dummyProvider
|
|
393
|
+
});
|
|
394
|
+
var RemoteSelectionChangedAnnotation = Annotation2.define();
|
|
395
|
+
var awareness = (provider = dummyProvider) => {
|
|
396
|
+
return [
|
|
397
|
+
AwarenessProvider.of(provider),
|
|
398
|
+
ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
|
|
399
|
+
decorations: (value) => value.decorations
|
|
400
|
+
}),
|
|
401
|
+
styles
|
|
402
|
+
];
|
|
403
|
+
};
|
|
404
|
+
var RemoteSelectionsDecorator = class {
|
|
405
|
+
constructor(view) {
|
|
406
|
+
this.decorations = RangeSet.of([]);
|
|
407
|
+
this._ctx = new Context();
|
|
408
|
+
this._lastAnchor = void 0;
|
|
409
|
+
this._lastHead = void 0;
|
|
410
|
+
this._cursorConverter = view.state.facet(Cursor.converter);
|
|
411
|
+
this._provider = view.state.facet(AwarenessProvider);
|
|
412
|
+
this._provider.open();
|
|
413
|
+
this._provider.remoteStateChange.on(this._ctx, () => {
|
|
414
|
+
view.dispatch({
|
|
415
|
+
annotations: [
|
|
416
|
+
RemoteSelectionChangedAnnotation.of([])
|
|
417
|
+
]
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
destroy() {
|
|
422
|
+
void this._ctx.dispose();
|
|
423
|
+
this._provider.close();
|
|
424
|
+
}
|
|
425
|
+
update(update2) {
|
|
426
|
+
this._updateLocalSelection(update2);
|
|
427
|
+
this._updateRemoteSelections(update2);
|
|
428
|
+
}
|
|
429
|
+
_updateLocalSelection(update2) {
|
|
430
|
+
const hasFocus = update2.view.hasFocus && update2.view.dom.ownerDocument.hasFocus();
|
|
431
|
+
const { anchor = void 0, head = void 0 } = hasFocus ? update2.state.selection.main : {};
|
|
432
|
+
if (this._lastAnchor === anchor && this._lastHead === head) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this._lastAnchor = anchor;
|
|
436
|
+
this._lastHead = head;
|
|
437
|
+
this._provider.update(anchor !== void 0 && head !== void 0 ? {
|
|
438
|
+
anchor: this._cursorConverter.toCursor(anchor),
|
|
439
|
+
head: this._cursorConverter.toCursor(head)
|
|
440
|
+
} : void 0);
|
|
441
|
+
}
|
|
442
|
+
_updateRemoteSelections(update2) {
|
|
443
|
+
const decorations = [];
|
|
444
|
+
const awarenessStates = this._provider.getRemoteStates();
|
|
445
|
+
for (const state of awarenessStates) {
|
|
446
|
+
const anchor = state.position?.anchor ? this._cursorConverter.fromCursor(state.position.anchor) : null;
|
|
447
|
+
const head = state.position?.head ? this._cursorConverter.fromCursor(state.position.head) : null;
|
|
448
|
+
if (anchor == null || head == null) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const start = Math.min(Math.min(anchor, head), update2.view.state.doc.length);
|
|
452
|
+
const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
|
|
453
|
+
const startLine = update2.view.state.doc.lineAt(start);
|
|
454
|
+
const endLine = update2.view.state.doc.lineAt(end);
|
|
455
|
+
const color = state.info.color ?? "#30bced";
|
|
456
|
+
const lightColor = state.info.lightColor ?? color + "33";
|
|
457
|
+
if (startLine.number === endLine.number) {
|
|
458
|
+
decorations.push({
|
|
459
|
+
from: start,
|
|
460
|
+
to: end,
|
|
461
|
+
value: Decoration.mark({
|
|
462
|
+
attributes: {
|
|
463
|
+
style: `background-color: ${lightColor}`
|
|
464
|
+
},
|
|
465
|
+
class: "cm-collab-selection"
|
|
466
|
+
})
|
|
467
|
+
});
|
|
468
|
+
} else {
|
|
469
|
+
decorations.push({
|
|
470
|
+
from: start,
|
|
471
|
+
to: startLine.from + startLine.length,
|
|
472
|
+
value: Decoration.mark({
|
|
473
|
+
attributes: {
|
|
474
|
+
style: `background-color: ${color}`
|
|
475
|
+
},
|
|
476
|
+
class: "cm-collab-selection"
|
|
477
|
+
})
|
|
478
|
+
});
|
|
479
|
+
decorations.push({
|
|
480
|
+
from: endLine.from,
|
|
481
|
+
to: end,
|
|
482
|
+
value: Decoration.mark({
|
|
483
|
+
attributes: {
|
|
484
|
+
style: `background-color: ${color}`
|
|
485
|
+
},
|
|
486
|
+
class: "cm-collab-selection"
|
|
487
|
+
})
|
|
488
|
+
});
|
|
489
|
+
for (let i = startLine.number + 1; i < endLine.number; i++) {
|
|
490
|
+
const linePos = update2.view.state.doc.line(i).from;
|
|
491
|
+
decorations.push({
|
|
492
|
+
from: linePos,
|
|
493
|
+
to: linePos,
|
|
494
|
+
value: Decoration.line({
|
|
495
|
+
attributes: {
|
|
496
|
+
style: `background-color: ${color}`,
|
|
497
|
+
class: "cm-collab-selectionLine"
|
|
498
|
+
}
|
|
499
|
+
})
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
decorations.push({
|
|
504
|
+
from: head,
|
|
505
|
+
to: head,
|
|
506
|
+
value: Decoration.widget({
|
|
507
|
+
side: head - anchor > 0 ? -1 : 1,
|
|
508
|
+
block: false,
|
|
509
|
+
widget: new RemoteCaretWidget(state.info.displayName ?? "Anonymous", color)
|
|
510
|
+
})
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
this.decorations = Decoration.set(decorations, true);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
var RemoteCaretWidget = class extends WidgetType {
|
|
517
|
+
constructor(_name, _color) {
|
|
518
|
+
super();
|
|
519
|
+
this._name = _name;
|
|
520
|
+
this._color = _color;
|
|
521
|
+
}
|
|
522
|
+
toDOM() {
|
|
523
|
+
const span = document.createElement("span");
|
|
524
|
+
span.className = "cm-collab-selectionCaret";
|
|
525
|
+
span.style.backgroundColor = this._color;
|
|
526
|
+
span.style.borderColor = this._color;
|
|
527
|
+
const dot = document.createElement("div");
|
|
528
|
+
dot.className = "cm-collab-selectionCaretDot";
|
|
529
|
+
const info = document.createElement("div");
|
|
530
|
+
info.className = "cm-collab-selectionInfo";
|
|
531
|
+
info.innerText = this._name;
|
|
532
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
533
|
+
span.appendChild(dot);
|
|
534
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
535
|
+
span.appendChild(info);
|
|
536
|
+
span.appendChild(document.createTextNode("\u2060"));
|
|
537
|
+
return span;
|
|
538
|
+
}
|
|
539
|
+
updateDOM() {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
eq(widget) {
|
|
543
|
+
return widget._color === this._color;
|
|
544
|
+
}
|
|
545
|
+
get estimatedHeight() {
|
|
546
|
+
return -1;
|
|
547
|
+
}
|
|
548
|
+
ignoreEvent() {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
var styles = EditorView.baseTheme({
|
|
553
|
+
".cm-collab-selection": {},
|
|
554
|
+
".cm-collab-selectionLine": {
|
|
555
|
+
padding: 0,
|
|
556
|
+
margin: "0px 2px 0px 4px"
|
|
557
|
+
},
|
|
558
|
+
".cm-collab-selectionCaret": {
|
|
559
|
+
position: "relative",
|
|
560
|
+
borderLeft: "1px solid black",
|
|
561
|
+
borderRight: "1px solid black",
|
|
562
|
+
marginLeft: "-1px",
|
|
563
|
+
marginRight: "-1px",
|
|
564
|
+
boxSizing: "border-box",
|
|
565
|
+
display: "inline"
|
|
566
|
+
},
|
|
567
|
+
".cm-collab-selectionCaretDot": {
|
|
568
|
+
borderRadius: "50%",
|
|
569
|
+
position: "absolute",
|
|
570
|
+
width: ".4em",
|
|
571
|
+
height: ".4em",
|
|
572
|
+
top: "-.2em",
|
|
573
|
+
left: "-.2em",
|
|
574
|
+
backgroundColor: "inherit",
|
|
575
|
+
transition: "transform .3s ease-in-out",
|
|
576
|
+
boxSizing: "border-box"
|
|
577
|
+
},
|
|
578
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
|
|
579
|
+
transformOrigin: "bottom center",
|
|
580
|
+
transform: "scale(0)"
|
|
581
|
+
},
|
|
582
|
+
".cm-collab-selectionInfo": {
|
|
583
|
+
position: "absolute",
|
|
584
|
+
top: "-1.05em",
|
|
585
|
+
left: "-1px",
|
|
586
|
+
fontSize: ".75em",
|
|
587
|
+
fontFamily: "serif",
|
|
588
|
+
fontStyle: "normal",
|
|
589
|
+
fontWeight: "normal",
|
|
590
|
+
lineHeight: "normal",
|
|
591
|
+
userSelect: "none",
|
|
592
|
+
color: "white",
|
|
593
|
+
paddingLeft: "2px",
|
|
594
|
+
paddingRight: "2px",
|
|
595
|
+
zIndex: 101,
|
|
596
|
+
transition: "opacity .3s ease-in-out",
|
|
597
|
+
backgroundColor: "inherit",
|
|
598
|
+
// these should be separate
|
|
599
|
+
opacity: 0,
|
|
600
|
+
transitionDelay: "0s",
|
|
601
|
+
whiteSpace: "nowrap"
|
|
602
|
+
},
|
|
603
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionInfo": {
|
|
604
|
+
opacity: 1,
|
|
605
|
+
transitionDelay: "0s"
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
42
610
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
43
611
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
44
612
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
45
|
-
import { drawSelection, EditorView, placeholder } from "@codemirror/view";
|
|
613
|
+
import { drawSelection, EditorView as EditorView2, placeholder } from "@codemirror/view";
|
|
46
614
|
var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
|
|
47
|
-
|
|
615
|
+
EditorView2.lineWrapping,
|
|
48
616
|
// TODO(burdon): Compare with minimalSetup.
|
|
49
617
|
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
50
618
|
bracketMatching(),
|
|
@@ -55,11 +623,11 @@ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
|
|
|
55
623
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
56
624
|
].filter(Boolean);
|
|
57
625
|
|
|
58
|
-
// packages/ui/react-ui-editor/src/
|
|
59
|
-
import { EditorView as
|
|
626
|
+
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
627
|
+
import { EditorView as EditorView3, keymap as keymap2 } from "@codemirror/view";
|
|
60
628
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
61
629
|
import { invariant } from "@dxos/invariant";
|
|
62
|
-
var __dxlog_file = "/home/
|
|
630
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
63
631
|
var defaultOptions = {
|
|
64
632
|
effect: 2,
|
|
65
633
|
maxParticles: 200,
|
|
@@ -104,12 +672,12 @@ var blast = (options = defaultOptions) => {
|
|
|
104
672
|
};
|
|
105
673
|
return [
|
|
106
674
|
// Cursor moved.
|
|
107
|
-
|
|
108
|
-
if (blaster?.node !==
|
|
675
|
+
EditorView3.updateListener.of((update2) => {
|
|
676
|
+
if (blaster?.node !== update2.view.scrollDOM) {
|
|
109
677
|
if (blaster) {
|
|
110
678
|
blaster.destroy();
|
|
111
679
|
}
|
|
112
|
-
blaster = new Blaster(
|
|
680
|
+
blaster = new Blaster(update2.view.scrollDOM, defaultsDeep({
|
|
113
681
|
particleGravity: 0.2,
|
|
114
682
|
particleShrinkRate: 0.995,
|
|
115
683
|
color: () => [
|
|
@@ -123,10 +691,10 @@ var blast = (options = defaultOptions) => {
|
|
|
123
691
|
} else {
|
|
124
692
|
blaster.resize();
|
|
125
693
|
}
|
|
126
|
-
const current =
|
|
694
|
+
const current = update2.state.selection.main.head;
|
|
127
695
|
if (current !== last) {
|
|
128
696
|
last = current;
|
|
129
|
-
const { element, point } = getPoint(
|
|
697
|
+
const { element, point } = getPoint(update2.view, current - 1);
|
|
130
698
|
if (element && point) {
|
|
131
699
|
blaster.spawn({
|
|
132
700
|
element,
|
|
@@ -365,11 +933,48 @@ var random = (min, max) => {
|
|
|
365
933
|
return min + ~~(Math.random() * (max - min + 1));
|
|
366
934
|
};
|
|
367
935
|
|
|
368
|
-
// packages/ui/react-ui-editor/src/
|
|
369
|
-
import { ViewPlugin, EditorView as
|
|
370
|
-
import get from "lodash.get";
|
|
936
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
937
|
+
import { ViewPlugin as ViewPlugin3, EditorView as EditorView4, Decoration as Decoration2 } from "@codemirror/view";
|
|
371
938
|
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
372
939
|
|
|
940
|
+
// packages/ui/react-ui-editor/src/styles/cursors.ts
|
|
941
|
+
import * as random2 from "lib0/random";
|
|
942
|
+
var cursorColors = [
|
|
943
|
+
{
|
|
944
|
+
color: "#30bced",
|
|
945
|
+
light: "#30bced33"
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
color: "#6eeb83",
|
|
949
|
+
light: "#6eeb8333"
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
color: "#ffbc42",
|
|
953
|
+
light: "#ffbc4233"
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
color: "#ecd444",
|
|
957
|
+
light: "#ecd44433"
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
color: "#ee6352",
|
|
961
|
+
light: "#ee635233"
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
color: "#9ac2c9",
|
|
965
|
+
light: "#9ac2c933"
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
color: "#8acb88",
|
|
969
|
+
light: "#8acb8833"
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
color: "#1be7ff",
|
|
973
|
+
light: "#1be7ff33"
|
|
974
|
+
}
|
|
975
|
+
];
|
|
976
|
+
var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
977
|
+
|
|
373
978
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
374
979
|
import { mx } from "@dxos/react-ui-theme";
|
|
375
980
|
var headings = {
|
|
@@ -411,395 +1016,453 @@ var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
|
|
|
411
1016
|
var codeMark = "font-mono text-primary-500";
|
|
412
1017
|
var inlineUrl = mx(code, "px-1");
|
|
413
1018
|
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
414
|
-
var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800";
|
|
415
1019
|
|
|
416
1020
|
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
1021
|
+
import get2 from "lodash.get";
|
|
417
1022
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
418
1023
|
var tokens = tailwindConfig({}).theme;
|
|
1024
|
+
var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
|
|
419
1025
|
|
|
420
|
-
// packages/ui/react-ui-editor/src/
|
|
1026
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
421
1027
|
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
422
|
-
var
|
|
423
|
-
"& .cm-
|
|
424
|
-
|
|
425
|
-
paddingInline: "4px !important"
|
|
426
|
-
},
|
|
427
|
-
"&light .cm-code-block": {
|
|
428
|
-
background: get(tokens, "extend.colors.neutral.50")
|
|
1028
|
+
var styles2 = EditorView4.baseTheme({
|
|
1029
|
+
"& .cm-codeblock": {
|
|
1030
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
429
1031
|
},
|
|
430
|
-
|
|
431
|
-
background:
|
|
1032
|
+
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1033
|
+
background: getToken("extend.colors.neutral.50")
|
|
432
1034
|
},
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
borderTopLeftRadius: "4px",
|
|
436
|
-
borderTopRightRadius: "4px"
|
|
1035
|
+
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1036
|
+
background: getToken("extend.colors.neutral.850")
|
|
437
1037
|
},
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const end = lines.findIndex((line) => line.to >= to);
|
|
447
|
-
return [
|
|
448
|
-
start,
|
|
449
|
-
end
|
|
450
|
-
];
|
|
451
|
-
};
|
|
452
|
-
var code2 = () => {
|
|
453
|
-
const getDecorations = (view) => {
|
|
454
|
-
const decorations = [];
|
|
455
|
-
if (view.state.readOnly) {
|
|
456
|
-
const text = view.state.doc.sliceString(0);
|
|
457
|
-
const matches = text.matchAll(CODE_REGEX);
|
|
458
|
-
const blocks = view.viewportLineBlocks;
|
|
459
|
-
for (const match of matches) {
|
|
460
|
-
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
461
|
-
for (let i = range[0]; i <= range[1]; i++) {
|
|
462
|
-
const block = blocks[i];
|
|
463
|
-
decorations.push(Decoration.line({
|
|
464
|
-
class: mx2("cm-code-block", i === range[0] && "cm-code-block-first", i === range[1] && "cm-code-block-last")
|
|
465
|
-
}).range(block.from));
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return Decoration.set(decorations);
|
|
470
|
-
};
|
|
471
|
-
return [
|
|
472
|
-
ViewPlugin.fromClass(class {
|
|
473
|
-
constructor(view) {
|
|
474
|
-
this.decorations = getDecorations(view);
|
|
475
|
-
}
|
|
476
|
-
update(update4) {
|
|
477
|
-
if (update4.docChanged || update4.viewportChanged) {
|
|
478
|
-
this.decorations = getDecorations(update4.view);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}, {
|
|
482
|
-
decorations: (value) => value.decorations
|
|
483
|
-
}),
|
|
484
|
-
styles
|
|
485
|
-
];
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
|
|
489
|
-
import { keymap as keymap3 } from "@codemirror/view";
|
|
490
|
-
var defaultItems = [
|
|
491
|
-
"hello world!",
|
|
492
|
-
"this is a test.",
|
|
493
|
-
"this is [DXOS](https://dxos.org)"
|
|
494
|
-
];
|
|
495
|
-
var demo = ({ delay = 75, items = defaultItems } = {}) => {
|
|
496
|
-
let t;
|
|
497
|
-
let idx = 0;
|
|
498
|
-
return [
|
|
499
|
-
keymap3.of([
|
|
500
|
-
{
|
|
501
|
-
// Reset.
|
|
502
|
-
key: "alt-meta-'",
|
|
503
|
-
run: (view) => {
|
|
504
|
-
clearTimeout(t);
|
|
505
|
-
idx = 0;
|
|
506
|
-
return true;
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
// Next prompt.
|
|
511
|
-
// TODO(burdon): Press 1-9 to select prompt?
|
|
512
|
-
key: "shift-meta-'",
|
|
513
|
-
run: (view) => {
|
|
514
|
-
clearTimeout(t);
|
|
515
|
-
const text = items[idx++];
|
|
516
|
-
if (idx === items?.length) {
|
|
517
|
-
idx = 0;
|
|
518
|
-
}
|
|
519
|
-
let i = 0;
|
|
520
|
-
const insert = (d = 0) => {
|
|
521
|
-
t = setTimeout(() => {
|
|
522
|
-
const pos = view.state.selection.main.head;
|
|
523
|
-
view.dispatch({
|
|
524
|
-
changes: {
|
|
525
|
-
from: pos,
|
|
526
|
-
insert: text[i++]
|
|
527
|
-
},
|
|
528
|
-
selection: {
|
|
529
|
-
anchor: pos + 1
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
if (i < text.length) {
|
|
533
|
-
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
534
|
-
}
|
|
535
|
-
}, d);
|
|
536
|
-
};
|
|
537
|
-
insert();
|
|
538
|
-
return true;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
])
|
|
542
|
-
];
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
|
|
546
|
-
import { keymap as keymap4, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
547
|
-
import { debounce } from "@dxos/async";
|
|
548
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
549
|
-
var __dxlog_file2 = "/home/circleci/project/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
|
|
550
|
-
var styles2 = EditorView4.baseTheme({
|
|
551
|
-
"& .cm-bookmark": {
|
|
552
|
-
cursor: "pointer",
|
|
553
|
-
margin: "4px",
|
|
554
|
-
padding: "4px",
|
|
555
|
-
backgroundColor: "yellow"
|
|
1038
|
+
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1039
|
+
display: "block",
|
|
1040
|
+
paddingInline: "4px !important"
|
|
1041
|
+
},
|
|
1042
|
+
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
1043
|
+
paddingTop: "4px !important",
|
|
1044
|
+
borderTopLeftRadius: "4px",
|
|
1045
|
+
borderTopRightRadius: "4px"
|
|
556
1046
|
},
|
|
557
|
-
|
|
558
|
-
|
|
1047
|
+
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
1048
|
+
paddingBottom: "4px !important",
|
|
1049
|
+
borderBottomLeftRadius: "4px",
|
|
1050
|
+
borderBottomRightRadius: "4px"
|
|
559
1051
|
}
|
|
560
1052
|
});
|
|
561
|
-
var
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
this._pos = _pos;
|
|
565
|
-
this._id = _id;
|
|
566
|
-
this._handleClick = _handleClick;
|
|
567
|
-
invariant2(this._id, void 0, {
|
|
568
|
-
F: __dxlog_file2,
|
|
569
|
-
L: 37,
|
|
570
|
-
S: this,
|
|
571
|
-
A: [
|
|
572
|
-
"this._id",
|
|
573
|
-
""
|
|
574
|
-
]
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
eq(other) {
|
|
578
|
-
return this._id === other._id;
|
|
579
|
-
}
|
|
580
|
-
toDOM() {
|
|
581
|
-
const span = document.createElement("span");
|
|
582
|
-
span.className = "cm-bookmark";
|
|
583
|
-
span.textContent = "\xA7";
|
|
584
|
-
span.onclick = () => this._handleClick();
|
|
585
|
-
return span;
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
var comments = (options = {}) => {
|
|
589
|
-
let active;
|
|
590
|
-
const bookmarkMatcher = new MatchDecorator({
|
|
591
|
-
regexp: /\[\^(\w+)\]/g,
|
|
592
|
-
decoration: (match, view, pos) => {
|
|
593
|
-
const id = match[1];
|
|
594
|
-
return Decoration2.replace({
|
|
595
|
-
id,
|
|
596
|
-
widget: new BookmarkWidget(pos, id, () => handleUpdate({
|
|
597
|
-
active: id
|
|
598
|
-
}))
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
const bookmarks = ViewPlugin2.fromClass(class {
|
|
603
|
-
constructor(view) {
|
|
604
|
-
this.bookmarks = bookmarkMatcher.createDeco(view);
|
|
605
|
-
}
|
|
606
|
-
update(update4) {
|
|
607
|
-
this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
|
|
608
|
-
}
|
|
609
|
-
}, {
|
|
610
|
-
decorations: (instance) => instance.bookmarks,
|
|
611
|
-
provide: (plugin) => EditorView4.atomicRanges.of((view) => {
|
|
612
|
-
return view.plugin(plugin)?.bookmarks || Decoration2.none;
|
|
613
|
-
})
|
|
614
|
-
});
|
|
615
|
-
const handleUpdate = debounce((data) => {
|
|
616
|
-
options.onUpdate?.(data);
|
|
617
|
-
}, 200);
|
|
1053
|
+
var getLineRange = (lines, from, to) => {
|
|
1054
|
+
const start = lines.findIndex((line) => line.from >= from);
|
|
1055
|
+
const end = lines.findIndex((line) => line.to >= to);
|
|
618
1056
|
return [
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
638
|
-
return false;
|
|
1057
|
+
start,
|
|
1058
|
+
end
|
|
1059
|
+
];
|
|
1060
|
+
};
|
|
1061
|
+
var code2 = () => {
|
|
1062
|
+
const getDecorations = (view) => {
|
|
1063
|
+
const decorations = [];
|
|
1064
|
+
if (!view.hasFocus || view.state.readOnly) {
|
|
1065
|
+
const text = view.state.doc.sliceString(0);
|
|
1066
|
+
const matches = text.matchAll(CODE_REGEX);
|
|
1067
|
+
const blocks = view.viewportLineBlocks;
|
|
1068
|
+
for (const match of matches) {
|
|
1069
|
+
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1070
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1071
|
+
const block = blocks[i];
|
|
1072
|
+
decorations.push(Decoration2.line({
|
|
1073
|
+
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1074
|
+
}).range(block.from));
|
|
639
1075
|
}
|
|
640
1076
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
1077
|
+
}
|
|
1078
|
+
return Decoration2.set(decorations);
|
|
1079
|
+
};
|
|
1080
|
+
return [
|
|
1081
|
+
ViewPlugin3.fromClass(class {
|
|
1082
|
+
constructor(view) {
|
|
1083
|
+
this.hasFocus = false;
|
|
1084
|
+
this.decorations = getDecorations(view);
|
|
648
1085
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
{
|
|
657
|
-
from: 0,
|
|
658
|
-
to: state.doc.length
|
|
1086
|
+
update(update2) {
|
|
1087
|
+
if (
|
|
1088
|
+
// TODO(burdon): Generalize for other extensions.
|
|
1089
|
+
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1090
|
+
) {
|
|
1091
|
+
this.hasFocus = update2.view.hasFocus;
|
|
1092
|
+
this.decorations = getDecorations(update2.view);
|
|
659
1093
|
}
|
|
660
|
-
);
|
|
661
|
-
rangeSet?.between(from, to, (from2, to2, value) => {
|
|
662
|
-
decorations.push({
|
|
663
|
-
from: from2,
|
|
664
|
-
to: to2,
|
|
665
|
-
value
|
|
666
|
-
});
|
|
667
|
-
const d = Math.min(Math.abs(pos - from2), Math.abs(pos - to2));
|
|
668
|
-
if (d < closest) {
|
|
669
|
-
active = value.spec.id;
|
|
670
|
-
closest = d;
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
if (decorations.length) {
|
|
674
|
-
handleUpdate({
|
|
675
|
-
active,
|
|
676
|
-
items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
|
|
677
|
-
id,
|
|
678
|
-
pos: from2,
|
|
679
|
-
location: view.coordsAtPos(from2)
|
|
680
|
-
}))
|
|
681
|
-
});
|
|
682
1094
|
}
|
|
1095
|
+
}, {
|
|
1096
|
+
decorations: (value) => value.decorations
|
|
683
1097
|
}),
|
|
684
1098
|
styles2
|
|
685
1099
|
];
|
|
686
1100
|
};
|
|
687
1101
|
|
|
688
|
-
// packages/ui/react-ui-editor/src/
|
|
689
|
-
import { StateEffect, StateField } from "@codemirror/state";
|
|
690
|
-
import { Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
1102
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1103
|
+
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1104
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
691
1105
|
import sortBy from "lodash.sortby";
|
|
692
|
-
import {
|
|
1106
|
+
import { debounce } from "@dxos/async";
|
|
1107
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1108
|
+
import { log as log2 } from "@dxos/log";
|
|
1109
|
+
import { nonNullable } from "@dxos/util";
|
|
1110
|
+
|
|
1111
|
+
// packages/ui/react-ui-editor/src/util.ts
|
|
1112
|
+
import { log } from "@dxos/log";
|
|
1113
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1114
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
1115
|
+
try {
|
|
1116
|
+
return fn(...args);
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
log.catch(error, void 0, {
|
|
1119
|
+
F: __dxlog_file2,
|
|
1120
|
+
L: 16,
|
|
1121
|
+
S: void 0,
|
|
1122
|
+
C: (f, a) => f(...a)
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1128
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
693
1129
|
var styles3 = EditorView5.baseTheme({
|
|
694
|
-
"& .cm-
|
|
695
|
-
backgroundColor: "yellow"
|
|
1130
|
+
"& .cm-comment": {
|
|
1131
|
+
backgroundColor: getToken("extend.colors.yellow.50")
|
|
696
1132
|
},
|
|
697
|
-
"& .cm-
|
|
698
|
-
|
|
699
|
-
},
|
|
700
|
-
"& .cm-highlight-active": {
|
|
701
|
-
backgroundColor: "lime"
|
|
1133
|
+
"& .cm-comment-active": {
|
|
1134
|
+
backgroundColor: getToken("extend.colors.yellow.100")
|
|
702
1135
|
}
|
|
703
1136
|
});
|
|
704
1137
|
var marks = {
|
|
705
1138
|
highlight: Decoration3.mark({
|
|
706
|
-
class: "cm-
|
|
1139
|
+
class: "cm-comment"
|
|
707
1140
|
}),
|
|
708
|
-
|
|
709
|
-
class: "cm-
|
|
1141
|
+
highlightActive: Decoration3.mark({
|
|
1142
|
+
class: "cm-comment-active"
|
|
710
1143
|
})
|
|
711
1144
|
};
|
|
712
|
-
var
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1145
|
+
var setFocus = (view, thread) => {
|
|
1146
|
+
const range = view.state.field(commentsStateField).ranges.find((range2) => range2.id === thread);
|
|
1147
|
+
if (!range) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
view.dispatch({
|
|
1151
|
+
effects: setSelection.of({
|
|
1152
|
+
active: thread
|
|
1153
|
+
}),
|
|
1154
|
+
selection: {
|
|
1155
|
+
anchor: range.from
|
|
1156
|
+
},
|
|
1157
|
+
scrollIntoView: true
|
|
1158
|
+
});
|
|
722
1159
|
};
|
|
723
|
-
var
|
|
724
|
-
|
|
725
|
-
|
|
1160
|
+
var setCommentRange = StateEffect2.define();
|
|
1161
|
+
var setSelection = StateEffect2.define();
|
|
1162
|
+
var setCommentState = StateEffect2.define();
|
|
1163
|
+
var commentsStateField = StateField2.define({
|
|
1164
|
+
create: () => ({
|
|
1165
|
+
ranges: []
|
|
1166
|
+
}),
|
|
1167
|
+
update: (value, tr) => {
|
|
1168
|
+
const cursorConverter3 = tr.state.facet(Cursor.converter);
|
|
726
1169
|
for (const effect of tr.effects) {
|
|
727
|
-
if (effect.is(
|
|
1170
|
+
if (effect.is(setSelection)) {
|
|
1171
|
+
return {
|
|
1172
|
+
...value,
|
|
1173
|
+
...effect.value
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
if (effect.is(setCommentRange)) {
|
|
1177
|
+
const { comments: comments2 } = effect.value;
|
|
1178
|
+
const ranges = comments2.map((comment) => {
|
|
1179
|
+
const range = Cursor.getRangeFromCursor(cursorConverter3, comment.cursor);
|
|
1180
|
+
return range && {
|
|
1181
|
+
...comment,
|
|
1182
|
+
...range
|
|
1183
|
+
};
|
|
1184
|
+
}).filter(nonNullable);
|
|
1185
|
+
return {
|
|
1186
|
+
...value,
|
|
1187
|
+
ranges
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
if (effect.is(setCommentState)) {
|
|
728
1191
|
return effect.value;
|
|
729
1192
|
}
|
|
730
1193
|
}
|
|
731
|
-
return
|
|
1194
|
+
return value;
|
|
732
1195
|
}
|
|
733
1196
|
});
|
|
734
1197
|
var highlightDecorations = EditorView5.decorations.compute([
|
|
735
|
-
|
|
1198
|
+
commentsStateField
|
|
736
1199
|
], (state) => {
|
|
737
|
-
const
|
|
738
|
-
const decorations = sortBy(
|
|
1200
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1201
|
+
const decorations = sortBy(ranges ?? [], (range) => range.from)?.flatMap((selection) => {
|
|
1202
|
+
if (selection.from === selection.to) {
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
739
1205
|
const range = {
|
|
740
1206
|
from: selection.from,
|
|
741
|
-
to: selection.to
|
|
1207
|
+
to: selection.to
|
|
742
1208
|
};
|
|
743
|
-
if (selection.
|
|
744
|
-
|
|
745
|
-
return marks.HighlightActive.range(range.from, range.to);
|
|
746
|
-
} else {
|
|
747
|
-
return marks.highlight.range(range.from, range.to);
|
|
748
|
-
}
|
|
1209
|
+
if (selection.id === active) {
|
|
1210
|
+
return marks.highlightActive.range(range.from, range.to);
|
|
749
1211
|
} else {
|
|
750
|
-
return
|
|
1212
|
+
return marks.highlight.range(range.from, range.to);
|
|
751
1213
|
}
|
|
752
|
-
}) ?? [];
|
|
1214
|
+
}).filter(nonNullable) ?? [];
|
|
753
1215
|
return Decoration3.set(decorations);
|
|
754
1216
|
});
|
|
755
|
-
var
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
};
|
|
769
|
-
} else {
|
|
770
|
-
return range;
|
|
771
|
-
}
|
|
1217
|
+
var comments = (options = {}) => {
|
|
1218
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1219
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1220
|
+
const createCommentThread = (view) => {
|
|
1221
|
+
const cursorConverter3 = view.state.facet(Cursor.converter);
|
|
1222
|
+
invariant2(options.onCreate, void 0, {
|
|
1223
|
+
F: __dxlog_file3,
|
|
1224
|
+
L: 177,
|
|
1225
|
+
S: void 0,
|
|
1226
|
+
A: [
|
|
1227
|
+
"options.onCreate",
|
|
1228
|
+
""
|
|
1229
|
+
]
|
|
772
1230
|
});
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
});
|
|
777
|
-
options?.onChange?.(newRanges.find((range) => range.active)?.id);
|
|
1231
|
+
const { head, from, to } = view.state.selection.main;
|
|
1232
|
+
if (from === to) {
|
|
1233
|
+
return false;
|
|
778
1234
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1235
|
+
const cursor = Cursor.getCursorFromRange(cursorConverter3, {
|
|
1236
|
+
from,
|
|
1237
|
+
to
|
|
1238
|
+
});
|
|
1239
|
+
if (cursor) {
|
|
1240
|
+
const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
|
|
1241
|
+
if (id) {
|
|
1242
|
+
view.dispatch({
|
|
1243
|
+
effects: setSelection.of({
|
|
1244
|
+
active: id
|
|
1245
|
+
}),
|
|
1246
|
+
selection: {
|
|
1247
|
+
anchor: from
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
if (options.footnote) {
|
|
1251
|
+
const tag = `[^${id}]`;
|
|
1252
|
+
view.dispatch({
|
|
1253
|
+
changes: {
|
|
1254
|
+
from: head,
|
|
1255
|
+
insert: tag
|
|
1256
|
+
},
|
|
1257
|
+
selection: {
|
|
1258
|
+
anchor: head + tag.length
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return false;
|
|
1266
|
+
};
|
|
782
1267
|
return [
|
|
783
|
-
|
|
1268
|
+
commentsStateField,
|
|
784
1269
|
highlightDecorations,
|
|
785
|
-
|
|
786
|
-
|
|
1270
|
+
styles3,
|
|
1271
|
+
//
|
|
1272
|
+
// Keymap.
|
|
1273
|
+
//
|
|
1274
|
+
options.onCreate ? keymap3.of([
|
|
1275
|
+
{
|
|
1276
|
+
key: shortcut,
|
|
1277
|
+
run: callbackWrapper(createCommentThread)
|
|
1278
|
+
}
|
|
1279
|
+
]) : [],
|
|
1280
|
+
//
|
|
1281
|
+
// Hover tooltip (for key shortcut hints, etc.)
|
|
1282
|
+
// TODO(burdon): Factor out to generic hints extension for current selection/line.
|
|
1283
|
+
//
|
|
1284
|
+
options.onHover ? hoverTooltip((view, pos) => {
|
|
1285
|
+
const selection = view.state.selection.main;
|
|
1286
|
+
if (selection && pos >= selection.from && pos <= selection.to) {
|
|
1287
|
+
return {
|
|
1288
|
+
pos: selection.from,
|
|
1289
|
+
end: selection.to,
|
|
1290
|
+
above: true,
|
|
1291
|
+
create: () => {
|
|
1292
|
+
const el = document.createElement("div");
|
|
1293
|
+
options.onHover?.(el, shortcut);
|
|
1294
|
+
return {
|
|
1295
|
+
dom: el,
|
|
1296
|
+
offset: {
|
|
1297
|
+
x: 0,
|
|
1298
|
+
y: 8
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
return null;
|
|
1305
|
+
}, {
|
|
1306
|
+
hideOnChange: true,
|
|
1307
|
+
hoverTime: 1e3
|
|
1308
|
+
}) : [],
|
|
1309
|
+
//
|
|
1310
|
+
// Monitor cursor movement and text updates.
|
|
1311
|
+
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1312
|
+
//
|
|
1313
|
+
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1314
|
+
const cursorConverter3 = view.state.facet(Cursor.converter);
|
|
1315
|
+
{
|
|
1316
|
+
let mod = false;
|
|
1317
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1318
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1319
|
+
ranges.forEach((range) => {
|
|
1320
|
+
if (from2 === to2) {
|
|
1321
|
+
const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
|
|
1322
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1323
|
+
log2.info("deleted comment", {
|
|
1324
|
+
thread: range.id
|
|
1325
|
+
}, {
|
|
1326
|
+
F: __dxlog_file3,
|
|
1327
|
+
L: 274,
|
|
1328
|
+
S: void 0,
|
|
1329
|
+
C: (f, a) => f(...a)
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
if (from <= range.to) {
|
|
1334
|
+
const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
|
|
1335
|
+
Object.assign(range, newRange);
|
|
1336
|
+
mod = true;
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
if (mod) {
|
|
1341
|
+
view.dispatch({
|
|
1342
|
+
effects: setCommentState.of({
|
|
1343
|
+
active,
|
|
1344
|
+
ranges
|
|
1345
|
+
})
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
{
|
|
1350
|
+
const { head } = state.selection.main;
|
|
1351
|
+
let min = Infinity;
|
|
1352
|
+
const { active, closest, ranges } = state.field(commentsStateField);
|
|
1353
|
+
const selected = {
|
|
1354
|
+
active: void 0,
|
|
1355
|
+
closest: void 0
|
|
1356
|
+
};
|
|
1357
|
+
ranges.forEach((comment) => {
|
|
1358
|
+
const d = Math.min(Math.abs(head - comment.from), Math.abs(head - comment.to));
|
|
1359
|
+
if (head >= comment.from && head <= comment.to) {
|
|
1360
|
+
selected.active = comment.id;
|
|
1361
|
+
}
|
|
1362
|
+
if (d < min) {
|
|
1363
|
+
selected.closest = comment.id;
|
|
1364
|
+
min = d;
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
if (selected.active !== active || selected.closest !== closest) {
|
|
1368
|
+
view.dispatch({
|
|
1369
|
+
effects: setSelection.of(selected)
|
|
1370
|
+
});
|
|
1371
|
+
handleSelect({
|
|
1372
|
+
...selected,
|
|
1373
|
+
ranges: ranges.map((range) => ({
|
|
1374
|
+
...range,
|
|
1375
|
+
location: view.coordsAtPos(range.from)
|
|
1376
|
+
}))
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
})
|
|
787
1381
|
];
|
|
788
1382
|
};
|
|
789
1383
|
|
|
790
|
-
// packages/ui/react-ui-editor/src/
|
|
1384
|
+
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1385
|
+
import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1386
|
+
var styles4 = EditorView6.baseTheme({
|
|
1387
|
+
"& .cm-hr": {
|
|
1388
|
+
// TODO(burdon): ???
|
|
1389
|
+
// Note that block-level decorations should not have vertical margins,
|
|
1390
|
+
borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1394
|
+
constructor(_pos) {
|
|
1395
|
+
super();
|
|
1396
|
+
this._pos = _pos;
|
|
1397
|
+
}
|
|
1398
|
+
eq(other) {
|
|
1399
|
+
return this._pos === other._pos;
|
|
1400
|
+
}
|
|
1401
|
+
toDOM(view) {
|
|
1402
|
+
const el = document.createElement("div");
|
|
1403
|
+
el.className = "cm-hr";
|
|
1404
|
+
return el;
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
var placeholderMatcher = new MatchDecorator({
|
|
1408
|
+
regexp: /^---$/g,
|
|
1409
|
+
decoration: (match, view, pos) => Decoration4.replace({
|
|
1410
|
+
widget: new HorizontalRuleWidget(pos)
|
|
1411
|
+
})
|
|
1412
|
+
});
|
|
1413
|
+
var hr = () => [
|
|
1414
|
+
styles4,
|
|
1415
|
+
ViewPlugin4.fromClass(class {
|
|
1416
|
+
constructor(view) {
|
|
1417
|
+
this.rules = placeholderMatcher.createDeco(view);
|
|
1418
|
+
}
|
|
1419
|
+
update(update2) {
|
|
1420
|
+
this.rules = placeholderMatcher.updateDeco(update2, this.rules);
|
|
1421
|
+
}
|
|
1422
|
+
}, {
|
|
1423
|
+
decorations: (instance) => instance.rules,
|
|
1424
|
+
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1425
|
+
provide: (plugin) => EditorView6.atomicRanges.of((view) => {
|
|
1426
|
+
return view.plugin(plugin)?.rules || Decoration4.none;
|
|
1427
|
+
})
|
|
1428
|
+
})
|
|
1429
|
+
];
|
|
1430
|
+
|
|
1431
|
+
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
791
1432
|
import { syntaxTree } from "@codemirror/language";
|
|
792
|
-
import {
|
|
793
|
-
import { Decoration as
|
|
1433
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
1434
|
+
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
794
1435
|
var image = (options = {}) => {
|
|
795
|
-
return
|
|
796
|
-
create: (state) =>
|
|
797
|
-
|
|
798
|
-
|
|
1436
|
+
return StateField3.define({
|
|
1437
|
+
create: (state) => {
|
|
1438
|
+
return Decoration5.set(buildDecorations(0, state.doc.length, state));
|
|
1439
|
+
},
|
|
1440
|
+
update: (value, tr) => {
|
|
1441
|
+
if (!tr.docChanged && !tr.selection) {
|
|
1442
|
+
return value;
|
|
1443
|
+
}
|
|
1444
|
+
const cursor = tr.state.selection.main.head;
|
|
1445
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1446
|
+
let from = Math.min(cursor, oldCursor);
|
|
1447
|
+
let to = Math.max(cursor, oldCursor);
|
|
1448
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1449
|
+
from = Math.min(from, fromB);
|
|
1450
|
+
to = Math.max(to, toB);
|
|
1451
|
+
});
|
|
1452
|
+
from = tr.state.doc.lineAt(from).from;
|
|
1453
|
+
to = tr.state.doc.lineAt(to).to;
|
|
1454
|
+
return value.map(tr.changes).update({
|
|
1455
|
+
filterFrom: from,
|
|
1456
|
+
filterTo: to,
|
|
1457
|
+
filter: () => false,
|
|
1458
|
+
add: buildDecorations(from, to, tr.state)
|
|
1459
|
+
});
|
|
1460
|
+
},
|
|
1461
|
+
provide: (field) => EditorView7.decorations.from(field)
|
|
799
1462
|
});
|
|
800
1463
|
};
|
|
801
|
-
var
|
|
802
|
-
const
|
|
1464
|
+
var buildDecorations = (from, to, state) => {
|
|
1465
|
+
const decorations = [];
|
|
803
1466
|
const cursor = state.selection.main.head;
|
|
804
1467
|
syntaxTree(state).iterate({
|
|
805
1468
|
enter: (node) => {
|
|
@@ -808,51 +1471,101 @@ var update = (state, options) => {
|
|
|
808
1471
|
if (urlNode) {
|
|
809
1472
|
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
810
1473
|
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
811
|
-
|
|
1474
|
+
decorations.push(Decoration5.replace({
|
|
1475
|
+
block: true,
|
|
812
1476
|
widget: new ImageWidget(url)
|
|
813
|
-
}));
|
|
1477
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
814
1478
|
}
|
|
815
1479
|
}
|
|
816
|
-
}
|
|
1480
|
+
},
|
|
1481
|
+
from,
|
|
1482
|
+
to
|
|
817
1483
|
});
|
|
818
|
-
return
|
|
1484
|
+
return decorations;
|
|
819
1485
|
};
|
|
820
|
-
var ImageWidget = class extends
|
|
1486
|
+
var ImageWidget = class extends WidgetType3 {
|
|
821
1487
|
constructor(_url) {
|
|
822
1488
|
super();
|
|
823
1489
|
this._url = _url;
|
|
824
1490
|
}
|
|
825
1491
|
eq(other) {
|
|
826
|
-
return this._url === other
|
|
1492
|
+
return this._url === other._url;
|
|
827
1493
|
}
|
|
828
1494
|
toDOM(view) {
|
|
829
1495
|
const img = document.createElement("img");
|
|
830
1496
|
img.setAttribute("src", this._url);
|
|
831
1497
|
img.setAttribute("class", "cm-image");
|
|
1498
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
832
1499
|
return img;
|
|
833
1500
|
}
|
|
834
1501
|
};
|
|
835
1502
|
|
|
836
|
-
// packages/ui/react-ui-editor/src/
|
|
1503
|
+
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
837
1504
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
838
|
-
import { RangeSetBuilder
|
|
839
|
-
import { Decoration as
|
|
1505
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1506
|
+
import { Decoration as Decoration6, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1507
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
840
1508
|
var link = (options = {}) => {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1509
|
+
const extensions = [
|
|
1510
|
+
ViewPlugin5.fromClass(class {
|
|
1511
|
+
constructor(view) {
|
|
1512
|
+
this.decorations = buildDecorations2(view, options);
|
|
1513
|
+
}
|
|
1514
|
+
update(update2) {
|
|
1515
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1516
|
+
this.decorations = buildDecorations2(update2.view, options);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}, {
|
|
1520
|
+
decorations: (v) => v.decorations
|
|
1521
|
+
})
|
|
1522
|
+
];
|
|
1523
|
+
if (options.onHover) {
|
|
1524
|
+
extensions.push(
|
|
1525
|
+
// https://codemirror.net/examples/tooltip
|
|
1526
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1527
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1528
|
+
hoverTooltip2((view, pos, side) => {
|
|
1529
|
+
const syntax = syntaxTree2(view.state).resolveInner(pos, side);
|
|
1530
|
+
let link2 = null;
|
|
1531
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1532
|
+
link2 = node.name === "Link" ? node : null;
|
|
1533
|
+
}
|
|
1534
|
+
const url = link2 && link2.getChild("URL");
|
|
1535
|
+
if (!url || !link2) {
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1539
|
+
return {
|
|
1540
|
+
pos: link2.from,
|
|
1541
|
+
end: link2.to,
|
|
1542
|
+
above: true,
|
|
1543
|
+
create: () => {
|
|
1544
|
+
const el = document.createElement("div");
|
|
1545
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1546
|
+
options.onHover(el, urlText);
|
|
1547
|
+
return {
|
|
1548
|
+
dom: el,
|
|
1549
|
+
offset: {
|
|
1550
|
+
x: 0,
|
|
1551
|
+
y: 4
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
})
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
return extensions;
|
|
846
1560
|
};
|
|
847
|
-
var LinkButton = class extends
|
|
848
|
-
constructor(
|
|
1561
|
+
var LinkButton = class extends WidgetType4 {
|
|
1562
|
+
constructor(_url, _onAttach) {
|
|
849
1563
|
super();
|
|
850
|
-
this._pos = _pos;
|
|
851
1564
|
this._url = _url;
|
|
852
1565
|
this._onAttach = _onAttach;
|
|
853
1566
|
}
|
|
854
1567
|
eq(other) {
|
|
855
|
-
return this.
|
|
1568
|
+
return this._url === other._url;
|
|
856
1569
|
}
|
|
857
1570
|
toDOM(view) {
|
|
858
1571
|
const el = document.createElement("span");
|
|
@@ -860,15 +1573,14 @@ var LinkButton = class extends WidgetType3 {
|
|
|
860
1573
|
return el;
|
|
861
1574
|
}
|
|
862
1575
|
};
|
|
863
|
-
var LinkText = class extends
|
|
864
|
-
constructor(
|
|
1576
|
+
var LinkText = class extends WidgetType4 {
|
|
1577
|
+
constructor(_text, _url) {
|
|
865
1578
|
super();
|
|
866
|
-
this._pos = _pos;
|
|
867
1579
|
this._text = _text;
|
|
868
1580
|
this._url = _url;
|
|
869
1581
|
}
|
|
870
1582
|
eq(other) {
|
|
871
|
-
return this.
|
|
1583
|
+
return this._url === other._url;
|
|
872
1584
|
}
|
|
873
1585
|
toDOM(view) {
|
|
874
1586
|
const link2 = document.createElement("a");
|
|
@@ -882,7 +1594,7 @@ var LinkText = class extends WidgetType3 {
|
|
|
882
1594
|
link2.onclick = () => {
|
|
883
1595
|
view.dispatch({
|
|
884
1596
|
selection: {
|
|
885
|
-
anchor:
|
|
1597
|
+
anchor: view.posAtDOM(link2)
|
|
886
1598
|
}
|
|
887
1599
|
});
|
|
888
1600
|
};
|
|
@@ -890,75 +1602,77 @@ var LinkText = class extends WidgetType3 {
|
|
|
890
1602
|
return link2;
|
|
891
1603
|
}
|
|
892
1604
|
};
|
|
893
|
-
var
|
|
894
|
-
const builder = new
|
|
1605
|
+
var buildDecorations2 = (view, options) => {
|
|
1606
|
+
const builder = new RangeSetBuilder();
|
|
1607
|
+
const { state } = view;
|
|
895
1608
|
const cursor = state.selection.main.head;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1609
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1610
|
+
syntaxTree2(state).iterate({
|
|
1611
|
+
enter: (node) => {
|
|
1612
|
+
if (node.name === "Link") {
|
|
1613
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1614
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1615
|
+
const urlNode = node.node.getChild("URL");
|
|
1616
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1617
|
+
if (!url) {
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1621
|
+
builder.add(node.from, node.to, Decoration6.replace({
|
|
1622
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
1623
|
+
}));
|
|
1624
|
+
}
|
|
1625
|
+
if (options.onRender) {
|
|
1626
|
+
builder.add(node.to, node.to, Decoration6.replace({
|
|
1627
|
+
widget: new LinkButton(url, options.onRender)
|
|
1628
|
+
}));
|
|
1629
|
+
}
|
|
904
1630
|
return false;
|
|
905
1631
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if (options.onRender) {
|
|
912
|
-
builder.add(node.to, node.to, Decoration5.replace({
|
|
913
|
-
widget: new LinkButton(node.from, url, options.onRender)
|
|
914
|
-
}));
|
|
915
|
-
}
|
|
916
|
-
return false;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
});
|
|
1632
|
+
},
|
|
1633
|
+
from,
|
|
1634
|
+
to
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
920
1637
|
return builder.finish();
|
|
921
1638
|
};
|
|
922
1639
|
|
|
923
|
-
// packages/ui/react-ui-editor/src/
|
|
924
|
-
import {
|
|
925
|
-
var
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1640
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1641
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1642
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1643
|
+
const extensions = [];
|
|
1644
|
+
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1645
|
+
onFocus(focused);
|
|
1646
|
+
return null;
|
|
1647
|
+
}));
|
|
1648
|
+
onChange && extensions.push(EditorView8.updateListener.of((update2) => {
|
|
1649
|
+
onChange(update2.state.doc.toString());
|
|
1650
|
+
}));
|
|
1651
|
+
return extensions;
|
|
935
1652
|
};
|
|
936
1653
|
|
|
937
|
-
// packages/ui/react-ui-editor/src/
|
|
1654
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
938
1655
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
939
1656
|
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
940
|
-
import { markdownLanguage as
|
|
1657
|
+
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
941
1658
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
942
1659
|
import { languages } from "@codemirror/language-data";
|
|
943
1660
|
import { searchKeymap } from "@codemirror/search";
|
|
944
1661
|
import { EditorState } from "@codemirror/state";
|
|
945
1662
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
946
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1663
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
947
1664
|
|
|
948
|
-
// packages/ui/react-ui-editor/src/
|
|
949
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1665
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1666
|
+
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
950
1667
|
import { HighlightStyle } from "@codemirror/language";
|
|
951
1668
|
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
952
1669
|
import { Table } from "@lezer/markdown";
|
|
953
|
-
import get2 from "lodash.get";
|
|
954
|
-
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
955
1670
|
var markdownTags = {
|
|
956
1671
|
Blockquote: Tag.define(),
|
|
957
1672
|
CodeMark: Tag.define(),
|
|
958
1673
|
CodeText: Tag.define(),
|
|
959
1674
|
EmphasisMark: Tag.define(),
|
|
960
1675
|
HeaderMark: Tag.define(),
|
|
961
|
-
HorizontalRule: Tag.define(),
|
|
962
1676
|
InlineCode: Tag.define(),
|
|
963
1677
|
LinkLabel: Tag.define(),
|
|
964
1678
|
LinkReference: Tag.define(),
|
|
@@ -1122,14 +1836,6 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1122
1836
|
],
|
|
1123
1837
|
class: blockquote
|
|
1124
1838
|
},
|
|
1125
|
-
// TODO(burdon): Replace with extension.
|
|
1126
|
-
{
|
|
1127
|
-
tag: [
|
|
1128
|
-
markdownTags.HorizontalRule
|
|
1129
|
-
],
|
|
1130
|
-
class: mx3(horizontalRule, readonly && "-indent-[100rem]")
|
|
1131
|
-
},
|
|
1132
|
-
// TODO(burdon): Only if being edited.
|
|
1133
1839
|
{
|
|
1134
1840
|
tag: [
|
|
1135
1841
|
markdownTags.TableCell
|
|
@@ -1137,19 +1843,19 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1137
1843
|
class: "font-mono"
|
|
1138
1844
|
}
|
|
1139
1845
|
], {
|
|
1140
|
-
scope:
|
|
1846
|
+
scope: markdownLanguage2,
|
|
1141
1847
|
all: {
|
|
1142
|
-
fontFamily:
|
|
1848
|
+
fontFamily: getToken("fontFamily.body", []).join(",")
|
|
1143
1849
|
}
|
|
1144
1850
|
});
|
|
1145
1851
|
};
|
|
1146
1852
|
|
|
1147
|
-
// packages/ui/react-ui-editor/src/
|
|
1853
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1148
1854
|
var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
1149
1855
|
return [
|
|
1150
1856
|
EditorState.allowMultipleSelections.of(true),
|
|
1151
1857
|
EditorState.tabSize.of(2),
|
|
1152
|
-
|
|
1858
|
+
EditorView9.lineWrapping,
|
|
1153
1859
|
customKeymap(),
|
|
1154
1860
|
bracketMatching2(),
|
|
1155
1861
|
closeBrackets2(),
|
|
@@ -1169,7 +1875,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1169
1875
|
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1170
1876
|
// https://github.github.com/gfm
|
|
1171
1877
|
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1172
|
-
base:
|
|
1878
|
+
base: markdownLanguage3,
|
|
1173
1879
|
// Languages for syntax highlighting fenced code blocks.
|
|
1174
1880
|
codeLanguages: languages,
|
|
1175
1881
|
// Parser extensions.
|
|
@@ -1184,7 +1890,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1184
1890
|
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1185
1891
|
].filter(Boolean);
|
|
1186
1892
|
};
|
|
1187
|
-
var customKeymap = () =>
|
|
1893
|
+
var customKeymap = () => keymap4.of([
|
|
1188
1894
|
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1189
1895
|
indentWithTab,
|
|
1190
1896
|
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
@@ -1197,7 +1903,7 @@ var customKeymap = () => keymap5.of([
|
|
|
1197
1903
|
...searchKeymap
|
|
1198
1904
|
]);
|
|
1199
1905
|
|
|
1200
|
-
// packages/ui/react-ui-editor/src/
|
|
1906
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1201
1907
|
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1202
1908
|
var mention = ({ onSearch }) => {
|
|
1203
1909
|
return autocompletion2({
|
|
@@ -1220,19 +1926,19 @@ var mention = ({ onSearch }) => {
|
|
|
1220
1926
|
});
|
|
1221
1927
|
};
|
|
1222
1928
|
|
|
1223
|
-
// packages/ui/react-ui-editor/src/
|
|
1929
|
+
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1224
1930
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1225
|
-
import { RangeSetBuilder as
|
|
1226
|
-
import { Decoration as
|
|
1931
|
+
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
|
|
1932
|
+
import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1227
1933
|
var table = (options = {}) => {
|
|
1228
|
-
return
|
|
1229
|
-
create: (state) =>
|
|
1230
|
-
update: (_, tr) =>
|
|
1231
|
-
provide: (field) =>
|
|
1934
|
+
return StateField4.define({
|
|
1935
|
+
create: (state) => update(state, options),
|
|
1936
|
+
update: (_, tr) => update(tr.state, options),
|
|
1937
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
1232
1938
|
});
|
|
1233
1939
|
};
|
|
1234
|
-
var
|
|
1235
|
-
const builder = new
|
|
1940
|
+
var update = (state, options) => {
|
|
1941
|
+
const builder = new RangeSetBuilder2();
|
|
1236
1942
|
const cursor = state.selection.main.head;
|
|
1237
1943
|
const tables = [];
|
|
1238
1944
|
const getTable = () => tables[tables.length - 1];
|
|
@@ -1272,22 +1978,22 @@ var update3 = (state, options) => {
|
|
|
1272
1978
|
});
|
|
1273
1979
|
tables.forEach((table2) => {
|
|
1274
1980
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1275
|
-
hide && builder.add(table2.from, table2.to,
|
|
1981
|
+
hide && builder.add(table2.from, table2.to, Decoration7.replace({
|
|
1276
1982
|
widget: new TableWidget(table2)
|
|
1277
1983
|
}));
|
|
1278
|
-
builder.add(table2.from, table2.to,
|
|
1984
|
+
builder.add(table2.from, table2.to, Decoration7.mark({
|
|
1279
1985
|
class: "cm-table"
|
|
1280
1986
|
}));
|
|
1281
1987
|
});
|
|
1282
1988
|
return builder.finish();
|
|
1283
1989
|
};
|
|
1284
|
-
var TableWidget = class extends
|
|
1990
|
+
var TableWidget = class extends WidgetType5 {
|
|
1285
1991
|
constructor(_table) {
|
|
1286
1992
|
super();
|
|
1287
1993
|
this._table = _table;
|
|
1288
1994
|
}
|
|
1289
1995
|
eq(other) {
|
|
1290
|
-
return this._table.
|
|
1996
|
+
return this._table.header?.join() === other._table.header?.join() && this._table.rows?.join() === other._table.rows?.join();
|
|
1291
1997
|
}
|
|
1292
1998
|
toDOM(view) {
|
|
1293
1999
|
const table2 = document.createElement("table");
|
|
@@ -1313,26 +2019,28 @@ var TableWidget = class extends WidgetType4 {
|
|
|
1313
2019
|
}
|
|
1314
2020
|
return table2;
|
|
1315
2021
|
}
|
|
2022
|
+
ignoreEvent(e) {
|
|
2023
|
+
return !/^mouse/.test(e.type);
|
|
2024
|
+
}
|
|
1316
2025
|
};
|
|
1317
2026
|
|
|
1318
|
-
// packages/ui/react-ui-editor/src/
|
|
1319
|
-
import { EditorView as
|
|
1320
|
-
var
|
|
2027
|
+
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
2028
|
+
import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
|
|
2029
|
+
var styles5 = EditorView11.baseTheme({
|
|
1321
2030
|
"& .cm-task-item": {
|
|
1322
2031
|
paddingLeft: "4px",
|
|
1323
2032
|
paddingRight: "8px"
|
|
1324
2033
|
}
|
|
1325
2034
|
});
|
|
1326
|
-
var CheckboxWidget = class extends
|
|
1327
|
-
constructor(
|
|
2035
|
+
var CheckboxWidget = class extends WidgetType6 {
|
|
2036
|
+
constructor(_checked, _indent, _onCheck) {
|
|
1328
2037
|
super();
|
|
1329
|
-
this._pos = _pos;
|
|
1330
2038
|
this._checked = _checked;
|
|
1331
2039
|
this._indent = _indent;
|
|
1332
2040
|
this._onCheck = _onCheck;
|
|
1333
2041
|
}
|
|
1334
2042
|
eq(other) {
|
|
1335
|
-
return this.
|
|
2043
|
+
return this._checked === other._checked && this._indent === other._indent;
|
|
1336
2044
|
}
|
|
1337
2045
|
toDOM(view) {
|
|
1338
2046
|
const wrap = document.createElement("span");
|
|
@@ -1363,8 +2071,8 @@ var tasklist = (options = {}) => {
|
|
|
1363
2071
|
decoration: (match, view, pos) => {
|
|
1364
2072
|
const indent = Math.floor(match[1].length / 2);
|
|
1365
2073
|
const checked = match[2] === "x" || match[2] === "X";
|
|
1366
|
-
return
|
|
1367
|
-
widget: new CheckboxWidget(
|
|
2074
|
+
return Decoration8.replace({
|
|
2075
|
+
widget: new CheckboxWidget(checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
1368
2076
|
const idx = pos + match[0].indexOf("[") + 1;
|
|
1369
2077
|
view.dispatch({
|
|
1370
2078
|
changes: {
|
|
@@ -1381,78 +2089,125 @@ var tasklist = (options = {}) => {
|
|
|
1381
2089
|
});
|
|
1382
2090
|
}
|
|
1383
2091
|
});
|
|
1384
|
-
const tasks =
|
|
2092
|
+
const tasks = ViewPlugin6.fromClass(class {
|
|
1385
2093
|
constructor(view) {
|
|
1386
2094
|
this.tasks = taskMatcher.createDeco(view);
|
|
1387
2095
|
}
|
|
1388
|
-
update(
|
|
1389
|
-
this.tasks = taskMatcher.updateDeco(
|
|
2096
|
+
update(update2) {
|
|
2097
|
+
this.tasks = taskMatcher.updateDeco(update2, this.tasks);
|
|
1390
2098
|
}
|
|
1391
2099
|
}, {
|
|
1392
2100
|
decorations: (value) => value.tasks,
|
|
1393
|
-
provide: (plugin) =>
|
|
1394
|
-
return view.plugin(plugin)?.tasks ||
|
|
2101
|
+
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2102
|
+
return view.plugin(plugin)?.tasks || Decoration8.none;
|
|
1395
2103
|
})
|
|
1396
2104
|
});
|
|
1397
2105
|
return [
|
|
1398
2106
|
tasks,
|
|
1399
|
-
|
|
2107
|
+
styles5
|
|
1400
2108
|
];
|
|
1401
2109
|
};
|
|
1402
2110
|
|
|
1403
|
-
// packages/ui/react-ui-editor/src/
|
|
1404
|
-
import {
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
2111
|
+
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2112
|
+
import { keymap as keymap5 } from "@codemirror/view";
|
|
2113
|
+
var defaultItems = [
|
|
2114
|
+
"hello world!",
|
|
2115
|
+
"this is a test.",
|
|
2116
|
+
"this is [DXOS](https://dxos.org)"
|
|
2117
|
+
];
|
|
2118
|
+
var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
2119
|
+
let t;
|
|
1410
2120
|
let idx = 0;
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
break;
|
|
1421
|
-
}
|
|
1422
|
-
idx += match[0].length;
|
|
1423
|
-
} while (true);
|
|
1424
|
-
if (!match) {
|
|
1425
|
-
return null;
|
|
1426
|
-
}
|
|
1427
|
-
const [, , url] = match ?? [];
|
|
1428
|
-
return {
|
|
1429
|
-
pos: idx + from,
|
|
1430
|
-
end: idx + from + match[0].length,
|
|
1431
|
-
above: true,
|
|
1432
|
-
create: () => {
|
|
1433
|
-
const el = document.createElement("div");
|
|
1434
|
-
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1435
|
-
onHover(el, url);
|
|
1436
|
-
return {
|
|
1437
|
-
dom: el,
|
|
1438
|
-
offset: {
|
|
1439
|
-
x: 0,
|
|
1440
|
-
y: 12
|
|
2121
|
+
return [
|
|
2122
|
+
keymap5.of([
|
|
2123
|
+
{
|
|
2124
|
+
// Reset.
|
|
2125
|
+
key: "alt-meta-'",
|
|
2126
|
+
run: (view) => {
|
|
2127
|
+
clearTimeout(t);
|
|
2128
|
+
idx = 0;
|
|
2129
|
+
return true;
|
|
1441
2130
|
}
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
|
|
2131
|
+
},
|
|
2132
|
+
{
|
|
2133
|
+
// Next prompt.
|
|
2134
|
+
// TODO(burdon): Press 1-9 to select prompt?
|
|
2135
|
+
key: "shift-meta-'",
|
|
2136
|
+
run: (view) => {
|
|
2137
|
+
clearTimeout(t);
|
|
2138
|
+
const text = items[idx++];
|
|
2139
|
+
if (idx === items?.length) {
|
|
2140
|
+
idx = 0;
|
|
2141
|
+
}
|
|
2142
|
+
let i = 0;
|
|
2143
|
+
const insert = (d = 0) => {
|
|
2144
|
+
t = setTimeout(() => {
|
|
2145
|
+
const pos = view.state.selection.main.head;
|
|
2146
|
+
view.dispatch({
|
|
2147
|
+
changes: {
|
|
2148
|
+
from: pos,
|
|
2149
|
+
insert: text[i++]
|
|
2150
|
+
},
|
|
2151
|
+
selection: {
|
|
2152
|
+
anchor: pos + 1
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
if (i < text.length) {
|
|
2156
|
+
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
2157
|
+
}
|
|
2158
|
+
}, d);
|
|
2159
|
+
};
|
|
2160
|
+
insert();
|
|
2161
|
+
return true;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
])
|
|
2165
|
+
];
|
|
2166
|
+
};
|
|
2167
|
+
|
|
2168
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2169
|
+
import { yCollab } from "y-codemirror.next";
|
|
2170
|
+
|
|
2171
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
|
|
2172
|
+
import * as Y from "yjs";
|
|
2173
|
+
import { arrayToString, stringToArray } from "@dxos/util";
|
|
2174
|
+
var cursorConverter2 = (text) => ({
|
|
2175
|
+
toCursor: (index, assoc) => {
|
|
2176
|
+
return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
|
|
2177
|
+
},
|
|
2178
|
+
fromCursor: (cursor) => {
|
|
2179
|
+
return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
|
|
2180
|
+
}
|
|
1445
2181
|
});
|
|
1446
2182
|
|
|
1447
|
-
// packages/ui/react-ui-editor/src/
|
|
2183
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2184
|
+
var yjs = (content, awareness2) => [
|
|
2185
|
+
Cursor.converter.of(cursorConverter2(content)),
|
|
2186
|
+
yCollab(content, awareness2)
|
|
2187
|
+
];
|
|
2188
|
+
|
|
2189
|
+
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
1448
2190
|
import get3 from "lodash.get";
|
|
1449
2191
|
var defaultTheme = {
|
|
1450
2192
|
//
|
|
1451
2193
|
// Main layout:
|
|
1452
|
-
//
|
|
1453
|
-
//
|
|
1454
|
-
//
|
|
1455
|
-
//
|
|
2194
|
+
// https://codemirror.net/examples/styling
|
|
2195
|
+
//
|
|
2196
|
+
// <div class="cm-editor [cm-focused] [generated classes]">
|
|
2197
|
+
// <div class="cm-scroller">
|
|
2198
|
+
// <div class="cm-gutters">
|
|
2199
|
+
// <div class="cm-gutter [...]">
|
|
2200
|
+
// <div class="cm-gutterElement">...</div>
|
|
2201
|
+
// </div>
|
|
2202
|
+
// </div>
|
|
2203
|
+
// <div class="cm-content" role="textbox" contenteditable="true">
|
|
2204
|
+
// <div class="cm-line"></div>
|
|
2205
|
+
// </div>
|
|
2206
|
+
// <div class="cm-selectionLayer">
|
|
2207
|
+
// <div class="cm-selectionBackground"></div>
|
|
2208
|
+
// </div>
|
|
2209
|
+
// <div class="cm-cursorLayer">
|
|
2210
|
+
// <div class="cm-cursor"></div>
|
|
1456
2211
|
// </div>
|
|
1457
2212
|
// </div>
|
|
1458
2213
|
// </div>
|
|
@@ -1461,13 +2216,13 @@ var defaultTheme = {
|
|
|
1461
2216
|
"&.cm-focused": {
|
|
1462
2217
|
outline: "none"
|
|
1463
2218
|
},
|
|
1464
|
-
"
|
|
2219
|
+
".cm-scroller": {
|
|
1465
2220
|
overflow: "visible",
|
|
1466
2221
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1467
2222
|
},
|
|
1468
|
-
"
|
|
2223
|
+
".cm-content": {
|
|
1469
2224
|
padding: 0,
|
|
1470
|
-
// Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2225
|
+
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
1471
2226
|
fontSize: "16px"
|
|
1472
2227
|
},
|
|
1473
2228
|
"&light .cm-content": {
|
|
@@ -1487,65 +2242,46 @@ var defaultTheme = {
|
|
|
1487
2242
|
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
1488
2243
|
borderLeft: "2px solid white"
|
|
1489
2244
|
},
|
|
1490
|
-
"
|
|
2245
|
+
".cm-placeholder": {
|
|
1491
2246
|
fontWeight: 100
|
|
1492
2247
|
},
|
|
1493
2248
|
//
|
|
1494
2249
|
// line
|
|
1495
2250
|
//
|
|
1496
|
-
"
|
|
2251
|
+
".cm-line": {
|
|
1497
2252
|
paddingInline: 0
|
|
1498
2253
|
},
|
|
1499
|
-
"
|
|
1500
|
-
|
|
1501
|
-
backgroundColor: "transparent"
|
|
2254
|
+
".cm-activeLine": {
|
|
2255
|
+
background: "inherit"
|
|
1502
2256
|
},
|
|
1503
2257
|
//
|
|
1504
|
-
//
|
|
2258
|
+
// Selection
|
|
1505
2259
|
//
|
|
1506
|
-
"&light .cm-selectionBackground
|
|
1507
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2260
|
+
"&light .cm-selectionBackground": {
|
|
2261
|
+
background: get3(tokens, "extend.colors.primary.100")
|
|
1508
2262
|
},
|
|
1509
|
-
"&
|
|
1510
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2263
|
+
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2264
|
+
background: get3(tokens, "extend.colors.primary.200")
|
|
1511
2265
|
},
|
|
1512
|
-
"&
|
|
1513
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2266
|
+
"&dark .cm-selectionBackground": {
|
|
2267
|
+
background: get3(tokens, "extend.colors.primary.700")
|
|
1514
2268
|
},
|
|
1515
|
-
"&dark .cm-
|
|
1516
|
-
background: get3(tokens, "extend.colors.primary.600"
|
|
2269
|
+
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2270
|
+
background: get3(tokens, "extend.colors.primary.600")
|
|
1517
2271
|
},
|
|
1518
2272
|
//
|
|
1519
|
-
//
|
|
1520
|
-
// TODO(burdon): Review classnames (YJS dependent?)
|
|
2273
|
+
// Search
|
|
1521
2274
|
//
|
|
1522
|
-
"&light .cm-
|
|
1523
|
-
|
|
1524
|
-
},
|
|
1525
|
-
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
1526
|
-
mixBlendMode: "screen"
|
|
1527
|
-
},
|
|
1528
|
-
"& .cm-ySelectionInfo": {
|
|
1529
|
-
padding: "2px 4px",
|
|
1530
|
-
marginBlockStart: "-4px"
|
|
1531
|
-
},
|
|
1532
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
1533
|
-
paddingBlockStart: ".15em",
|
|
1534
|
-
paddingBlockEnd: ".15em"
|
|
1535
|
-
},
|
|
1536
|
-
"& .cm-ySelectionCaret": {
|
|
1537
|
-
display: "inline-block",
|
|
1538
|
-
insetBlockStart: ".1em",
|
|
1539
|
-
blockSize: "1.4em",
|
|
1540
|
-
verticalAlign: "top"
|
|
2275
|
+
"&light .cm-searchMatch": {
|
|
2276
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.100")
|
|
1541
2277
|
},
|
|
1542
|
-
"& .cm-
|
|
1543
|
-
|
|
2278
|
+
"&dark .cm-searchMatch": {
|
|
2279
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.700")
|
|
1544
2280
|
},
|
|
1545
2281
|
//
|
|
1546
2282
|
// link
|
|
1547
2283
|
//
|
|
1548
|
-
"
|
|
2284
|
+
".cm-link": {
|
|
1549
2285
|
color: get3(tokens, "extend.colors.primary.500"),
|
|
1550
2286
|
textDecorationLine: "underline",
|
|
1551
2287
|
textDecorationThickness: "1px",
|
|
@@ -1556,83 +2292,73 @@ var defaultTheme = {
|
|
|
1556
2292
|
//
|
|
1557
2293
|
// tooltip
|
|
1558
2294
|
//
|
|
1559
|
-
"
|
|
2295
|
+
".cm-tooltip": {
|
|
1560
2296
|
border: "none"
|
|
1561
2297
|
},
|
|
1562
|
-
"
|
|
2298
|
+
".cm-tooltip-below": {},
|
|
1563
2299
|
//
|
|
1564
2300
|
// autocomplete
|
|
1565
2301
|
//
|
|
1566
|
-
"
|
|
2302
|
+
".cm-tooltip-autocomplete": {
|
|
1567
2303
|
marginTop: "4px",
|
|
1568
2304
|
marginLeft: "-3px"
|
|
1569
2305
|
},
|
|
1570
|
-
"
|
|
1571
|
-
"
|
|
1572
|
-
"
|
|
2306
|
+
".cm-tooltip-autocomplete ul li": {},
|
|
2307
|
+
".cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2308
|
+
".cm-completionIcon": {
|
|
1573
2309
|
display: "none"
|
|
1574
2310
|
},
|
|
1575
|
-
"
|
|
2311
|
+
".cm-completionLabel": {
|
|
1576
2312
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1577
2313
|
},
|
|
1578
|
-
"
|
|
2314
|
+
".cm-completionMatchedText": {
|
|
1579
2315
|
textDecoration: "none"
|
|
1580
2316
|
},
|
|
1581
2317
|
//
|
|
1582
|
-
// widgets
|
|
1583
|
-
//
|
|
1584
|
-
"& .cm-widgetBuffer": {
|
|
1585
|
-
display: "none",
|
|
1586
|
-
height: 0
|
|
1587
|
-
},
|
|
1588
|
-
//
|
|
1589
2318
|
// table
|
|
1590
2319
|
//
|
|
1591
|
-
"
|
|
2320
|
+
".cm-table *": {
|
|
1592
2321
|
fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
1593
2322
|
textDecoration: "none !important"
|
|
1594
2323
|
},
|
|
1595
|
-
"
|
|
2324
|
+
".cm-table-head": {
|
|
1596
2325
|
padding: "2px 16px 2px 0px",
|
|
1597
2326
|
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
|
|
1598
2327
|
fontWeight: 100,
|
|
1599
2328
|
textAlign: "left",
|
|
1600
2329
|
color: get3(tokens, "extend.colors.neutral.500")
|
|
1601
2330
|
},
|
|
1602
|
-
"
|
|
2331
|
+
".cm-table-cell": {
|
|
1603
2332
|
padding: "2px 16px 2px 0px"
|
|
1604
2333
|
},
|
|
1605
2334
|
//
|
|
1606
2335
|
// image
|
|
1607
2336
|
//
|
|
1608
|
-
"
|
|
1609
|
-
|
|
2337
|
+
".cm-image": {
|
|
2338
|
+
display: "block",
|
|
2339
|
+
height: "0"
|
|
2340
|
+
},
|
|
2341
|
+
".cm-image.cm-loaded-image": {
|
|
2342
|
+
height: "auto",
|
|
2343
|
+
borderTop: "0.5rem solid transparent",
|
|
2344
|
+
borderBottom: "0.5rem solid transparent"
|
|
1610
2345
|
},
|
|
1611
2346
|
//
|
|
1612
2347
|
// font size
|
|
1613
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2348
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2349
|
+
// but it's far more verbose than it needs to be.
|
|
1614
2350
|
//
|
|
1615
|
-
...Object.keys(
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
1624
|
-
height
|
|
1625
|
-
};
|
|
1626
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
1627
|
-
height
|
|
1628
|
-
};
|
|
1629
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
1630
|
-
height
|
|
1631
|
-
};
|
|
1632
|
-
return acc;
|
|
1633
|
-
}, {}),
|
|
2351
|
+
// ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
|
|
2352
|
+
// const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
|
|
2353
|
+
//
|
|
2354
|
+
// acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
|
|
2355
|
+
// acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
|
|
2356
|
+
// acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
|
|
2357
|
+
// return acc;
|
|
2358
|
+
// }, {}),
|
|
1634
2359
|
/**
|
|
1635
2360
|
* Panels
|
|
2361
|
+
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
1636
2362
|
* https://github.com/codemirror/search/blob/main/src/search.ts#L745
|
|
1637
2363
|
*
|
|
1638
2364
|
* Find/replace panel.
|
|
@@ -1646,85 +2372,83 @@ var defaultTheme = {
|
|
|
1646
2372
|
* </div>
|
|
1647
2373
|
* </div
|
|
1648
2374
|
*/
|
|
1649
|
-
"
|
|
1650
|
-
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}
|
|
1651
|
-
borderBottom: "none"
|
|
2375
|
+
".cm-panels": {
|
|
2376
|
+
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
1652
2377
|
},
|
|
1653
|
-
"
|
|
2378
|
+
".cm-panel": {
|
|
1654
2379
|
background: get3(tokens, "extend.colors.neutral.50"),
|
|
1655
2380
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1656
2381
|
},
|
|
1657
|
-
"
|
|
2382
|
+
".cm-button": {
|
|
1658
2383
|
margin: "4px",
|
|
1659
2384
|
fontFamily: get3(tokens, "fontFamily.body", []).join(","),
|
|
1660
2385
|
background: get3(tokens, "extend.colors.neutral.100"),
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2386
|
+
backgroundImage: "none",
|
|
2387
|
+
border: "none",
|
|
2388
|
+
"&:hover": {
|
|
2389
|
+
background: get3(tokens, "extend.colors.neutral.200")
|
|
2390
|
+
},
|
|
2391
|
+
"&:active": {
|
|
2392
|
+
background: get3(tokens, "extend.colors.neutral.300"),
|
|
2393
|
+
backgroundImage: "none"
|
|
2394
|
+
}
|
|
1665
2395
|
},
|
|
1666
|
-
"
|
|
1667
|
-
|
|
2396
|
+
".cm-panel input[type=checkbox]": {
|
|
2397
|
+
marginRight: "0.4rem !important"
|
|
1668
2398
|
}
|
|
1669
2399
|
};
|
|
1670
2400
|
var textTheme = {
|
|
1671
|
-
"
|
|
2401
|
+
".cm-scroller": {
|
|
1672
2402
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1673
2403
|
},
|
|
1674
|
-
"
|
|
2404
|
+
".cm-placeholder": {
|
|
1675
2405
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1676
2406
|
}
|
|
1677
2407
|
};
|
|
1678
2408
|
var markdownTheme = {
|
|
1679
|
-
|
|
1680
|
-
|
|
2409
|
+
".cm-scroller": {
|
|
2410
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2411
|
+
},
|
|
2412
|
+
".cm-placeholder": {
|
|
1681
2413
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1682
2414
|
}
|
|
1683
2415
|
};
|
|
1684
2416
|
var codeTheme = {
|
|
1685
|
-
"
|
|
2417
|
+
".cm-scroller": {
|
|
1686
2418
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1687
2419
|
},
|
|
1688
|
-
"
|
|
2420
|
+
".cm-placeholder": {
|
|
1689
2421
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1690
2422
|
}
|
|
1691
2423
|
};
|
|
1692
2424
|
|
|
1693
2425
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
1694
|
-
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
1695
|
-
import { EditorView as EditorView11 } from "@codemirror/view";
|
|
1696
|
-
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
1697
|
-
import { vim } from "@replit/codemirror-vim";
|
|
1698
|
-
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
1699
|
-
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
|
|
1700
|
-
import { generateName } from "@dxos/display-name";
|
|
1701
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
1702
|
-
import { getColorForValue, inputSurface, mx as mx4 } from "@dxos/react-ui-theme";
|
|
1703
2426
|
var EditorModes = [
|
|
1704
2427
|
"default",
|
|
1705
2428
|
"vim"
|
|
1706
2429
|
];
|
|
1707
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly,
|
|
1708
|
-
const { themeMode } = useThemeContext();
|
|
2430
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, readonly, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
|
|
1709
2431
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
1710
2432
|
tabBehavior: "limited"
|
|
1711
2433
|
});
|
|
1712
|
-
const
|
|
1713
|
-
const
|
|
1714
|
-
const [view, setView] = useState();
|
|
1715
|
-
useImperativeHandle(forwardedRef, () =>
|
|
1716
|
-
root,
|
|
1717
|
-
state,
|
|
2434
|
+
const { themeMode } = useThemeContext();
|
|
2435
|
+
const rootRef = useRef(null);
|
|
2436
|
+
const [view, setView] = useState(null);
|
|
2437
|
+
useImperativeHandle(forwardedRef, () => view, [
|
|
1718
2438
|
view
|
|
1719
|
-
|
|
2439
|
+
]);
|
|
2440
|
+
useEffect(() => {
|
|
2441
|
+
if (view && focus) {
|
|
2442
|
+
view.focus();
|
|
2443
|
+
}
|
|
2444
|
+
}, [
|
|
1720
2445
|
view,
|
|
1721
|
-
|
|
1722
|
-
root
|
|
2446
|
+
focus
|
|
1723
2447
|
]);
|
|
1724
|
-
const { awareness, peer } = model;
|
|
1725
|
-
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
2448
|
+
const { awareness: awareness2, peer } = model;
|
|
2449
|
+
useEffect(() => {
|
|
2450
|
+
if (awareness2 && peer) {
|
|
2451
|
+
awareness2.setLocalStateField("user", {
|
|
1728
2452
|
name: peer.name ?? generateName(peer.id),
|
|
1729
2453
|
color: getColorForValue({
|
|
1730
2454
|
value: peer.id,
|
|
@@ -1738,26 +2462,40 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1738
2462
|
});
|
|
1739
2463
|
}
|
|
1740
2464
|
}, [
|
|
1741
|
-
|
|
2465
|
+
awareness2,
|
|
1742
2466
|
peer,
|
|
1743
2467
|
themeMode
|
|
1744
2468
|
]);
|
|
1745
|
-
|
|
1746
|
-
if (
|
|
2469
|
+
useEffect(() => {
|
|
2470
|
+
if (view && comments2 !== void 0) {
|
|
2471
|
+
view.dispatch({
|
|
2472
|
+
effects: setCommentRange.of({
|
|
2473
|
+
model,
|
|
2474
|
+
comments: comments2
|
|
2475
|
+
})
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
}, [
|
|
2479
|
+
view,
|
|
2480
|
+
comments2
|
|
2481
|
+
]);
|
|
2482
|
+
useEffect(() => {
|
|
2483
|
+
if (!model || !rootRef.current) {
|
|
1747
2484
|
return;
|
|
1748
2485
|
}
|
|
1749
|
-
const
|
|
2486
|
+
const state = EditorState2.create({
|
|
1750
2487
|
doc: model.text(),
|
|
2488
|
+
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
2489
|
+
selection,
|
|
1751
2490
|
extensions: [
|
|
1752
2491
|
readonly && EditorState2.readOnly.of(readonly),
|
|
1753
|
-
// TODO(burdon): Factor out VIM mode?
|
|
2492
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
1754
2493
|
editorMode === "vim" && vim(),
|
|
1755
2494
|
// Theme.
|
|
1756
|
-
// TODO(burdon): Make
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
EditorView11.darkTheme.of(themeMode === "dark"),
|
|
2495
|
+
// TODO(burdon): Make theme configurable.
|
|
2496
|
+
EditorView12.baseTheme(defaultTheme),
|
|
2497
|
+
EditorView12.theme(theme ?? {}),
|
|
2498
|
+
EditorView12.darkTheme.of(themeMode === "dark"),
|
|
1761
2499
|
// Storage and replication.
|
|
1762
2500
|
model.extension,
|
|
1763
2501
|
// Custom.
|
|
@@ -1765,32 +2503,27 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1765
2503
|
].filter(Boolean)
|
|
1766
2504
|
});
|
|
1767
2505
|
view?.destroy();
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
})
|
|
1772
|
-
|
|
2506
|
+
const newView = new EditorView12({
|
|
2507
|
+
parent: rootRef.current,
|
|
2508
|
+
state
|
|
2509
|
+
});
|
|
2510
|
+
setView(newView);
|
|
1773
2511
|
return () => {
|
|
1774
|
-
|
|
1775
|
-
setView(
|
|
1776
|
-
setState(void 0);
|
|
2512
|
+
newView?.destroy();
|
|
2513
|
+
setView(null);
|
|
1777
2514
|
};
|
|
1778
2515
|
}, [
|
|
1779
|
-
|
|
2516
|
+
rootRef,
|
|
1780
2517
|
model,
|
|
1781
|
-
themeMode,
|
|
1782
2518
|
readonly,
|
|
1783
|
-
editorMode
|
|
2519
|
+
editorMode,
|
|
2520
|
+
themeMode
|
|
1784
2521
|
]);
|
|
1785
2522
|
const handleKeyUp = useCallback((event) => {
|
|
1786
2523
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
1787
2524
|
switch (key) {
|
|
1788
|
-
case "Enter": {
|
|
1789
|
-
view?.contentDOM.focus();
|
|
1790
|
-
break;
|
|
1791
|
-
}
|
|
1792
2525
|
case "Escape": {
|
|
1793
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) &&
|
|
2526
|
+
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && rootRef.current?.focus();
|
|
1794
2527
|
break;
|
|
1795
2528
|
}
|
|
1796
2529
|
}
|
|
@@ -1800,16 +2533,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1800
2533
|
]);
|
|
1801
2534
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1802
2535
|
key: model.id,
|
|
1803
|
-
|
|
2536
|
+
role: "none",
|
|
2537
|
+
ref: rootRef,
|
|
1804
2538
|
tabIndex: 0,
|
|
1805
|
-
|
|
1806
|
-
...
|
|
1807
|
-
|
|
2539
|
+
onKeyUp: handleKeyUp,
|
|
2540
|
+
...slots.root,
|
|
2541
|
+
...editorMode !== "vim" && tabsterDOMAttribute
|
|
1808
2542
|
});
|
|
1809
2543
|
});
|
|
1810
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [],
|
|
2544
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
|
|
1811
2545
|
const { themeMode } = useThemeContext();
|
|
1812
|
-
const
|
|
2546
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
1813
2547
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1814
2548
|
ref: forwardedRef,
|
|
1815
2549
|
readonly,
|
|
@@ -1817,359 +2551,207 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots:
|
|
|
1817
2551
|
basicBundle({
|
|
1818
2552
|
readonly,
|
|
1819
2553
|
themeMode,
|
|
1820
|
-
placeholder:
|
|
2554
|
+
placeholder: placeholder3
|
|
1821
2555
|
}),
|
|
1822
2556
|
...extensions
|
|
1823
2557
|
],
|
|
1824
|
-
|
|
2558
|
+
theme,
|
|
2559
|
+
slots: updatedSlots,
|
|
1825
2560
|
...props
|
|
1826
2561
|
});
|
|
1827
2562
|
});
|
|
1828
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [],
|
|
2563
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
|
|
1829
2564
|
const { themeMode } = useThemeContext();
|
|
1830
|
-
const
|
|
2565
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
1831
2566
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1832
2567
|
ref: forwardedRef,
|
|
1833
2568
|
readonly,
|
|
1834
|
-
extensions: [
|
|
1835
|
-
markdownBundle({
|
|
1836
|
-
readonly,
|
|
1837
|
-
themeMode,
|
|
1838
|
-
placeholder:
|
|
1839
|
-
}),
|
|
1840
|
-
...extensions
|
|
1841
|
-
],
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
});
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
};
|
|
1851
|
-
var defaultTextSlots = {
|
|
1852
|
-
...defaultSlots,
|
|
1853
|
-
editor: {
|
|
1854
|
-
theme: textTheme
|
|
1855
|
-
}
|
|
1856
|
-
};
|
|
1857
|
-
var defaultMarkdownSlots = {
|
|
1858
|
-
...defaultSlots,
|
|
1859
|
-
editor: {
|
|
1860
|
-
theme: markdownTheme
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1865
|
-
import get4 from "lodash.get";
|
|
1866
|
-
import { useEffect as useEffect3, useState as useState2 } from "react";
|
|
1867
|
-
import { yCollab } from "y-codemirror.next";
|
|
1868
|
-
import { invariant as invariant3 } from "@dxos/invariant";
|
|
1869
|
-
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
1870
|
-
|
|
1871
|
-
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
1872
|
-
import { Annotation, Facet, StateEffect as StateEffect2, StateField as StateField6 } from "@codemirror/state";
|
|
1873
|
-
import { ViewPlugin as ViewPlugin4 } from "@codemirror/view";
|
|
1874
|
-
import * as automerge2 from "@dxos/automerge/automerge";
|
|
1875
|
-
|
|
1876
|
-
// packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
|
|
1877
|
-
import { next as automerge } from "@dxos/automerge/automerge";
|
|
1878
|
-
|
|
1879
|
-
// packages/ui/react-ui-editor/src/hooks/automerge/translation/automerge-to-codemirror.ts
|
|
1880
|
-
import { ChangeSet } from "@codemirror/state";
|
|
1881
|
-
var automergeToCodemirror = (view, selection, target, patches) => {
|
|
1882
|
-
for (const patch of patches) {
|
|
1883
|
-
const changeSpec = handlePatch(patch, target, view.state);
|
|
1884
|
-
if (changeSpec != null) {
|
|
1885
|
-
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
1886
|
-
selection = selection.map(changeSet, 1);
|
|
1887
|
-
view.dispatch({
|
|
1888
|
-
changes: changeSet,
|
|
1889
|
-
annotations: reconcileAnnotationType.of({})
|
|
1890
|
-
});
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
view.dispatch({
|
|
1894
|
-
selection,
|
|
1895
|
-
annotations: reconcileAnnotationType.of({})
|
|
1896
|
-
});
|
|
1897
|
-
};
|
|
1898
|
-
var handlePatch = (patch, target, state) => {
|
|
1899
|
-
if (patch.action === "insert") {
|
|
1900
|
-
return handleInsert(target, patch);
|
|
1901
|
-
} else if (patch.action === "splice") {
|
|
1902
|
-
return handleSplice(target, patch);
|
|
1903
|
-
} else if (patch.action === "del") {
|
|
1904
|
-
return handleDel(target, patch);
|
|
1905
|
-
} else if (patch.action === "put") {
|
|
1906
|
-
return handlePut(target, patch, state);
|
|
1907
|
-
} else {
|
|
1908
|
-
return null;
|
|
1909
|
-
}
|
|
1910
|
-
};
|
|
1911
|
-
var handleInsert = (target, patch) => {
|
|
1912
|
-
const index = charPath(target, patch.path);
|
|
1913
|
-
if (index == null) {
|
|
1914
|
-
return [];
|
|
1915
|
-
}
|
|
1916
|
-
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
1917
|
-
return [
|
|
1918
|
-
{
|
|
1919
|
-
from: index,
|
|
1920
|
-
to: index,
|
|
1921
|
-
insert: text
|
|
1922
|
-
}
|
|
1923
|
-
];
|
|
1924
|
-
};
|
|
1925
|
-
var handleSplice = (target, patch) => {
|
|
1926
|
-
const index = charPath(target, patch.path);
|
|
1927
|
-
if (index == null) {
|
|
1928
|
-
return [];
|
|
1929
|
-
}
|
|
1930
|
-
return [
|
|
1931
|
-
{
|
|
1932
|
-
from: index,
|
|
1933
|
-
insert: patch.value
|
|
1934
|
-
}
|
|
1935
|
-
];
|
|
1936
|
-
};
|
|
1937
|
-
var handleDel = (target, patch) => {
|
|
1938
|
-
const index = charPath(target, patch.path);
|
|
1939
|
-
if (index == null) {
|
|
1940
|
-
return [];
|
|
1941
|
-
}
|
|
1942
|
-
const length = patch.length || 1;
|
|
1943
|
-
return [
|
|
1944
|
-
{
|
|
1945
|
-
from: index,
|
|
1946
|
-
to: index + length
|
|
1947
|
-
}
|
|
1948
|
-
];
|
|
1949
|
-
};
|
|
1950
|
-
var handlePut = (target, patch, state) => {
|
|
1951
|
-
const index = charPath(target, [
|
|
1952
|
-
...patch.path,
|
|
1953
|
-
0
|
|
1954
|
-
]);
|
|
1955
|
-
if (index == null) {
|
|
1956
|
-
return [];
|
|
1957
|
-
}
|
|
1958
|
-
const length = state.doc.length;
|
|
1959
|
-
if (typeof patch.value !== "string") {
|
|
1960
|
-
return [];
|
|
1961
|
-
}
|
|
1962
|
-
return [
|
|
1963
|
-
{
|
|
1964
|
-
from: 0,
|
|
1965
|
-
to: length,
|
|
1966
|
-
insert: patch.value
|
|
1967
|
-
}
|
|
1968
|
-
];
|
|
1969
|
-
};
|
|
1970
|
-
var charPath = (textPath, candidatePath) => {
|
|
1971
|
-
if (candidatePath.length !== textPath.length + 1) {
|
|
1972
|
-
return null;
|
|
1973
|
-
}
|
|
1974
|
-
for (let i = 0; i < textPath.length; i++) {
|
|
1975
|
-
if (textPath[i] !== candidatePath[i]) {
|
|
1976
|
-
return null;
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
const index = candidatePath[candidatePath.length - 1];
|
|
1980
|
-
if (typeof index === "number") {
|
|
1981
|
-
return index;
|
|
2569
|
+
extensions: [
|
|
2570
|
+
markdownBundle({
|
|
2571
|
+
readonly,
|
|
2572
|
+
themeMode,
|
|
2573
|
+
placeholder: placeholder3
|
|
2574
|
+
}),
|
|
2575
|
+
...extensions
|
|
2576
|
+
],
|
|
2577
|
+
theme,
|
|
2578
|
+
slots: updatedSlots,
|
|
2579
|
+
...props
|
|
2580
|
+
});
|
|
2581
|
+
});
|
|
2582
|
+
var defaultSlots = {
|
|
2583
|
+
root: {
|
|
2584
|
+
className: mx3("p-2 overflow-y-auto", inputSurface)
|
|
1982
2585
|
}
|
|
1983
|
-
return null;
|
|
1984
2586
|
};
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
var
|
|
1989
|
-
|
|
1990
|
-
let hasChanges = false;
|
|
1991
|
-
for (const tr of transactions) {
|
|
1992
|
-
tr.changes.iterChanges(() => {
|
|
1993
|
-
hasChanges = true;
|
|
1994
|
-
});
|
|
1995
|
-
}
|
|
1996
|
-
if (!hasChanges) {
|
|
1997
|
-
return void 0;
|
|
1998
|
-
}
|
|
1999
|
-
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
2000
|
-
for (const tr of transactions) {
|
|
2001
|
-
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
2002
|
-
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2005
|
-
});
|
|
2006
|
-
return newHeads ?? void 0;
|
|
2587
|
+
var defaultTextSlots = {
|
|
2588
|
+
...defaultSlots
|
|
2589
|
+
};
|
|
2590
|
+
var defaultMarkdownSlots = {
|
|
2591
|
+
...defaultSlots
|
|
2007
2592
|
};
|
|
2008
2593
|
|
|
2009
|
-
// packages/ui/react-ui-editor/src/hooks/automerge
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2594
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2595
|
+
import get4 from "lodash.get";
|
|
2596
|
+
import { getRawDoc } from "@dxos/echo-schema";
|
|
2597
|
+
import { isNotNullOrUndefined } from "@dxos/util";
|
|
2598
|
+
|
|
2599
|
+
// packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
|
|
2600
|
+
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2601
|
+
import { Context as Context2 } from "@dxos/context";
|
|
2602
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
2603
|
+
import { log as log3 } from "@dxos/log";
|
|
2604
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2605
|
+
var DEBOUNCE_INTERVAL = 100;
|
|
2606
|
+
var SpaceAwarenessProvider = class {
|
|
2607
|
+
constructor(params) {
|
|
2608
|
+
this.remoteStateChange = new Event2();
|
|
2609
|
+
this._remoteStates = /* @__PURE__ */ new Map();
|
|
2610
|
+
this._space = params.space;
|
|
2611
|
+
this._channel = params.channel;
|
|
2612
|
+
this._peerId = params.peerId;
|
|
2613
|
+
this._info = params.info;
|
|
2614
|
+
}
|
|
2615
|
+
open() {
|
|
2616
|
+
this._ctx = new Context2();
|
|
2617
|
+
this._postTask = new DeferredTask(this._ctx, async () => {
|
|
2618
|
+
if (this._localState) {
|
|
2619
|
+
await this._space.postMessage(this._channel, {
|
|
2620
|
+
kind: "post",
|
|
2621
|
+
state: this._localState
|
|
2030
2622
|
});
|
|
2623
|
+
await sleep(DEBOUNCE_INTERVAL);
|
|
2031
2624
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2625
|
+
});
|
|
2626
|
+
const unsubscribe = this._space.listen(this._channel, (message) => {
|
|
2627
|
+
switch (message.payload.kind) {
|
|
2628
|
+
case "query": {
|
|
2629
|
+
this._handleQueryMessage();
|
|
2630
|
+
break;
|
|
2631
|
+
}
|
|
2632
|
+
case "post": {
|
|
2633
|
+
this._handlePostMessage(message.payload);
|
|
2634
|
+
break;
|
|
2635
|
+
}
|
|
2035
2636
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2637
|
+
});
|
|
2638
|
+
this._ctx.onDispose(unsubscribe);
|
|
2639
|
+
void this._space.postMessage(this._channel, {
|
|
2640
|
+
kind: "query"
|
|
2641
|
+
}).catch((err) => {
|
|
2642
|
+
log3.debug("failed to query awareness", {
|
|
2643
|
+
err
|
|
2644
|
+
}, {
|
|
2645
|
+
F: __dxlog_file4,
|
|
2646
|
+
L: 85,
|
|
2647
|
+
S: this,
|
|
2648
|
+
C: (f, a) => f(...a)
|
|
2041
2649
|
});
|
|
2042
|
-
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
close() {
|
|
2653
|
+
void this._ctx?.dispose();
|
|
2654
|
+
this._ctx = void 0;
|
|
2655
|
+
this._postTask = void 0;
|
|
2656
|
+
}
|
|
2657
|
+
update(position) {
|
|
2658
|
+
invariant3(this._postTask, void 0, {
|
|
2659
|
+
F: __dxlog_file4,
|
|
2660
|
+
L: 96,
|
|
2661
|
+
S: this,
|
|
2662
|
+
A: [
|
|
2663
|
+
"this._postTask",
|
|
2664
|
+
""
|
|
2665
|
+
]
|
|
2666
|
+
});
|
|
2667
|
+
this._localState = {
|
|
2668
|
+
peerId: this._peerId,
|
|
2669
|
+
position,
|
|
2670
|
+
info: this._info
|
|
2043
2671
|
};
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2672
|
+
this._postTask.schedule();
|
|
2673
|
+
}
|
|
2674
|
+
getRemoteStates() {
|
|
2675
|
+
return Array.from(this._remoteStates.values());
|
|
2676
|
+
}
|
|
2677
|
+
_handleQueryMessage() {
|
|
2678
|
+
invariant3(this._postTask, void 0, {
|
|
2679
|
+
F: __dxlog_file4,
|
|
2680
|
+
L: 111,
|
|
2681
|
+
S: this,
|
|
2682
|
+
A: [
|
|
2683
|
+
"this._postTask",
|
|
2684
|
+
""
|
|
2685
|
+
]
|
|
2686
|
+
});
|
|
2687
|
+
this._postTask.schedule();
|
|
2688
|
+
}
|
|
2689
|
+
_handlePostMessage(message) {
|
|
2690
|
+
invariant3(message.kind === "post", void 0, {
|
|
2691
|
+
F: __dxlog_file4,
|
|
2692
|
+
L: 116,
|
|
2693
|
+
S: this,
|
|
2694
|
+
A: [
|
|
2695
|
+
"message.kind === 'post'",
|
|
2696
|
+
""
|
|
2697
|
+
]
|
|
2698
|
+
});
|
|
2699
|
+
this._remoteStates.set(message.state.peerId, message.state);
|
|
2700
|
+
this.remoteStateChange.emit();
|
|
2047
2701
|
}
|
|
2048
2702
|
};
|
|
2049
2703
|
|
|
2050
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
});
|
|
2055
|
-
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
2056
|
-
var getPath = (state, field) => state.field(field).path;
|
|
2057
|
-
var semaphoreFacet = Facet.define({
|
|
2058
|
-
combine: (values) => values.at(-1)
|
|
2059
|
-
});
|
|
2060
|
-
var automergePlugin = (handle, path) => {
|
|
2061
|
-
const stateField = StateField6.define({
|
|
2062
|
-
create: () => ({
|
|
2063
|
-
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
2064
|
-
unreconciledTransactions: [],
|
|
2065
|
-
path: path.slice()
|
|
2066
|
-
}),
|
|
2067
|
-
update: (value, tr) => {
|
|
2068
|
-
const result = {
|
|
2069
|
-
lastHeads: value.lastHeads,
|
|
2070
|
-
unreconciledTransactions: value.unreconciledTransactions.slice(),
|
|
2071
|
-
path: path.slice()
|
|
2072
|
-
};
|
|
2073
|
-
let clearUnreconciled = false;
|
|
2074
|
-
for (const effect of tr.effects) {
|
|
2075
|
-
if (effect.is(effectType)) {
|
|
2076
|
-
result.lastHeads = effect.value.newHeads;
|
|
2077
|
-
clearUnreconciled = true;
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
if (clearUnreconciled) {
|
|
2081
|
-
result.unreconciledTransactions = [];
|
|
2082
|
-
} else {
|
|
2083
|
-
if (!isReconcileTx(tr)) {
|
|
2084
|
-
result.unreconciledTransactions.push(tr);
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
return result;
|
|
2088
|
-
}
|
|
2089
|
-
});
|
|
2090
|
-
const semaphore = new PatchSemaphore(stateField);
|
|
2091
|
-
const viewPlugin = ViewPlugin4.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
2092
|
-
constructor(view) {
|
|
2093
|
-
this._handleChange = () => {
|
|
2094
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
2095
|
-
};
|
|
2096
|
-
this._view = view;
|
|
2097
|
-
handle.addListener("change", this._handleChange);
|
|
2098
|
-
}
|
|
2099
|
-
update(update4) {
|
|
2100
|
-
if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
|
|
2101
|
-
queueMicrotask(() => {
|
|
2102
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
destroy() {
|
|
2107
|
-
handle.addListener("change", this._handleChange);
|
|
2108
|
-
}
|
|
2109
|
-
});
|
|
2110
|
-
return {
|
|
2111
|
-
extension: [
|
|
2112
|
-
stateField,
|
|
2113
|
-
semaphoreFacet.of(semaphore),
|
|
2114
|
-
viewPlugin
|
|
2115
|
-
]
|
|
2116
|
-
};
|
|
2117
|
-
};
|
|
2118
|
-
var reconcileAnnotationType = Annotation.define();
|
|
2119
|
-
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
2120
|
-
|
|
2121
|
-
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
2122
|
-
import * as random2 from "lib0/random";
|
|
2123
|
-
var cursorColors = [
|
|
2124
|
-
{
|
|
2125
|
-
color: "#30bced",
|
|
2126
|
-
light: "#30bced33"
|
|
2127
|
-
},
|
|
2128
|
-
{
|
|
2129
|
-
color: "#6eeb83",
|
|
2130
|
-
light: "#6eeb8333"
|
|
2131
|
-
},
|
|
2132
|
-
{
|
|
2133
|
-
color: "#ffbc42",
|
|
2134
|
-
light: "#ffbc4233"
|
|
2135
|
-
},
|
|
2136
|
-
{
|
|
2137
|
-
color: "#ecd444",
|
|
2138
|
-
light: "#ecd44433"
|
|
2139
|
-
},
|
|
2140
|
-
{
|
|
2141
|
-
color: "#ee6352",
|
|
2142
|
-
light: "#ee635233"
|
|
2143
|
-
},
|
|
2144
|
-
{
|
|
2145
|
-
color: "#9ac2c9",
|
|
2146
|
-
light: "#9ac2c933"
|
|
2147
|
-
},
|
|
2148
|
-
{
|
|
2149
|
-
color: "#8acb88",
|
|
2150
|
-
light: "#8acb8833"
|
|
2151
|
-
},
|
|
2152
|
-
{
|
|
2153
|
-
color: "#1be7ff",
|
|
2154
|
-
light: "#1be7ff33"
|
|
2155
|
-
}
|
|
2156
|
-
];
|
|
2157
|
-
var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
2704
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2705
|
+
import { StateField as StateField5 } from "@codemirror/state";
|
|
2706
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
2707
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
2158
2708
|
|
|
2159
|
-
// packages/ui/react-ui-editor/src/hooks/yjs
|
|
2709
|
+
// packages/ui/react-ui-editor/src/hooks/yjs.ts
|
|
2160
2710
|
import * as decoding from "lib0/decoding";
|
|
2161
2711
|
import * as encoding from "lib0/encoding";
|
|
2162
2712
|
import { Observable } from "lib0/observable";
|
|
2163
|
-
import * as
|
|
2164
|
-
import {
|
|
2165
|
-
|
|
2713
|
+
import * as YP from "y-protocols/awareness";
|
|
2714
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2715
|
+
import { log as log4 } from "@dxos/log";
|
|
2716
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2717
|
+
var createYjsModel = ({ identity, space, text }) => {
|
|
2718
|
+
invariant4(text?.doc && text?.content, void 0, {
|
|
2719
|
+
F: __dxlog_file5,
|
|
2720
|
+
L: 21,
|
|
2721
|
+
S: void 0,
|
|
2722
|
+
A: [
|
|
2723
|
+
"text?.doc && text?.content",
|
|
2724
|
+
""
|
|
2725
|
+
]
|
|
2726
|
+
});
|
|
2727
|
+
const provider = space ? new YAwarenessProvider({
|
|
2728
|
+
space,
|
|
2729
|
+
doc: text.doc,
|
|
2730
|
+
channel: `yjs.awareness.${text.id}`
|
|
2731
|
+
}) : void 0;
|
|
2732
|
+
const model = {
|
|
2733
|
+
id: text.doc.guid,
|
|
2734
|
+
content: text.content,
|
|
2735
|
+
text: () => text.content.toString(),
|
|
2736
|
+
extension: [
|
|
2737
|
+
modelState.init(() => model),
|
|
2738
|
+
yjs(text.content, provider?.awareness)
|
|
2739
|
+
],
|
|
2740
|
+
awareness: provider?.awareness,
|
|
2741
|
+
peer: identity ? {
|
|
2742
|
+
id: identity.identityKey.toHex(),
|
|
2743
|
+
name: identity.profile?.displayName
|
|
2744
|
+
} : void 0
|
|
2745
|
+
};
|
|
2746
|
+
return model;
|
|
2747
|
+
};
|
|
2166
2748
|
var messageAwareness = 1;
|
|
2167
2749
|
var messageQueryAwareness = 3;
|
|
2168
|
-
var
|
|
2169
|
-
constructor({ space, doc, channel, awareness }) {
|
|
2750
|
+
var YAwarenessProvider = class extends Observable {
|
|
2751
|
+
constructor({ space, doc, channel, awareness: awareness2 }) {
|
|
2170
2752
|
super();
|
|
2171
2753
|
this._space = space;
|
|
2172
|
-
this._awareness =
|
|
2754
|
+
this._awareness = awareness2 ?? new YP.Awareness(doc);
|
|
2173
2755
|
this._channel = channel;
|
|
2174
2756
|
this._clientId = doc.clientID;
|
|
2175
2757
|
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
@@ -2184,7 +2766,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2184
2766
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
2185
2767
|
const encoderAwarenessState = encoding.createEncoder();
|
|
2186
2768
|
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
2187
|
-
encoding.writeVarUint8Array(encoderAwarenessState,
|
|
2769
|
+
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
|
|
2188
2770
|
this._clientId
|
|
2189
2771
|
]));
|
|
2190
2772
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
@@ -2193,27 +2775,27 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2193
2775
|
return this._awareness;
|
|
2194
2776
|
}
|
|
2195
2777
|
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
2196
|
-
|
|
2778
|
+
log4("awareness update", {
|
|
2197
2779
|
added,
|
|
2198
2780
|
updated,
|
|
2199
2781
|
removed,
|
|
2200
2782
|
origin
|
|
2201
2783
|
}, {
|
|
2202
|
-
F:
|
|
2203
|
-
L:
|
|
2784
|
+
F: __dxlog_file5,
|
|
2785
|
+
L: 100,
|
|
2204
2786
|
S: this,
|
|
2205
2787
|
C: (f, a) => f(...a)
|
|
2206
2788
|
});
|
|
2207
2789
|
const changedClients = added.concat(updated).concat(removed);
|
|
2208
2790
|
const encoderAwareness = encoding.createEncoder();
|
|
2209
2791
|
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
2210
|
-
encoding.writeVarUint8Array(encoderAwareness,
|
|
2792
|
+
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
2211
2793
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
2212
2794
|
}
|
|
2213
2795
|
_handleSpaceMessage({ payload }) {
|
|
2214
|
-
|
|
2215
|
-
F:
|
|
2216
|
-
L:
|
|
2796
|
+
log4("space message", payload, {
|
|
2797
|
+
F: __dxlog_file5,
|
|
2798
|
+
L: 109,
|
|
2217
2799
|
S: this,
|
|
2218
2800
|
C: (f, a) => f(...a)
|
|
2219
2801
|
});
|
|
@@ -2230,12 +2812,12 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2230
2812
|
let sendReply = false;
|
|
2231
2813
|
switch (messageType) {
|
|
2232
2814
|
case messageAwareness: {
|
|
2233
|
-
|
|
2815
|
+
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
2234
2816
|
break;
|
|
2235
2817
|
}
|
|
2236
2818
|
case messageQueryAwareness: {
|
|
2237
2819
|
encoding.writeVarUint(encoder, messageAwareness);
|
|
2238
|
-
encoding.writeVarUint8Array(encoder,
|
|
2820
|
+
encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
2239
2821
|
sendReply = true;
|
|
2240
2822
|
break;
|
|
2241
2823
|
}
|
|
@@ -2250,130 +2832,127 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2250
2832
|
return encoder;
|
|
2251
2833
|
}
|
|
2252
2834
|
_handleBeforeUnload() {
|
|
2253
|
-
|
|
2835
|
+
YP.removeAwarenessStates(this._awareness, [
|
|
2254
2836
|
this._clientId
|
|
2255
2837
|
], "window unload");
|
|
2256
2838
|
}
|
|
2257
2839
|
};
|
|
2258
2840
|
|
|
2259
2841
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2260
|
-
var
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
setModel(createModel({
|
|
2269
|
-
identity,
|
|
2270
|
-
space,
|
|
2271
|
-
text
|
|
2272
|
-
}));
|
|
2273
|
-
}, [
|
|
2842
|
+
var modelState = StateField5.define({
|
|
2843
|
+
create: () => void 0,
|
|
2844
|
+
update: (model) => model
|
|
2845
|
+
});
|
|
2846
|
+
var useTextModel = (props) => {
|
|
2847
|
+
const { identity, space, text } = props;
|
|
2848
|
+
const [model, setModel] = useState2();
|
|
2849
|
+
useEffect2(() => setModel(createModel(props)), [
|
|
2274
2850
|
identity,
|
|
2275
2851
|
space,
|
|
2276
2852
|
text
|
|
2277
2853
|
]);
|
|
2278
2854
|
return model;
|
|
2279
2855
|
};
|
|
2280
|
-
var createModel = (
|
|
2281
|
-
const { text } =
|
|
2282
|
-
if (
|
|
2283
|
-
return createAutomergeModel(
|
|
2856
|
+
var createModel = (props) => {
|
|
2857
|
+
const { text } = props;
|
|
2858
|
+
if (isAutomergeObject(text)) {
|
|
2859
|
+
return createAutomergeModel(props);
|
|
2860
|
+
} else if (text?.doc) {
|
|
2861
|
+
return createYjsModel(props);
|
|
2284
2862
|
} else {
|
|
2285
|
-
|
|
2286
|
-
return void 0;
|
|
2287
|
-
}
|
|
2288
|
-
return createYjsModel(options);
|
|
2863
|
+
return void 0;
|
|
2289
2864
|
}
|
|
2290
2865
|
};
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
L: 81,
|
|
2295
|
-
S: void 0,
|
|
2296
|
-
A: [
|
|
2297
|
-
"text?.doc && text?.content",
|
|
2298
|
-
""
|
|
2299
|
-
]
|
|
2300
|
-
});
|
|
2301
|
-
const provider = space ? new SpaceAwarenessProvider({
|
|
2302
|
-
space,
|
|
2303
|
-
doc: text.doc,
|
|
2304
|
-
channel: `yjs.awareness.${text.id}`
|
|
2305
|
-
}) : void 0;
|
|
2306
|
-
return {
|
|
2307
|
-
id: text.doc.guid,
|
|
2308
|
-
content: text.content,
|
|
2309
|
-
text: () => text.content.toString(),
|
|
2310
|
-
ranges: [],
|
|
2311
|
-
extension: yCollab(text.content, provider?.awareness),
|
|
2312
|
-
awareness: provider?.awareness,
|
|
2313
|
-
peer: identity ? {
|
|
2314
|
-
id: identity.identityKey.toHex(),
|
|
2315
|
-
name: identity.profile?.displayName
|
|
2316
|
-
} : void 0
|
|
2317
|
-
};
|
|
2318
|
-
};
|
|
2319
|
-
var createAutomergeModel = ({ identity, text }) => {
|
|
2866
|
+
|
|
2867
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2868
|
+
var createAutomergeModel = ({ space, identity, text }) => {
|
|
2320
2869
|
const obj = text;
|
|
2321
2870
|
const doc = getRawDoc(obj, [
|
|
2322
2871
|
obj.field
|
|
2323
2872
|
]);
|
|
2324
|
-
|
|
2873
|
+
const awarenessProvider = space && new SpaceAwarenessProvider({
|
|
2874
|
+
space,
|
|
2875
|
+
channel: `automerge.awareness.${obj.id}`,
|
|
2876
|
+
info: {
|
|
2877
|
+
displayName: identity?.profile?.displayName ?? "",
|
|
2878
|
+
color: cursorColor.color,
|
|
2879
|
+
lightColor: cursorColor.light
|
|
2880
|
+
},
|
|
2881
|
+
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2882
|
+
});
|
|
2883
|
+
const model = {
|
|
2325
2884
|
id: obj.id,
|
|
2326
2885
|
content: doc,
|
|
2327
2886
|
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2328
|
-
|
|
2329
|
-
extension:
|
|
2887
|
+
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
2888
|
+
extension: [
|
|
2889
|
+
modelState.init(() => model),
|
|
2890
|
+
automerge3({
|
|
2891
|
+
handle: doc.handle,
|
|
2892
|
+
path: doc.path
|
|
2893
|
+
}),
|
|
2894
|
+
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
2895
|
+
awareness()
|
|
2896
|
+
].filter(isNotNullOrUndefined),
|
|
2330
2897
|
peer: identity ? {
|
|
2331
2898
|
id: identity.identityKey.toHex(),
|
|
2332
2899
|
name: identity.profile?.displayName
|
|
2333
2900
|
} : void 0
|
|
2334
2901
|
};
|
|
2902
|
+
return model;
|
|
2335
2903
|
};
|
|
2336
2904
|
export {
|
|
2905
|
+
AwarenessProvider,
|
|
2337
2906
|
BaseTextEditor,
|
|
2907
|
+
Cursor,
|
|
2338
2908
|
Doc,
|
|
2339
2909
|
EditorModes,
|
|
2340
2910
|
MarkdownEditor,
|
|
2911
|
+
RemoteSelectionsDecorator,
|
|
2341
2912
|
SpaceAwarenessProvider,
|
|
2342
2913
|
TextEditor,
|
|
2343
2914
|
TextKind,
|
|
2915
|
+
YAwarenessProvider,
|
|
2344
2916
|
YText,
|
|
2345
2917
|
YXmlFragment,
|
|
2346
2918
|
autocomplete,
|
|
2919
|
+
automerge3 as automerge,
|
|
2920
|
+
awareness,
|
|
2347
2921
|
basicBundle,
|
|
2348
2922
|
blast,
|
|
2923
|
+
callbackWrapper,
|
|
2349
2924
|
code2 as code,
|
|
2350
2925
|
codeTheme,
|
|
2351
2926
|
comments,
|
|
2352
|
-
|
|
2927
|
+
createAutomergeModel,
|
|
2928
|
+
createYjsModel,
|
|
2929
|
+
cursorConverter,
|
|
2353
2930
|
defaultMarkdownSlots,
|
|
2354
2931
|
defaultOptions,
|
|
2355
2932
|
defaultSlots,
|
|
2356
2933
|
defaultTextSlots,
|
|
2357
2934
|
defaultTheme,
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
highlightDecorations,
|
|
2361
|
-
highlightStateField,
|
|
2935
|
+
getToken,
|
|
2936
|
+
hr,
|
|
2362
2937
|
image,
|
|
2363
2938
|
link,
|
|
2364
|
-
|
|
2939
|
+
listener,
|
|
2365
2940
|
markdownBundle,
|
|
2366
2941
|
markdownHighlightStyle,
|
|
2367
2942
|
markdownTags,
|
|
2368
2943
|
markdownTagsExtensions,
|
|
2369
2944
|
markdownTheme,
|
|
2370
2945
|
mention,
|
|
2946
|
+
modelState,
|
|
2947
|
+
setCommentRange,
|
|
2948
|
+
setFocus,
|
|
2949
|
+
setSelection,
|
|
2371
2950
|
table,
|
|
2372
2951
|
tags2 as tags,
|
|
2373
2952
|
tasklist,
|
|
2374
2953
|
textTheme,
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2954
|
+
typewriter,
|
|
2955
|
+
useTextModel,
|
|
2956
|
+
yjs
|
|
2378
2957
|
};
|
|
2379
2958
|
//# sourceMappingURL=index.mjs.map
|