@dxos/react-ui-editor 0.3.11-main.a996413 → 0.3.11-main.afa73e0

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 (186) hide show
  1. package/dist/lib/browser/index.mjs +2014 -1648
  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 +16 -13
  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/extensions/autocomplete.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
  16. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
  17. package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
  18. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
  19. package/dist/types/src/extensions/automerge/defs.d.ts +24 -0
  20. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
  21. package/dist/types/src/extensions/automerge/index.d.ts +3 -0
  22. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
  23. package/dist/types/src/extensions/automerge/semaphore.d.ts +14 -0
  24. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
  26. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
  27. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
  28. package/dist/types/src/extensions/awareness.d.ts +46 -0
  29. package/dist/types/src/extensions/awareness.d.ts.map +1 -0
  30. package/dist/types/src/extensions/basic.d.ts +4 -5
  31. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  32. package/dist/types/src/extensions/comments.d.ts +32 -19
  33. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  34. package/dist/types/src/extensions/cursor.d.ts +25 -0
  35. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  36. package/dist/types/src/extensions/index.d.ts +4 -7
  37. package/dist/types/src/extensions/index.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  39. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  40. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  42. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  45. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  46. package/dist/types/src/extensions/markdown/index.d.ts +7 -0
  47. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  49. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  50. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  51. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  52. package/dist/types/src/extensions/outliner.d.ts +4 -0
  53. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  54. package/dist/types/src/hooks/awareness-provider.d.ts +31 -0
  55. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
  56. package/dist/types/src/hooks/defs.d.ts +25 -0
  57. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  58. package/dist/types/src/hooks/index.d.ts +3 -1
  59. package/dist/types/src/hooks/index.d.ts.map +1 -1
  60. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  61. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  62. package/dist/types/src/hooks/useTextModel.d.ts +2 -31
  63. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  64. package/dist/types/src/index.d.ts +3 -0
  65. package/dist/types/src/index.d.ts.map +1 -1
  66. package/dist/types/src/styles/cursors.d.ts.map +1 -0
  67. package/dist/types/src/styles/index.d.ts +1 -0
  68. package/dist/types/src/styles/index.d.ts.map +1 -1
  69. package/dist/types/src/styles/markdown.d.ts +2 -2
  70. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  71. package/dist/types/src/styles/tokens.d.ts +1 -0
  72. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  73. package/dist/types/src/themes/default.d.ts.map +1 -1
  74. package/dist/types/src/util.d.ts +7 -0
  75. package/dist/types/src/util.d.ts.map +1 -0
  76. package/package.json +34 -35
  77. package/src/components/TextEditor/MarkdownEditor.stories.tsx +82 -83
  78. package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
  79. package/src/components/TextEditor/TextEditor.tsx +122 -93
  80. package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
  81. package/src/components/TextEditor/comments.stories.tsx +357 -0
  82. package/src/extensions/autocomplete.ts +23 -19
  83. package/src/extensions/automerge/automerge.ts +88 -0
  84. package/src/extensions/automerge/cursor.ts +52 -0
  85. package/src/extensions/automerge/defs.ts +41 -0
  86. package/src/extensions/automerge/index.ts +6 -0
  87. package/src/{hooks → extensions}/automerge/semaphore.ts +34 -29
  88. package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
  89. package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
  90. package/src/extensions/awareness.ts +310 -0
  91. package/src/extensions/basic.ts +22 -17
  92. package/src/extensions/comments.ts +252 -229
  93. package/src/extensions/cursor.ts +50 -0
  94. package/src/extensions/index.ts +4 -7
  95. package/src/extensions/markdown/bundle.ts +20 -30
  96. package/src/extensions/markdown/code.ts +127 -0
  97. package/src/extensions/markdown/heading.ts +59 -0
  98. package/src/extensions/markdown/highlight.ts +11 -26
  99. package/src/extensions/markdown/hr.ts +71 -0
  100. package/src/extensions/markdown/image.ts +87 -0
  101. package/src/extensions/markdown/index.ts +7 -0
  102. package/src/extensions/{link.ts → markdown/link.ts} +85 -88
  103. package/src/extensions/{table.ts → markdown/table.ts} +10 -4
  104. package/src/extensions/markdown/tasklist.ts +110 -0
  105. package/src/extensions/mention.ts +9 -2
  106. package/src/extensions/outliner.ts +12 -0
  107. package/src/hooks/awareness-provider.ts +124 -0
  108. package/src/hooks/defs.ts +42 -0
  109. package/src/hooks/index.ts +4 -1
  110. package/src/hooks/useTextEditor.ts +29 -0
  111. package/src/hooks/useTextModel.ts +30 -116
  112. package/src/index.ts +3 -0
  113. package/src/styles/index.ts +1 -0
  114. package/src/styles/markdown.ts +9 -12
  115. package/src/styles/tokens.ts +3 -0
  116. package/src/themes/default.ts +49 -78
  117. package/src/util.ts +41 -0
  118. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  119. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  120. package/dist/types/src/extensions/code.d.ts.map +0 -1
  121. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  122. package/dist/types/src/extensions/image.d.ts.map +0 -1
  123. package/dist/types/src/extensions/link.d.ts.map +0 -1
  124. package/dist/types/src/extensions/mermaid.d.ts +0 -3
  125. package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
  126. package/dist/types/src/extensions/table.d.ts.map +0 -1
  127. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  128. package/dist/types/src/extensions/util.d.ts +0 -5
  129. package/dist/types/src/extensions/util.d.ts.map +0 -1
  130. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
  131. package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
  132. package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
  133. package/dist/types/src/hooks/automerge/index.d.ts +0 -3
  134. package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
  135. package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
  136. package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
  137. package/dist/types/src/hooks/automerge/semaphore.d.ts +0 -19
  138. package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
  139. package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
  140. package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
  141. package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
  142. package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
  143. package/dist/types/src/hooks/yjs/index.d.ts +0 -3
  144. package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
  145. package/dist/types/src/hooks/yjs/space-provider.d.ts +0 -27
  146. package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
  147. package/dist/types/src/hooks/yjs/yjs.stories.d.ts +0 -12
  148. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
  149. package/dist/types/src/hooks/yjs/yjs.test.d.ts +0 -2
  150. package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
  151. package/dist/types/src/testing/index.d.ts +0 -3
  152. package/dist/types/src/testing/index.d.ts.map +0 -1
  153. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  154. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  155. package/dist/types/src/testing/proto/index.d.ts +0 -2
  156. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  157. package/dist/types/src/testing/replicator.d.ts +0 -45
  158. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  159. package/src/components/TextEditor/codemirror.stories.ts +0 -114
  160. package/src/extensions/code.ts +0 -99
  161. package/src/extensions/hr.ts +0 -74
  162. package/src/extensions/image.ts +0 -69
  163. package/src/extensions/mermaid.ts +0 -11
  164. package/src/extensions/tasklist.ts +0 -121
  165. package/src/extensions/util.ts +0 -18
  166. package/src/hooks/automerge/handle.ts +0 -14
  167. package/src/hooks/automerge/index.ts +0 -6
  168. package/src/hooks/automerge/plugin.ts +0 -132
  169. package/src/hooks/yjs/index.ts +0 -6
  170. package/src/hooks/yjs/space-provider.ts +0 -126
  171. package/src/hooks/yjs/yjs.stories.tsx +0 -56
  172. package/src/hooks/yjs/yjs.test.ts +0 -59
  173. package/src/testing/index.ts +0 -6
  174. package/src/testing/proto/gen/schema.ts +0 -49
  175. package/src/testing/proto/index.ts +0 -5
  176. package/src/testing/proto/schema.proto +0 -15
  177. package/src/testing/replicator.ts +0 -189
  178. /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
  179. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  180. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  181. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  182. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  183. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  184. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
  185. /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
  186. /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
