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

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 (262) hide show
  1. package/dist/lib/browser/index.mjs +2561 -1619
  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 +14 -15
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
  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/comments.stories.d.ts +26 -0
  13. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
  15. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  17. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  18. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  19. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  20. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  21. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  23. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  24. package/dist/types/src/components/index.d.ts +1 -0
  25. package/dist/types/src/components/index.d.ts.map +1 -1
  26. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
  27. package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
  28. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
  29. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  30. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  31. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  32. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  33. package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
  34. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
  35. package/dist/types/src/extensions/automerge/defs.d.ts +24 -0
  36. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
  37. package/dist/types/src/extensions/automerge/index.d.ts +3 -0
  38. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
  39. package/dist/types/src/extensions/automerge/semaphore.d.ts +15 -0
  40. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
  41. package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
  42. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
  43. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
  44. package/dist/types/src/extensions/awareness.d.ts +46 -0
  45. package/dist/types/src/extensions/awareness.d.ts.map +1 -0
  46. package/dist/types/src/extensions/basic.d.ts +8 -0
  47. package/dist/types/src/extensions/basic.d.ts.map +1 -0
  48. package/dist/types/src/extensions/blast.d.ts.map +1 -0
  49. package/dist/types/src/extensions/comments.d.ts +67 -0
  50. package/dist/types/src/extensions/comments.d.ts.map +1 -0
  51. package/dist/types/src/extensions/cursor.d.ts +25 -0
  52. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  53. package/dist/types/src/extensions/index.d.ts +13 -0
  54. package/dist/types/src/extensions/index.d.ts.map +1 -0
  55. package/dist/types/src/extensions/listener.d.ts +10 -0
  56. package/dist/types/src/extensions/listener.d.ts.map +1 -0
  57. package/dist/types/src/extensions/markdown/bundle.d.ts +14 -0
  58. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
  59. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  60. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  61. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  62. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  63. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  64. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
  65. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  66. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  67. package/dist/types/src/extensions/markdown/index.d.ts +11 -0
  68. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
  69. package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/link.d.ts +1 -0
  70. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  71. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  72. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  73. package/dist/types/src/extensions/mention.d.ts.map +1 -0
  74. package/dist/types/src/extensions/outliner.d.ts +4 -0
  75. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  76. package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
  77. package/dist/types/src/hooks/awareness-provider.d.ts +31 -0
  78. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
  79. package/dist/types/src/hooks/defs.d.ts +25 -0
  80. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  81. package/dist/types/src/hooks/index.d.ts +5 -1
  82. package/dist/types/src/hooks/index.d.ts.map +1 -1
  83. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  84. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  85. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  86. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  87. package/dist/types/src/hooks/useTextEditor.d.ts +39 -0
  88. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  89. package/dist/types/src/hooks/useTextModel.d.ts +2 -31
  90. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  91. package/dist/types/src/index.d.ts +5 -0
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/styles/cursors.d.ts.map +1 -0
  94. package/dist/types/src/styles/index.d.ts +1 -0
  95. package/dist/types/src/styles/index.d.ts.map +1 -1
  96. package/dist/types/src/styles/markdown.d.ts +2 -2
  97. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  98. package/dist/types/src/styles/tokens.d.ts +1 -0
  99. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  100. package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
  101. package/dist/types/src/themes/default.d.ts.map +1 -0
  102. package/dist/types/src/themes/index.d.ts.map +1 -0
  103. package/dist/types/src/util.d.ts +7 -0
  104. package/dist/types/src/util.d.ts.map +1 -0
  105. package/package.json +48 -36
  106. package/src/components/TextEditor/MarkdownEditor.stories.tsx +93 -102
  107. package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
  108. package/src/components/TextEditor/TextEditor.tsx +138 -97
  109. package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
  110. package/src/components/TextEditor/comments.stories.tsx +357 -0
  111. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  112. package/src/components/TextEditor/index.ts +0 -3
  113. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  114. package/src/components/Toolbar/Toolbar.tsx +166 -0
  115. package/src/components/Toolbar/index.ts +5 -0
  116. package/src/components/index.ts +1 -0
  117. package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +23 -19
  118. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  119. package/src/extensions/automerge/automerge.test.ts +13 -0
  120. package/src/extensions/automerge/automerge.ts +103 -0
  121. package/src/extensions/automerge/cursor.ts +52 -0
  122. package/src/extensions/automerge/defs.ts +41 -0
  123. package/src/extensions/automerge/index.ts +6 -0
  124. package/src/extensions/automerge/semaphore.ts +81 -0
  125. package/src/extensions/automerge/update-automerge.ts +49 -0
  126. package/src/{hooks → extensions}/automerge/update-codemirror.ts +17 -9
  127. package/src/extensions/awareness.ts +310 -0
  128. package/src/{components/TextEditor/extensions → extensions}/basic.ts +17 -10
  129. package/src/extensions/comments.ts +476 -0
  130. package/src/extensions/cursor.ts +50 -0
  131. package/src/{components/TextEditor/extensions → extensions}/index.ts +4 -8
  132. package/src/extensions/listener.ts +35 -0
  133. package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +23 -28
  134. package/src/extensions/markdown/code.ts +150 -0
  135. package/src/extensions/markdown/formatting.ts +188 -0
  136. package/src/extensions/markdown/heading.ts +59 -0
  137. package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +12 -27
  138. package/src/extensions/markdown/hr.ts +71 -0
  139. package/src/extensions/markdown/image.ts +87 -0
  140. package/src/extensions/markdown/index.ts +14 -0
  141. package/src/{components/TextEditor/extensions → extensions/markdown}/link.ts +119 -62
  142. package/src/{components/TextEditor/extensions → extensions/markdown}/table.ts +10 -4
  143. package/src/extensions/markdown/tasklist.ts +110 -0
  144. package/src/{components/TextEditor/extensions → extensions}/mention.ts +9 -2
  145. package/src/extensions/outliner.ts +12 -0
  146. package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
  147. package/src/hooks/awareness-provider.ts +124 -0
  148. package/src/hooks/defs.ts +42 -0
  149. package/src/hooks/index.ts +6 -1
  150. package/src/hooks/useActionHandler.ts +62 -0
  151. package/src/hooks/useEditorView.ts +29 -0
  152. package/src/hooks/useTextEditor.ts +153 -0
  153. package/src/hooks/useTextModel.ts +33 -115
  154. package/src/index.ts +5 -0
  155. package/src/styles/index.ts +1 -0
  156. package/src/styles/markdown.ts +9 -12
  157. package/src/styles/tokens.ts +3 -0
  158. package/src/{components/TextEditor/themes → themes}/default.ts +54 -81
  159. package/src/util.ts +41 -0
  160. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  161. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  162. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
  163. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +0 -9
  164. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
  165. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
  166. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
  167. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +0 -48
  168. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
  169. package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
  170. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
  171. package/dist/types/src/components/TextEditor/extensions/index.d.ts +0 -17
  172. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
  173. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
  174. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
  175. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
  176. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +0 -17
  177. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
  178. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
  179. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +0 -3
  180. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
  181. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
  182. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +0 -3
  183. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
  184. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
  185. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
  186. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
  187. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
  188. package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
  189. package/dist/types/src/components/TextEditor/extensions/util.d.ts +0 -5
  190. package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
  191. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
  192. package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
  193. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
  194. package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
  195. package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
  196. package/dist/types/src/hooks/automerge/index.d.ts +0 -3
  197. package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
  198. package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
  199. package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
  200. package/dist/types/src/hooks/automerge/semaphore.d.ts +0 -19
  201. package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
  202. package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
  203. package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
  204. package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
  205. package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
  206. package/dist/types/src/hooks/yjs/index.d.ts +0 -3
  207. package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
  208. package/dist/types/src/hooks/yjs/space-provider.d.ts +0 -27
  209. package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
  210. package/dist/types/src/hooks/yjs/yjs.stories.d.ts +0 -12
  211. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
  212. package/dist/types/src/hooks/yjs/yjs.test.d.ts +0 -2
  213. package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
  214. package/dist/types/src/testing/index.d.ts +0 -3
  215. package/dist/types/src/testing/index.d.ts.map +0 -1
  216. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  217. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  218. package/dist/types/src/testing/proto/index.d.ts +0 -2
  219. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  220. package/dist/types/src/testing/replicator.d.ts +0 -45
  221. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  222. package/src/components/TextEditor/codemirror.stories.ts +0 -114
  223. package/src/components/TextEditor/extensions/code.ts +0 -99
  224. package/src/components/TextEditor/extensions/comments.ts +0 -388
  225. package/src/components/TextEditor/extensions/hr.ts +0 -74
  226. package/src/components/TextEditor/extensions/image.ts +0 -69
  227. package/src/components/TextEditor/extensions/listener.ts +0 -23
  228. package/src/components/TextEditor/extensions/markdown/index.ts +0 -6
  229. package/src/components/TextEditor/extensions/mermaid.ts +0 -11
  230. package/src/components/TextEditor/extensions/tasklist.ts +0 -121
  231. package/src/components/TextEditor/extensions/tooltip.ts +0 -61
  232. package/src/components/TextEditor/extensions/util.ts +0 -18
  233. package/src/hooks/automerge/handle.ts +0 -14
  234. package/src/hooks/automerge/index.ts +0 -6
  235. package/src/hooks/automerge/plugin.ts +0 -134
  236. package/src/hooks/automerge/semaphore.ts +0 -78
  237. package/src/hooks/automerge/update-automerge.ts +0 -44
  238. package/src/hooks/yjs/index.ts +0 -6
  239. package/src/hooks/yjs/space-provider.ts +0 -126
  240. package/src/hooks/yjs/yjs.stories.tsx +0 -56
  241. package/src/hooks/yjs/yjs.test.ts +0 -59
  242. package/src/testing/index.ts +0 -6
  243. package/src/testing/proto/gen/schema.ts +0 -49
  244. package/src/testing/proto/index.ts +0 -5
  245. package/src/testing/proto/schema.proto +0 -15
  246. package/src/testing/replicator.ts +0 -189
  247. /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
  248. /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
  249. /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
  250. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/code.d.ts +0 -0
  251. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
  252. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/hr.d.ts +0 -0
  253. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/image.d.ts +0 -0
  254. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/table.d.ts +0 -0
  255. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/tasklist.d.ts +0 -0
  256. /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
  257. /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
  258. /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
  259. /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
  260. /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
  261. /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
  262. /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
