@dxos/react-ui-editor 0.3.11-main.bd26370 → 0.3.11-main.cf6cc01

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.
Files changed (208) hide show
  1. package/dist/lib/browser/index.mjs +1706 -1338
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +9 -8
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +14 -19
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -1
  11. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -1
  13. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  14. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  16. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  17. package/dist/types/src/{hooks/yjs → components/TextEditor}/yjs.stories.d.ts +1 -1
  18. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
  19. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
  20. package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
  21. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
  23. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
  24. package/dist/types/src/extensions/automerge/defs.d.ts +26 -0
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
  26. package/dist/types/src/extensions/automerge/index.d.ts +4 -0
  27. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
  28. package/dist/types/src/{hooks → extensions}/automerge/semaphore.d.ts +4 -4
  29. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
  30. package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
  31. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
  32. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
  33. package/dist/types/src/extensions/awareness.d.ts +46 -0
  34. package/dist/types/src/extensions/awareness.d.ts.map +1 -0
  35. package/dist/types/src/extensions/basic.d.ts.map +1 -0
  36. package/dist/types/src/extensions/blast.d.ts.map +1 -0
  37. package/dist/types/src/extensions/code.d.ts.map +1 -0
  38. package/dist/types/src/{components/TextEditor/extensions → extensions}/comments.d.ts +6 -4
  39. package/dist/types/src/extensions/comments.d.ts.map +1 -0
  40. package/dist/types/src/extensions/cursor.d.ts +25 -0
  41. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  42. package/dist/types/src/extensions/hr.d.ts.map +1 -0
  43. package/dist/types/src/extensions/image.d.ts.map +1 -0
  44. package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +4 -2
  45. package/dist/types/src/extensions/index.d.ts.map +1 -0
  46. package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
  47. package/dist/types/src/extensions/link.d.ts.map +1 -0
  48. package/dist/types/src/extensions/listener.d.ts +10 -0
  49. package/dist/types/src/extensions/listener.d.ts.map +1 -0
  50. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
  51. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
  52. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
  53. package/dist/types/src/extensions/mention.d.ts.map +1 -0
  54. package/dist/types/src/extensions/table.d.ts.map +1 -0
  55. package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
  56. package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
  57. package/dist/types/src/extensions/yjs/cursor.d.ts +4 -0
  58. package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -0
  59. package/dist/types/src/extensions/yjs/index.d.ts +2 -0
  60. package/dist/types/src/extensions/yjs/index.d.ts.map +1 -0
  61. package/dist/types/src/extensions/yjs/yjs.d.ts +4 -0
  62. package/dist/types/src/extensions/yjs/yjs.d.ts.map +1 -0
  63. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +1 -0
  64. package/dist/types/src/hooks/automerge.d.ts +3 -0
  65. package/dist/types/src/hooks/automerge.d.ts.map +1 -0
  66. package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
  67. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
  68. package/dist/types/src/hooks/index.d.ts +2 -0
  69. package/dist/types/src/hooks/index.d.ts.map +1 -1
  70. package/dist/types/src/hooks/useTextModel.d.ts +4 -8
  71. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  72. package/dist/types/src/hooks/{yjs/space-provider.d.ts → yjs.d.ts} +8 -8
  73. package/dist/types/src/hooks/yjs.d.ts.map +1 -0
  74. package/dist/types/src/index.d.ts +5 -0
  75. package/dist/types/src/index.d.ts.map +1 -1
  76. package/dist/types/src/styles/cursors.d.ts.map +1 -0
  77. package/dist/types/src/styles/index.d.ts +1 -0
  78. package/dist/types/src/styles/index.d.ts.map +1 -1
  79. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  80. package/dist/types/src/styles/tokens.d.ts +1 -0
  81. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  82. package/dist/types/src/testing/replicator.d.ts +2 -2
  83. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  84. package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
  85. package/dist/types/src/themes/default.d.ts.map +1 -0
  86. package/dist/types/src/themes/index.d.ts.map +1 -0
  87. package/dist/types/src/util.d.ts.map +1 -0
  88. package/package.json +23 -21
  89. package/src/components/TextEditor/MarkdownEditor.stories.tsx +36 -36
  90. package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
  91. package/src/components/TextEditor/TextEditor.tsx +74 -70
  92. package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
  93. package/src/components/TextEditor/codemirror.stories.ts +3 -2
  94. package/src/components/TextEditor/comments.stories.tsx +315 -0
  95. package/src/components/TextEditor/index.ts +0 -3
  96. package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
  97. package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +18 -17
  98. package/src/extensions/automerge/automerge.ts +94 -0
  99. package/src/extensions/automerge/cursor.ts +52 -0
  100. package/src/extensions/automerge/defs.ts +62 -0
  101. package/src/extensions/automerge/index.ts +7 -0
  102. package/src/{hooks → extensions}/automerge/semaphore.ts +14 -12
  103. package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
  104. package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
  105. package/src/extensions/awareness.ts +309 -0
  106. package/src/{components/TextEditor/extensions → extensions}/code.ts +14 -7
  107. package/src/{components/TextEditor/extensions → extensions}/comments.ts +50 -113
  108. package/src/extensions/cursor.ts +46 -0
  109. package/src/{components/TextEditor/extensions → extensions}/hr.ts +4 -5
  110. package/src/extensions/image.ts +87 -0
  111. package/src/{components/TextEditor/extensions → extensions}/index.ts +4 -2
  112. package/src/{components/TextEditor/extensions → extensions}/link.ts +116 -62
  113. package/src/extensions/listener.ts +35 -0
  114. package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +4 -13
  115. package/src/{components/TextEditor/extensions → extensions}/table.ts +10 -4
  116. package/src/{components/TextEditor/extensions → extensions}/tasklist.ts +3 -5
  117. package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
  118. package/src/extensions/yjs/cursor.ts +22 -0
  119. package/src/extensions/yjs/index.ts +5 -0
  120. package/src/{hooks → extensions}/yjs/yjs.test.ts +1 -25
  121. package/src/extensions/yjs/yjs.ts +16 -0
  122. package/src/hooks/automerge.ts +52 -0
  123. package/src/hooks/awareness-provider.ts +120 -0
  124. package/src/hooks/index.ts +2 -0
  125. package/src/hooks/useTextModel.ts +20 -96
  126. package/src/hooks/{yjs/space-provider.ts → yjs.ts} +49 -19
  127. package/src/index.ts +5 -0
  128. package/src/styles/index.ts +1 -0
  129. package/src/styles/markdown.ts +0 -2
  130. package/src/styles/tokens.ts +3 -0
  131. package/src/testing/replicator.ts +10 -15
  132. package/src/{components/TextEditor/themes → themes}/default.ts +52 -79
  133. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
  134. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
  135. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
  136. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
  137. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
  138. package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
  139. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
  140. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
  141. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
  142. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
  143. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
  144. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
  145. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
  146. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
  147. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
  148. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +0 -3
  149. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
  150. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
  151. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
  152. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
  153. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
  154. package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
  155. package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
  156. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
  157. package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
  158. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
  159. package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
  160. package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
  161. package/dist/types/src/hooks/automerge/index.d.ts +0 -3
  162. package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
  163. package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
  164. package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
  165. package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
  166. package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
  167. package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
  168. package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
  169. package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
  170. package/dist/types/src/hooks/yjs/index.d.ts +0 -3
  171. package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
  172. package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
  173. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
  174. package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
  175. package/src/components/TextEditor/extensions/image.ts +0 -69
  176. package/src/components/TextEditor/extensions/listener.ts +0 -23
  177. package/src/components/TextEditor/extensions/mermaid.ts +0 -11
  178. package/src/components/TextEditor/extensions/tooltip.ts +0 -61
  179. package/src/hooks/automerge/handle.ts +0 -14
  180. package/src/hooks/automerge/index.ts +0 -6
  181. package/src/hooks/automerge/plugin.ts +0 -134
  182. package/src/hooks/yjs/index.ts +0 -6
  183. /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
  184. /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
  185. /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
  186. /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
  187. /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
  188. /package/dist/types/src/{components/TextEditor/extensions → extensions}/hr.d.ts +0 -0
  189. /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
  190. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
  191. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
  192. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
  193. /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
  194. /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
  195. /package/dist/types/src/{components/TextEditor/extensions → extensions}/tasklist.d.ts +0 -0
  196. /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
  197. /package/dist/types/src/{hooks → extensions}/yjs/yjs.test.d.ts +0 -0
  198. /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
  199. /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
  200. /package/dist/types/src/{components/TextEditor/extensions/util.d.ts → util.d.ts} +0 -0
  201. /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
  202. /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
  203. /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
  204. /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
  205. /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
  206. /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
  207. /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
  208. /package/src/{components/TextEditor/extensions/util.ts → util.ts} +0 -0
