@dxos/react-ui-editor 0.3.10 → 0.3.11-main.00a28cb
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 +1724 -1105
- 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 +24 -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 +3 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts +14 -0
- 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 +59 -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 +7 -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/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts +7 -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 +3 -1
- 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 +28 -22
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +150 -87
- 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 +355 -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 +92 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +66 -0
- package/src/extensions/automerge/index.ts +6 -0
- package/src/{hooks/automerge/PatchSemaphore.ts → extensions/automerge/semaphore.ts} +26 -25
- 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 +310 -0
- package/src/extensions/code.ts +104 -0
- package/src/extensions/comments.ts +408 -0
- package/src/extensions/cursor.ts +46 -0
- package/src/extensions/hr.ts +72 -0
- package/src/extensions/image.ts +87 -0
- package/src/{components/TextEditor/extensions → extensions}/index.ts +7 -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/extensions/outliner.ts +12 -0
- package/src/{components/TextEditor/extensions → extensions}/table.ts +23 -4
- package/src/extensions/tasklist.ts +96 -0
- 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 +4 -1
- 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 +0 -3
- 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 +0 -19
- 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/code.ts +0 -99
- 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/tasklist.ts +0 -121
- 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/{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 { 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, Facet } 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
|
+
var semaphoreFacet = Facet.define({
|
|
105
|
+
combine: (values) => values.at(-1)
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
109
|
+
import { next as automerge2 } from "@dxos/automerge/automerge";
|
|
110
|
+
|
|
111
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
112
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
113
|
+
var updateAutomerge = (field, handle, transactions, state) => {
|
|
114
|
+
const { lastHeads, path } = state.field(field);
|
|
115
|
+
let hasChanges = false;
|
|
116
|
+
for (const tr of transactions) {
|
|
117
|
+
tr.changes.iterChanges(() => {
|
|
118
|
+
hasChanges = true;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (!hasChanges) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
125
|
+
for (const tr of transactions) {
|
|
126
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
127
|
+
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return newHeads ?? void 0;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/update-codemirror.ts
|
|
135
|
+
import { ChangeSet } from "@codemirror/state";
|
|
136
|
+
var updateCodeMirror = (view, selection, target, patches) => {
|
|
137
|
+
for (const patch of patches) {
|
|
138
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
139
|
+
if (changeSpec != null) {
|
|
140
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
141
|
+
selection = selection.map(changeSet, 1);
|
|
142
|
+
view.dispatch({
|
|
143
|
+
changes: changeSet,
|
|
144
|
+
annotations: reconcileAnnotationType.of({})
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
view.dispatch({
|
|
149
|
+
selection,
|
|
150
|
+
annotations: reconcileAnnotationType.of({})
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
var handlePatch = (patch, target, state) => {
|
|
154
|
+
if (patch.action === "insert") {
|
|
155
|
+
return handleInsert(target, patch);
|
|
156
|
+
} else if (patch.action === "splice") {
|
|
157
|
+
return handleSplice(target, patch);
|
|
158
|
+
} else if (patch.action === "del") {
|
|
159
|
+
return handleDel(target, patch);
|
|
160
|
+
} else if (patch.action === "put") {
|
|
161
|
+
return handlePut(target, patch, state);
|
|
162
|
+
} else {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var handleInsert = (target, patch) => {
|
|
167
|
+
const index = charPath(target, patch.path);
|
|
168
|
+
if (index == null) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
172
|
+
return [
|
|
173
|
+
{
|
|
174
|
+
from: index,
|
|
175
|
+
to: index,
|
|
176
|
+
insert: text
|
|
177
|
+
}
|
|
178
|
+
];
|
|
179
|
+
};
|
|
180
|
+
var handleSplice = (target, patch) => {
|
|
181
|
+
const index = charPath(target, patch.path);
|
|
182
|
+
if (index == null) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
from: index,
|
|
188
|
+
insert: patch.value
|
|
189
|
+
}
|
|
190
|
+
];
|
|
191
|
+
};
|
|
192
|
+
var handleDel = (target, patch) => {
|
|
193
|
+
const index = charPath(target, patch.path);
|
|
194
|
+
if (index == null) {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
const length = patch.length || 1;
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
from: index,
|
|
201
|
+
to: index + length
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
};
|
|
205
|
+
var handlePut = (target, patch, state) => {
|
|
206
|
+
const index = charPath(target, [
|
|
207
|
+
...patch.path,
|
|
208
|
+
0
|
|
209
|
+
]);
|
|
210
|
+
if (index == null) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
const length = state.doc.length;
|
|
214
|
+
if (typeof patch.value !== "string") {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
return [
|
|
218
|
+
{
|
|
219
|
+
from: 0,
|
|
220
|
+
to: length,
|
|
221
|
+
insert: patch.value
|
|
222
|
+
}
|
|
223
|
+
];
|
|
224
|
+
};
|
|
225
|
+
var charPath = (textPath, candidatePath) => {
|
|
226
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
230
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
235
|
+
if (typeof index === "number") {
|
|
236
|
+
return index;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
242
|
+
var PatchSemaphore = class {
|
|
243
|
+
constructor(_handle, _field) {
|
|
244
|
+
this._handle = _handle;
|
|
245
|
+
this._field = _field;
|
|
246
|
+
this._inReconcile = false;
|
|
247
|
+
this.reconcile = (view) => {
|
|
248
|
+
if (this._inReconcile) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
this._inReconcile = true;
|
|
252
|
+
const path = getPath(view.state, this._field);
|
|
253
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
254
|
+
let selection = view.state.selection;
|
|
255
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
256
|
+
const toInvert = transactions.slice().reverse();
|
|
257
|
+
for (const tx of toInvert) {
|
|
258
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
259
|
+
selection = selection.map(inverted);
|
|
260
|
+
view.dispatch({
|
|
261
|
+
changes: inverted,
|
|
262
|
+
annotations: reconcileAnnotationType.of(true)
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
let newHeads = updateAutomerge(this._field, this._handle, transactions, view.state);
|
|
266
|
+
if (newHeads === null || newHeads === void 0) {
|
|
267
|
+
newHeads = automerge2.getHeads(this._handle.docSync());
|
|
268
|
+
}
|
|
269
|
+
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
|
|
270
|
+
updateCodeMirror(view, selection, path, diff);
|
|
271
|
+
view.dispatch({
|
|
272
|
+
effects: updateHeads(newHeads),
|
|
273
|
+
annotations: reconcileAnnotationType.of({})
|
|
274
|
+
});
|
|
275
|
+
this._inReconcile = false;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// packages/ui/react-ui-editor/src/extensions/cursor.ts
|
|
281
|
+
import { Facet as Facet2 } from "@codemirror/state";
|
|
282
|
+
var defaultCursorConverter = {
|
|
283
|
+
toCursor: (position) => position.toString(),
|
|
284
|
+
fromCursor: (cursor) => parseInt(cursor)
|
|
285
|
+
};
|
|
286
|
+
var Cursor = class {
|
|
287
|
+
static {
|
|
288
|
+
this.converter = Facet2.define({
|
|
289
|
+
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
static {
|
|
293
|
+
this.getCursorFromRange = (cursorConverter3, range) => {
|
|
294
|
+
const from = cursorConverter3.toCursor(range.from);
|
|
295
|
+
const to = cursorConverter3.toCursor(range.to, -1);
|
|
296
|
+
return [
|
|
297
|
+
from,
|
|
298
|
+
to
|
|
299
|
+
].join(":");
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
static {
|
|
303
|
+
this.getRangeFromCursor = (cursorConverter3, cursor) => {
|
|
304
|
+
const parts = cursor.split(":");
|
|
305
|
+
const from = cursorConverter3.fromCursor(parts[0]);
|
|
306
|
+
const to = cursorConverter3.fromCursor(parts[1]);
|
|
307
|
+
return from !== void 0 && to !== void 0 ? {
|
|
308
|
+
from,
|
|
309
|
+
to
|
|
310
|
+
} : void 0;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
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(handle, stateField);
|
|
347
|
+
const viewPlugin = ViewPlugin.fromClass(class AutomergeViewPlugin {
|
|
348
|
+
constructor(_view) {
|
|
349
|
+
this._view = _view;
|
|
350
|
+
this._handleChange = () => {
|
|
351
|
+
this._view.state.facet(semaphoreFacet).reconcile(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(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,31 +1016,35 @@ 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
|
-
|
|
1028
|
+
var styles2 = EditorView4.baseTheme({
|
|
1029
|
+
"& .cm-codeblock": {
|
|
1030
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1031
|
+
},
|
|
1032
|
+
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1033
|
+
background: getToken("extend.colors.neutral.50")
|
|
426
1034
|
},
|
|
427
|
-
|
|
428
|
-
background:
|
|
1035
|
+
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1036
|
+
background: getToken("extend.colors.neutral.850")
|
|
429
1037
|
},
|
|
430
|
-
|
|
431
|
-
|
|
1038
|
+
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1039
|
+
display: "block",
|
|
1040
|
+
paddingInline: "4px !important"
|
|
432
1041
|
},
|
|
433
|
-
|
|
1042
|
+
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
434
1043
|
paddingTop: "4px !important",
|
|
435
1044
|
borderTopLeftRadius: "4px",
|
|
436
1045
|
borderTopRightRadius: "4px"
|
|
437
1046
|
},
|
|
438
|
-
|
|
1047
|
+
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
439
1048
|
paddingBottom: "4px !important",
|
|
440
1049
|
borderBottomLeftRadius: "4px",
|
|
441
1050
|
borderBottomRightRadius: "4px"
|
|
@@ -445,361 +1054,451 @@ var getLineRange = (lines, from, to) => {
|
|
|
445
1054
|
const start = lines.findIndex((line) => line.from >= from);
|
|
446
1055
|
const end = lines.findIndex((line) => line.to >= to);
|
|
447
1056
|
return [
|
|
448
|
-
start,
|
|
449
|
-
end
|
|
450
|
-
];
|
|
451
|
-
};
|
|
452
|
-
var code2 = () => {
|
|
453
|
-
const
|
|
454
|
-
const decorations = [];
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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"
|
|
556
|
-
},
|
|
557
|
-
"& .cm-bookmark-selected": {
|
|
558
|
-
backgroundColor: "orange"
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
var BookmarkWidget = class extends WidgetType {
|
|
562
|
-
constructor(_pos, _id, _handleClick) {
|
|
563
|
-
super();
|
|
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);
|
|
618
|
-
return [
|
|
619
|
-
keymap4.of([
|
|
620
|
-
{
|
|
621
|
-
key: options?.key ?? "shift-meta-c",
|
|
622
|
-
run: (view) => {
|
|
623
|
-
const { from, to, head } = view.state.selection.main;
|
|
624
|
-
const id = options.onCreate?.(from, to);
|
|
625
|
-
if (id) {
|
|
626
|
-
const tag = `[^${id}]`;
|
|
627
|
-
view.dispatch({
|
|
628
|
-
changes: {
|
|
629
|
-
from: head,
|
|
630
|
-
insert: tag
|
|
631
|
-
},
|
|
632
|
-
selection: {
|
|
633
|
-
anchor: head + tag.length
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
return true;
|
|
637
|
-
}
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
1057
|
+
start,
|
|
1058
|
+
end
|
|
1059
|
+
];
|
|
1060
|
+
};
|
|
1061
|
+
var code2 = () => {
|
|
1062
|
+
const buildDecorations4 = (view) => {
|
|
1063
|
+
const decorations = [];
|
|
1064
|
+
const text = view.state.doc.sliceString(0);
|
|
1065
|
+
const matches = text.matchAll(CODE_REGEX);
|
|
1066
|
+
const blocks = view.viewportLineBlocks;
|
|
1067
|
+
for (const match of matches) {
|
|
1068
|
+
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1069
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1070
|
+
const block = blocks[i];
|
|
1071
|
+
decorations.push(Decoration2.line({
|
|
1072
|
+
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1073
|
+
}).range(block.from));
|
|
640
1074
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
1075
|
+
}
|
|
1076
|
+
return Decoration2.set(decorations);
|
|
1077
|
+
};
|
|
1078
|
+
return [
|
|
1079
|
+
ViewPlugin3.fromClass(class {
|
|
1080
|
+
constructor(view) {
|
|
1081
|
+
this.hasFocus = false;
|
|
1082
|
+
this.decorations = buildDecorations4(view);
|
|
648
1083
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
{
|
|
657
|
-
from: 0,
|
|
658
|
-
to: state.doc.length
|
|
659
|
-
}
|
|
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;
|
|
1084
|
+
update(update2) {
|
|
1085
|
+
if (
|
|
1086
|
+
// TODO(burdon): Generalize for other extensions.
|
|
1087
|
+
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1088
|
+
) {
|
|
1089
|
+
this.hasFocus = update2.view.hasFocus;
|
|
1090
|
+
this.decorations = buildDecorations4(update2.view);
|
|
671
1091
|
}
|
|
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
1092
|
}
|
|
1093
|
+
}, {
|
|
1094
|
+
decorations: (value) => value.decorations
|
|
683
1095
|
}),
|
|
684
1096
|
styles2
|
|
685
1097
|
];
|
|
686
1098
|
};
|
|
687
1099
|
|
|
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";
|
|
1100
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1101
|
+
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1102
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
691
1103
|
import sortBy from "lodash.sortby";
|
|
692
|
-
import {
|
|
1104
|
+
import { debounce } from "@dxos/async";
|
|
1105
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1106
|
+
import { nonNullable } from "@dxos/util";
|
|
1107
|
+
|
|
1108
|
+
// packages/ui/react-ui-editor/src/util.ts
|
|
1109
|
+
import { log } from "@dxos/log";
|
|
1110
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1111
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
1112
|
+
try {
|
|
1113
|
+
return fn(...args);
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
log.catch(error, void 0, {
|
|
1116
|
+
F: __dxlog_file2,
|
|
1117
|
+
L: 16,
|
|
1118
|
+
S: void 0,
|
|
1119
|
+
C: (f, a) => f(...a)
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1125
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
693
1126
|
var styles3 = EditorView5.baseTheme({
|
|
694
|
-
"& .cm-
|
|
695
|
-
backgroundColor: "yellow"
|
|
696
|
-
},
|
|
697
|
-
"& .cm-highlight::before": {
|
|
698
|
-
content: "]"
|
|
1127
|
+
"& .cm-comment": {
|
|
1128
|
+
backgroundColor: getToken("extend.colors.yellow.50")
|
|
699
1129
|
},
|
|
700
|
-
"& .cm-
|
|
701
|
-
backgroundColor: "
|
|
1130
|
+
"& .cm-comment-active": {
|
|
1131
|
+
backgroundColor: getToken("extend.colors.yellow.100")
|
|
702
1132
|
}
|
|
703
1133
|
});
|
|
704
1134
|
var marks = {
|
|
705
1135
|
highlight: Decoration3.mark({
|
|
706
|
-
class: "cm-
|
|
1136
|
+
class: "cm-comment"
|
|
707
1137
|
}),
|
|
708
|
-
|
|
709
|
-
class: "cm-
|
|
1138
|
+
highlightActive: Decoration3.mark({
|
|
1139
|
+
class: "cm-comment-active"
|
|
710
1140
|
})
|
|
711
1141
|
};
|
|
712
|
-
var
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1142
|
+
var setFocus = (view, thread) => {
|
|
1143
|
+
const range = view.state.field(commentsStateField).ranges.find((range2) => range2.id === thread);
|
|
1144
|
+
if (!range) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
view.dispatch({
|
|
1148
|
+
effects: setSelection.of({
|
|
1149
|
+
active: thread
|
|
1150
|
+
}),
|
|
1151
|
+
selection: {
|
|
1152
|
+
anchor: range.from
|
|
1153
|
+
},
|
|
1154
|
+
scrollIntoView: true
|
|
1155
|
+
});
|
|
722
1156
|
};
|
|
723
|
-
var
|
|
724
|
-
|
|
725
|
-
|
|
1157
|
+
var setCommentRange = StateEffect2.define();
|
|
1158
|
+
var setSelection = StateEffect2.define();
|
|
1159
|
+
var setCommentState = StateEffect2.define();
|
|
1160
|
+
var commentsStateField = StateField2.define({
|
|
1161
|
+
create: () => ({
|
|
1162
|
+
ranges: []
|
|
1163
|
+
}),
|
|
1164
|
+
update: (value, tr) => {
|
|
1165
|
+
const cursorConverter3 = tr.state.facet(Cursor.converter);
|
|
726
1166
|
for (const effect of tr.effects) {
|
|
727
|
-
if (effect.is(
|
|
1167
|
+
if (effect.is(setSelection)) {
|
|
1168
|
+
return {
|
|
1169
|
+
...value,
|
|
1170
|
+
...effect.value
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
if (effect.is(setCommentRange)) {
|
|
1174
|
+
const { comments: comments2 } = effect.value;
|
|
1175
|
+
const ranges = comments2.map((comment) => {
|
|
1176
|
+
const range = Cursor.getRangeFromCursor(cursorConverter3, comment.cursor);
|
|
1177
|
+
return range && {
|
|
1178
|
+
...comment,
|
|
1179
|
+
...range
|
|
1180
|
+
};
|
|
1181
|
+
}).filter(nonNullable);
|
|
1182
|
+
return {
|
|
1183
|
+
...value,
|
|
1184
|
+
ranges
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
if (effect.is(setCommentState)) {
|
|
728
1188
|
return effect.value;
|
|
729
1189
|
}
|
|
730
1190
|
}
|
|
731
|
-
return
|
|
1191
|
+
return value;
|
|
732
1192
|
}
|
|
733
1193
|
});
|
|
734
1194
|
var highlightDecorations = EditorView5.decorations.compute([
|
|
735
|
-
|
|
1195
|
+
commentsStateField
|
|
736
1196
|
], (state) => {
|
|
737
|
-
const
|
|
738
|
-
const decorations = sortBy(
|
|
1197
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1198
|
+
const decorations = sortBy(ranges ?? [], (range) => range.from)?.flatMap((selection) => {
|
|
1199
|
+
if (selection.from === selection.to) {
|
|
1200
|
+
return void 0;
|
|
1201
|
+
}
|
|
739
1202
|
const range = {
|
|
740
1203
|
from: selection.from,
|
|
741
|
-
to: selection.to
|
|
1204
|
+
to: selection.to
|
|
742
1205
|
};
|
|
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
|
-
}
|
|
1206
|
+
if (selection.id === active) {
|
|
1207
|
+
return marks.highlightActive.range(range.from, range.to);
|
|
749
1208
|
} else {
|
|
750
|
-
return
|
|
1209
|
+
return marks.highlight.range(range.from, range.to);
|
|
751
1210
|
}
|
|
752
|
-
}) ?? [];
|
|
1211
|
+
}).filter(nonNullable) ?? [];
|
|
753
1212
|
return Decoration3.set(decorations);
|
|
754
1213
|
});
|
|
755
|
-
var
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1214
|
+
var trackPastedComments = (onUpdate) => {
|
|
1215
|
+
let tracked = null;
|
|
1216
|
+
const handleTrack = (event, view) => {
|
|
1217
|
+
const comments2 = view.state.field(commentsStateField);
|
|
1218
|
+
const { main } = view.state.selection;
|
|
1219
|
+
const selectedRanges = comments2.ranges.filter((range) => range.from >= main.from && range.to <= main.to && range.from < range.to);
|
|
1220
|
+
if (!selectedRanges.length) {
|
|
1221
|
+
tracked = null;
|
|
1222
|
+
} else {
|
|
1223
|
+
tracked = {
|
|
1224
|
+
text: view.state.doc.slice(main.from, main.to),
|
|
1225
|
+
comments: selectedRanges.map((range) => ({
|
|
1226
|
+
id: range.id,
|
|
1227
|
+
from: range.from - main.from,
|
|
1228
|
+
to: range.to - main.from
|
|
1229
|
+
}))
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
return [
|
|
1234
|
+
EditorView5.domEventHandlers({
|
|
1235
|
+
cut: handleTrack,
|
|
1236
|
+
copy: handleTrack
|
|
1237
|
+
}),
|
|
1238
|
+
// Handle paste.
|
|
1239
|
+
EditorView5.updateListener.of(({ view, state, transactions }) => {
|
|
1240
|
+
if (tracked) {
|
|
1241
|
+
const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
|
|
1242
|
+
if (paste) {
|
|
1243
|
+
let found = -1;
|
|
1244
|
+
paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
1245
|
+
if (text.eq(tracked.text)) {
|
|
1246
|
+
for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
|
|
1247
|
+
fromB = transactions[i].changes.mapPos(fromB);
|
|
1248
|
+
}
|
|
1249
|
+
found = fromB;
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
if (found > -1) {
|
|
1253
|
+
const comments2 = tracked.comments;
|
|
1254
|
+
view.state.facet(semaphoreFacet).reconcile(view);
|
|
1255
|
+
const active = state.field(commentsStateField).ranges;
|
|
1256
|
+
for (const moved of comments2) {
|
|
1257
|
+
if (active.some((range) => range.id === moved.id && range.from === range.to)) {
|
|
1258
|
+
const range = {
|
|
1259
|
+
from: found + moved.from,
|
|
1260
|
+
to: found + moved.to
|
|
1261
|
+
};
|
|
1262
|
+
const cursor = Cursor.getCursorFromRange(state.facet(Cursor.converter), range);
|
|
1263
|
+
onUpdate(moved.id, cursor);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
tracked = null;
|
|
1268
|
+
}
|
|
771
1269
|
}
|
|
1270
|
+
})
|
|
1271
|
+
];
|
|
1272
|
+
};
|
|
1273
|
+
var comments = (options = {}) => {
|
|
1274
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1275
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1276
|
+
const createCommentThread = (view) => {
|
|
1277
|
+
const cursorConverter3 = view.state.facet(Cursor.converter);
|
|
1278
|
+
invariant2(options.onCreate, void 0, {
|
|
1279
|
+
F: __dxlog_file3,
|
|
1280
|
+
L: 264,
|
|
1281
|
+
S: void 0,
|
|
1282
|
+
A: [
|
|
1283
|
+
"options.onCreate",
|
|
1284
|
+
""
|
|
1285
|
+
]
|
|
772
1286
|
});
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
});
|
|
777
|
-
options?.onChange?.(newRanges.find((range) => range.active)?.id);
|
|
1287
|
+
const { from, to } = view.state.selection.main;
|
|
1288
|
+
if (from === to) {
|
|
1289
|
+
return false;
|
|
778
1290
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1291
|
+
const cursor = Cursor.getCursorFromRange(cursorConverter3, {
|
|
1292
|
+
from,
|
|
1293
|
+
to
|
|
1294
|
+
});
|
|
1295
|
+
if (cursor) {
|
|
1296
|
+
const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
|
|
1297
|
+
if (id) {
|
|
1298
|
+
view.dispatch({
|
|
1299
|
+
effects: setSelection.of({
|
|
1300
|
+
active: id
|
|
1301
|
+
}),
|
|
1302
|
+
selection: {
|
|
1303
|
+
anchor: from
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return false;
|
|
1310
|
+
};
|
|
782
1311
|
return [
|
|
783
|
-
|
|
1312
|
+
commentsStateField,
|
|
784
1313
|
highlightDecorations,
|
|
785
|
-
|
|
786
|
-
|
|
1314
|
+
styles3,
|
|
1315
|
+
//
|
|
1316
|
+
// Keymap.
|
|
1317
|
+
//
|
|
1318
|
+
options.onCreate ? keymap3.of([
|
|
1319
|
+
{
|
|
1320
|
+
key: shortcut,
|
|
1321
|
+
run: callbackWrapper(createCommentThread)
|
|
1322
|
+
}
|
|
1323
|
+
]) : [],
|
|
1324
|
+
//
|
|
1325
|
+
// Hover tooltip (for key shortcut hints, etc.)
|
|
1326
|
+
// TODO(burdon): Factor out to generic hints extension for current selection/line.
|
|
1327
|
+
//
|
|
1328
|
+
options.onHover ? hoverTooltip((view, pos) => {
|
|
1329
|
+
const selection = view.state.selection.main;
|
|
1330
|
+
if (selection && pos >= selection.from && pos <= selection.to) {
|
|
1331
|
+
return {
|
|
1332
|
+
pos: selection.from,
|
|
1333
|
+
end: selection.to,
|
|
1334
|
+
above: true,
|
|
1335
|
+
create: () => {
|
|
1336
|
+
const el = document.createElement("div");
|
|
1337
|
+
options.onHover(el, shortcut);
|
|
1338
|
+
return {
|
|
1339
|
+
dom: el,
|
|
1340
|
+
offset: {
|
|
1341
|
+
x: 0,
|
|
1342
|
+
y: 8
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
return null;
|
|
1349
|
+
}, {
|
|
1350
|
+
// TODO(burdon): Hide on change triggered immediately?
|
|
1351
|
+
// hideOnChange: true,
|
|
1352
|
+
hoverTime: 1e3
|
|
1353
|
+
}) : [],
|
|
1354
|
+
//
|
|
1355
|
+
// Monitor cursor movement and text updates.
|
|
1356
|
+
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1357
|
+
//
|
|
1358
|
+
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1359
|
+
const cursorConverter3 = view.state.facet(Cursor.converter);
|
|
1360
|
+
{
|
|
1361
|
+
let mod = false;
|
|
1362
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1363
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1364
|
+
ranges.forEach((range) => {
|
|
1365
|
+
if (from2 === to2) {
|
|
1366
|
+
const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
|
|
1367
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1368
|
+
options.onDelete?.(range.id);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (from <= range.to) {
|
|
1372
|
+
const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
|
|
1373
|
+
Object.assign(range, newRange);
|
|
1374
|
+
mod = true;
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
});
|
|
1378
|
+
if (mod) {
|
|
1379
|
+
view.dispatch({
|
|
1380
|
+
effects: setCommentState.of({
|
|
1381
|
+
active,
|
|
1382
|
+
ranges
|
|
1383
|
+
})
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
{
|
|
1388
|
+
const { head } = state.selection.main;
|
|
1389
|
+
let min = Infinity;
|
|
1390
|
+
const { active, closest, ranges } = state.field(commentsStateField);
|
|
1391
|
+
const selected = {
|
|
1392
|
+
active: void 0,
|
|
1393
|
+
closest: void 0
|
|
1394
|
+
};
|
|
1395
|
+
ranges.forEach((comment) => {
|
|
1396
|
+
const d = Math.min(Math.abs(head - comment.from), Math.abs(head - comment.to));
|
|
1397
|
+
if (head >= comment.from && head <= comment.to) {
|
|
1398
|
+
selected.active = comment.id;
|
|
1399
|
+
}
|
|
1400
|
+
if (d < min) {
|
|
1401
|
+
selected.closest = comment.id;
|
|
1402
|
+
min = d;
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
if (selected.active !== active || selected.closest !== closest) {
|
|
1406
|
+
view.dispatch({
|
|
1407
|
+
effects: setSelection.of(selected)
|
|
1408
|
+
});
|
|
1409
|
+
handleSelect({
|
|
1410
|
+
...selected,
|
|
1411
|
+
ranges: ranges.map((range) => ({
|
|
1412
|
+
...range,
|
|
1413
|
+
location: view.coordsAtPos(range.from)
|
|
1414
|
+
}))
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}),
|
|
1419
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : []
|
|
787
1420
|
];
|
|
788
1421
|
};
|
|
789
1422
|
|
|
790
|
-
// packages/ui/react-ui-editor/src/
|
|
1423
|
+
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1424
|
+
import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1425
|
+
var styles4 = EditorView6.baseTheme({
|
|
1426
|
+
"& .cm-hr": {
|
|
1427
|
+
// TODO(burdon): ???
|
|
1428
|
+
// Note that block-level decorations should not have vertical margins,
|
|
1429
|
+
borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1433
|
+
constructor(_pos) {
|
|
1434
|
+
super();
|
|
1435
|
+
this._pos = _pos;
|
|
1436
|
+
}
|
|
1437
|
+
eq(other) {
|
|
1438
|
+
return this._pos === other._pos;
|
|
1439
|
+
}
|
|
1440
|
+
toDOM(view) {
|
|
1441
|
+
const el = document.createElement("div");
|
|
1442
|
+
el.className = "cm-hr";
|
|
1443
|
+
return el;
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
var placeholderMatcher = new MatchDecorator({
|
|
1447
|
+
// regexp: /(?<=\n\n)---/gs,
|
|
1448
|
+
regexp: /^---$/gs,
|
|
1449
|
+
decoration: (match, view, pos) => Decoration4.replace({
|
|
1450
|
+
widget: new HorizontalRuleWidget(pos)
|
|
1451
|
+
})
|
|
1452
|
+
});
|
|
1453
|
+
var hr = () => [
|
|
1454
|
+
styles4,
|
|
1455
|
+
ViewPlugin4.fromClass(class {
|
|
1456
|
+
constructor(view) {
|
|
1457
|
+
this.rules = placeholderMatcher.createDeco(view);
|
|
1458
|
+
}
|
|
1459
|
+
update(update2) {
|
|
1460
|
+
this.rules = placeholderMatcher.updateDeco(update2, this.rules);
|
|
1461
|
+
}
|
|
1462
|
+
}, {
|
|
1463
|
+
decorations: (instance) => instance.rules
|
|
1464
|
+
})
|
|
1465
|
+
];
|
|
1466
|
+
|
|
1467
|
+
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
791
1468
|
import { syntaxTree } from "@codemirror/language";
|
|
792
|
-
import {
|
|
793
|
-
import { Decoration as
|
|
1469
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
1470
|
+
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
794
1471
|
var image = (options = {}) => {
|
|
795
|
-
return
|
|
796
|
-
create: (state) =>
|
|
797
|
-
|
|
798
|
-
|
|
1472
|
+
return StateField3.define({
|
|
1473
|
+
create: (state) => {
|
|
1474
|
+
return Decoration5.set(buildDecorations(0, state.doc.length, state));
|
|
1475
|
+
},
|
|
1476
|
+
update: (value, tr) => {
|
|
1477
|
+
if (!tr.docChanged && !tr.selection) {
|
|
1478
|
+
return value;
|
|
1479
|
+
}
|
|
1480
|
+
const cursor = tr.state.selection.main.head;
|
|
1481
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1482
|
+
let from = Math.min(cursor, oldCursor);
|
|
1483
|
+
let to = Math.max(cursor, oldCursor);
|
|
1484
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1485
|
+
from = Math.min(from, fromB);
|
|
1486
|
+
to = Math.max(to, toB);
|
|
1487
|
+
});
|
|
1488
|
+
from = tr.state.doc.lineAt(from).from;
|
|
1489
|
+
to = tr.state.doc.lineAt(to).to;
|
|
1490
|
+
return value.map(tr.changes).update({
|
|
1491
|
+
filterFrom: from,
|
|
1492
|
+
filterTo: to,
|
|
1493
|
+
filter: () => false,
|
|
1494
|
+
add: buildDecorations(from, to, tr.state)
|
|
1495
|
+
});
|
|
1496
|
+
},
|
|
1497
|
+
provide: (field) => EditorView7.decorations.from(field)
|
|
799
1498
|
});
|
|
800
1499
|
};
|
|
801
|
-
var
|
|
802
|
-
const
|
|
1500
|
+
var buildDecorations = (from, to, state) => {
|
|
1501
|
+
const decorations = [];
|
|
803
1502
|
const cursor = state.selection.main.head;
|
|
804
1503
|
syntaxTree(state).iterate({
|
|
805
1504
|
enter: (node) => {
|
|
@@ -808,51 +1507,101 @@ var update = (state, options) => {
|
|
|
808
1507
|
if (urlNode) {
|
|
809
1508
|
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
810
1509
|
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
811
|
-
|
|
1510
|
+
decorations.push(Decoration5.replace({
|
|
1511
|
+
block: true,
|
|
812
1512
|
widget: new ImageWidget(url)
|
|
813
|
-
}));
|
|
1513
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
814
1514
|
}
|
|
815
1515
|
}
|
|
816
|
-
}
|
|
1516
|
+
},
|
|
1517
|
+
from,
|
|
1518
|
+
to
|
|
817
1519
|
});
|
|
818
|
-
return
|
|
1520
|
+
return decorations;
|
|
819
1521
|
};
|
|
820
|
-
var ImageWidget = class extends
|
|
1522
|
+
var ImageWidget = class extends WidgetType3 {
|
|
821
1523
|
constructor(_url) {
|
|
822
1524
|
super();
|
|
823
1525
|
this._url = _url;
|
|
824
1526
|
}
|
|
825
1527
|
eq(other) {
|
|
826
|
-
return this._url === other
|
|
1528
|
+
return this._url === other._url;
|
|
827
1529
|
}
|
|
828
1530
|
toDOM(view) {
|
|
829
1531
|
const img = document.createElement("img");
|
|
830
1532
|
img.setAttribute("src", this._url);
|
|
831
1533
|
img.setAttribute("class", "cm-image");
|
|
1534
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
832
1535
|
return img;
|
|
833
1536
|
}
|
|
834
1537
|
};
|
|
835
1538
|
|
|
836
|
-
// packages/ui/react-ui-editor/src/
|
|
1539
|
+
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
837
1540
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
838
|
-
import { RangeSetBuilder
|
|
839
|
-
import { Decoration as
|
|
1541
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1542
|
+
import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin5, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1543
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
840
1544
|
var link = (options = {}) => {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1545
|
+
const extensions = [
|
|
1546
|
+
ViewPlugin5.fromClass(class {
|
|
1547
|
+
constructor(view) {
|
|
1548
|
+
this.decorations = buildDecorations2(view, options);
|
|
1549
|
+
}
|
|
1550
|
+
update(update2) {
|
|
1551
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1552
|
+
this.decorations = buildDecorations2(update2.view, options);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}, {
|
|
1556
|
+
decorations: (v) => v.decorations
|
|
1557
|
+
})
|
|
1558
|
+
];
|
|
1559
|
+
if (options.onHover) {
|
|
1560
|
+
extensions.push(
|
|
1561
|
+
// https://codemirror.net/examples/tooltip
|
|
1562
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1563
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1564
|
+
hoverTooltip2((view, pos, side) => {
|
|
1565
|
+
const syntax = syntaxTree2(view.state).resolveInner(pos, side);
|
|
1566
|
+
let link2 = null;
|
|
1567
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1568
|
+
link2 = node.name === "Link" ? node : null;
|
|
1569
|
+
}
|
|
1570
|
+
const url = link2 && link2.getChild("URL");
|
|
1571
|
+
if (!url || !link2) {
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1575
|
+
return {
|
|
1576
|
+
pos: link2.from,
|
|
1577
|
+
end: link2.to,
|
|
1578
|
+
above: true,
|
|
1579
|
+
create: () => {
|
|
1580
|
+
const el = document.createElement("div");
|
|
1581
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1582
|
+
options.onHover(el, urlText);
|
|
1583
|
+
return {
|
|
1584
|
+
dom: el,
|
|
1585
|
+
offset: {
|
|
1586
|
+
x: 0,
|
|
1587
|
+
y: 4
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
};
|
|
1592
|
+
})
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
return extensions;
|
|
846
1596
|
};
|
|
847
|
-
var LinkButton = class extends
|
|
848
|
-
constructor(
|
|
1597
|
+
var LinkButton = class extends WidgetType4 {
|
|
1598
|
+
constructor(_url, _onAttach) {
|
|
849
1599
|
super();
|
|
850
|
-
this._pos = _pos;
|
|
851
1600
|
this._url = _url;
|
|
852
1601
|
this._onAttach = _onAttach;
|
|
853
1602
|
}
|
|
854
1603
|
eq(other) {
|
|
855
|
-
return this.
|
|
1604
|
+
return this._url === other._url;
|
|
856
1605
|
}
|
|
857
1606
|
toDOM(view) {
|
|
858
1607
|
const el = document.createElement("span");
|
|
@@ -860,15 +1609,14 @@ var LinkButton = class extends WidgetType3 {
|
|
|
860
1609
|
return el;
|
|
861
1610
|
}
|
|
862
1611
|
};
|
|
863
|
-
var LinkText = class extends
|
|
864
|
-
constructor(
|
|
1612
|
+
var LinkText = class extends WidgetType4 {
|
|
1613
|
+
constructor(_text, _url) {
|
|
865
1614
|
super();
|
|
866
|
-
this._pos = _pos;
|
|
867
1615
|
this._text = _text;
|
|
868
1616
|
this._url = _url;
|
|
869
1617
|
}
|
|
870
1618
|
eq(other) {
|
|
871
|
-
return this.
|
|
1619
|
+
return this._url === other._url;
|
|
872
1620
|
}
|
|
873
1621
|
toDOM(view) {
|
|
874
1622
|
const link2 = document.createElement("a");
|
|
@@ -882,7 +1630,7 @@ var LinkText = class extends WidgetType3 {
|
|
|
882
1630
|
link2.onclick = () => {
|
|
883
1631
|
view.dispatch({
|
|
884
1632
|
selection: {
|
|
885
|
-
anchor:
|
|
1633
|
+
anchor: view.posAtDOM(link2)
|
|
886
1634
|
}
|
|
887
1635
|
});
|
|
888
1636
|
};
|
|
@@ -890,75 +1638,77 @@ var LinkText = class extends WidgetType3 {
|
|
|
890
1638
|
return link2;
|
|
891
1639
|
}
|
|
892
1640
|
};
|
|
893
|
-
var
|
|
894
|
-
const builder = new
|
|
1641
|
+
var buildDecorations2 = (view, options) => {
|
|
1642
|
+
const builder = new RangeSetBuilder();
|
|
1643
|
+
const { state } = view;
|
|
895
1644
|
const cursor = state.selection.main.head;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1645
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1646
|
+
syntaxTree2(state).iterate({
|
|
1647
|
+
enter: (node) => {
|
|
1648
|
+
if (node.name === "Link") {
|
|
1649
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1650
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1651
|
+
const urlNode = node.node.getChild("URL");
|
|
1652
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1653
|
+
if (!url) {
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1657
|
+
builder.add(node.from, node.to, Decoration6.replace({
|
|
1658
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
1659
|
+
}));
|
|
1660
|
+
}
|
|
1661
|
+
if (options.onRender) {
|
|
1662
|
+
builder.add(node.to, node.to, Decoration6.replace({
|
|
1663
|
+
widget: new LinkButton(url, options.onRender)
|
|
1664
|
+
}));
|
|
1665
|
+
}
|
|
904
1666
|
return false;
|
|
905
1667
|
}
|
|
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
|
-
});
|
|
1668
|
+
},
|
|
1669
|
+
from,
|
|
1670
|
+
to
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
920
1673
|
return builder.finish();
|
|
921
1674
|
};
|
|
922
1675
|
|
|
923
|
-
// packages/ui/react-ui-editor/src/
|
|
924
|
-
import {
|
|
925
|
-
var
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1676
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1677
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1678
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1679
|
+
const extensions = [];
|
|
1680
|
+
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1681
|
+
onFocus(focused);
|
|
1682
|
+
return null;
|
|
1683
|
+
}));
|
|
1684
|
+
onChange && extensions.push(EditorView8.updateListener.of((update2) => {
|
|
1685
|
+
onChange(update2.state.doc.toString());
|
|
1686
|
+
}));
|
|
1687
|
+
return extensions;
|
|
935
1688
|
};
|
|
936
1689
|
|
|
937
|
-
// packages/ui/react-ui-editor/src/
|
|
1690
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
938
1691
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
939
1692
|
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
940
|
-
import { markdownLanguage as
|
|
1693
|
+
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
941
1694
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
942
1695
|
import { languages } from "@codemirror/language-data";
|
|
943
1696
|
import { searchKeymap } from "@codemirror/search";
|
|
944
1697
|
import { EditorState } from "@codemirror/state";
|
|
945
1698
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
946
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1699
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
947
1700
|
|
|
948
|
-
// packages/ui/react-ui-editor/src/
|
|
949
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1701
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1702
|
+
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
950
1703
|
import { HighlightStyle } from "@codemirror/language";
|
|
951
1704
|
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
952
1705
|
import { Table } from "@lezer/markdown";
|
|
953
|
-
import get2 from "lodash.get";
|
|
954
|
-
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
955
1706
|
var markdownTags = {
|
|
956
1707
|
Blockquote: Tag.define(),
|
|
957
1708
|
CodeMark: Tag.define(),
|
|
958
1709
|
CodeText: Tag.define(),
|
|
959
1710
|
EmphasisMark: Tag.define(),
|
|
960
1711
|
HeaderMark: Tag.define(),
|
|
961
|
-
HorizontalRule: Tag.define(),
|
|
962
1712
|
InlineCode: Tag.define(),
|
|
963
1713
|
LinkLabel: Tag.define(),
|
|
964
1714
|
LinkReference: Tag.define(),
|
|
@@ -1122,14 +1872,6 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1122
1872
|
],
|
|
1123
1873
|
class: blockquote
|
|
1124
1874
|
},
|
|
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
1875
|
{
|
|
1134
1876
|
tag: [
|
|
1135
1877
|
markdownTags.TableCell
|
|
@@ -1137,19 +1879,19 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1137
1879
|
class: "font-mono"
|
|
1138
1880
|
}
|
|
1139
1881
|
], {
|
|
1140
|
-
scope:
|
|
1882
|
+
scope: markdownLanguage2,
|
|
1141
1883
|
all: {
|
|
1142
|
-
fontFamily:
|
|
1884
|
+
fontFamily: getToken("fontFamily.body", []).join(",")
|
|
1143
1885
|
}
|
|
1144
1886
|
});
|
|
1145
1887
|
};
|
|
1146
1888
|
|
|
1147
|
-
// packages/ui/react-ui-editor/src/
|
|
1889
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1148
1890
|
var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
1149
1891
|
return [
|
|
1150
1892
|
EditorState.allowMultipleSelections.of(true),
|
|
1151
1893
|
EditorState.tabSize.of(2),
|
|
1152
|
-
|
|
1894
|
+
EditorView9.lineWrapping,
|
|
1153
1895
|
customKeymap(),
|
|
1154
1896
|
bracketMatching2(),
|
|
1155
1897
|
closeBrackets2(),
|
|
@@ -1169,7 +1911,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1169
1911
|
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1170
1912
|
// https://github.github.com/gfm
|
|
1171
1913
|
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1172
|
-
base:
|
|
1914
|
+
base: markdownLanguage3,
|
|
1173
1915
|
// Languages for syntax highlighting fenced code blocks.
|
|
1174
1916
|
codeLanguages: languages,
|
|
1175
1917
|
// Parser extensions.
|
|
@@ -1184,7 +1926,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1184
1926
|
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1185
1927
|
].filter(Boolean);
|
|
1186
1928
|
};
|
|
1187
|
-
var customKeymap = () =>
|
|
1929
|
+
var customKeymap = () => keymap4.of([
|
|
1188
1930
|
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1189
1931
|
indentWithTab,
|
|
1190
1932
|
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
@@ -1197,7 +1939,7 @@ var customKeymap = () => keymap5.of([
|
|
|
1197
1939
|
...searchKeymap
|
|
1198
1940
|
]);
|
|
1199
1941
|
|
|
1200
|
-
// packages/ui/react-ui-editor/src/
|
|
1942
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1201
1943
|
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1202
1944
|
var mention = ({ onSearch }) => {
|
|
1203
1945
|
return autocompletion2({
|
|
@@ -1220,19 +1962,24 @@ var mention = ({ onSearch }) => {
|
|
|
1220
1962
|
});
|
|
1221
1963
|
};
|
|
1222
1964
|
|
|
1223
|
-
// packages/ui/react-ui-editor/src/
|
|
1965
|
+
// packages/ui/react-ui-editor/src/extensions/outliner.ts
|
|
1966
|
+
var outliner = (options = {}) => {
|
|
1967
|
+
return [];
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1224
1971
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1225
|
-
import { RangeSetBuilder as
|
|
1226
|
-
import { Decoration as
|
|
1972
|
+
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
|
|
1973
|
+
import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1227
1974
|
var table = (options = {}) => {
|
|
1228
|
-
return
|
|
1229
|
-
create: (state) =>
|
|
1230
|
-
update: (_, tr) =>
|
|
1231
|
-
provide: (field) =>
|
|
1975
|
+
return StateField4.define({
|
|
1976
|
+
create: (state) => update(state, options),
|
|
1977
|
+
update: (_, tr) => update(tr.state, options),
|
|
1978
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
1232
1979
|
});
|
|
1233
1980
|
};
|
|
1234
|
-
var
|
|
1235
|
-
const builder = new
|
|
1981
|
+
var update = (state, options) => {
|
|
1982
|
+
const builder = new RangeSetBuilder2();
|
|
1236
1983
|
const cursor = state.selection.main.head;
|
|
1237
1984
|
const tables = [];
|
|
1238
1985
|
const getTable = () => tables[tables.length - 1];
|
|
@@ -1272,22 +2019,22 @@ var update3 = (state, options) => {
|
|
|
1272
2019
|
});
|
|
1273
2020
|
tables.forEach((table2) => {
|
|
1274
2021
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1275
|
-
hide && builder.add(table2.from, table2.to,
|
|
2022
|
+
hide && builder.add(table2.from, table2.to, Decoration7.replace({
|
|
1276
2023
|
widget: new TableWidget(table2)
|
|
1277
2024
|
}));
|
|
1278
|
-
builder.add(table2.from, table2.to,
|
|
2025
|
+
builder.add(table2.from, table2.to, Decoration7.mark({
|
|
1279
2026
|
class: "cm-table"
|
|
1280
2027
|
}));
|
|
1281
2028
|
});
|
|
1282
2029
|
return builder.finish();
|
|
1283
2030
|
};
|
|
1284
|
-
var TableWidget = class extends
|
|
2031
|
+
var TableWidget = class extends WidgetType5 {
|
|
1285
2032
|
constructor(_table) {
|
|
1286
2033
|
super();
|
|
1287
2034
|
this._table = _table;
|
|
1288
2035
|
}
|
|
1289
2036
|
eq(other) {
|
|
1290
|
-
return this._table.
|
|
2037
|
+
return this._table.header?.join() === other._table.header?.join() && this._table.rows?.join() === other._table.rows?.join();
|
|
1291
2038
|
}
|
|
1292
2039
|
toDOM(view) {
|
|
1293
2040
|
const table2 = document.createElement("table");
|
|
@@ -1313,146 +2060,194 @@ var TableWidget = class extends WidgetType4 {
|
|
|
1313
2060
|
}
|
|
1314
2061
|
return table2;
|
|
1315
2062
|
}
|
|
2063
|
+
ignoreEvent(e) {
|
|
2064
|
+
return !/^mouse/.test(e.type);
|
|
2065
|
+
}
|
|
1316
2066
|
};
|
|
1317
2067
|
|
|
1318
|
-
// packages/ui/react-ui-editor/src/
|
|
1319
|
-
import {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
var CheckboxWidget = class extends WidgetType5 {
|
|
1327
|
-
constructor(_pos, _checked, _indent, _onCheck) {
|
|
2068
|
+
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
2069
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2070
|
+
import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
|
|
2071
|
+
import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
|
|
2072
|
+
var CheckboxWidget = class extends WidgetType6 {
|
|
2073
|
+
constructor(_checked) {
|
|
1328
2074
|
super();
|
|
1329
|
-
this._pos = _pos;
|
|
1330
2075
|
this._checked = _checked;
|
|
1331
|
-
this._indent = _indent;
|
|
1332
|
-
this._onCheck = _onCheck;
|
|
1333
2076
|
}
|
|
1334
2077
|
eq(other) {
|
|
1335
|
-
return this.
|
|
2078
|
+
return this._checked === other._checked;
|
|
1336
2079
|
}
|
|
1337
2080
|
toDOM(view) {
|
|
1338
|
-
const
|
|
1339
|
-
wrap.className = "cm-task-item";
|
|
1340
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
1341
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
1342
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
2081
|
+
const input = document.createElement("input");
|
|
1343
2082
|
input.type = "checkbox";
|
|
1344
2083
|
input.checked = this._checked;
|
|
1345
|
-
if (
|
|
2084
|
+
if (view.state.readOnly) {
|
|
1346
2085
|
input.setAttribute("disabled", "true");
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
2086
|
+
} else {
|
|
2087
|
+
input.onmousedown = (event) => {
|
|
2088
|
+
const pos = view.posAtDOM(input);
|
|
2089
|
+
const text = view.state.sliceDoc(pos, pos + 3);
|
|
2090
|
+
if (text === (this._checked ? "[x]" : "[ ]")) {
|
|
2091
|
+
view.dispatch({
|
|
2092
|
+
changes: {
|
|
2093
|
+
from: pos + 1,
|
|
2094
|
+
to: pos + 2,
|
|
2095
|
+
insert: this._checked ? " " : "x"
|
|
2096
|
+
}
|
|
2097
|
+
});
|
|
2098
|
+
event.preventDefault();
|
|
2099
|
+
}
|
|
1352
2100
|
};
|
|
1353
2101
|
}
|
|
1354
|
-
return
|
|
2102
|
+
return input;
|
|
1355
2103
|
}
|
|
1356
2104
|
ignoreEvent() {
|
|
1357
2105
|
return false;
|
|
1358
2106
|
}
|
|
1359
2107
|
};
|
|
2108
|
+
var checkedDeco = Decoration8.replace({
|
|
2109
|
+
widget: new CheckboxWidget(true)
|
|
2110
|
+
});
|
|
2111
|
+
var uncheckedDeco = Decoration8.replace({
|
|
2112
|
+
widget: new CheckboxWidget(false)
|
|
2113
|
+
});
|
|
1360
2114
|
var tasklist = (options = {}) => {
|
|
1361
|
-
|
|
1362
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
1363
|
-
decoration: (match, view, pos) => {
|
|
1364
|
-
const indent = Math.floor(match[1].length / 2);
|
|
1365
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
1366
|
-
return Decoration7.replace({
|
|
1367
|
-
widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
1368
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
1369
|
-
view.dispatch({
|
|
1370
|
-
changes: {
|
|
1371
|
-
from: idx,
|
|
1372
|
-
to: idx + 1,
|
|
1373
|
-
insert: checked2 ? "x" : " "
|
|
1374
|
-
},
|
|
1375
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
1376
|
-
selection: {
|
|
1377
|
-
anchor: idx + 3
|
|
1378
|
-
}
|
|
1379
|
-
});
|
|
1380
|
-
})
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
});
|
|
1384
|
-
const tasks = ViewPlugin3.fromClass(class {
|
|
2115
|
+
return ViewPlugin6.fromClass(class {
|
|
1385
2116
|
constructor(view) {
|
|
1386
|
-
this.
|
|
2117
|
+
this.decorations = buildDecorations3(view);
|
|
1387
2118
|
}
|
|
1388
|
-
update(
|
|
1389
|
-
|
|
2119
|
+
update(update2) {
|
|
2120
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2121
|
+
this.decorations = buildDecorations3(update2.view);
|
|
2122
|
+
}
|
|
1390
2123
|
}
|
|
1391
2124
|
}, {
|
|
1392
|
-
decorations: (
|
|
1393
|
-
|
|
1394
|
-
|
|
2125
|
+
decorations: (v) => v.decorations,
|
|
2126
|
+
// TODO(burdon): Is this still required?
|
|
2127
|
+
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2128
|
+
return view.plugin(plugin)?.decorations || Decoration8.none;
|
|
1395
2129
|
})
|
|
1396
2130
|
});
|
|
2131
|
+
};
|
|
2132
|
+
var buildDecorations3 = (view) => {
|
|
2133
|
+
const builder = new RangeSetBuilder3();
|
|
2134
|
+
const { state } = view;
|
|
2135
|
+
const cursor = state.selection.main.head;
|
|
2136
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2137
|
+
syntaxTree4(state).iterate({
|
|
2138
|
+
enter: (node) => {
|
|
2139
|
+
if (node.name === "TaskMarker" && (cursor < node.from || cursor > node.to)) {
|
|
2140
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2141
|
+
builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
|
|
2142
|
+
}
|
|
2143
|
+
},
|
|
2144
|
+
from,
|
|
2145
|
+
to
|
|
2146
|
+
});
|
|
2147
|
+
}
|
|
2148
|
+
return builder.finish();
|
|
2149
|
+
};
|
|
2150
|
+
|
|
2151
|
+
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2152
|
+
import { keymap as keymap5 } from "@codemirror/view";
|
|
2153
|
+
var defaultItems = [
|
|
2154
|
+
"hello world!",
|
|
2155
|
+
"this is a test.",
|
|
2156
|
+
"this is [DXOS](https://dxos.org)"
|
|
2157
|
+
];
|
|
2158
|
+
var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
2159
|
+
let t;
|
|
2160
|
+
let idx = 0;
|
|
1397
2161
|
return [
|
|
1398
|
-
|
|
1399
|
-
|
|
2162
|
+
keymap5.of([
|
|
2163
|
+
{
|
|
2164
|
+
// Reset.
|
|
2165
|
+
key: "alt-meta-'",
|
|
2166
|
+
run: (view) => {
|
|
2167
|
+
clearTimeout(t);
|
|
2168
|
+
idx = 0;
|
|
2169
|
+
return true;
|
|
2170
|
+
}
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
// Next prompt.
|
|
2174
|
+
// TODO(burdon): Press 1-9 to select prompt?
|
|
2175
|
+
key: "shift-meta-'",
|
|
2176
|
+
run: (view) => {
|
|
2177
|
+
clearTimeout(t);
|
|
2178
|
+
const text = items[idx++];
|
|
2179
|
+
if (idx === items?.length) {
|
|
2180
|
+
idx = 0;
|
|
2181
|
+
}
|
|
2182
|
+
let i = 0;
|
|
2183
|
+
const insert = (d = 0) => {
|
|
2184
|
+
t = setTimeout(() => {
|
|
2185
|
+
const pos = view.state.selection.main.head;
|
|
2186
|
+
view.dispatch({
|
|
2187
|
+
changes: {
|
|
2188
|
+
from: pos,
|
|
2189
|
+
insert: text[i++]
|
|
2190
|
+
},
|
|
2191
|
+
selection: {
|
|
2192
|
+
anchor: pos + 1
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2195
|
+
if (i < text.length) {
|
|
2196
|
+
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
2197
|
+
}
|
|
2198
|
+
}, d);
|
|
2199
|
+
};
|
|
2200
|
+
insert();
|
|
2201
|
+
return true;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
])
|
|
1400
2205
|
];
|
|
1401
2206
|
};
|
|
1402
2207
|
|
|
1403
|
-
// packages/ui/react-ui-editor/src/
|
|
1404
|
-
import {
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
match = void 0;
|
|
1416
|
-
break;
|
|
1417
|
-
}
|
|
1418
|
-
idx = text.indexOf(match[0]);
|
|
1419
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1420
|
-
break;
|
|
1421
|
-
}
|
|
1422
|
-
idx += match[0].length;
|
|
1423
|
-
} while (true);
|
|
1424
|
-
if (!match) {
|
|
1425
|
-
return null;
|
|
2208
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2209
|
+
import { yCollab } from "y-codemirror.next";
|
|
2210
|
+
|
|
2211
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
|
|
2212
|
+
import * as Y from "yjs";
|
|
2213
|
+
import { arrayToString, stringToArray } from "@dxos/util";
|
|
2214
|
+
var cursorConverter2 = (text) => ({
|
|
2215
|
+
toCursor: (index, assoc) => {
|
|
2216
|
+
return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
|
|
2217
|
+
},
|
|
2218
|
+
fromCursor: (cursor) => {
|
|
2219
|
+
return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
|
|
1426
2220
|
}
|
|
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
|
|
1441
|
-
}
|
|
1442
|
-
};
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
2221
|
});
|
|
1446
2222
|
|
|
1447
|
-
// packages/ui/react-ui-editor/src/
|
|
2223
|
+
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2224
|
+
var yjs = (content, awareness2) => [
|
|
2225
|
+
Cursor.converter.of(cursorConverter2(content)),
|
|
2226
|
+
yCollab(content, awareness2)
|
|
2227
|
+
];
|
|
2228
|
+
|
|
2229
|
+
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
1448
2230
|
import get3 from "lodash.get";
|
|
1449
2231
|
var defaultTheme = {
|
|
1450
2232
|
//
|
|
1451
2233
|
// Main layout:
|
|
1452
|
-
//
|
|
1453
|
-
//
|
|
1454
|
-
//
|
|
1455
|
-
//
|
|
2234
|
+
// https://codemirror.net/examples/styling
|
|
2235
|
+
//
|
|
2236
|
+
// <div class="cm-editor [cm-focused] [generated classes]">
|
|
2237
|
+
// <div class="cm-scroller">
|
|
2238
|
+
// <div class="cm-gutters">
|
|
2239
|
+
// <div class="cm-gutter [...]">
|
|
2240
|
+
// <div class="cm-gutterElement">...</div>
|
|
2241
|
+
// </div>
|
|
2242
|
+
// </div>
|
|
2243
|
+
// <div class="cm-content" role="textbox" contenteditable="true">
|
|
2244
|
+
// <div class="cm-line"></div>
|
|
2245
|
+
// </div>
|
|
2246
|
+
// <div class="cm-selectionLayer">
|
|
2247
|
+
// <div class="cm-selectionBackground"></div>
|
|
2248
|
+
// </div>
|
|
2249
|
+
// <div class="cm-cursorLayer">
|
|
2250
|
+
// <div class="cm-cursor"></div>
|
|
1456
2251
|
// </div>
|
|
1457
2252
|
// </div>
|
|
1458
2253
|
// </div>
|
|
@@ -1461,13 +2256,13 @@ var defaultTheme = {
|
|
|
1461
2256
|
"&.cm-focused": {
|
|
1462
2257
|
outline: "none"
|
|
1463
2258
|
},
|
|
1464
|
-
"
|
|
2259
|
+
".cm-scroller": {
|
|
1465
2260
|
overflow: "visible",
|
|
1466
2261
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1467
2262
|
},
|
|
1468
|
-
"
|
|
2263
|
+
".cm-content": {
|
|
1469
2264
|
padding: 0,
|
|
1470
|
-
// Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2265
|
+
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
1471
2266
|
fontSize: "16px"
|
|
1472
2267
|
},
|
|
1473
2268
|
"&light .cm-content": {
|
|
@@ -1487,65 +2282,46 @@ var defaultTheme = {
|
|
|
1487
2282
|
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
1488
2283
|
borderLeft: "2px solid white"
|
|
1489
2284
|
},
|
|
1490
|
-
"
|
|
2285
|
+
".cm-placeholder": {
|
|
1491
2286
|
fontWeight: 100
|
|
1492
2287
|
},
|
|
1493
2288
|
//
|
|
1494
2289
|
// line
|
|
1495
2290
|
//
|
|
1496
|
-
"
|
|
2291
|
+
".cm-line": {
|
|
1497
2292
|
paddingInline: 0
|
|
1498
2293
|
},
|
|
1499
|
-
"
|
|
1500
|
-
|
|
1501
|
-
backgroundColor: "transparent"
|
|
2294
|
+
".cm-activeLine": {
|
|
2295
|
+
background: "inherit"
|
|
1502
2296
|
},
|
|
1503
2297
|
//
|
|
1504
|
-
//
|
|
2298
|
+
// Selection
|
|
1505
2299
|
//
|
|
1506
|
-
"&light .cm-selectionBackground
|
|
1507
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2300
|
+
"&light .cm-selectionBackground": {
|
|
2301
|
+
background: get3(tokens, "extend.colors.primary.100")
|
|
1508
2302
|
},
|
|
1509
|
-
"&
|
|
1510
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2303
|
+
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2304
|
+
background: get3(tokens, "extend.colors.primary.200")
|
|
1511
2305
|
},
|
|
1512
|
-
"&
|
|
1513
|
-
background: get3(tokens, "extend.colors.primary.
|
|
2306
|
+
"&dark .cm-selectionBackground": {
|
|
2307
|
+
background: get3(tokens, "extend.colors.primary.700")
|
|
1514
2308
|
},
|
|
1515
|
-
"&dark .cm-
|
|
1516
|
-
background: get3(tokens, "extend.colors.primary.600"
|
|
2309
|
+
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2310
|
+
background: get3(tokens, "extend.colors.primary.600")
|
|
1517
2311
|
},
|
|
1518
2312
|
//
|
|
1519
|
-
//
|
|
1520
|
-
// TODO(burdon): Review classnames (YJS dependent?)
|
|
2313
|
+
// Search
|
|
1521
2314
|
//
|
|
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"
|
|
2315
|
+
"&light .cm-searchMatch": {
|
|
2316
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.100")
|
|
1541
2317
|
},
|
|
1542
|
-
"& .cm-
|
|
1543
|
-
|
|
2318
|
+
"&dark .cm-searchMatch": {
|
|
2319
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.700")
|
|
1544
2320
|
},
|
|
1545
2321
|
//
|
|
1546
2322
|
// link
|
|
1547
2323
|
//
|
|
1548
|
-
"
|
|
2324
|
+
".cm-link": {
|
|
1549
2325
|
color: get3(tokens, "extend.colors.primary.500"),
|
|
1550
2326
|
textDecorationLine: "underline",
|
|
1551
2327
|
textDecorationThickness: "1px",
|
|
@@ -1556,83 +2332,73 @@ var defaultTheme = {
|
|
|
1556
2332
|
//
|
|
1557
2333
|
// tooltip
|
|
1558
2334
|
//
|
|
1559
|
-
"
|
|
2335
|
+
".cm-tooltip": {
|
|
1560
2336
|
border: "none"
|
|
1561
2337
|
},
|
|
1562
|
-
"
|
|
2338
|
+
".cm-tooltip-below": {},
|
|
1563
2339
|
//
|
|
1564
2340
|
// autocomplete
|
|
1565
2341
|
//
|
|
1566
|
-
"
|
|
2342
|
+
".cm-tooltip-autocomplete": {
|
|
1567
2343
|
marginTop: "4px",
|
|
1568
2344
|
marginLeft: "-3px"
|
|
1569
2345
|
},
|
|
1570
|
-
"
|
|
1571
|
-
"
|
|
1572
|
-
"
|
|
2346
|
+
".cm-tooltip-autocomplete ul li": {},
|
|
2347
|
+
".cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2348
|
+
".cm-completionIcon": {
|
|
1573
2349
|
display: "none"
|
|
1574
2350
|
},
|
|
1575
|
-
"
|
|
2351
|
+
".cm-completionLabel": {
|
|
1576
2352
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1577
2353
|
},
|
|
1578
|
-
"
|
|
2354
|
+
".cm-completionMatchedText": {
|
|
1579
2355
|
textDecoration: "none"
|
|
1580
2356
|
},
|
|
1581
2357
|
//
|
|
1582
|
-
// widgets
|
|
1583
|
-
//
|
|
1584
|
-
"& .cm-widgetBuffer": {
|
|
1585
|
-
display: "none",
|
|
1586
|
-
height: 0
|
|
1587
|
-
},
|
|
1588
|
-
//
|
|
1589
2358
|
// table
|
|
1590
2359
|
//
|
|
1591
|
-
"
|
|
2360
|
+
".cm-table *": {
|
|
1592
2361
|
fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
1593
2362
|
textDecoration: "none !important"
|
|
1594
2363
|
},
|
|
1595
|
-
"
|
|
2364
|
+
".cm-table-head": {
|
|
1596
2365
|
padding: "2px 16px 2px 0px",
|
|
1597
2366
|
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
|
|
1598
2367
|
fontWeight: 100,
|
|
1599
2368
|
textAlign: "left",
|
|
1600
2369
|
color: get3(tokens, "extend.colors.neutral.500")
|
|
1601
2370
|
},
|
|
1602
|
-
"
|
|
2371
|
+
".cm-table-cell": {
|
|
1603
2372
|
padding: "2px 16px 2px 0px"
|
|
1604
2373
|
},
|
|
1605
2374
|
//
|
|
1606
2375
|
// image
|
|
1607
2376
|
//
|
|
1608
|
-
"
|
|
1609
|
-
|
|
2377
|
+
".cm-image": {
|
|
2378
|
+
display: "block",
|
|
2379
|
+
height: "0"
|
|
2380
|
+
},
|
|
2381
|
+
".cm-image.cm-loaded-image": {
|
|
2382
|
+
height: "auto",
|
|
2383
|
+
borderTop: "0.5rem solid transparent",
|
|
2384
|
+
borderBottom: "0.5rem solid transparent"
|
|
1610
2385
|
},
|
|
1611
2386
|
//
|
|
1612
2387
|
// font size
|
|
1613
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2388
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2389
|
+
// but it's far more verbose than it needs to be.
|
|
1614
2390
|
//
|
|
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
|
-
}, {}),
|
|
2391
|
+
// ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
|
|
2392
|
+
// const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
|
|
2393
|
+
//
|
|
2394
|
+
// acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
|
|
2395
|
+
// acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
|
|
2396
|
+
// acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
|
|
2397
|
+
// return acc;
|
|
2398
|
+
// }, {}),
|
|
1634
2399
|
/**
|
|
1635
2400
|
* Panels
|
|
2401
|
+
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
1636
2402
|
* https://github.com/codemirror/search/blob/main/src/search.ts#L745
|
|
1637
2403
|
*
|
|
1638
2404
|
* Find/replace panel.
|
|
@@ -1646,85 +2412,83 @@ var defaultTheme = {
|
|
|
1646
2412
|
* </div>
|
|
1647
2413
|
* </div
|
|
1648
2414
|
*/
|
|
1649
|
-
"
|
|
1650
|
-
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}
|
|
1651
|
-
borderBottom: "none"
|
|
2415
|
+
".cm-panels": {
|
|
2416
|
+
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
1652
2417
|
},
|
|
1653
|
-
"
|
|
2418
|
+
".cm-panel": {
|
|
1654
2419
|
background: get3(tokens, "extend.colors.neutral.50"),
|
|
1655
2420
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1656
2421
|
},
|
|
1657
|
-
"
|
|
2422
|
+
".cm-button": {
|
|
1658
2423
|
margin: "4px",
|
|
1659
2424
|
fontFamily: get3(tokens, "fontFamily.body", []).join(","),
|
|
1660
2425
|
background: get3(tokens, "extend.colors.neutral.100"),
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2426
|
+
backgroundImage: "none",
|
|
2427
|
+
border: "none",
|
|
2428
|
+
"&:hover": {
|
|
2429
|
+
background: get3(tokens, "extend.colors.neutral.200")
|
|
2430
|
+
},
|
|
2431
|
+
"&:active": {
|
|
2432
|
+
background: get3(tokens, "extend.colors.neutral.300"),
|
|
2433
|
+
backgroundImage: "none"
|
|
2434
|
+
}
|
|
1665
2435
|
},
|
|
1666
|
-
"
|
|
1667
|
-
|
|
2436
|
+
".cm-panel input[type=checkbox]": {
|
|
2437
|
+
marginRight: "0.4rem !important"
|
|
1668
2438
|
}
|
|
1669
2439
|
};
|
|
1670
2440
|
var textTheme = {
|
|
1671
|
-
"
|
|
2441
|
+
".cm-scroller": {
|
|
1672
2442
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1673
2443
|
},
|
|
1674
|
-
"
|
|
2444
|
+
".cm-placeholder": {
|
|
1675
2445
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1676
2446
|
}
|
|
1677
2447
|
};
|
|
1678
2448
|
var markdownTheme = {
|
|
1679
|
-
|
|
1680
|
-
|
|
2449
|
+
".cm-scroller": {
|
|
2450
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2451
|
+
},
|
|
2452
|
+
".cm-placeholder": {
|
|
1681
2453
|
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1682
2454
|
}
|
|
1683
2455
|
};
|
|
1684
2456
|
var codeTheme = {
|
|
1685
|
-
"
|
|
2457
|
+
".cm-scroller": {
|
|
1686
2458
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1687
2459
|
},
|
|
1688
|
-
"
|
|
2460
|
+
".cm-placeholder": {
|
|
1689
2461
|
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1690
2462
|
}
|
|
1691
2463
|
};
|
|
1692
2464
|
|
|
1693
2465
|
// 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
2466
|
var EditorModes = [
|
|
1704
2467
|
"default",
|
|
1705
2468
|
"vim"
|
|
1706
2469
|
];
|
|
1707
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly,
|
|
1708
|
-
const { themeMode } = useThemeContext();
|
|
2470
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, selection, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
|
|
1709
2471
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
1710
2472
|
tabBehavior: "limited"
|
|
1711
2473
|
});
|
|
1712
|
-
const
|
|
1713
|
-
const
|
|
1714
|
-
const [view, setView] = useState();
|
|
1715
|
-
useImperativeHandle(forwardedRef, () =>
|
|
1716
|
-
|
|
1717
|
-
|
|
2474
|
+
const { themeMode } = useThemeContext();
|
|
2475
|
+
const rootRef = useRef(null);
|
|
2476
|
+
const [view, setView] = useState(null);
|
|
2477
|
+
useImperativeHandle(forwardedRef, () => view, [
|
|
2478
|
+
view
|
|
2479
|
+
]);
|
|
2480
|
+
useEffect(() => {
|
|
2481
|
+
if (autofocus) {
|
|
2482
|
+
view?.focus();
|
|
2483
|
+
}
|
|
2484
|
+
}, [
|
|
2485
|
+
autofocus,
|
|
1718
2486
|
view
|
|
1719
|
-
}), [
|
|
1720
|
-
view,
|
|
1721
|
-
state,
|
|
1722
|
-
root
|
|
1723
2487
|
]);
|
|
1724
|
-
const { awareness, peer } = model;
|
|
1725
|
-
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
2488
|
+
const { awareness: awareness2, peer } = model;
|
|
2489
|
+
useEffect(() => {
|
|
2490
|
+
if (awareness2 && peer) {
|
|
2491
|
+
awareness2.setLocalStateField("user", {
|
|
1728
2492
|
name: peer.name ?? generateName(peer.id),
|
|
1729
2493
|
color: getColorForValue({
|
|
1730
2494
|
value: peer.id,
|
|
@@ -1738,26 +2502,40 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1738
2502
|
});
|
|
1739
2503
|
}
|
|
1740
2504
|
}, [
|
|
1741
|
-
|
|
2505
|
+
awareness2,
|
|
1742
2506
|
peer,
|
|
1743
2507
|
themeMode
|
|
1744
2508
|
]);
|
|
1745
|
-
|
|
1746
|
-
if (
|
|
2509
|
+
useEffect(() => {
|
|
2510
|
+
if (view && comments2 !== void 0) {
|
|
2511
|
+
view.dispatch({
|
|
2512
|
+
effects: setCommentRange.of({
|
|
2513
|
+
model,
|
|
2514
|
+
comments: comments2
|
|
2515
|
+
})
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
}, [
|
|
2519
|
+
view,
|
|
2520
|
+
comments2
|
|
2521
|
+
]);
|
|
2522
|
+
useEffect(() => {
|
|
2523
|
+
if (!model || !rootRef.current) {
|
|
1747
2524
|
return;
|
|
1748
2525
|
}
|
|
1749
|
-
const
|
|
2526
|
+
const state = EditorState2.create({
|
|
1750
2527
|
doc: model.text(),
|
|
2528
|
+
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
2529
|
+
selection,
|
|
1751
2530
|
extensions: [
|
|
1752
2531
|
readonly && EditorState2.readOnly.of(readonly),
|
|
1753
|
-
// TODO(burdon): Factor out VIM mode?
|
|
2532
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
1754
2533
|
editorMode === "vim" && vim(),
|
|
1755
2534
|
// Theme.
|
|
1756
|
-
// TODO(burdon): Make
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
EditorView11.darkTheme.of(themeMode === "dark"),
|
|
2535
|
+
// TODO(burdon): Make theme configurable.
|
|
2536
|
+
EditorView12.baseTheme(defaultTheme),
|
|
2537
|
+
EditorView12.theme(theme ?? {}),
|
|
2538
|
+
EditorView12.darkTheme.of(themeMode === "dark"),
|
|
1761
2539
|
// Storage and replication.
|
|
1762
2540
|
model.extension,
|
|
1763
2541
|
// Custom.
|
|
@@ -1765,32 +2543,27 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1765
2543
|
].filter(Boolean)
|
|
1766
2544
|
});
|
|
1767
2545
|
view?.destroy();
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
})
|
|
1772
|
-
|
|
2546
|
+
const newView = new EditorView12({
|
|
2547
|
+
parent: rootRef.current,
|
|
2548
|
+
state
|
|
2549
|
+
});
|
|
2550
|
+
setView(newView);
|
|
1773
2551
|
return () => {
|
|
1774
|
-
|
|
1775
|
-
setView(
|
|
1776
|
-
setState(void 0);
|
|
2552
|
+
newView?.destroy();
|
|
2553
|
+
setView(null);
|
|
1777
2554
|
};
|
|
1778
2555
|
}, [
|
|
1779
|
-
|
|
2556
|
+
rootRef,
|
|
1780
2557
|
model,
|
|
1781
|
-
themeMode,
|
|
1782
2558
|
readonly,
|
|
1783
|
-
editorMode
|
|
2559
|
+
editorMode,
|
|
2560
|
+
themeMode
|
|
1784
2561
|
]);
|
|
1785
2562
|
const handleKeyUp = useCallback((event) => {
|
|
1786
2563
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
1787
2564
|
switch (key) {
|
|
1788
|
-
case "Enter": {
|
|
1789
|
-
view?.contentDOM.focus();
|
|
1790
|
-
break;
|
|
1791
|
-
}
|
|
1792
2565
|
case "Escape": {
|
|
1793
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) &&
|
|
2566
|
+
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && rootRef.current?.focus();
|
|
1794
2567
|
break;
|
|
1795
2568
|
}
|
|
1796
2569
|
}
|
|
@@ -1800,16 +2573,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode,
|
|
|
1800
2573
|
]);
|
|
1801
2574
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1802
2575
|
key: model.id,
|
|
1803
|
-
|
|
2576
|
+
role: "none",
|
|
1804
2577
|
tabIndex: 0,
|
|
1805
|
-
|
|
2578
|
+
onKeyUp: handleKeyUp,
|
|
2579
|
+
...slots.root,
|
|
1806
2580
|
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
1807
|
-
|
|
2581
|
+
ref: rootRef
|
|
1808
2582
|
});
|
|
1809
2583
|
});
|
|
1810
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [],
|
|
2584
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
|
|
1811
2585
|
const { themeMode } = useThemeContext();
|
|
1812
|
-
const
|
|
2586
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
1813
2587
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1814
2588
|
ref: forwardedRef,
|
|
1815
2589
|
readonly,
|
|
@@ -1817,359 +2591,207 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots:
|
|
|
1817
2591
|
basicBundle({
|
|
1818
2592
|
readonly,
|
|
1819
2593
|
themeMode,
|
|
1820
|
-
placeholder:
|
|
1821
|
-
}),
|
|
1822
|
-
...extensions
|
|
1823
|
-
],
|
|
1824
|
-
slots,
|
|
1825
|
-
...props
|
|
1826
|
-
});
|
|
1827
|
-
});
|
|
1828
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
1829
|
-
const { themeMode } = useThemeContext();
|
|
1830
|
-
const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
|
|
1831
|
-
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
1832
|
-
ref: forwardedRef,
|
|
1833
|
-
readonly,
|
|
1834
|
-
extensions: [
|
|
1835
|
-
markdownBundle({
|
|
1836
|
-
readonly,
|
|
1837
|
-
themeMode,
|
|
1838
|
-
placeholder: slots?.editor?.placeholder
|
|
2594
|
+
placeholder: placeholder3
|
|
1839
2595
|
}),
|
|
1840
2596
|
...extensions
|
|
1841
2597
|
],
|
|
1842
|
-
|
|
2598
|
+
theme,
|
|
2599
|
+
slots: updatedSlots,
|
|
1843
2600
|
...props
|
|
1844
2601
|
});
|
|
1845
2602
|
});
|
|
1846
|
-
var
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
theme
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
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;
|
|
2603
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
|
|
2604
|
+
const { themeMode } = useThemeContext();
|
|
2605
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
2606
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2607
|
+
ref: forwardedRef,
|
|
2608
|
+
readonly,
|
|
2609
|
+
extensions: [
|
|
2610
|
+
markdownBundle({
|
|
2611
|
+
readonly,
|
|
2612
|
+
themeMode,
|
|
2613
|
+
placeholder: placeholder3
|
|
2614
|
+
}),
|
|
2615
|
+
...extensions
|
|
2616
|
+
],
|
|
2617
|
+
theme,
|
|
2618
|
+
slots: updatedSlots,
|
|
2619
|
+
...props
|
|
2620
|
+
});
|
|
2621
|
+
});
|
|
2622
|
+
var defaultSlots = {
|
|
2623
|
+
root: {
|
|
2624
|
+
className: mx3("w-full p-2 overflow-y-auto", inputSurface)
|
|
1982
2625
|
}
|
|
1983
|
-
return null;
|
|
1984
2626
|
};
|
|
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;
|
|
2627
|
+
var defaultTextSlots = {
|
|
2628
|
+
...defaultSlots
|
|
2629
|
+
};
|
|
2630
|
+
var defaultMarkdownSlots = {
|
|
2631
|
+
...defaultSlots
|
|
2007
2632
|
};
|
|
2008
2633
|
|
|
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
|
-
|
|
2634
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2635
|
+
import get4 from "lodash.get";
|
|
2636
|
+
import { getRawDoc } from "@dxos/echo-schema";
|
|
2637
|
+
import { isNotNullOrUndefined } from "@dxos/util";
|
|
2638
|
+
|
|
2639
|
+
// packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
|
|
2640
|
+
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2641
|
+
import { Context as Context2 } from "@dxos/context";
|
|
2642
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
2643
|
+
import { log as log2 } from "@dxos/log";
|
|
2644
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2645
|
+
var DEBOUNCE_INTERVAL = 100;
|
|
2646
|
+
var SpaceAwarenessProvider = class {
|
|
2647
|
+
constructor(params) {
|
|
2648
|
+
this.remoteStateChange = new Event2();
|
|
2649
|
+
this._remoteStates = /* @__PURE__ */ new Map();
|
|
2650
|
+
this._space = params.space;
|
|
2651
|
+
this._channel = params.channel;
|
|
2652
|
+
this._peerId = params.peerId;
|
|
2653
|
+
this._info = params.info;
|
|
2654
|
+
}
|
|
2655
|
+
open() {
|
|
2656
|
+
this._ctx = new Context2();
|
|
2657
|
+
this._postTask = new DeferredTask(this._ctx, async () => {
|
|
2658
|
+
if (this._localState) {
|
|
2659
|
+
await this._space.postMessage(this._channel, {
|
|
2660
|
+
kind: "post",
|
|
2661
|
+
state: this._localState
|
|
2030
2662
|
});
|
|
2663
|
+
await sleep(DEBOUNCE_INTERVAL);
|
|
2031
2664
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2665
|
+
});
|
|
2666
|
+
const unsubscribe = this._space.listen(this._channel, (message) => {
|
|
2667
|
+
switch (message.payload.kind) {
|
|
2668
|
+
case "query": {
|
|
2669
|
+
this._handleQueryMessage();
|
|
2670
|
+
break;
|
|
2671
|
+
}
|
|
2672
|
+
case "post": {
|
|
2673
|
+
this._handlePostMessage(message.payload);
|
|
2674
|
+
break;
|
|
2675
|
+
}
|
|
2035
2676
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2677
|
+
});
|
|
2678
|
+
this._ctx.onDispose(unsubscribe);
|
|
2679
|
+
void this._space.postMessage(this._channel, {
|
|
2680
|
+
kind: "query"
|
|
2681
|
+
}).catch((err) => {
|
|
2682
|
+
log2.debug("failed to query awareness", {
|
|
2683
|
+
err
|
|
2684
|
+
}, {
|
|
2685
|
+
F: __dxlog_file4,
|
|
2686
|
+
L: 85,
|
|
2687
|
+
S: this,
|
|
2688
|
+
C: (f, a) => f(...a)
|
|
2041
2689
|
});
|
|
2042
|
-
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2692
|
+
close() {
|
|
2693
|
+
void this._ctx?.dispose();
|
|
2694
|
+
this._ctx = void 0;
|
|
2695
|
+
this._postTask = void 0;
|
|
2696
|
+
}
|
|
2697
|
+
update(position) {
|
|
2698
|
+
invariant3(this._postTask, void 0, {
|
|
2699
|
+
F: __dxlog_file4,
|
|
2700
|
+
L: 96,
|
|
2701
|
+
S: this,
|
|
2702
|
+
A: [
|
|
2703
|
+
"this._postTask",
|
|
2704
|
+
""
|
|
2705
|
+
]
|
|
2706
|
+
});
|
|
2707
|
+
this._localState = {
|
|
2708
|
+
peerId: this._peerId,
|
|
2709
|
+
position,
|
|
2710
|
+
info: this._info
|
|
2043
2711
|
};
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2712
|
+
this._postTask.schedule();
|
|
2713
|
+
}
|
|
2714
|
+
getRemoteStates() {
|
|
2715
|
+
return Array.from(this._remoteStates.values());
|
|
2716
|
+
}
|
|
2717
|
+
_handleQueryMessage() {
|
|
2718
|
+
invariant3(this._postTask, void 0, {
|
|
2719
|
+
F: __dxlog_file4,
|
|
2720
|
+
L: 111,
|
|
2721
|
+
S: this,
|
|
2722
|
+
A: [
|
|
2723
|
+
"this._postTask",
|
|
2724
|
+
""
|
|
2725
|
+
]
|
|
2726
|
+
});
|
|
2727
|
+
this._postTask.schedule();
|
|
2728
|
+
}
|
|
2729
|
+
_handlePostMessage(message) {
|
|
2730
|
+
invariant3(message.kind === "post", void 0, {
|
|
2731
|
+
F: __dxlog_file4,
|
|
2732
|
+
L: 116,
|
|
2733
|
+
S: this,
|
|
2734
|
+
A: [
|
|
2735
|
+
"message.kind === 'post'",
|
|
2736
|
+
""
|
|
2737
|
+
]
|
|
2738
|
+
});
|
|
2739
|
+
this._remoteStates.set(message.state.peerId, message.state);
|
|
2740
|
+
this.remoteStateChange.emit();
|
|
2047
2741
|
}
|
|
2048
2742
|
};
|
|
2049
2743
|
|
|
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];
|
|
2744
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2745
|
+
import { StateField as StateField5 } from "@codemirror/state";
|
|
2746
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
2747
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
2158
2748
|
|
|
2159
|
-
// packages/ui/react-ui-editor/src/hooks/yjs
|
|
2749
|
+
// packages/ui/react-ui-editor/src/hooks/yjs.ts
|
|
2160
2750
|
import * as decoding from "lib0/decoding";
|
|
2161
2751
|
import * as encoding from "lib0/encoding";
|
|
2162
2752
|
import { Observable } from "lib0/observable";
|
|
2163
|
-
import * as
|
|
2164
|
-
import {
|
|
2165
|
-
|
|
2753
|
+
import * as YP from "y-protocols/awareness";
|
|
2754
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2755
|
+
import { log as log3 } from "@dxos/log";
|
|
2756
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2757
|
+
var createYjsModel = ({ identity, space, text }) => {
|
|
2758
|
+
invariant4(text?.doc && text?.content, void 0, {
|
|
2759
|
+
F: __dxlog_file5,
|
|
2760
|
+
L: 21,
|
|
2761
|
+
S: void 0,
|
|
2762
|
+
A: [
|
|
2763
|
+
"text?.doc && text?.content",
|
|
2764
|
+
""
|
|
2765
|
+
]
|
|
2766
|
+
});
|
|
2767
|
+
const provider = space ? new YAwarenessProvider({
|
|
2768
|
+
space,
|
|
2769
|
+
doc: text.doc,
|
|
2770
|
+
channel: `yjs.awareness.${text.id}`
|
|
2771
|
+
}) : void 0;
|
|
2772
|
+
const model = {
|
|
2773
|
+
id: text.doc.guid,
|
|
2774
|
+
content: text.content,
|
|
2775
|
+
text: () => text.content.toString(),
|
|
2776
|
+
extension: [
|
|
2777
|
+
modelState.init(() => model),
|
|
2778
|
+
yjs(text.content, provider?.awareness)
|
|
2779
|
+
],
|
|
2780
|
+
awareness: provider?.awareness,
|
|
2781
|
+
peer: identity ? {
|
|
2782
|
+
id: identity.identityKey.toHex(),
|
|
2783
|
+
name: identity.profile?.displayName
|
|
2784
|
+
} : void 0
|
|
2785
|
+
};
|
|
2786
|
+
return model;
|
|
2787
|
+
};
|
|
2166
2788
|
var messageAwareness = 1;
|
|
2167
2789
|
var messageQueryAwareness = 3;
|
|
2168
|
-
var
|
|
2169
|
-
constructor({ space, doc, channel, awareness }) {
|
|
2790
|
+
var YAwarenessProvider = class extends Observable {
|
|
2791
|
+
constructor({ space, doc, channel, awareness: awareness2 }) {
|
|
2170
2792
|
super();
|
|
2171
2793
|
this._space = space;
|
|
2172
|
-
this._awareness =
|
|
2794
|
+
this._awareness = awareness2 ?? new YP.Awareness(doc);
|
|
2173
2795
|
this._channel = channel;
|
|
2174
2796
|
this._clientId = doc.clientID;
|
|
2175
2797
|
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
@@ -2184,7 +2806,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2184
2806
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
2185
2807
|
const encoderAwarenessState = encoding.createEncoder();
|
|
2186
2808
|
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
2187
|
-
encoding.writeVarUint8Array(encoderAwarenessState,
|
|
2809
|
+
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
|
|
2188
2810
|
this._clientId
|
|
2189
2811
|
]));
|
|
2190
2812
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
@@ -2193,27 +2815,27 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2193
2815
|
return this._awareness;
|
|
2194
2816
|
}
|
|
2195
2817
|
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
2196
|
-
|
|
2818
|
+
log3("awareness update", {
|
|
2197
2819
|
added,
|
|
2198
2820
|
updated,
|
|
2199
2821
|
removed,
|
|
2200
2822
|
origin
|
|
2201
2823
|
}, {
|
|
2202
|
-
F:
|
|
2203
|
-
L:
|
|
2824
|
+
F: __dxlog_file5,
|
|
2825
|
+
L: 100,
|
|
2204
2826
|
S: this,
|
|
2205
2827
|
C: (f, a) => f(...a)
|
|
2206
2828
|
});
|
|
2207
2829
|
const changedClients = added.concat(updated).concat(removed);
|
|
2208
2830
|
const encoderAwareness = encoding.createEncoder();
|
|
2209
2831
|
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
2210
|
-
encoding.writeVarUint8Array(encoderAwareness,
|
|
2832
|
+
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
2211
2833
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
2212
2834
|
}
|
|
2213
2835
|
_handleSpaceMessage({ payload }) {
|
|
2214
|
-
|
|
2215
|
-
F:
|
|
2216
|
-
L:
|
|
2836
|
+
log3("space message", payload, {
|
|
2837
|
+
F: __dxlog_file5,
|
|
2838
|
+
L: 109,
|
|
2217
2839
|
S: this,
|
|
2218
2840
|
C: (f, a) => f(...a)
|
|
2219
2841
|
});
|
|
@@ -2230,12 +2852,12 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2230
2852
|
let sendReply = false;
|
|
2231
2853
|
switch (messageType) {
|
|
2232
2854
|
case messageAwareness: {
|
|
2233
|
-
|
|
2855
|
+
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
2234
2856
|
break;
|
|
2235
2857
|
}
|
|
2236
2858
|
case messageQueryAwareness: {
|
|
2237
2859
|
encoding.writeVarUint(encoder, messageAwareness);
|
|
2238
|
-
encoding.writeVarUint8Array(encoder,
|
|
2860
|
+
encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
2239
2861
|
sendReply = true;
|
|
2240
2862
|
break;
|
|
2241
2863
|
}
|
|
@@ -2250,130 +2872,127 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
2250
2872
|
return encoder;
|
|
2251
2873
|
}
|
|
2252
2874
|
_handleBeforeUnload() {
|
|
2253
|
-
|
|
2875
|
+
YP.removeAwarenessStates(this._awareness, [
|
|
2254
2876
|
this._clientId
|
|
2255
2877
|
], "window unload");
|
|
2256
2878
|
}
|
|
2257
2879
|
};
|
|
2258
2880
|
|
|
2259
2881
|
// 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
|
-
}, [
|
|
2882
|
+
var modelState = StateField5.define({
|
|
2883
|
+
create: () => void 0,
|
|
2884
|
+
update: (model) => model
|
|
2885
|
+
});
|
|
2886
|
+
var useTextModel = (props) => {
|
|
2887
|
+
const { identity, space, text } = props;
|
|
2888
|
+
const [model, setModel] = useState2();
|
|
2889
|
+
useEffect2(() => setModel(createModel(props)), [
|
|
2274
2890
|
identity,
|
|
2275
2891
|
space,
|
|
2276
2892
|
text
|
|
2277
2893
|
]);
|
|
2278
2894
|
return model;
|
|
2279
2895
|
};
|
|
2280
|
-
var createModel = (
|
|
2281
|
-
const { text } =
|
|
2282
|
-
if (
|
|
2283
|
-
return createAutomergeModel(
|
|
2896
|
+
var createModel = (props) => {
|
|
2897
|
+
const { text } = props;
|
|
2898
|
+
if (isAutomergeObject(text)) {
|
|
2899
|
+
return createAutomergeModel(props);
|
|
2900
|
+
} else if (text?.doc) {
|
|
2901
|
+
return createYjsModel(props);
|
|
2284
2902
|
} else {
|
|
2285
|
-
|
|
2286
|
-
return void 0;
|
|
2287
|
-
}
|
|
2288
|
-
return createYjsModel(options);
|
|
2903
|
+
return void 0;
|
|
2289
2904
|
}
|
|
2290
2905
|
};
|
|
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 }) => {
|
|
2906
|
+
|
|
2907
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2908
|
+
var createAutomergeModel = ({ space, identity, text }) => {
|
|
2320
2909
|
const obj = text;
|
|
2321
2910
|
const doc = getRawDoc(obj, [
|
|
2322
2911
|
obj.field
|
|
2323
2912
|
]);
|
|
2324
|
-
|
|
2913
|
+
const awarenessProvider = space && new SpaceAwarenessProvider({
|
|
2914
|
+
space,
|
|
2915
|
+
channel: `automerge.awareness.${obj.id}`,
|
|
2916
|
+
info: {
|
|
2917
|
+
displayName: identity?.profile?.displayName ?? "",
|
|
2918
|
+
color: cursorColor.color,
|
|
2919
|
+
lightColor: cursorColor.light
|
|
2920
|
+
},
|
|
2921
|
+
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2922
|
+
});
|
|
2923
|
+
const model = {
|
|
2325
2924
|
id: obj.id,
|
|
2326
2925
|
content: doc,
|
|
2327
2926
|
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2328
|
-
|
|
2329
|
-
extension:
|
|
2927
|
+
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
2928
|
+
extension: [
|
|
2929
|
+
modelState.init(() => model),
|
|
2930
|
+
automerge3({
|
|
2931
|
+
handle: doc.handle,
|
|
2932
|
+
path: doc.path
|
|
2933
|
+
}),
|
|
2934
|
+
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
2935
|
+
awareness()
|
|
2936
|
+
].filter(isNotNullOrUndefined),
|
|
2330
2937
|
peer: identity ? {
|
|
2331
2938
|
id: identity.identityKey.toHex(),
|
|
2332
2939
|
name: identity.profile?.displayName
|
|
2333
2940
|
} : void 0
|
|
2334
2941
|
};
|
|
2942
|
+
return model;
|
|
2335
2943
|
};
|
|
2336
2944
|
export {
|
|
2945
|
+
AwarenessProvider,
|
|
2337
2946
|
BaseTextEditor,
|
|
2947
|
+
Cursor,
|
|
2338
2948
|
Doc,
|
|
2339
2949
|
EditorModes,
|
|
2340
2950
|
MarkdownEditor,
|
|
2951
|
+
RemoteSelectionsDecorator,
|
|
2341
2952
|
SpaceAwarenessProvider,
|
|
2342
2953
|
TextEditor,
|
|
2343
2954
|
TextKind,
|
|
2955
|
+
YAwarenessProvider,
|
|
2344
2956
|
YText,
|
|
2345
2957
|
YXmlFragment,
|
|
2346
2958
|
autocomplete,
|
|
2959
|
+
automerge3 as automerge,
|
|
2960
|
+
awareness,
|
|
2347
2961
|
basicBundle,
|
|
2348
2962
|
blast,
|
|
2963
|
+
callbackWrapper,
|
|
2349
2964
|
code2 as code,
|
|
2350
2965
|
codeTheme,
|
|
2351
2966
|
comments,
|
|
2352
|
-
|
|
2967
|
+
createAutomergeModel,
|
|
2968
|
+
createYjsModel,
|
|
2353
2969
|
defaultMarkdownSlots,
|
|
2354
2970
|
defaultOptions,
|
|
2355
2971
|
defaultSlots,
|
|
2356
2972
|
defaultTextSlots,
|
|
2357
2973
|
defaultTheme,
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
highlightDecorations,
|
|
2361
|
-
highlightStateField,
|
|
2974
|
+
getToken,
|
|
2975
|
+
hr,
|
|
2362
2976
|
image,
|
|
2363
2977
|
link,
|
|
2364
|
-
|
|
2978
|
+
listener,
|
|
2365
2979
|
markdownBundle,
|
|
2366
2980
|
markdownHighlightStyle,
|
|
2367
2981
|
markdownTags,
|
|
2368
2982
|
markdownTagsExtensions,
|
|
2369
2983
|
markdownTheme,
|
|
2370
2984
|
mention,
|
|
2985
|
+
modelState,
|
|
2986
|
+
outliner,
|
|
2987
|
+
setCommentRange,
|
|
2988
|
+
setFocus,
|
|
2989
|
+
setSelection,
|
|
2371
2990
|
table,
|
|
2372
2991
|
tags2 as tags,
|
|
2373
2992
|
tasklist,
|
|
2374
2993
|
textTheme,
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2994
|
+
typewriter,
|
|
2995
|
+
useTextModel,
|
|
2996
|
+
yjs
|
|
2378
2997
|
};
|
|
2379
2998
|
//# sourceMappingURL=index.mjs.map
|