@@ -0,0 +1,29 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import { type RefCallback, useState } from 'react';
7
+
8
+ /**
9
+ * Hook for accessing the editor view.
10
+ *
11
+ * ```tsx
12
+ * const Test = () => {
13
+ * const [editorRef, editorView] = useEditorView();
14
+ * useEffect(() => {
15
+ * editorView?.focus();
16
+ * }, [editorView]);
17
+ * return <TextEditor ref={editorRef} />;
18
+ * };
19
+ * ```
20
+ */
21
+ export const useEditorView = (): [RefCallback<EditorView | null>, EditorView | null] => {
22
+ const [view, setView] = useState<EditorView | null>(null);
23
+ return [
24
+ (ref: EditorView | null) => {
25
+ setView(ref);
26
+ },
27
+ view,
28
+ ];
29
+ };
@@ -0,0 +1,153 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import {
6
+ EditorSelection,
7
+ EditorState,
8
+ type EditorStateConfig,
9
+ type Extension,
10
+ type StateEffect,
11
+ } from '@codemirror/state';
12
+ import { EditorView } from '@codemirror/view';
13
+ import { type RefObject, useEffect, useRef, useState } from 'react';
14
+
15
+ import { log } from '@dxos/log';
16
+ import { type ThemeMode } from '@dxos/react-ui';
17
+ import { isNotFalsy } from '@dxos/util';
18
+
19
+ import { type ThemeStyles } from '..//styles';
20
+ import { defaultTheme } from '../themes';
21
+ import { logChanges } from '../util';
22
+
23
+ export type UseTextEditorOptions = {
24
+ autoFocus?: boolean;
25
+ scrollTo?: StateEffect<any>;
26
+ debug?: boolean;
27
+ } & EditorStateConfig;
28
+
29
+ export type UseTextEditor = {
30
+ parentRef: RefObject<HTMLDivElement>;
31
+ view?: EditorView;
32
+ };
33
+
34
+ /**
35
+ * Hook for creating editor.
36
+ */
37
+ export const useTextEditor = ({
38
+ autoFocus,
39
+ scrollTo,
40
+ debug,
41
+ doc,
42
+ selection,
43
+ extensions,
44
+ }: UseTextEditorOptions = {}): UseTextEditor => {
45
+ const onUpdate = useRef<() => void>();
46
+ const [view, setView] = useState<EditorView>();
47
+ const parentRef = useRef<HTMLDivElement>(null);
48
+ useEffect(() => {
49
+ if (parentRef.current) {
50
+ // https://codemirror.net/docs/ref/#state.EditorStateConfig
51
+ const state = EditorState.create({
52
+ doc,
53
+ selection,
54
+ extensions: [
55
+ // TODO(burdon): Doesn't catch errors in keymap functions.
56
+ EditorView.exceptionSink.of((err) => {
57
+ log.catch(err);
58
+ }),
59
+ extensions,
60
+ EditorView.updateListener.of(() => {
61
+ onUpdate.current?.();
62
+ }),
63
+ ].filter(isNotFalsy),
64
+ });
65
+
66
+ // https://codemirror.net/docs/ref/#view.EditorViewConfig
67
+ const view = new EditorView({
68
+ parent: parentRef.current,
69
+ scrollTo,
70
+ state,
71
+ // NOTE: Uncomment to debug/monitor all transactions.
72
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
73
+ dispatchTransactions: (trs, view) => {
74
+ if (debug) {
75
+ logChanges(trs);
76
+ }
77
+ view.update(trs);
78
+ },
79
+ });
80
+
81
+ setView(view);
82
+ }
83
+
84
+ return () => {
85
+ view?.destroy();
86
+ };
87
+ }, [parentRef]);
88
+
89
+ useEffect(() => {
90
+ if (view) {
91
+ // Select end of line if not specified.
92
+ if (!selection && !view.state.selection.main.anchor) {
93
+ selection = EditorSelection.single(view.state.doc.line(1).to);
94
+ }
95
+
96
+ // Set selection after first update (since content may rerender on focus).
97
+ // TODO(burdon): BUG on first render may appear in middle of formatted heading.
98
+ // TODO(burdon): Make invisible until first render?
99
+ if (selection || scrollTo) {
100
+ onUpdate.current = () => {
101
+ onUpdate.current = undefined;
102
+ view.dispatch({ selection, effects: scrollTo && [scrollTo], scrollIntoView: !scrollTo });
103
+ };
104
+ }
105
+
106
+ if (autoFocus) {
107
+ view.focus();
108
+ }
109
+ }
110
+ }, [view, autoFocus, selection, scrollTo]);
111
+
112
+ return { parentRef, view };
113
+ };
114
+
115
+ // TODO(burdon): Factor out extension factories.
116
+
117
+ export type DataExtensionsOptions = {
118
+ readonly?: boolean;
119
+ };
120
+
121
+ // TODO(burdon): Pass in TextObject (remove model).
122
+ export const createDataExtensions = ({ readonly = false } = {}): Extension => {
123
+ return [
124
+ //
125
+ EditorState.readOnly.of(readonly),
126
+ EditorView.editable.of(!readonly),
127
+ ];
128
+ };
129
+
130
+ export type ThemeExtensionsOptions = {
131
+ theme?: ThemeStyles;
132
+ themeMode?: ThemeMode;
133
+ lineWrap?: boolean;
134
+ slots?: {
135
+ editor?: {
136
+ className?: string;
137
+ };
138
+ content?: {
139
+ className?: string;
140
+ };
141
+ };
142
+ };
143
+
144
+ export const createThemeExtensions = ({ theme, themeMode, slots }: ThemeExtensionsOptions = {}): Extension => {
145
+ return [
146
+ //
147
+ EditorView.baseTheme(defaultTheme),
148
+ theme && EditorView.theme(theme),
149
+ EditorView.darkTheme.of(themeMode === 'dark'),
150
+ EditorView.editorAttributes.of({ class: slots?.editor?.className ?? '' }),
151
+ EditorView.contentAttributes.of({ class: slots?.content?.className ?? '' }),
152
+ ].filter(isNotFalsy);
153
+ };
@@ -2,67 +2,19 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Extension, StateField } from '@codemirror/state';
6
5
  import get from 'lodash.get';
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';
11
7
 
