@dxos/react-ui-editor 0.3.11-main.870164f → 0.3.11-main.8be6c8a
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 +1609 -1139
- 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 +9 -5
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +10 -10
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +2 -2
- 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 -1
- 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/{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 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +26 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +4 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/{hooks → extensions}/automerge/semaphore.d.ts +4 -4
- 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.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/code.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/hr.d.ts.map +1 -1
- package/dist/types/src/extensions/image.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +3 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/link.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
- 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.map +1 -0
- package/dist/types/src/hooks/automerge.d.ts +3 -0
- package/dist/types/src/hooks/automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +8 -7
- 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 +3 -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/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/themes/default.d.ts.map +1 -1
- package/dist/types/src/util/cursor.d.ts +17 -0
- package/dist/types/src/util/cursor.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +3 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/util.d.ts.map +1 -0
- package/package.json +22 -21
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +13 -11
- package/src/components/TextEditor/TextEditor.stories.tsx +3 -4
- package/src/components/TextEditor/TextEditor.tsx +60 -46
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
- package/src/components/TextEditor/codemirror.stories.ts +3 -2
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/extensions/autocomplete.ts +18 -17
- package/src/{hooks/automerge/plugin.ts → extensions/automerge/automerge.ts} +20 -58
- package/src/extensions/automerge/cursor.ts +51 -0
- package/src/extensions/automerge/defs.ts +62 -0
- package/src/extensions/automerge/index.ts +7 -0
- package/src/{hooks → extensions}/automerge/semaphore.ts +14 -12
- package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
- package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
- package/src/extensions/awareness.ts +316 -0
- package/src/extensions/code.ts +4 -5
- package/src/extensions/comments.ts +30 -15
- package/src/extensions/hr.ts +2 -3
- package/src/extensions/image.ts +38 -20
- package/src/extensions/index.ts +3 -1
- package/src/extensions/link.ts +82 -84
- package/src/extensions/markdown/highlight.ts +3 -4
- package/src/extensions/table.ts +9 -3
- package/src/extensions/tasklist.ts +1 -3
- package/src/extensions/yjs/cursor.ts +21 -0
- package/src/extensions/yjs/index.ts +5 -0
- package/src/{hooks → extensions}/yjs/yjs.test.ts +1 -25
- package/src/extensions/yjs/yjs.ts +16 -0
- package/src/hooks/automerge.ts +52 -0
- package/src/hooks/awareness-provider.ts +120 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTextModel.ts +20 -94
- package/src/hooks/{yjs/space-provider.ts → yjs.ts} +71 -19
- package/src/index.ts +3 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/tokens.ts +3 -0
- package/src/testing/replicator.ts +10 -15
- package/src/themes/default.ts +39 -33
- package/src/util/cursor.ts +29 -0
- package/src/util/index.ts +6 -0
- package/dist/types/src/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/extensions/util.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/semaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-codemirror.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/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
- package/src/extensions/mermaid.ts +0 -11
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/yjs/index.ts +0 -6
- /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
- /package/dist/types/src/{hooks → extensions}/yjs/yjs.test.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/dist/types/src/{extensions → util}/util.d.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
- /package/src/{extensions → util}/util.ts +0 -0
|
@@ -2,36 +2,22 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Extension
|
|
6
|
-
import get from 'lodash.get';
|
|
5
|
+
import { StateField, type Extension } from '@codemirror/state';
|
|
7
6
|
import { useEffect, useState } from 'react';
|
|
8
|
-
import
|
|
9
|
-
import type * as awarenessProtocol from 'y-protocols/awareness';
|
|
10
|
-
import * as Y from 'yjs';
|
|
7
|
+
import type * as YP from 'y-protocols/awareness';
|
|
11
8
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
type DocAccessor,
|
|
15
|
-
type Space,
|
|
16
|
-
type TextObject,
|
|
17
|
-
type AutomergeTextCompat,
|
|
18
|
-
getRawDoc,
|
|
19
|
-
isActualAutomergeObject,
|
|
20
|
-
} from '@dxos/react-client/echo';
|
|
9
|
+
import { isAutomergeObject, type DocAccessor, type Space, type TextObject } from '@dxos/react-client/echo';
|
|
21
10
|
import { type Identity } from '@dxos/react-client/halo';
|
|
22
11
|
import type { YText, YXmlFragment } from '@dxos/text-model';
|
|
23
|
-
import { arrayToString, stringToArray } from '@dxos/util';
|
|
24
12
|
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
13
|
+
import { createAutomergeModel } from './automerge';
|
|
14
|
+
import { createYjsModel } from './yjs';
|
|
27
15
|
|
|
28
|
-
// TODO(burdon):
|
|
29
|
-
type Awareness = awarenessProtocol.Awareness;
|
|
16
|
+
// TODO(burdon): Factor out defs.
|
|
30
17
|
|
|
31
18
|
/**
|
|
32
19
|
* State field makes the model available to other extensions.
|
|
33
20
|
*/
|
|
34
|
-
// TODO(burdon): Use facet?
|
|
35
21
|
export const modelState = StateField.define<EditorModel | undefined>({
|
|
36
22
|
create: () => undefined,
|
|
37
23
|
update: (model) => model,
|
|
@@ -44,20 +30,25 @@ export type Range = {
|
|
|
44
30
|
|
|
45
31
|
export type CommentRange = {
|
|
46
32
|
id: string;
|
|
47
|
-
// TODO(burdon): Split into begin/end?
|
|
48
33
|
cursor: string;
|
|
49
34
|
};
|
|
50
35
|
|
|
51
36
|
// TODO(wittjosiah): Factor out to common package? @dxos/react-client?
|
|
52
37
|
export type EditorModel = {
|
|
53
38
|
id: string;
|
|
39
|
+
text: () => string;
|
|
40
|
+
extension?: Extension;
|
|
41
|
+
|
|
54
42
|
// TODO(burdon): Remove.
|
|
55
43
|
content: string | YText | YXmlFragment | DocAccessor;
|
|
56
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Use CursorConverter.
|
|
46
|
+
*/
|
|
57
47
|
getCursorFromRange?: (value: Range) => string;
|
|
58
48
|
getRangeFromCursor?: (cursor: string) => Range | undefined;
|
|
59
|
-
extension
|
|
60
|
-
awareness?: Awareness;
|
|
49
|
+
// TODO(burdon): Move into extension.
|
|
50
|
+
awareness?: YP.Awareness;
|
|
51
|
+
// TODO(burdon): Remove.
|
|
61
52
|
peer?: {
|
|
62
53
|
id: string;
|
|
63
54
|
name?: string;
|
|
@@ -75,83 +66,18 @@ export type UseTextModelProps = {
|
|
|
75
66
|
// TODO(wittjosiah): Factor out to common package? @dxos/react-client?
|
|
76
67
|
export const useTextModel = (props: UseTextModelProps): EditorModel | undefined => {
|
|
77
68
|
const { identity, space, text } = props;
|
|
78
|
-
const [model, setModel] = useState<EditorModel | undefined>(
|
|
69
|
+
const [model, setModel] = useState<EditorModel | undefined>();
|
|
79
70
|
useEffect(() => setModel(createModel(props)), [identity, space, text]);
|
|
80
71
|
return model;
|
|
81
72
|
};
|
|
82
73
|
|
|
83
74
|
const createModel = (props: UseTextModelProps) => {
|
|
84
75
|
const { text } = props;
|
|
85
|
-
if (
|
|
76
|
+
if (isAutomergeObject(text)) {
|
|
86
77
|
return createAutomergeModel(props);
|
|
87
|
-
} else {
|
|
88
|
-
if (!text?.doc) {
|
|
89
|
-
return undefined;
|
|
90
|
-
}
|
|
91
|
-
|
|
78
|
+
} else if (text?.doc) {
|
|
92
79
|
return createYjsModel(props);
|
|
80
|
+
} else {
|
|
81
|
+
return undefined;
|
|
93
82
|
}
|
|
94
83
|
};
|
|
95
|
-
|
|
96
|
-
const createYjsModel = ({ identity, space, text }: UseTextModelProps): EditorModel => {
|
|
97
|
-
invariant(text?.doc && text?.content);
|
|
98
|
-
const provider = space
|
|
99
|
-
? new SpaceAwarenessProvider({ space, doc: text.doc, channel: `yjs.awareness.${text.id}` })
|
|
100
|
-
: undefined;
|
|
101
|
-
|
|
102
|
-
const model: EditorModel = {
|
|
103
|
-
id: text.doc.guid,
|
|
104
|
-
content: text.content,
|
|
105
|
-
text: () => text.content!.toString(),
|
|
106
|
-
// https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
107
|
-
getCursorFromRange: (range: Range) => {
|
|
108
|
-
const from = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.from));
|
|
109
|
-
const to = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.to, -1));
|
|
110
|
-
return [arrayToString(from), arrayToString(to)].join(':');
|
|
111
|
-
},
|
|
112
|
-
getRangeFromCursor: (cursor: string) => {
|
|
113
|
-
invariant(text.doc);
|
|
114
|
-
const parts = cursor.split(':');
|
|
115
|
-
const from = Y.createAbsolutePositionFromRelativePosition(
|
|
116
|
-
Y.decodeRelativePosition(stringToArray(parts[0])),
|
|
117
|
-
text.doc,
|
|
118
|
-
);
|
|
119
|
-
const to = Y.createAbsolutePositionFromRelativePosition(
|
|
120
|
-
Y.decodeRelativePosition(stringToArray(parts[1])),
|
|
121
|
-
text.doc,
|
|
122
|
-
);
|
|
123
|
-
return from && to ? { from: from.index, to: to.index } : undefined;
|
|
124
|
-
},
|
|
125
|
-
extension: [yCollab(text.content as YText, provider?.awareness), modelState.init(() => model)],
|
|
126
|
-
awareness: provider?.awareness,
|
|
127
|
-
peer: identity
|
|
128
|
-
? {
|
|
129
|
-
id: identity.identityKey.toHex(),
|
|
130
|
-
name: identity.profile?.displayName,
|
|
131
|
-
}
|
|
132
|
-
: undefined,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return model;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const createAutomergeModel = ({ identity, text }: UseTextModelProps): EditorModel => {
|
|
139
|
-
const obj = text as any as AutomergeTextCompat;
|
|
140
|
-
const doc = getRawDoc(obj, [obj.field]);
|
|
141
|
-
|
|
142
|
-
const model: EditorModel = {
|
|
143
|
-
id: obj.id,
|
|
144
|
-
content: doc,
|
|
145
|
-
text: () => get(doc.handle.docSync(), doc.path),
|
|
146
|
-
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
147
|
-
extension: [automergePlugin(doc.handle, doc.path), modelState.init(() => model)],
|
|
148
|
-
peer: identity
|
|
149
|
-
? {
|
|
150
|
-
id: identity.identityKey.toHex(),
|
|
151
|
-
name: identity.profile?.displayName,
|
|
152
|
-
}
|
|
153
|
-
: undefined,
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
return model;
|
|
157
|
-
};
|
|
@@ -5,14 +5,62 @@
|
|
|
5
5
|
import * as decoding from 'lib0/decoding';
|
|
6
6
|
import * as encoding from 'lib0/encoding';
|
|
7
7
|
import { Observable } from 'lib0/observable';
|
|
8
|
-
import * as
|
|
8
|
+
import * as YP from 'y-protocols/awareness';
|
|
9
|
+
import * as Y from 'yjs';
|
|
9
10
|
import { type Doc } from 'yjs';
|
|
10
11
|
|
|
12
|
+
import { invariant } from '@dxos/invariant';
|
|
11
13
|
import { log } from '@dxos/log';
|
|
12
14
|
import { type Space } from '@dxos/react-client/echo';
|
|
13
15
|
import { type GossipMessage } from '@dxos/react-client/mesh';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
import type { YText } from '@dxos/text-model';
|
|
17
|
+
import { arrayToString, stringToArray } from '@dxos/util';
|
|
18
|
+
|
|
19
|
+
import { type EditorModel, modelState, type Range, type UseTextModelProps } from './useTextModel';
|
|
20
|
+
import { yjs } from '../extensions';
|
|
21
|
+
|
|
22
|
+
export const createYjsModel = ({ identity, space, text }: UseTextModelProps): EditorModel => {
|
|
23
|
+
invariant(text?.doc && text?.content);
|
|
24
|
+
const provider = space
|
|
25
|
+
? new YAwarenessProvider({ space, doc: text.doc, channel: `yjs.awareness.${text.id}` })
|
|
26
|
+
: undefined;
|
|
27
|
+
|
|
28
|
+
const model: EditorModel = {
|
|
29
|
+
id: text.doc.guid,
|
|
30
|
+
content: text.content,
|
|
31
|
+
text: () => text.content!.toString(),
|
|
32
|
+
// https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
33
|
+
// TODO(dmaretskyi): Refactor as cursor-converter.
|
|
34
|
+
getCursorFromRange: (range: Range) => {
|
|
35
|
+
const from = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.from));
|
|
36
|
+
const to = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.to, -1));
|
|
37
|
+
return [arrayToString(from), arrayToString(to)].join(':');
|
|
38
|
+
},
|
|
39
|
+
getRangeFromCursor: (cursor: string) => {
|
|
40
|
+
invariant(text.doc);
|
|
41
|
+
const parts = cursor.split(':');
|
|
42
|
+
const from = Y.createAbsolutePositionFromRelativePosition(
|
|
43
|
+
Y.decodeRelativePosition(stringToArray(parts[0])),
|
|
44
|
+
text.doc,
|
|
45
|
+
);
|
|
46
|
+
const to = Y.createAbsolutePositionFromRelativePosition(
|
|
47
|
+
Y.decodeRelativePosition(stringToArray(parts[1])),
|
|
48
|
+
text.doc,
|
|
49
|
+
);
|
|
50
|
+
return from && to ? { from: from.index, to: to.index } : undefined;
|
|
51
|
+
},
|
|
52
|
+
extension: [modelState.init(() => model), yjs(text.content as YText, provider?.awareness)],
|
|
53
|
+
awareness: provider?.awareness,
|
|
54
|
+
peer: identity
|
|
55
|
+
? {
|
|
56
|
+
id: identity.identityKey.toHex(),
|
|
57
|
+
name: identity.profile?.displayName,
|
|
58
|
+
}
|
|
59
|
+
: undefined,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return model;
|
|
63
|
+
};
|
|
16
64
|
|
|
17
65
|
// Based on https://github.com/yjs/y-webrtc/blob/88baab2/src/y-webrtc.js.
|
|
18
66
|
|
|
@@ -22,16 +70,26 @@ const messageQueryAwareness = 3;
|
|
|
22
70
|
/**
|
|
23
71
|
* Yjs awareness provider on top of a DXOS space.
|
|
24
72
|
*/
|
|
25
|
-
export class
|
|
73
|
+
export class YAwarenessProvider extends Observable<any> {
|
|
26
74
|
private readonly _space: Space;
|
|
27
|
-
private readonly _awareness: Awareness;
|
|
75
|
+
private readonly _awareness: YP.Awareness;
|
|
28
76
|
private readonly _clientId: number;
|
|
29
77
|
private readonly _channel: string;
|
|
30
78
|
|
|
31
|
-
constructor({
|
|
79
|
+
constructor({
|
|
80
|
+
space,
|
|
81
|
+
doc,
|
|
82
|
+
channel,
|
|
83
|
+
awareness,
|
|
84
|
+
}: {
|
|
85
|
+
space: Space;
|
|
86
|
+
doc: Doc;
|
|
87
|
+
channel: string;
|
|
88
|
+
awareness?: YP.Awareness;
|
|
89
|
+
}) {
|
|
32
90
|
super();
|
|
33
91
|
this._space = space;
|
|
34
|
-
this._awareness = awareness ?? new
|
|
92
|
+
this._awareness = awareness ?? new YP.Awareness(doc);
|
|
35
93
|
this._channel = channel;
|
|
36
94
|
this._clientId = doc.clientID;
|
|
37
95
|
|
|
@@ -52,14 +110,11 @@ export class SpaceAwarenessProvider extends Observable<any> {
|
|
|
52
110
|
// Post local awareness state.
|
|
53
111
|
const encoderAwarenessState = encoding.createEncoder();
|
|
54
112
|
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
55
|
-
encoding.writeVarUint8Array(
|
|
56
|
-
encoderAwarenessState,
|
|
57
|
-
awarenessProtocol.encodeAwarenessUpdate(this.awareness, [this._clientId]),
|
|
58
|
-
);
|
|
113
|
+
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [this._clientId]));
|
|
59
114
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
60
115
|
}
|
|
61
116
|
|
|
62
|
-
get awareness(): Awareness {
|
|
117
|
+
get awareness(): YP.Awareness {
|
|
63
118
|
return this._awareness;
|
|
64
119
|
}
|
|
65
120
|
|
|
@@ -68,10 +123,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
|
|
|
68
123
|
const changedClients = added.concat(updated).concat(removed);
|
|
69
124
|
const encoderAwareness = encoding.createEncoder();
|
|
70
125
|
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
71
|
-
encoding.writeVarUint8Array(
|
|
72
|
-
encoderAwareness,
|
|
73
|
-
awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients),
|
|
74
|
-
);
|
|
126
|
+
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
75
127
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
76
128
|
}
|
|
77
129
|
|
|
@@ -92,7 +144,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
|
|
|
92
144
|
let sendReply = false;
|
|
93
145
|
switch (messageType) {
|
|
94
146
|
case messageAwareness: {
|
|
95
|
-
|
|
147
|
+
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
96
148
|
break;
|
|
97
149
|
}
|
|
98
150
|
|
|
@@ -100,7 +152,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
|
|
|
100
152
|
encoding.writeVarUint(encoder, messageAwareness);
|
|
101
153
|
encoding.writeVarUint8Array(
|
|
102
154
|
encoder,
|
|
103
|
-
|
|
155
|
+
YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())),
|
|
104
156
|
);
|
|
105
157
|
sendReply = true;
|
|
106
158
|
break;
|
|
@@ -121,6 +173,6 @@ export class SpaceAwarenessProvider extends Observable<any> {
|
|
|
121
173
|
}
|
|
122
174
|
|
|
123
175
|
private _handleBeforeUnload() {
|
|
124
|
-
|
|
176
|
+
YP.removeAwarenessStates(this._awareness, [this._clientId], 'window unload');
|
|
125
177
|
}
|
|
126
178
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
export { TextKind } from '@dxos/protocols/proto/dxos/echo/model/text';
|
|
6
6
|
export { Doc, YText, YXmlFragment } from '@dxos/text-model';
|
|
7
7
|
export { type Extension } from '@codemirror/state';
|
|
8
|
+
export { type EditorView } from '@codemirror/view';
|
|
8
9
|
|
|
9
10
|
export * from './components';
|
|
10
11
|
export * from './extensions';
|
|
11
12
|
export * from './hooks';
|
|
13
|
+
export { getToken } from './styles';
|
|
12
14
|
export * from './themes';
|
|
15
|
+
export * from './util';
|
package/src/styles/index.ts
CHANGED
package/src/styles/tokens.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import get from 'lodash.get';
|
|
5
6
|
import type { StyleSpec } from 'style-mod';
|
|
6
7
|
|
|
7
8
|
import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
|
|
@@ -12,3 +13,5 @@ export type ThemeStyles = {
|
|
|
12
13
|
|
|
13
14
|
// TODO(thure): Why export the whole theme? Can this be done differently?
|
|
14
15
|
export const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
16
|
+
|
|
17
|
+
export const getToken = (path: string, defaultValue: any = undefined) => get(tokens, path, defaultValue);
|
|
@@ -7,7 +7,7 @@ import * as decoding from 'lib0/decoding';
|
|
|
7
7
|
import * as encoding from 'lib0/encoding';
|
|
8
8
|
import { Observable } from 'lib0/observable';
|
|
9
9
|
import { useMemo } from 'react';
|
|
10
|
-
import * as
|
|
10
|
+
import * as YP from 'y-protocols/awareness';
|
|
11
11
|
import * as syncProtocol from 'y-protocols/sync';
|
|
12
12
|
import { Doc } from 'yjs';
|
|
13
13
|
|
|
@@ -15,9 +15,10 @@ import { Event } from '@dxos/async';
|
|
|
15
15
|
import { log } from '@dxos/log';
|
|
16
16
|
import { TextKind } from '@dxos/protocols/proto/dxos/echo/model/text';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { type EditorModel } from '../hooks';
|
|
19
|
+
import { cursorColor } from '../styles';
|
|
19
20
|
|
|
20
|
-
type Awareness =
|
|
21
|
+
type Awareness = YP.Awareness;
|
|
21
22
|
|
|
22
23
|
const messageSync = 0;
|
|
23
24
|
const messageAwareness = 1;
|
|
@@ -35,7 +36,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
35
36
|
super();
|
|
36
37
|
this._remoteUpdate = update;
|
|
37
38
|
this._doc = doc;
|
|
38
|
-
this._awareness = awareness ?? new
|
|
39
|
+
this._awareness = awareness ?? new YP.Awareness(doc);
|
|
39
40
|
|
|
40
41
|
this._doc.on('update', this._handleDocUpdate.bind(this));
|
|
41
42
|
this._awareness.on('update', this._handleAwarenessUpdate.bind(this));
|
|
@@ -55,10 +56,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
55
56
|
// Post local awareness state.
|
|
56
57
|
const encoderAwarenessState = encoding.createEncoder();
|
|
57
58
|
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
58
|
-
encoding.writeVarUint8Array(
|
|
59
|
-
encoderAwarenessState,
|
|
60
|
-
awarenessProtocol.encodeAwarenessUpdate(this.awareness, [this._doc.clientID]),
|
|
61
|
-
);
|
|
59
|
+
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [this._doc.clientID]));
|
|
62
60
|
void this._remoteUpdate.emit(encoding.toUint8Array(encoderAwarenessState));
|
|
63
61
|
}
|
|
64
62
|
|
|
@@ -79,10 +77,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
79
77
|
const changedClients = added.concat(updated).concat(removed);
|
|
80
78
|
const encoderAwareness = encoding.createEncoder();
|
|
81
79
|
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
82
|
-
encoding.writeVarUint8Array(
|
|
83
|
-
encoderAwareness,
|
|
84
|
-
awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients),
|
|
85
|
-
);
|
|
80
|
+
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
86
81
|
this._remoteUpdate.emit(encoding.toUint8Array(encoderAwareness));
|
|
87
82
|
}
|
|
88
83
|
|
|
@@ -109,7 +104,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
case messageAwareness: {
|
|
112
|
-
|
|
107
|
+
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
113
108
|
break;
|
|
114
109
|
}
|
|
115
110
|
|
|
@@ -117,7 +112,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
117
112
|
encoding.writeVarUint(encoder, messageAwareness);
|
|
118
113
|
encoding.writeVarUint8Array(
|
|
119
114
|
encoder,
|
|
120
|
-
|
|
115
|
+
YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())),
|
|
121
116
|
);
|
|
122
117
|
sendReply = true;
|
|
123
118
|
break;
|
|
@@ -138,7 +133,7 @@ export class AwarenessProvider extends Observable<any> {
|
|
|
138
133
|
}
|
|
139
134
|
|
|
140
135
|
private _handleBeforeUnload() {
|
|
141
|
-
|
|
136
|
+
YP.removeAwarenessStates(this._awareness, [this._doc.clientID], 'window unload');
|
|
142
137
|
}
|
|
143
138
|
}
|
|
144
139
|
|
package/src/themes/default.ts
CHANGED
|
@@ -49,12 +49,12 @@ export const defaultTheme: ThemeStyles = {
|
|
|
49
49
|
'&.cm-focused': {
|
|
50
50
|
outline: 'none',
|
|
51
51
|
},
|
|
52
|
-
'
|
|
52
|
+
'.cm-scroller': {
|
|
53
53
|
overflow: 'visible',
|
|
54
54
|
fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
|
|
55
55
|
},
|
|
56
56
|
|
|
57
|
-
'
|
|
57
|
+
'.cm-content': {
|
|
58
58
|
padding: 0,
|
|
59
59
|
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
60
60
|
fontSize: '16px',
|
|
@@ -77,17 +77,17 @@ export const defaultTheme: ThemeStyles = {
|
|
|
77
77
|
'&dark .cm-cursor, &dark .cm-dropCursor': {
|
|
78
78
|
borderLeft: '2px solid white',
|
|
79
79
|
},
|
|
80
|
-
'
|
|
80
|
+
'.cm-placeholder': {
|
|
81
81
|
fontWeight: 100,
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
//
|
|
85
85
|
// line
|
|
86
86
|
//
|
|
87
|
-
'
|
|
87
|
+
'.cm-line': {
|
|
88
88
|
paddingInline: 0,
|
|
89
89
|
},
|
|
90
|
-
'
|
|
90
|
+
'.cm-activeLine': {
|
|
91
91
|
background: 'inherit',
|
|
92
92
|
},
|
|
93
93
|
|
|
@@ -131,28 +131,28 @@ export const defaultTheme: ThemeStyles = {
|
|
|
131
131
|
mixBlendMode: 'screen',
|
|
132
132
|
},
|
|
133
133
|
|
|
134
|
-
'
|
|
134
|
+
'.cm-ySelectionInfo': {
|
|
135
135
|
padding: '2px 4px',
|
|
136
136
|
marginBlockStart: '-4px',
|
|
137
137
|
},
|
|
138
|
-
'
|
|
138
|
+
'.cm-ySelection, & .cm-selectionMatch': {
|
|
139
139
|
paddingBlockStart: '.15em',
|
|
140
140
|
paddingBlockEnd: '.15em',
|
|
141
141
|
},
|
|
142
|
-
'
|
|
142
|
+
'.cm-ySelectionCaret': {
|
|
143
143
|
display: 'inline-block',
|
|
144
144
|
insetBlockStart: '.1em',
|
|
145
145
|
blockSize: '1.4em',
|
|
146
146
|
verticalAlign: 'top',
|
|
147
147
|
},
|
|
148
|
-
'
|
|
148
|
+
'.cm-yLineSelection': {
|
|
149
149
|
margin: 0,
|
|
150
150
|
},
|
|
151
151
|
|
|
152
152
|
//
|
|
153
153
|
// link
|
|
154
154
|
//
|
|
155
|
-
'
|
|
155
|
+
'.cm-link': {
|
|
156
156
|
color: get(tokens, 'extend.colors.primary.500'),
|
|
157
157
|
textDecorationLine: 'underline',
|
|
158
158
|
textDecorationThickness: '1px',
|
|
@@ -164,34 +164,34 @@ export const defaultTheme: ThemeStyles = {
|
|
|
164
164
|
//
|
|
165
165
|
// tooltip
|
|
166
166
|
//
|
|
167
|
-
'
|
|
167
|
+
'.cm-tooltip': {
|
|
168
168
|
border: 'none',
|
|
169
169
|
},
|
|
170
|
-
'
|
|
170
|
+
'.cm-tooltip-below': {},
|
|
171
171
|
|
|
172
172
|
//
|
|
173
173
|
// autocomplete
|
|
174
174
|
//
|
|
175
|
-
'
|
|
175
|
+
'.cm-tooltip-autocomplete': {
|
|
176
176
|
marginTop: '4px',
|
|
177
177
|
marginLeft: '-3px',
|
|
178
178
|
},
|
|
179
|
-
'
|
|
180
|
-
'
|
|
181
|
-
'
|
|
179
|
+
'.cm-tooltip-autocomplete ul li': {},
|
|
180
|
+
'.cm-tooltip-autocomplete ul li[aria-selected]': {},
|
|
181
|
+
'.cm-completionIcon': {
|
|
182
182
|
display: 'none',
|
|
183
183
|
},
|
|
184
|
-
'
|
|
184
|
+
'.cm-completionLabel': {
|
|
185
185
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
186
186
|
},
|
|
187
|
-
'
|
|
187
|
+
'.cm-completionMatchedText': {
|
|
188
188
|
textDecoration: 'none',
|
|
189
189
|
},
|
|
190
190
|
|
|
191
191
|
//
|
|
192
192
|
// widgets
|
|
193
193
|
//
|
|
194
|
-
'
|
|
194
|
+
'.cm-widgetBuffer': {
|
|
195
195
|
display: 'none',
|
|
196
196
|
height: 0,
|
|
197
197
|
},
|
|
@@ -199,26 +199,32 @@ export const defaultTheme: ThemeStyles = {
|
|
|
199
199
|
//
|
|
200
200
|
// table
|
|
201
201
|
//
|
|
202
|
-
'
|
|
202
|
+
'.cm-table *': {
|
|
203
203
|
fontFamily: `${get(tokens, 'fontFamily.mono', []).join(',')} !important`,
|
|
204
204
|
textDecoration: 'none !important',
|
|
205
205
|
},
|
|
206
|
-
'
|
|
206
|
+
'.cm-table-head': {
|
|
207
207
|
padding: '2px 16px 2px 0px',
|
|
208
208
|
borderBottom: `1px solid ${get(tokens, 'extend.colors.neutral.500')}`,
|
|
209
209
|
fontWeight: 100,
|
|
210
210
|
textAlign: 'left',
|
|
211
211
|
color: get(tokens, 'extend.colors.neutral.500'),
|
|
212
212
|
},
|
|
213
|
-
'
|
|
213
|
+
'.cm-table-cell': {
|
|
214
214
|
padding: '2px 16px 2px 0px',
|
|
215
215
|
},
|
|
216
216
|
|
|
217
217
|
//
|
|
218
218
|
// image
|
|
219
219
|
//
|
|
220
|
-
'
|
|
221
|
-
|
|
220
|
+
'.cm-image': {
|
|
221
|
+
display: 'block',
|
|
222
|
+
height: '0',
|
|
223
|
+
},
|
|
224
|
+
'.cm-image.cm-loaded-image': {
|
|
225
|
+
height: 'auto',
|
|
226
|
+
borderTop: '0.5rem solid transparent',
|
|
227
|
+
borderBottom: '0.5rem solid transparent',
|
|
222
228
|
},
|
|
223
229
|
|
|
224
230
|
//
|
|
@@ -250,14 +256,14 @@ export const defaultTheme: ThemeStyles = {
|
|
|
250
256
|
* </div>
|
|
251
257
|
* </div
|
|
252
258
|
*/
|
|
253
|
-
'
|
|
259
|
+
'.cm-panels': {
|
|
254
260
|
border: `1px solid ${get(tokens, 'extend.colors.neutral.200')}`,
|
|
255
261
|
},
|
|
256
|
-
'
|
|
262
|
+
'.cm-panel': {
|
|
257
263
|
background: get(tokens, 'extend.colors.neutral.50'),
|
|
258
264
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
259
265
|
},
|
|
260
|
-
'
|
|
266
|
+
'.cm-button': {
|
|
261
267
|
margin: '4px',
|
|
262
268
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
263
269
|
background: get(tokens, 'extend.colors.neutral.100'),
|
|
@@ -271,32 +277,32 @@ export const defaultTheme: ThemeStyles = {
|
|
|
271
277
|
backgroundImage: 'none',
|
|
272
278
|
},
|
|
273
279
|
},
|
|
274
|
-
'
|
|
280
|
+
'.cm-panel input[type=checkbox]': {
|
|
275
281
|
marginRight: '0.4rem !important',
|
|
276
282
|
},
|
|
277
283
|
};
|
|
278
284
|
|
|
279
285
|
export const textTheme: ThemeStyles = {
|
|
280
|
-
'
|
|
286
|
+
'.cm-scroller': {
|
|
281
287
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
282
288
|
},
|
|
283
|
-
'
|
|
289
|
+
'.cm-placeholder': {
|
|
284
290
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
285
291
|
},
|
|
286
292
|
};
|
|
287
293
|
|
|
288
294
|
export const markdownTheme: ThemeStyles = {
|
|
289
295
|
// NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
|
|
290
|
-
'
|
|
296
|
+
'.cm-placeholder': {
|
|
291
297
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
292
298
|
},
|
|
293
299
|
};
|
|
294
300
|
|
|
295
301
|
export const codeTheme: ThemeStyles = {
|
|
296
|
-
'
|
|
302
|
+
'.cm-scroller': {
|
|
297
303
|
fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
|
|
298
304
|
},
|
|
299
|
-
'
|
|
305
|
+
'.cm-placeholder': {
|
|
300
306
|
fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
|
|
301
307
|
},
|
|
302
308
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Facet } from '@codemirror/state';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts indexes into the text document into stable peer-independent cursors.
|
|
9
|
+
*
|
|
10
|
+
* See:
|
|
11
|
+
* - https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
12
|
+
* - https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
13
|
+
*
|
|
14
|
+
* @param {assoc} number Negative values will associate the cursor with the previous character
|
|
15
|
+
* while positive - with the next one.
|
|
16
|
+
*/
|
|
17
|
+
export interface CursorConverter {
|
|
18
|
+
toCursor(position: number, assoc?: -1 | 1 | undefined): string;
|
|
19
|
+
fromCursor(cursor: string): number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DEFAULT_CURSOR_CONVERTER: CursorConverter = {
|
|
23
|
+
toCursor: (position) => position.toString(),
|
|
24
|
+
fromCursor: (cursor) => parseInt(cursor),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const CursorConverter = Facet.define<CursorConverter, CursorConverter>({
|
|
28
|
+
combine: (providers) => providers[0] ?? DEFAULT_CURSOR_CONVERTER,
|
|
29
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../../../src/extensions/mermaid.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,eAAO,MAAM,OAAO,QAAO,SAE1B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/extensions/util.ts"],"names":[],"mappings":"AAMA;;GAEG;AAEH,eAAO,MAAM,eAAe,kCAOR,CAAC"}
|