@@ -2,36 +2,22 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Extension, StateField } from '@codemirror/state';
6
- import get from 'lodash.get';
5
+ import { StateField, type Extension } from '@codemirror/state';
7
6
  import { useEffect, useState } from 'react';
8
- import { yCollab } from 'y-codemirror.next';
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 { invariant } from '@dxos/invariant';
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 { automergePlugin } from './automerge';
26
- import { SpaceAwarenessProvider } from './yjs';
13
+ import { createAutomergeModel } from './automerge';
14
+ import { createYjsModel } from './yjs';
27
15
 
28
- // TODO(burdon): Move.
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,22 @@ export type Range = {
44
30
 
45
31
  export type CommentRange = {
46
32
  id: string;
47
- // TODO(burdon): Split into begin/end?
48
- cursor: string;
33
+ cursor: string; // TODO(burdon): Rename range.
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;
54
- // TODO(burdon): Remove.
55
- content: string | YText | YXmlFragment | DocAccessor;
56
39
  text: () => string;
57
- getCursorFromRange?: (value: Range) => string;
58
- getRangeFromCursor?: (cursor: string) => Range;
59
40
  extension?: Extension;
60
- awareness?: Awareness;
41
+
42
+ // TODO(burdon): Remove.
43
+ content: string | YText | YXmlFragment | DocAccessor;
44
+
45
+ // TODO(burdon): Move into extension.
46
+ awareness?: YP.Awareness;
47
+
48
+ // TODO(burdon): Remove.
61
49
  peer?: {
62
50
  id: string;
63
51
  name?: string;
@@ -75,82 +63,18 @@ export type UseTextModelProps = {
75
63
  // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
76
64
  export const useTextModel = (props: UseTextModelProps): EditorModel | undefined => {
77
65
  const { identity, space, text } = props;
78
- const [model, setModel] = useState<EditorModel | undefined>(() => createModel(props));
66
+ const [model, setModel] = useState<EditorModel | undefined>();
79
67
  useEffect(() => setModel(createModel(props)), [identity, space, text]);
80
68
  return model;
81
69
  };
82
70
 
83
71
  const createModel = (props: UseTextModelProps) => {
84
72
  const { text } = props;
85
- if (isActualAutomergeObject(text)) {
73
+ if (isAutomergeObject(text)) {
86
74
  return createAutomergeModel(props);
87
- } else {
88
- if (!text?.doc) {
89
- return undefined;
90
- }
91
-
75
+ } else if (text?.doc) {
92
76
  return createYjsModel(props);
77
+ } else {
78
+ return undefined;
93
79
  }
94
80
  };
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
- const parts = cursor.split(':');
114
- const from = Y.createAbsolutePositionFromRelativePosition(
115
- Y.decodeRelativePosition(stringToArray(parts[0])),
116
- text.doc!,
117
- );
118
- const to = Y.createAbsolutePositionFromRelativePosition(
119
- Y.decodeRelativePosition(stringToArray(parts[1])),
120
- text.doc!,
121
- );
122
- return { from: from!.index, to: to!.index };
123
- },
124
- extension: [yCollab(text.content as YText, provider?.awareness), modelState.init(() => model)],
125
- awareness: provider?.awareness,
126
- peer: identity
127
- ? {
128
- id: identity.identityKey.toHex(),
129
- name: identity.profile?.displayName,
130
- }
131
- : undefined,
132
- };
133
-
134
- return model;
135
- };
136
-
137
- const createAutomergeModel = ({ identity, text }: UseTextModelProps): EditorModel => {
138
- const obj = text as any as AutomergeTextCompat;
139
- const doc = getRawDoc(obj, [obj.field]);
140
-
141
- const model: EditorModel = {
142
- id: obj.id,
143
- content: doc,
144
- text: () => get(doc.handle.docSync(), doc.path),
145
- // TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
146
- extension: [automergePlugin(doc.handle, doc.path), modelState.init(() => model)],
147
- peer: identity
148
- ? {
149
- id: identity.identityKey.toHex(),
150
- name: identity.profile?.displayName,
151
- }
152
- : undefined,
153
- };
154
-
155
- return model;
156
- };
@@ -5,14 +5,40 @@
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 awarenessProtocol from 'y-protocols/awareness';
8
+ import * as YP from 'y-protocols/awareness';
9
9
  import { type Doc } from 'yjs';
10
10
 
11
+ import { invariant } from '@dxos/invariant';
11
12
  import { log } from '@dxos/log';
12
13
  import { type Space } from '@dxos/react-client/echo';
13
14
  import { type GossipMessage } from '@dxos/react-client/mesh';
14
-
15
- type Awareness = awarenessProtocol.Awareness;
15
+ import type { YText } from '@dxos/text-model';
16
+
17
+ import { type EditorModel, modelState, type UseTextModelProps } from './useTextModel';
18
+ import { yjs } from '../extensions';
19
+
20
+ export const createYjsModel = ({ identity, space, text }: UseTextModelProps): EditorModel => {
21
+ invariant(text?.doc && text?.content);
22
+ const provider = space
23
+ ? new YAwarenessProvider({ space, doc: text.doc, channel: `yjs.awareness.${text.id}` })
24
+ : undefined;
25
+
26
+ const model: EditorModel = {
27
+ id: text.doc.guid,
28
+ content: text.content,
29
+ text: () => text.content!.toString(),
30
+ extension: [modelState.init(() => model), yjs(text.content as YText, provider?.awareness)],
31
+ awareness: provider?.awareness,
32
+ peer: identity
33
+ ? {
34
+ id: identity.identityKey.toHex(),
35
+ name: identity.profile?.displayName,
36
+ }
37
+ : undefined,
38
+ };
39
+
40
+ return model;
41
+ };
16
42
 
17
43
  // Based on https://github.com/yjs/y-webrtc/blob/88baab2/src/y-webrtc.js.
18
44
 
@@ -22,16 +48,26 @@ const messageQueryAwareness = 3;
22
48
  /**
23
49
  * Yjs awareness provider on top of a DXOS space.
24
50
  */
25
- export class SpaceAwarenessProvider extends Observable<any> {
51
+ export class YAwarenessProvider extends Observable<any> {
26
52
  private readonly _space: Space;
27
- private readonly _awareness: Awareness;
53
+ private readonly _awareness: YP.Awareness;
28
54
  private readonly _clientId: number;
29
55
  private readonly _channel: string;
30
56
 
31
- constructor({ space, doc, channel, awareness }: { space: Space; doc: Doc; channel: string; awareness?: Awareness }) {
57
+ constructor({
58
+ space,
59
+ doc,
60
+ channel,
61
+ awareness,
62
+ }: {
63
+ space: Space;
64
+ doc: Doc;
65
+ channel: string;
66
+ awareness?: YP.Awareness;
67
+ }) {
32
68
  super();
33
69
  this._space = space;
34
- this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
70
+ this._awareness = awareness ?? new YP.Awareness(doc);
35
71
  this._channel = channel;
36
72
  this._clientId = doc.clientID;
37
73
 
@@ -52,14 +88,11 @@ export class SpaceAwarenessProvider extends Observable<any> {
52
88
  // Post local awareness state.
53
89
  const encoderAwarenessState = encoding.createEncoder();
54
90
  encoding.writeVarUint(encoderAwarenessState, messageAwareness);
55
- encoding.writeVarUint8Array(
56
- encoderAwarenessState,
57
- awarenessProtocol.encodeAwarenessUpdate(this.awareness, [this._clientId]),
58
- );
91
+ encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [this._clientId]));
59
92
  void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
60
93
  }
61
94
 
62
- get awareness(): Awareness {
95
+ get awareness(): YP.Awareness {
63
96
  return this._awareness;
64
97
  }
65
98
 
@@ -68,10 +101,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
68
101
  const changedClients = added.concat(updated).concat(removed);
69
102
  const encoderAwareness = encoding.createEncoder();
70
103
  encoding.writeVarUint(encoderAwareness, messageAwareness);
71
- encoding.writeVarUint8Array(
72
- encoderAwareness,
73
- awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients),
74
- );
104
+ encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
75
105
  void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
76
106
  }
77
107
 
@@ -92,7 +122,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
92
122
  let sendReply = false;
93
123
  switch (messageType) {
94
124
  case messageAwareness: {
95
- awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
125
+ YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
96
126
  break;
97
127
  }
98
128
 
@@ -100,7 +130,7 @@ export class SpaceAwarenessProvider extends Observable<any> {
100
130
  encoding.writeVarUint(encoder, messageAwareness);
101
131
  encoding.writeVarUint8Array(
102
132
  encoder,
103
- awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())),
133
+ YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())),
104
134
  );
105
135
  sendReply = true;
106
136
  break;
@@ -121,6 +151,6 @@ export class SpaceAwarenessProvider extends Observable<any> {
121
151
  }
122
152
 
123
153
  private _handleBeforeUnload() {
124
- awarenessProtocol.removeAwarenessStates(this._awareness, [this._clientId], 'window unload');
154
+ YP.removeAwarenessStates(this._awareness, [this._clientId], 'window unload');
125
155
  }
126
156
  }