@@ -2,67 +2,20 @@
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';
13
+ import { isNotNullOrUndefined } from '@dxos/util';
24
14
 
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 | undefined;
59
- extension?: Extension;
60
- awareness?: Awareness;
61
- peer?: {
62
- id: string;
63
- name?: string;
64
- };
65
- };
15
+ import { SpaceAwarenessProvider } from './awareness-provider';
16
+ import { type EditorModel, modelState } from './defs';
17
+ import { automerge, awareness, AwarenessProvider } from '../extensions';
18
+ import { cursorColor } from '../styles';
66
19
 
67
20
  // TODO(burdon): Remove space/identity dependency. Define interface for the framework re content and presence.
68
21
  export type UseTextModelProps = {
@@ -71,80 +24,41 @@ export type UseTextModelProps = {
71
24
  text?: TextObject;
72
25
  };
73
26
 
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
27
  export const useTextModel = (props: UseTextModelProps): EditorModel | undefined => {
77
28
  const { identity, space, text } = props;
78
- const [model, setModel] = useState<EditorModel | undefined>(() => createModel(props));
29
+ const [model, setModel] = useState<EditorModel | undefined>();
79
30
  useEffect(() => setModel(createModel(props)), [identity, space, text]);
80
31
  return model;
81
32
  };
82
33
 
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
- 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 => {
34
+ const createModel = ({ space, identity, text }: UseTextModelProps) => {
35
+ invariant(isAutomergeObject(text));
139
36
  const obj = text as any as AutomergeTextCompat;
140
37
  const doc = getRawDoc(obj, [obj.field]);
141
38
 
39
+ const awarenessProvider =
40
+ space &&
41
+ new SpaceAwarenessProvider({
42
+ space,
43
+ channel: `automerge.awareness.${obj.id}`,
44
+ info: {
45
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : undefined,
46
+ color: cursorColor.color,
47
+ lightColor: cursorColor.light,
48
+ },
49
+ peerId: identity?.identityKey.toHex() ?? 'Anonymous',
50
+ });
51
+
142
52
  const model: EditorModel = {
143
53
  id: obj.id,
144
54
  content: doc,
145
55
  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)],
56
+ extension: [
57
+ modelState.init(() => model),
58
+ automerge({ handle: doc.handle, path: doc.path }),
59
+ awarenessProvider && AwarenessProvider.of(awarenessProvider),
60
+ awareness(),
61
+ ].filter(isNotNullOrUndefined),
148
62
  peer: identity
149
63
  ? {
150
64
  id: identity.identityKey.toHex(),
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';
@@ -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);
@@ -6,8 +6,8 @@ import get from 'lodash.get';
6
6
 
7
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,17 @@ 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
89
  },
90
- '& .cm-activeLine': {
90
+ '.cm-activeLine': {
91
91
  background: 'inherit',
92
92
  },
93
93
 
@@ -119,40 +119,10 @@ export const defaultTheme: ThemeStyles = {
119
119
  backgroundColor: get(tokens, 'extend.colors.yellow.700'),
120
120
  },
121
121
 
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
122
  //
153
123
  // link
154
124
  //
155
- '& .cm-link': {
125
+ '.cm-link': {
156
126
  color: get(tokens, 'extend.colors.primary.500'),
157
127
  textDecorationLine: 'underline',
158
128
  textDecorationThickness: '1px',
@@ -164,75 +134,74 @@ export const defaultTheme: ThemeStyles = {
164
134
  //
165
135
  // tooltip
166
136
  //
167
- '& .cm-tooltip': {
137
+ '.cm-tooltip': {
168
138
  border: 'none',
169
139
  },
170
- '& .cm-tooltip-below': {},
140
+ '.cm-tooltip-below': {},
171
141
 
172
142
  //
173
143
  // autocomplete
174
144
  //
175
- '& .cm-tooltip-autocomplete': {
145
+ '.cm-tooltip-autocomplete': {
176
146
  marginTop: '4px',
177
147
  marginLeft: '-3px',
178
148
  },
179
- '& .cm-tooltip-autocomplete ul li': {},
180
- '& .cm-tooltip-autocomplete ul li[aria-selected]': {},
181
- '& .cm-completionIcon': {
149
+ '.cm-tooltip-autocomplete ul li': {},
150
+ '.cm-tooltip-autocomplete ul li[aria-selected]': {},
151
+ '.cm-completionIcon': {
182
152
  display: 'none',
183
153
  },
184
- '& .cm-completionLabel': {
154
+ '.cm-completionLabel': {
185
155
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
186
156
  },
187
- '& .cm-completionMatchedText': {
157
+ '.cm-completionMatchedText': {
188
158
  textDecoration: 'none',
189
159
  },
190
160
 
191
- //
192
- // widgets
193
- //
194
- '& .cm-widgetBuffer': {
195
- display: 'none',
196
- height: 0,
197
- },
198
-
199
161
  //
200
162
  // table
201
163
  //
202
- '& .cm-table *': {
164
+ '.cm-table *': {
203
165
  fontFamily: `${get(tokens, 'fontFamily.mono', []).join(',')} !important`,
204
166
  textDecoration: 'none !important',
205
167
  },
206
- '& .cm-table-head': {
168
+ '.cm-table-head': {
207
169
  padding: '2px 16px 2px 0px',
208
170
  borderBottom: `1px solid ${get(tokens, 'extend.colors.neutral.500')}`,
209
171
  fontWeight: 100,
210
172
  textAlign: 'left',
211
173
  color: get(tokens, 'extend.colors.neutral.500'),
212
174
  },
213
- '& .cm-table-cell': {
175
+ '.cm-table-cell': {
214
176
  padding: '2px 16px 2px 0px',
215
177
  },
216
178
 
217
179
  //
218
180
  // image
219
181
  //
220
- '& .cm-image': {
221
- margin: '0.5rem 0',
182
+ '.cm-image': {
183
+ display: 'block',
184
+ height: '0',
185
+ },
186
+ '.cm-image.cm-loaded-image': {
187
+ height: 'auto',
188
+ borderTop: '0.5rem solid transparent',
189
+ borderBottom: '0.5rem solid transparent',
222
190
  },
223
191
 
224
192
  //
225
193
  // 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.
194
+ // TODO(thure): This appears to be the best or only way to set selection caret heights,
195
+ // but it's far more verbose than it needs to be.
227
196
  //
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
- }, {}),
197
+ // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
198
+ // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
199
+ //
200
+ // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
201
+ // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
202
+ // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
203
+ // return acc;
204
+ // }, {}),
236
205
 
237
206
  /**
238
207
  * Panels
@@ -250,14 +219,14 @@ export const defaultTheme: ThemeStyles = {
250
219
  * </div>
251
220
  * </div
252
221
  */
253
- '& .cm-panels': {
222
+ '.cm-panels': {
254
223
  border: `1px solid ${get(tokens, 'extend.colors.neutral.200')}`,
255
224
  },
256
- '& .cm-panel': {
225
+ '.cm-panel': {
257
226
  background: get(tokens, 'extend.colors.neutral.50'),
258
227
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
259
228
  },
260
- '& .cm-button': {
229
+ '.cm-button': {
261
230
  margin: '4px',
262
231
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
263
232
  background: get(tokens, 'extend.colors.neutral.100'),
@@ -271,32 +240,34 @@ export const defaultTheme: ThemeStyles = {
271
240
  backgroundImage: 'none',
272
241
  },
273
242
  },
274
- '& .cm-panel input[type=checkbox]': {
243
+ '.cm-panel input[type=checkbox]': {
275
244
  marginRight: '0.4rem !important',
276
245
  },
277
246
  };
278
247
 
279
248
  export const textTheme: ThemeStyles = {
280
- '& .cm-scroller': {
249
+ '.cm-scroller': {
281
250
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
282
251
  },
283
- '& .cm-placeholder': {
252
+ '.cm-placeholder': {
284
253
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
285
254
  },
286
255
  };
287
256
 
288
257
  export const markdownTheme: ThemeStyles = {
289
- // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
290
- '& .cm-placeholder': {
258
+ '.cm-scroller': {
259
+ fontFamily: get(tokens, 'fontFamily.body', []).join(','),
260
+ },
261
+ '.cm-placeholder': {
291
262
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
292
263
  },
293
264
  };
294
265
 
295
266
  export const codeTheme: ThemeStyles = {
296
- '& .cm-scroller': {
267
+ '.cm-scroller': {
297
268
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
298
269
  },
299
- '& .cm-placeholder': {
270
+ '.cm-placeholder': {
300
271
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
301
272
  },
302
273
  };
package/src/util.ts ADDED
@@ -0,0 +1,41 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import type { Transaction } from '@codemirror/state';
6
+
7
+ import { log } from '@dxos/log';
8
+
9
+ /**
10
+ * CodeMirror callbacks swallow errors so wrap handlers.
11
+ */
12
+ // TODO(burdon): EditorView.exceptionSink should catch errors.
13
+ export const callbackWrapper = <T extends Function>(fn: T): T =>
14
+ ((...args: any[]) => {
15
+ try {
16
+ return fn(...args);
17
+ } catch (err) {
18
+ log.catch(err);
19
+ }
20
+ }) as unknown as T;
21
+
22
+ export const logChanges = (trs: readonly Transaction[]) => {
23
+ const changes = trs
24
+ .flatMap((tr) => {
25
+ if (tr.changes.empty) {
26
+ return undefined;
27
+ }
28
+
29
+ const changes: any[] = [];
30
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) =>
31
+ changes.push(JSON.stringify({ fromA, toA, fromB, toB, inserted: inserted.toString() })),
32
+ );
33
+
34
+ return changes;
35
+ })
36
+ .filter(Boolean);
37
+
38
+ if (changes.length) {
39
+ console.log('changes', changes);
40
+ }
41
+ };
@@ -1,12 +0,0 @@
1
- import { type StoryObj } from '@storybook/html';
2
- import { Client, type PublicKey } from '@dxos/react-client';
3
- declare const _default: {
4
- title: string;
5
- };
6
- export default _default;
7
- export declare const Default: StoryObj<{
8
- id: number;
9
- client: Client;
10
- spaceKey: PublicKey;
11
- }>;
12
- //# sourceMappingURL=codemirror.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"codemirror.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/TextEditor/codemirror.stories.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMhD,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;;;;AAS5D,wBAEE;AAqEF,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAoBjF,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/extensions/code.ts"],"names":[],"mappings":"AAuDA,eAAO,MAAM,IAAI,+CA2ChB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"hr.d.ts","sourceRoot":"","sources":["../../../../src/extensions/hr.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,EAAE,+CAsBd,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/extensions/image.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,mBAAmB,CAAC;AAG3B,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B,eAAO,MAAM,KAAK,aAAa,YAAY,KAAQ,SAMlD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../../src/extensions/link.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,mBAAmB,CAAC;AAW3B,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,aAAa,WAAW,KAAQ,SAyDhD,CAAC"}
@@ -1,3 +0,0 @@
1
- import { type Extension } from '@codemirror/state';
2
- export declare const mermaid: () => Extension;
3
- //# sourceMappingURL=mermaid.d.ts.map
@@ -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":"table.d.ts","sourceRoot":"","sources":["../../../../src/extensions/table.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,QAAQ,EAAmB,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AASnH,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B;;;GAGG;AACH,eAAO,MAAM,KAAK,aAAa,YAAY,8BAM1C,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasklist.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasklist.ts"],"names":[],"mappings":"AA+DA,MAAM,MAAM,eAAe,GAAG,EAAE,CAAC;AAEjC,eAAO,MAAM,QAAQ,aAAa,eAAe,4CAuDhD,CAAC"}
@@ -1,5 +0,0 @@
1
- /**
2
- * CodeMirror callbacks swallow errors so wrap handlers.
3
- */
4
- export declare const callbackWrapper: <T extends Function>(fn: T) => T;
5
- //# sourceMappingURL=util.d.ts.map
@@ -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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"automerge.stories.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/automerge/automerge.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,uBAAuB,CAAC;AAQ/B,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAGtD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,KAAK,WAAW,GAAG;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,IAAI,EAAE,CAAC;CACd,CAAC;;;;;;AA+DF,wBAIE;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
@@ -1,9 +0,0 @@
1
- import type * as A from '@dxos/automerge/automerge';
2
- export type IDocHandle<T = any> = {
3
- docSync(): A.Doc<T> | undefined;
4
- change(callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): void;
5
- changeAt(heads: A.Heads, callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): string[] | undefined;
6
- addListener(event: 'change', listener: () => void): void;
7
- removeListener(event: 'change', listener: () => void): void;
8
- };
9
- //# sourceMappingURL=handle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/automerge/handle.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,CAAC,MAAM,2BAA2B,CAAC;AAEpD,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,GAAG,IAAI;IAChC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACpE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;IAEtG,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACzD,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAC7D,CAAC"}
@@ -1,3 +0,0 @@
1
- export { type IDocHandle } from './handle';
2
- export { automergePlugin, type AutomergePlugin } from './plugin';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/automerge/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC"}
@@ -1,26 +0,0 @@
1
- import { type EditorState, type Extension, StateEffect, StateField, type Transaction, type TransactionSpec } from '@codemirror/state';
2
- import * as automerge from '@dxos/automerge/automerge';
3
- import { type Heads, type Prop } from '@dxos/automerge/automerge';
4
- import { type IDocHandle } from './handle';
5
- export type Value = {
6
- lastHeads: Heads;
7
- path: Prop[];
8
- unreconciledTransactions: Transaction[];
9
- };
10
- type UpdateHeads = {
11
- newHeads: Heads;
12
- };
13
- export declare const effectType: import("@codemirror/state").StateEffectType<UpdateHeads>;
14
- export declare const updateHeads: (newHeads: automerge.next.Heads) => StateEffect<UpdateHeads>;
15
- export declare const getLastHeads: (state: EditorState, field: Field) => automerge.next.Heads;
16
- export declare const getPath: (state: EditorState, field: Field) => Prop[];
17
- export type Field = StateField<Value>;
18
- export type AutomergePlugin = {
19
- extension: Extension;
20
- };
21
- export declare const automergePlugin: (handle: IDocHandle, path: Prop[]) => AutomergePlugin;
22
- export declare const reconcileAnnotationType: import("@codemirror/state").AnnotationType<unknown>;
23
- export declare const isReconcileTx: (tr: Transaction) => boolean;
24
- export declare const makeReconcile: (tr: TransactionSpec) => void;
25
- export {};
26
- //# sourceMappingURL=plugin.d.ts.map