8
+ import { generateName } from '@dxos/display-name';
9
+ import { type AutomergeTextCompat, getRawDoc } from '@dxos/echo-schema';
12
10
  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';
11
+ import { isAutomergeObject, type Space, type TextObject } from '@dxos/react-client/echo';
21
12
  import { type Identity } from '@dxos/react-client/halo';
22
- import type { YText, YXmlFragment } from '@dxos/text-model';
23
- import { arrayToString, stringToArray } from '@dxos/util';
24
13
 
25
- import { automergePlugin } from './automerge';
26
- import { SpaceAwarenessProvider } from './yjs';
27
-
28
- // TODO(burdon): Move.
29
- type Awareness = awarenessProtocol.Awareness;
30
-
31
- /**
32
- * State field makes the model available to other extensions.
33
- */
34
- // TODO(burdon): Use facet?
35
- export const modelState = StateField.define<EditorModel | undefined>({
36
- create: () => undefined,
37
- update: (model) => model,
38
- });
39
-
40
- export type Range = {
41
- from: number;
42
- to: number;
43
- };
44
-
45
- export type CommentRange = {
46
- id: string;
47
- // TODO(burdon): Split into begin/end?
48
- cursor: string;
49
- };
50
-
51
- // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
52
- export type EditorModel = {
53
- id: string;
54
- // TODO(burdon): Remove.
55
- content: string | YText | YXmlFragment | DocAccessor;
56
- text: () => string;
57
- getCursorFromRange?: (value: Range) => string;
58
- getRangeFromCursor?: (cursor: string) => Range;
59
- extension?: Extension;
60
- awareness?: Awareness;
61
- peer?: {
62
- id: string;
63
- name?: string;
64
- };
65
- };
14
+ import { SpaceAwarenessProvider } from './awareness-provider';
15
+ import { type EditorModel, modelState } from './defs';
16
+ import { automerge, awareness } from '../extensions';
17
+ import { cursorColor } from '../styles';
66
18
 
