@dxos/react-ui-editor 0.3.11-main.008b7c6 → 0.3.11-main.0252f93

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 (187) hide show
  1. package/dist/lib/browser/index.mjs +1510 -1369
  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 -14
  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 -6
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  11. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  15. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  16. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  19. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  20. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  21. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  23. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/defs.d.ts +9 -11
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts +8 -12
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
  29. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  30. package/dist/types/src/extensions/awareness.d.ts +2 -2
  31. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  32. package/dist/types/src/extensions/basic.d.ts +4 -5
  33. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  34. package/dist/types/src/extensions/comments.d.ts +37 -20
  35. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  36. package/dist/types/src/{util → extensions}/cursor.d.ts +10 -2
  37. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  38. package/dist/types/src/extensions/index.d.ts +2 -8
  39. package/dist/types/src/extensions/index.d.ts.map +1 -1
  40. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  41. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  42. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
  44. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  45. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  46. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  47. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  49. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  50. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  51. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  52. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  53. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  54. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  55. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  56. package/dist/types/src/extensions/outliner.d.ts +4 -0
  57. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  58. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  59. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  60. package/dist/types/src/hooks/defs.d.ts +25 -0
  61. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  62. package/dist/types/src/hooks/index.d.ts +3 -2
  63. package/dist/types/src/hooks/index.d.ts.map +1 -1
  64. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  65. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  66. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  67. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  68. package/dist/types/src/hooks/useTextModel.d.ts +2 -32
  69. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  70. package/dist/types/src/index.d.ts +3 -0
  71. package/dist/types/src/index.d.ts.map +1 -1
  72. package/dist/types/src/styles/markdown.d.ts +2 -2
  73. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  74. package/dist/types/src/styles/tokens.d.ts +1 -0
  75. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  76. package/dist/types/src/themes/default.d.ts.map +1 -1
  77. package/dist/types/src/util.d.ts +7 -0
  78. package/dist/types/src/util.d.ts.map +1 -0
  79. package/package.json +47 -37
  80. package/src/components/TextEditor/MarkdownEditor.stories.tsx +84 -91
  81. package/src/components/TextEditor/TextEditor.stories.tsx +12 -46
  82. package/src/components/TextEditor/TextEditor.tsx +131 -95
  83. package/src/components/TextEditor/comments.stories.tsx +357 -0
  84. package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
  85. package/src/components/Toolbar/Toolbar.tsx +115 -0
  86. package/src/extensions/autocomplete.ts +23 -19
  87. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  88. package/src/extensions/automerge/automerge.test.ts +13 -0
  89. package/src/extensions/automerge/automerge.ts +55 -46
  90. package/src/extensions/automerge/cursor.ts +5 -4
  91. package/src/extensions/automerge/defs.ts +13 -34
  92. package/src/extensions/automerge/semaphore.ts +33 -32
  93. package/src/extensions/automerge/update-automerge.ts +14 -8
  94. package/src/extensions/automerge/update-codemirror.ts +5 -5
  95. package/src/extensions/awareness.ts +43 -49
  96. package/src/extensions/basic.ts +22 -17
  97. package/src/extensions/comments.ts +323 -268
  98. package/src/extensions/cursor.ts +50 -0
  99. package/src/extensions/index.ts +2 -8
  100. package/src/extensions/markdown/bundle.ts +20 -30
  101. package/src/extensions/markdown/code.ts +142 -0
  102. package/src/extensions/markdown/formatting.ts +159 -0
  103. package/src/extensions/markdown/heading.ts +59 -0
  104. package/src/extensions/markdown/highlight.ts +11 -26
  105. package/src/extensions/markdown/hr.ts +71 -0
  106. package/src/extensions/markdown/image.ts +87 -0
  107. package/src/extensions/markdown/index.ts +8 -0
  108. package/src/extensions/{link.ts → markdown/link.ts} +87 -86
  109. package/src/extensions/{table.ts → markdown/table.ts} +10 -4
  110. package/src/extensions/markdown/tasklist.ts +110 -0
  111. package/src/extensions/mention.ts +9 -2
  112. package/src/extensions/outliner.ts +12 -0
  113. package/src/hooks/awareness-provider.ts +24 -20
  114. package/src/hooks/defs.ts +42 -0
  115. package/src/hooks/index.ts +4 -2
  116. package/src/hooks/useActionHandler.ts +45 -0
  117. package/src/hooks/useTextEditor.ts +29 -0
  118. package/src/hooks/useTextModel.ts +50 -59
  119. package/src/index.ts +3 -0
  120. package/src/styles/markdown.ts +9 -12
  121. package/src/styles/tokens.ts +3 -0
  122. package/src/themes/default.ts +50 -79
  123. package/src/util.ts +41 -0
  124. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  125. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  126. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
  127. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  128. package/dist/types/src/extensions/code.d.ts.map +0 -1
  129. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  130. package/dist/types/src/extensions/image.d.ts.map +0 -1
  131. package/dist/types/src/extensions/link.d.ts.map +0 -1
  132. package/dist/types/src/extensions/mermaid.d.ts +0 -3
  133. package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
  134. package/dist/types/src/extensions/table.d.ts.map +0 -1
  135. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  136. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  137. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  138. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  139. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  140. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  141. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  142. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  143. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  144. package/dist/types/src/hooks/automerge.d.ts +0 -3
  145. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  146. package/dist/types/src/hooks/yjs.d.ts +0 -27
  147. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  148. package/dist/types/src/testing/index.d.ts +0 -3
  149. package/dist/types/src/testing/index.d.ts.map +0 -1
  150. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  151. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  152. package/dist/types/src/testing/proto/index.d.ts +0 -2
  153. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  154. package/dist/types/src/testing/replicator.d.ts +0 -45
  155. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  156. package/dist/types/src/util/cursor.d.ts.map +0 -1
  157. package/dist/types/src/util/index.d.ts +0 -3
  158. package/dist/types/src/util/index.d.ts.map +0 -1
  159. package/dist/types/src/util/util.d.ts +0 -5
  160. package/dist/types/src/util/util.d.ts.map +0 -1
  161. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  162. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  163. package/src/extensions/code.ts +0 -99
  164. package/src/extensions/hr.ts +0 -74
  165. package/src/extensions/image.ts +0 -69
  166. package/src/extensions/mermaid.ts +0 -11
  167. package/src/extensions/tasklist.ts +0 -121
  168. package/src/extensions/yjs/cursor.ts +0 -21
  169. package/src/extensions/yjs/index.ts +0 -5
  170. package/src/extensions/yjs/yjs.test.ts +0 -35
  171. package/src/extensions/yjs/yjs.ts +0 -16
  172. package/src/hooks/automerge.ts +0 -52
  173. package/src/hooks/yjs.ts +0 -178
  174. package/src/testing/index.ts +0 -6
  175. package/src/testing/proto/gen/schema.ts +0 -49
  176. package/src/testing/proto/index.ts +0 -5
  177. package/src/testing/proto/schema.proto +0 -15
  178. package/src/testing/replicator.ts +0 -184
  179. package/src/util/cursor.ts +0 -29
  180. package/src/util/index.ts +0 -6
  181. package/src/util/util.ts +0 -18
  182. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  183. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  184. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  185. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  186. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  187. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