package/src/index.ts CHANGED
@@ -5,6 +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';
11
+ export * from './extensions';
10
12
  export * from './hooks';
13
+ export { getToken } from './styles';
14
+ export * from './themes';
15
+ export * from './util';
@@ -2,5 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './cursors';
5
6
  export * from './markdown';
6
7
  export * from './tokens';
@@ -39,10 +39,8 @@ export const codeBlock = 'mlb-2 font-mono bg-neutral-500/10 p-3 rounded';
39
39
 
40
40
  export const inlineUrl = mx(code, 'px-1');
41
41
 
42
- // TODO(burdon): Replace with widget.
43
42
  export const blockquote = mx('pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30', light);
44
43
 
45
- // TODO(burdon): Replace with widget.
46
44
  export const horizontalRule =
47
45
  'flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800';
48
46
 
@@ -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 awarenessProtocol from 'y-protocols/awareness';
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 { cursorColor, type EditorModel } from '../hooks';
18
+ import { type EditorModel } from '../hooks';
19
+ import { cursorColor } from '../styles';
19
20
 
20
- type Awareness = awarenessProtocol.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 awarenessProtocol.Awareness(doc);
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
- awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
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
- awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())),
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
- awarenessProtocol.removeAwarenessStates(this._awareness, [this._doc.clientID], 'window unload');
136
+ YP.removeAwarenessStates(this._awareness, [this._doc.clientID], 'window unload');
142
137
  }