67
19
  // TODO(burdon): Remove space/identity dependency. Define interface for the framework re content and presence.
68
20
  export type UseTextModelProps = {
@@ -71,79 +23,45 @@ export type UseTextModelProps = {
71
23
  text?: TextObject;
72
24
  };
73
25
 
74
- // TODO(burdon): Remove YJS/Automerge deps (from UI component -- create abstraction; incl. all ECHO/Space deps).
75
- // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
76
26
  export const useTextModel = (props: UseTextModelProps): EditorModel | undefined => {
77
27
  const { identity, space, text } = props;
78
- const [model, setModel] = useState<EditorModel | undefined>(() => createModel(props));
28
+ const [model, setModel] = useState<EditorModel | undefined>();
79
29
  useEffect(() => setModel(createModel(props)), [identity, space, text]);
80
30
  return model;
81
31
  };
82
32
 
83
- const createModel = (props: UseTextModelProps) => {
84
- const { text } = props;
85
- if (isActualAutomergeObject(text)) {
86
- return createAutomergeModel(props);
87
- } else {
88
- if (!text?.doc) {
89
- return undefined;
90
- }
91
-
92
- return createYjsModel(props);
93
- }
94
- };
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 => {
33
+ const createModel = ({ space, identity, text }: UseTextModelProps) => {
34
+ invariant(isAutomergeObject(text));
138
35
  const obj = text as any as AutomergeTextCompat;
139
36
  const doc = getRawDoc(obj, [obj.field]);
140
37
 
38
+ const awarenessProvider =
39
+ space &&
40
+ new SpaceAwarenessProvider({
41
+ space,
42
+ channel: `automerge.awareness.${obj.id}`,
43
+ info: {
44
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : undefined,
45
+ color: cursorColor.color,
46
+ lightColor: cursorColor.light,
47
+ },
48
+ peerId: identity?.identityKey.toHex() ?? 'Anonymous',
49
+ });
50
+
51
+ const extensions = [
52
+ //
53
+ modelState.init(() => model),
54
+ automerge({ handle: doc.handle, path: doc.path }),
55
+ ];
56
+ if (awarenessProvider) {
57
+ extensions.push(awareness(awarenessProvider));
58
+ }
59
+
141
60
  const model: EditorModel = {
142
61
  id: obj.id,
143
62
  content: doc,
144
63
  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)],
64
+ extension: extensions,
147
65
  peer: identity
148
66
  ? {
149
67
  id: identity.identityKey.toHex(),
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';
@@ -8,18 +8,17 @@ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
8
8
 
9
9
  // TODO(burdon): Better way to align vertically than negative margin? Font-specific?
10
10
  // https://tailwindcss.com/docs/font-weight
11
- export const headings: Record<HeadingLevel, string[]> = {
12
- 1: ['mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl', '-ml-[10px]'],
13
- 2: ['mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl', '-ml-[8px]'],
14
- 3: ['mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl', '-ml-[7px]'],
15
- 4: ['mbs-4 mbe-2 font-medium text-inherit no-underline text-xl', '-ml-[6px]'],
16
- 5: ['mbs-4 mbe-2 font-medium text-inherit no-underline text-lg', '-ml-[5px]'],
17
- 6: ['mbs-4 mbe-2 font-medium text-inherit no-underline', '-ml-[4px]'],
11
+ export const headings: Record<HeadingLevel, string> = {
12
+ 1: 'mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl',
13
+ 2: 'mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl',
14
+ 3: 'mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl',
15
+ 4: 'mbs-4 mbe-2 font-medium text-inherit no-underline text-xl',
16
+ 5: 'mbs-4 mbe-2 font-medium text-inherit no-underline text-lg',
17
+ 6: 'mbs-4 mbe-2 font-medium text-inherit no-underline',
18
18
  };
19
19
 
20
- export const heading = (level: HeadingLevel, readonly?: boolean) => {
21
- const [style, offset] = headings[level];
22
- return mx(style, readonly && offset);
20
+ export const heading = (level: HeadingLevel) => {
21
+ return headings[level];
23
22
  };
24
23
 
25
24
  export const text = 'text-neutral-800 dark:text-neutral-200';
@@ -39,10 +38,8 @@ export const codeBlock = 'mlb-2 font-mono bg-neutral-500/10 p-3 rounded';
39
38
 
40
39
  export const inlineUrl = mx(code, 'px-1');
41
40
 
42
- // TODO(burdon): Replace with widget.
43
41
  export const blockquote = mx('pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30', light);
44
42
 
45
- // TODO(burdon): Replace with widget.
46
43
  export const horizontalRule =
47
44
  'flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800';
48
45
 
@@ -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);