@@ -0,0 +1,45 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import type { EditorView } from '@codemirror/view';
6
+
7
+ import type { ToolbarProps } from '../components/Toolbar/Toolbar';
8
+ import { createComment, setHeading, toggleBold, toggleItalic, toggleList, toggleStrikethrough } from '../extensions';
9
+
10
+ export const useActionHandler = (view: EditorView | null): ToolbarProps['onAction'] => {
11
+ return (action) => {
12
+ if (!view) {
13
+ return;
14
+ }
15
+
16
+ switch (action.type) {
17
+ case 'heading':
18
+ setHeading(parseInt(action.data))(view);
19
+ break;
20
+
21
+ case 'bold':
22
+ toggleBold(view);
23
+ break;
24
+ case 'italic':
25
+ toggleItalic(view);
26
+ break;
27
+ case 'strikethrough':
28
+ toggleStrikethrough(view);
29
+ break;
30
+
31
+ case 'list':
32
+ toggleList(view);
33
+ break;
34
+
35
+ case 'comment':
36
+ createComment(view);
37
+ break;
38
+ }
39
+
40
+ // TODO(burdon): Hack since otherwise focus doesn't leave heading selector.
41
+ setTimeout(() => {
42
+ view.focus();
43
+ });
44
+ };
45
+ };
@@ -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] = useTextEditor();
14
+ * useEffect(() => {
15
+ * editorView?.focus();
16
+ * }, [editorView]);
17
+ * return <TextEditor ref={editorRef} />;
18
+ * };
19
+ * ```
20
+ */
21
+ export const useTextEditor = (): [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
+ };
@@ -2,58 +2,19 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { StateField, type Extension } from '@codemirror/state';
5
+ import get from 'lodash.get';
6
6
  import { useEffect, useState } from 'react';
7
- import type * as YP from 'y-protocols/awareness';
8
7
 
9
- import { isAutomergeObject, type DocAccessor, type Space, type TextObject } from '@dxos/react-client/echo';
8
+ import { generateName } from '@dxos/display-name';
9
+ import { type AutomergeTextCompat, getRawDoc } from '@dxos/echo-schema';
10
+ import { invariant } from '@dxos/invariant';
11
+ import { isAutomergeObject, type Space, type TextObject } from '@dxos/react-client/echo';
10
12
  import { type Identity } from '@dxos/react-client/halo';
11
- import type { YText, YXmlFragment } from '@dxos/text-model';
12
13
 
13
- import { createAutomergeModel } from './automerge';
14
- import { createYjsModel } from './yjs';
15
-
16
- // TODO(burdon): Factor out defs.
17
-
18
- /**
19
- * State field makes the model available to other extensions.
20
- */
21
- export const modelState = StateField.define<EditorModel | undefined>({
22
- create: () => undefined,
23
- update: (model) => model,
24
- });
25
-
26
- export type Range = {
27
- from: number;
28
- to: number;
29
- };
30
-
31
- export type CommentRange = {
32
- id: string;
33
- cursor: string;
34
- };
35
-
36
- // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
37
- export type EditorModel = {
38
- id: string;
39
- text: () => string;
40
- extension?: Extension;
41
-
42
- // TODO(burdon): Remove.
43
- content: string | YText | YXmlFragment | DocAccessor;
44
- /**
45
- * @deprecated Use CursorConverter.
46
- */
47
- getCursorFromRange?: (value: Range) => string;
48
- getRangeFromCursor?: (cursor: string) => Range | undefined;
49
- // TODO(burdon): Move into extension.
50
- awareness?: YP.Awareness;
51
- // TODO(burdon): Remove.
52
- peer?: {
53
- id: string;
54
- name?: string;
55
- };
56
- };
14
+ import { SpaceAwarenessProvider } from './awareness-provider';
15
+ import { type EditorModel, modelState } from './defs';
16
+ import { automerge, awareness } from '../extensions';
17
+ import { cursorColor } from '../styles';
57
18
 
58
19
  // TODO(burdon): Remove space/identity dependency. Define interface for the framework re content and presence.
59
20
  export type UseTextModelProps = {
@@ -62,22 +23,52 @@ export type UseTextModelProps = {
62
23
  text?: TextObject;
63
24
  };
64
25
 
65
- // TODO(burdon): Remove YJS/Automerge deps (from UI component -- create abstraction; incl. all ECHO/Space deps).
66
- // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
67
26
  export const useTextModel = (props: UseTextModelProps): EditorModel | undefined => {
68
27
  const { identity, space, text } = props;
69
- const [model, setModel] = useState<EditorModel | undefined>(() => createModel(props));
28
+ const [model, setModel] = useState<EditorModel | undefined>();
70
29
  useEffect(() => setModel(createModel(props)), [identity, space, text]);
71
30
  return model;
72
31
  };
73
32
 
74
- const createModel = (props: UseTextModelProps) => {
75
- const { text } = props;
76
- if (isAutomergeObject(text)) {
77
- return createAutomergeModel(props);
78
- } else if (text?.doc) {
79
- return createYjsModel(props);
80
- } else {
81
- return undefined;
33
+ const createModel = ({ space, identity, text }: UseTextModelProps) => {
34
+ invariant(isAutomergeObject(text));
35
+ const obj = text as any as AutomergeTextCompat;
36
+ const doc = getRawDoc(obj, [obj.field]);
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));
82
58
  }
59
+
60
+ const model: EditorModel = {
61
+ id: obj.id,
62
+ content: doc,
63
+ text: () => get(doc.handle.docSync(), doc.path),
64
+ extension: extensions,
65
+ peer: identity
66
+ ? {
67
+ id: identity.identityKey.toHex(),
68
+ name: identity.profile?.displayName,
69
+ }
70
+ : undefined,
71
+ };
72
+
73
+ return model;
83
74
  };
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';
@@ -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,18 +77,18 @@ 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': {
91
- background: 'inherit',
90
+ '.cm-activeLine': {
91
+ background: 'transparent',
92
92
  },
93
93
 
94
94
  //
@@ -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;;;;AAU5D,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,12 +0,0 @@
1
- import '@dxosTheme';
2
- import React from 'react';
3
- declare const _default: {
4
- title: string;
5
- component: React.ForwardRefExoticComponent<import("./TextEditor").TextEditorProps & React.RefAttributes<import("./TextEditor").TextEditorRef>>;
6
- decorators: import("@storybook/react").Decorator[];
7
- };
8
- export default _default;
9
- export declare const Default: {
10
- render: () => JSX.Element;
11
- };
12
- //# sourceMappingURL=yjs.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"yjs.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/TextEditor/yjs.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AAEpB,OAAO,KAAmB,MAAM,OAAO,CAAC;;;;;;AAWxC,wBAIE;AA+BF,eAAO,MAAM,OAAO;;CAGnB,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,4 +0,0 @@
1
- import * as Y from 'yjs';
2
- import { type CursorConverter } from '../../util';
3
- export declare const cursorConverter: (text: Y.Text) => CursorConverter;
4
- //# sourceMappingURL=cursor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/yjs/cursor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAIzB,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,eAU7C,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './yjs';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/yjs/index.ts"],"names":[],"mappings":"AAIA,cAAc,OAAO,CAAC"}
@@ -1,4 +0,0 @@
1
- import type * as YP from 'y-protocols/awareness';
2
- import type { YText } from '@dxos/text-model';
3
- export declare const yjs: (content: YText, awareness?: YP.Awareness) => import("@codemirror/state").Extension[];
4
- //# sourceMappingURL=yjs.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"yjs.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/yjs/yjs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAK9C,eAAO,MAAM,GAAG,YAAa,KAAK,cAAc,GAAG,SAAS,4CAG3D,CAAC"}