143
138
  }
144
139
 
@@ -4,10 +4,10 @@
4
4
 
5
5
  import get from 'lodash.get';
6
6
 
7
- import { type ThemeStyles, tokens } from '../../../styles';
7
+ import { type ThemeStyles, tokens } from '../styles';
8
8
 
9
- // TODO(burdon): Can we erase All of the default CM styles?
10
- // TODO(burdon): Theme styles seem to be added multiple times?
9
+ // TODO(burdon): Can we use @apply and import css file?
10
+ // https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply?
11
11
 
12
12
  /**
13
13
  * Minimal styles.
@@ -49,12 +49,12 @@ export const defaultTheme: ThemeStyles = {
49
49
  '&.cm-focused': {
50
50
  outline: 'none',
51
51
  },
52
- '& .cm-scroller': {
52
+ '.cm-scroller': {
53
53
  overflow: 'visible',
54
54
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
55
55
  },
56
56
 
57
- '& .cm-content': {
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,19 @@ export const defaultTheme: ThemeStyles = {
77
77
  '&dark .cm-cursor, &dark .cm-dropCursor': {
78
78
  borderLeft: '2px solid white',
79
79
  },
80
- '& .cm-placeholder': {
80
+ '.cm-placeholder': {
81
81
  fontWeight: 100,
82
82
  },
83
83
 
84
84
  //
85
85
  // line
86
86
  //
87
- '& .cm-line': {
87
+ '.cm-line': {
88
88
  paddingInline: 0,
89
+ // TODO(burdon): Other values leave gaps between lines when highlighted.
90
+ // lineHeight: 'normal',
89
91
  },
90
- '& .cm-activeLine': {
92
+ '.cm-activeLine': {
91
93
  background: 'inherit',
92
94
  },
93
95
 
@@ -119,40 +121,10 @@ export const defaultTheme: ThemeStyles = {
119
121
  backgroundColor: get(tokens, 'extend.colors.yellow.700'),
120
122
  },
121
123
 
122
- //
123
- // collaboration
124
- // TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
125
- //
126
-
127
- '&light .cm-ySelection, &light .cm-yLineSelection': {
128
- mixBlendMode: 'multiply',
129
- },
130
- '&dark .cm-ySelection, &dark .cm-yLineSelection': {
131
- mixBlendMode: 'screen',
132
- },
133
-
134
- '& .cm-ySelectionInfo': {
135
- padding: '2px 4px',
136
- marginBlockStart: '-4px',
137
- },
138
- '& .cm-ySelection, & .cm-selectionMatch': {
139
- paddingBlockStart: '.15em',
140
- paddingBlockEnd: '.15em',
141
- },
142
- '& .cm-ySelectionCaret': {
143
- display: 'inline-block',
144
- insetBlockStart: '.1em',
145
- blockSize: '1.4em',
146
- verticalAlign: 'top',
147
- },
148
- '& .cm-yLineSelection': {
149
- margin: 0,
150
- },
151
-
152
124
  //
153
125
  // link
154
126
  //
155
- '& .cm-link': {
127
+ '.cm-link': {
156
128
  color: get(tokens, 'extend.colors.primary.500'),
157
129
  textDecorationLine: 'underline',
158
130
  textDecorationThickness: '1px',
@@ -164,75 +136,74 @@ export const defaultTheme: ThemeStyles = {
164
136
  //
165
137
  // tooltip
166
138
  //
167
- '& .cm-tooltip': {
139
+ '.cm-tooltip': {
168
140
  border: 'none',
169
141
  },
170
- '& .cm-tooltip-below': {},
142
+ '.cm-tooltip-below': {},
171
143
 
172
144
  //
173
145
  // autocomplete
174
146
  //
175
- '& .cm-tooltip-autocomplete': {
147
+ '.cm-tooltip-autocomplete': {
176
148
  marginTop: '4px',
177
149
  marginLeft: '-3px',
178
150
  },
179
- '& .cm-tooltip-autocomplete ul li': {},
180
- '& .cm-tooltip-autocomplete ul li[aria-selected]': {},
181
- '& .cm-completionIcon': {
151
+ '.cm-tooltip-autocomplete ul li': {},
152
+ '.cm-tooltip-autocomplete ul li[aria-selected]': {},
153
+ '.cm-completionIcon': {
182
154
  display: 'none',
183
155
  },
184
- '& .cm-completionLabel': {
156
+ '.cm-completionLabel': {
185
157
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
186
158
  },
187
- '& .cm-completionMatchedText': {
159
+ '.cm-completionMatchedText': {
188
160
  textDecoration: 'none',
189
161
  },
190
162
 
191
- //
192
- // widgets
193
- //
194
- '& .cm-widgetBuffer': {
195
- display: 'none',
196
- height: 0,
197
- },
198
-
199
163
  //
200
164
  // table
201
165
  //
202
- '& .cm-table *': {
166
+ '.cm-table *': {
203
167
  fontFamily: `${get(tokens, 'fontFamily.mono', []).join(',')} !important`,
204
168
  textDecoration: 'none !important',
205
169
  },
206
- '& .cm-table-head': {
170
+ '.cm-table-head': {
207
171
  padding: '2px 16px 2px 0px',
208
172
  borderBottom: `1px solid ${get(tokens, 'extend.colors.neutral.500')}`,
209
173
  fontWeight: 100,
210
174
  textAlign: 'left',
211
175
  color: get(tokens, 'extend.colors.neutral.500'),
212
176
  },
213
- '& .cm-table-cell': {
177
+ '.cm-table-cell': {
214
178
  padding: '2px 16px 2px 0px',
215
179
  },
216
180
 
217
181
  //
218
182
  // image
219
183
  //
220
- '& .cm-image': {
221
- margin: '0.5rem 0',
184
+ '.cm-image': {
185
+ display: 'block',
186
+ height: '0',
187
+ },
188
+ '.cm-image.cm-loaded-image': {
189
+ height: 'auto',
190
+ borderTop: '0.5rem solid transparent',
191
+ borderBottom: '0.5rem solid transparent',
222
192
  },
223
193
 
224
194
  //
225
195
  // font size
226
- // TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
196
+ // TODO(thure): This appears to be the best or only way to set selection caret heights,
197
+ // but it's far more verbose than it needs to be.
227
198
  //
228
- ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
229
- const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
230
-
231
- acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
232
- acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
233
- acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
234
- return acc;
235
- }, {}),
199
+ // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
200
+ // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
201
+ //
202
+ // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
203
+ // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
204
+ // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
205
+ // return acc;
206
+ // }, {}),
236
207
 
237
208
  /**
238
209
  * Panels
@@ -250,14 +221,14 @@ export const defaultTheme: ThemeStyles = {
250
221
  * </div>
251
222
  * </div
252
223
  */
253
- '& .cm-panels': {
224
+ '.cm-panels': {
254
225
  border: `1px solid ${get(tokens, 'extend.colors.neutral.200')}`,
255
226
  },
256
- '& .cm-panel': {
227
+ '.cm-panel': {
257
228
  background: get(tokens, 'extend.colors.neutral.50'),
258
229
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
259
230
  },
260
- '& .cm-button': {
231
+ '.cm-button': {
261
232
  margin: '4px',
262
233
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
263
234
  background: get(tokens, 'extend.colors.neutral.100'),
@@ -271,32 +242,34 @@ export const defaultTheme: ThemeStyles = {
271
242
  backgroundImage: 'none',
272
243
  },
273
244
  },
274
- '& .cm-panel input[type=checkbox]': {
245
+ '.cm-panel input[type=checkbox]': {
275
246
  marginRight: '0.4rem !important',
276
247
  },
277
248
  };
278
249
 
279
250
  export const textTheme: ThemeStyles = {
280
- '& .cm-scroller': {
251
+ '.cm-scroller': {
281
252
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
282
253
  },
283
- '& .cm-placeholder': {
254
+ '.cm-placeholder': {
284
255
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
285
256
  },
286
257
  };
287
258
 
288
259
  export const markdownTheme: ThemeStyles = {
289
- // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
290
- '& .cm-placeholder': {
260
+ '.cm-scroller': {
261
+ fontFamily: get(tokens, 'fontFamily.body', []).join(','),
262
+ },
263
+ '.cm-placeholder': {
291
264
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
292
265
  },
293
266
  };
294
267
 
295
268
  export const codeTheme: ThemeStyles = {
296
- '& .cm-scroller': {
269
+ '.cm-scroller': {
297
270
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
298
271
  },
299
- '& .cm-placeholder': {
272
+ '.cm-placeholder': {
300
273
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
301
274
  },
302
275
  };
@@ -1 +0,0 @@
1
- {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/autocomplete.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;CAC1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,iBAAkB,mBAAmB,4CAgC7D,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"basic.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/basic.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,WAAW,uDAAwD,kBAAkB,KAAG,SAAS,EAa5E,CAAC"}