@dxos/react-ui-editor 0.8.2-main.f11618f → 0.8.2-staging.42af850

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 (247) hide show
  1. package/dist/lib/browser/index.mjs +4450 -3278
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +3 -64
  5. package/dist/lib/browser/testing/index.mjs.map +4 -4
  6. package/dist/lib/node/index.cjs +2701 -1528
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +3 -75
  10. package/dist/lib/node/testing/index.cjs.map +4 -4
  11. package/dist/lib/node-esm/index.mjs +4450 -3278
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +3 -64
  15. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  17. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  19. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  21. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  23. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/{comment.d.ts → image.d.ts} +4 -5
  25. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  26. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
  27. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  29. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  31. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  32. package/dist/types/src/components/EditorToolbar/util.d.ts +14 -22
  33. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
  35. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  36. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +21 -0
  37. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -0
  38. package/dist/types/src/{testing → components/Popover}/RefPopover.d.ts +1 -1
  39. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -0
  40. package/dist/types/src/components/Popover/index.d.ts +3 -0
  41. package/dist/types/src/components/Popover/index.d.ts.map +1 -0
  42. package/dist/types/src/components/index.d.ts +1 -0
  43. package/dist/types/src/components/index.d.ts.map +1 -1
  44. package/dist/types/src/defaults.d.ts +2 -5
  45. package/dist/types/src/defaults.d.ts.map +1 -1
  46. package/dist/types/src/extensions/annotations.d.ts +4 -1
  47. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  48. package/dist/types/src/extensions/autocomplete.d.ts +1 -2
  49. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  50. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  51. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  52. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  53. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  54. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  55. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  56. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  57. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  58. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  59. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  60. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  61. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  62. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  63. package/dist/types/src/extensions/command/command.d.ts +1 -2
  64. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  65. package/dist/types/src/extensions/command/hint.d.ts +14 -2
  66. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  67. package/dist/types/src/extensions/command/index.d.ts +2 -0
  68. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  69. package/dist/types/src/extensions/command/menu.d.ts +4 -14
  70. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  71. package/dist/types/src/extensions/command/state.d.ts +1 -1
  72. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  73. package/dist/types/src/extensions/command/typeahead.d.ts +17 -0
  74. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -0
  75. package/dist/types/src/extensions/comments.d.ts +2 -12
  76. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  77. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  78. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  79. package/dist/types/src/extensions/factories.d.ts +4 -0
  80. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  81. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  82. package/dist/types/src/extensions/index.d.ts +2 -0
  83. package/dist/types/src/extensions/index.d.ts.map +1 -1
  84. package/dist/types/src/extensions/json.d.ts +7 -0
  85. package/dist/types/src/extensions/json.d.ts.map +1 -0
  86. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  87. package/dist/types/src/extensions/markdown/{editorAction.d.ts → action.d.ts} +1 -1
  88. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  89. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -1
  90. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  91. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  92. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  93. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -0
  94. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  95. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  96. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  97. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  98. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  99. package/dist/types/src/extensions/markdown/index.d.ts +1 -1
  100. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  101. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  102. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  103. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  104. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  105. package/dist/types/src/extensions/modes.d.ts +5 -5
  106. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  107. package/dist/types/src/extensions/outliner/commands.d.ts +10 -0
  108. package/dist/types/src/extensions/outliner/commands.d.ts.map +1 -0
  109. package/dist/types/src/extensions/outliner/editor.d.ts +5 -0
  110. package/dist/types/src/extensions/outliner/editor.d.ts.map +1 -0
  111. package/dist/types/src/extensions/outliner/editor.test.d.ts +2 -0
  112. package/dist/types/src/extensions/outliner/editor.test.d.ts.map +1 -0
  113. package/dist/types/src/extensions/outliner/index.d.ts +4 -0
  114. package/dist/types/src/extensions/outliner/index.d.ts.map +1 -0
  115. package/dist/types/src/extensions/outliner/outliner.d.ts +13 -0
  116. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -0
  117. package/dist/types/src/extensions/outliner/outliner.test.d.ts +2 -0
  118. package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +1 -0
  119. package/dist/types/src/extensions/outliner/selection.d.ts +12 -0
  120. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -0
  121. package/dist/types/src/extensions/outliner/tree.d.ts +79 -0
  122. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -0
  123. package/dist/types/src/extensions/outliner/tree.test.d.ts +2 -0
  124. package/dist/types/src/extensions/outliner/tree.test.d.ts.map +1 -0
  125. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  126. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  127. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  128. package/dist/types/src/hooks/index.d.ts +0 -1
  129. package/dist/types/src/hooks/index.d.ts.map +1 -1
  130. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  131. package/dist/types/src/stories/Command.stories.d.ts +7 -0
  132. package/dist/types/src/stories/Command.stories.d.ts.map +1 -0
  133. package/dist/types/src/stories/{TextEditorComments.stories.d.ts → Comments.stories.d.ts} +3 -3
  134. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -0
  135. package/dist/types/src/stories/EditorToolbar.stories.d.ts +12 -0
  136. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -0
  137. package/dist/types/src/stories/{TextEditorSpecial.stories.d.ts → Experimental.stories.d.ts} +3 -6
  138. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -0
  139. package/dist/types/src/stories/Markdown.stories.d.ts +46 -0
  140. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -0
  141. package/dist/types/src/stories/Outliner.stories.d.ts +26 -0
  142. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -0
  143. package/dist/types/src/stories/Preview.stories.d.ts +10 -0
  144. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -0
  145. package/dist/types/src/stories/{TextEditorBasic.stories.d.ts → TextEditor.stories.d.ts} +9 -36
  146. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -0
  147. package/dist/types/src/stories/{story-utils.d.ts → util.d.ts} +6 -6
  148. package/dist/types/src/stories/util.d.ts.map +1 -0
  149. package/dist/types/src/styles/theme.d.ts.map +1 -1
  150. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  151. package/dist/types/src/testing/index.d.ts +1 -1
  152. package/dist/types/src/testing/index.d.ts.map +1 -1
  153. package/dist/types/src/testing/util.d.ts +2 -0
  154. package/dist/types/src/testing/util.d.ts.map +1 -0
  155. package/dist/types/src/util/cursor.d.ts.map +1 -1
  156. package/dist/types/src/util/debug.d.ts.map +1 -1
  157. package/dist/types/src/util/dom.d.ts.map +1 -1
  158. package/dist/types/src/util/facet.d.ts.map +1 -1
  159. package/dist/types/src/util/react.d.ts.map +1 -1
  160. package/dist/types/tsconfig.tsbuildinfo +1 -1
  161. package/package.json +41 -31
  162. package/src/components/EditorToolbar/EditorToolbar.tsx +93 -70
  163. package/src/components/EditorToolbar/blocks.ts +27 -6
  164. package/src/components/EditorToolbar/formatting.ts +34 -7
  165. package/src/components/EditorToolbar/headings.ts +9 -8
  166. package/src/components/EditorToolbar/image.ts +16 -0
  167. package/src/components/EditorToolbar/index.ts +7 -1
  168. package/src/components/EditorToolbar/lists.ts +26 -7
  169. package/src/components/EditorToolbar/search.ts +19 -0
  170. package/src/components/EditorToolbar/util.ts +16 -17
  171. package/src/components/EditorToolbar/view-mode.ts +9 -8
  172. package/src/components/Popover/RefDropdownMenu.tsx +77 -0
  173. package/src/{testing → components/Popover}/RefPopover.tsx +5 -4
  174. package/src/components/Popover/index.ts +6 -0
  175. package/src/components/index.ts +1 -0
  176. package/src/defaults.ts +10 -13
  177. package/src/extensions/annotations.ts +41 -64
  178. package/src/extensions/autocomplete.ts +5 -6
  179. package/src/extensions/automerge/automerge.stories.tsx +11 -14
  180. package/src/extensions/automerge/automerge.test.tsx +6 -5
  181. package/src/extensions/automerge/automerge.ts +2 -2
  182. package/src/extensions/automerge/defs.ts +1 -2
  183. package/src/extensions/automerge/sync.ts +7 -7
  184. package/src/extensions/automerge/update-automerge.ts +1 -1
  185. package/src/extensions/automerge/update-codemirror.ts +3 -4
  186. package/src/extensions/awareness/awareness-provider.ts +4 -4
  187. package/src/extensions/awareness/awareness.ts +7 -7
  188. package/src/extensions/blast.ts +9 -9
  189. package/src/extensions/command/command.ts +1 -3
  190. package/src/extensions/command/hint.ts +7 -7
  191. package/src/extensions/command/index.ts +2 -0
  192. package/src/extensions/command/menu.ts +75 -50
  193. package/src/extensions/command/typeahead.ts +116 -0
  194. package/src/extensions/comments.ts +4 -69
  195. package/src/extensions/factories.ts +13 -0
  196. package/src/extensions/index.ts +2 -0
  197. package/src/extensions/json.ts +56 -0
  198. package/src/extensions/markdown/bundle.ts +13 -9
  199. package/src/extensions/markdown/changes.ts +3 -2
  200. package/src/extensions/markdown/decorate.ts +15 -14
  201. package/src/extensions/markdown/formatting.ts +4 -4
  202. package/src/extensions/markdown/image.ts +2 -2
  203. package/src/extensions/markdown/index.ts +1 -1
  204. package/src/extensions/markdown/styles.ts +4 -3
  205. package/src/extensions/markdown/table.ts +3 -3
  206. package/src/extensions/modes.ts +5 -6
  207. package/src/extensions/outliner/commands.ts +270 -0
  208. package/src/extensions/outliner/editor.test.ts +33 -0
  209. package/src/extensions/outliner/editor.ts +184 -0
  210. package/src/extensions/outliner/index.ts +7 -0
  211. package/src/extensions/outliner/outliner.test.ts +99 -0
  212. package/src/extensions/outliner/outliner.ts +168 -0
  213. package/src/extensions/outliner/selection.ts +50 -0
  214. package/src/extensions/outliner/tree.test.ts +164 -0
  215. package/src/extensions/outliner/tree.ts +315 -0
  216. package/src/extensions/preview/preview.ts +5 -5
  217. package/src/hooks/index.ts +0 -1
  218. package/src/stories/Command.stories.tsx +97 -0
  219. package/src/stories/{TextEditorComments.stories.tsx → Comments.stories.tsx} +13 -14
  220. package/src/stories/EditorToolbar.stories.tsx +96 -0
  221. package/src/stories/{TextEditorSpecial.stories.tsx → Experimental.stories.tsx} +9 -30
  222. package/src/stories/Markdown.stories.tsx +121 -0
  223. package/src/stories/Outliner.stories.tsx +108 -0
  224. package/src/stories/{TextEditorPreview.stories.tsx → Preview.stories.tsx} +46 -136
  225. package/src/stories/{TextEditorBasic.stories.tsx → TextEditor.stories.tsx} +78 -111
  226. package/src/stories/{story-utils.tsx → util.tsx} +28 -31
  227. package/src/styles/theme.ts +15 -5
  228. package/src/styles/tokens.ts +1 -2
  229. package/src/testing/index.ts +1 -1
  230. package/src/testing/util.ts +5 -0
  231. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +0 -1
  232. package/dist/types/src/extensions/markdown/editorAction.d.ts.map +0 -1
  233. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  234. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  235. package/dist/types/src/stories/InputMode.stories.d.ts +0 -57
  236. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  237. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +0 -1
  238. package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +0 -1
  239. package/dist/types/src/stories/TextEditorPreview.stories.d.ts +0 -13
  240. package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +0 -1
  241. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +0 -1
  242. package/dist/types/src/stories/story-utils.d.ts.map +0 -1
  243. package/dist/types/src/testing/RefPopover.d.ts.map +0 -1
  244. package/src/components/EditorToolbar/comment.ts +0 -23
  245. package/src/hooks/useActionHandler.ts +0 -12
  246. package/src/stories/InputMode.stories.tsx +0 -124
  247. /package/src/extensions/markdown/{editorAction.ts → action.ts} +0 -0
@@ -0,0 +1,77 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { createContext } from '@radix-ui/react-context';
6
+ import React, { type PropsWithChildren, useRef, useState, useEffect, useCallback, type RefObject } from 'react';
7
+
8
+ import { addEventListener } from '@dxos/async';
9
+ import { type DxRefTag, type DxRefTagActivate } from '@dxos/lit-ui';
10
+ import { DropdownMenu } from '@dxos/react-ui';
11
+
12
+ import { type PreviewLinkRef, type PreviewLinkTarget, type PreviewLookup } from '../../extensions';
13
+
14
+ // TODO(burdon): Reconcile with RefPopover?
15
+
16
+ const customEventOptions = { capture: true, passive: false };
17
+
18
+ // Create a context for the dxn value.
19
+ type RefDropdownMenuValue = Partial<{ link: PreviewLinkRef; target: PreviewLinkTarget; pending: boolean }>;
20
+
21
+ const REF_DROPDOWN_MENU = 'RefDropdownMenu';
22
+ const [RefDropdownMenuContextProvider, useRefDropdownMenu] = createContext<RefDropdownMenuValue>(REF_DROPDOWN_MENU, {});
23
+
24
+ type RefDropdownMenuProviderProps = PropsWithChildren<{ onLookup?: PreviewLookup }>;
25
+
26
+ const RefDropdownMenuProvider = ({ children, onLookup }: RefDropdownMenuProviderProps) => {
27
+ const trigger = useRef<DxRefTag | null>(null);
28
+ const [value, setValue] = useState<RefDropdownMenuValue>({});
29
+ const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
30
+ const [open, setOpen] = useState(false);
31
+
32
+ const handleDxRefTagActivate = useCallback(
33
+ (event: DxRefTagActivate) => {
34
+ const { refId, label, trigger: dxTrigger } = event;
35
+ setValue((value) => ({
36
+ ...value,
37
+ link: { label, ref: refId },
38
+ pending: true,
39
+ }));
40
+ trigger.current = dxTrigger;
41
+ queueMicrotask(() => setOpen(true));
42
+ void onLookup?.({ label, ref: refId }).then((target) =>
43
+ setValue((value) => ({
44
+ ...value,
45
+ target: target ?? undefined,
46
+ pending: false,
47
+ })),
48
+ );
49
+ },
50
+ [onLookup],
51
+ );
52
+
53
+ useEffect(() => {
54
+ return rootRef
55
+ ? addEventListener(rootRef, 'dx-ref-tag-activate', handleDxRefTagActivate, customEventOptions)
56
+ : undefined;
57
+ }, [rootRef]);
58
+
59
+ return (
60
+ <RefDropdownMenuContextProvider pending={value.pending} link={value.link} target={value.target}>
61
+ <DropdownMenu.Root open={open} onOpenChange={setOpen}>
62
+ <DropdownMenu.VirtualTrigger virtualRef={trigger as unknown as RefObject<HTMLButtonElement>} />
63
+ <div role='none' className='contents' ref={setRootRef}>
64
+ {children}
65
+ </div>
66
+ </DropdownMenu.Root>
67
+ </RefDropdownMenuContextProvider>
68
+ );
69
+ };
70
+
71
+ export const RefDropdownMenu = {
72
+ Provider: RefDropdownMenuProvider,
73
+ };
74
+
75
+ export { useRefDropdownMenu };
76
+
77
+ export type { RefDropdownMenuProviderProps, RefDropdownMenuValue };
@@ -9,12 +9,13 @@ import { addEventListener } from '@dxos/async';
9
9
  import { type DxRefTag, type DxRefTagActivate } from '@dxos/lit-ui';
10
10
  import { Popover } from '@dxos/react-ui';
11
11
 
12
- import { type PreviewLinkRef, type PreviewLinkTarget, type PreviewLookup } from '../extensions';
12
+ import { type PreviewLinkRef, type PreviewLinkTarget, type PreviewLookup } from '../../extensions';
13
13
 
14
14
  const customEventOptions = { capture: true, passive: false };
15
15
 
16
16
  // Create a context for the dxn value.
17
17
  type RefPopoverValue = Partial<{ link: PreviewLinkRef; target: PreviewLinkTarget; pending: boolean }>;
18
+
18
19
  const REF_POPOVER = 'RefPopover';
19
20
  const [RefPopoverContextProvider, useRefPopover] = createContext<RefPopoverValue>(REF_POPOVER, {});
20
21
 
@@ -28,15 +29,15 @@ const RefPopoverProvider = ({ children, onLookup }: RefPopoverProviderProps) =>
28
29
 
29
30
  const handleDxRefTagActivate = useCallback(
30
31
  (event: DxRefTagActivate) => {
31
- const { ref, label, trigger: dxTrigger } = event;
32
+ const { refId, label, trigger: dxTrigger } = event;
32
33
  setValue((value) => ({
33
34
  ...value,
34
- link: { label, ref },
35
+ link: { label, ref: refId },
35
36
  pending: true,
36
37
  }));
37
38
  trigger.current = dxTrigger;
38
39
  queueMicrotask(() => setOpen(true));
39
- void onLookup?.({ label, ref }).then((target) =>
40
+ void onLookup?.({ label, ref: refId }).then((target) =>
40
41
  setValue((value) => ({
41
42
  ...value,
42
43
  target: target ?? undefined,
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ export * from './RefPopover';
6
+ export * from './RefDropdownMenu';
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './EditorToolbar';
6
+ export * from './Popover';
package/src/defaults.ts CHANGED
@@ -6,31 +6,28 @@ import { EditorView } from '@codemirror/view';
6
6
 
7
7
  import { mx } from '@dxos/react-ui-theme';
8
8
 
9
+ import { type ThemeExtensionsOptions } from './extensions';
9
10
  import { fontMono } from './styles';
10
11
 
11
- const margin = '!mt-[1rem]';
12
-
13
12
  /**
14
13
  * CodeMirror content width.
15
14
  * 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)
16
15
  * 50rem = 800px. Maximum content width for solo mode.
17
16
  * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
18
17
  */
19
- // TOOD(burdon): Adjust depending on
20
18
  export const editorWidth = '!mli-auto is-full max-is-[min(50rem,100%-4rem)]';
21
19
 
22
- export const editorContent = mx(margin, editorWidth);
23
-
24
- /**
25
- * Margin for numbers.
26
- */
27
- export const editorFullWidth = mx(margin);
20
+ export const editorSlots: ThemeExtensionsOptions['slots'] = {
21
+ scroll: {
22
+ className: 'pbs-2',
23
+ },
24
+ content: {
25
+ className: editorWidth,
26
+ },
27
+ };
28
28
 
29
29
  export const editorGutter = EditorView.theme({
30
- // Match margin from content.
31
- // Gutter = 2rem + 1rem margin.
32
30
  '.cm-gutters': {
33
- marginTop: '1rem',
34
31
  paddingRight: '1rem',
35
32
  },
36
33
  });
@@ -52,6 +49,6 @@ export const stackItemContentEditorClassNames = (role?: string) =>
52
49
 
53
50
  export const stackItemContentToolbarClassNames = (role?: string) =>
54
51
  mx(
55
- 'attention-surface is-full border-be !border-separator relative z-[1]',
52
+ 'relative z-[1] flex is-full bg-toolbarSurface border-be border-subduedSeparator',
56
53
  role === 'section' && 'sticky block-start-0 -mbe-px min-is-0',
57
54
  );
@@ -2,77 +2,54 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type EditorState, type Extension, StateField } from '@codemirror/state';
6
- import { Decoration, EditorView } from '@codemirror/view';
5
+ import { type Extension, RangeSetBuilder } from '@codemirror/state';
6
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
7
7
 
8
- import { isNotFalsy } from '@dxos/util';
9
-
10
- import { Cursor } from '../util';
11
-
12
- type Annotation = {
13
- cursor: string;
14
- };
8
+ const annotationMark = Decoration.mark({ class: 'cm-annotation' });
15
9
 
16
10
  export type AnnotationOptions = {
17
11
  match?: RegExp; // TODO(burdon): Update via hook (e.g., for search).
18
12
  };
19
13
 
20
- const annotationMark = Decoration.mark({ class: 'cm-annotation' });
21
-
22
- export const annotations = (options: AnnotationOptions = {}): Extension => {
23
- // TODO(burdon): Build index of matches and cursors (in background function).
24
- // Define annotation action in prompt. E.g., extract company names. Find links, etc.
25
- // Show popover card. A16Z chain demo. Identify, extract, research, link. Multi-agent.
26
- const match = (state: EditorState) => {
27
- const annotations: Annotation[] = [];
28
- const text = state.doc.toString();
29
- if (options.match) {
30
- const matches = text.matchAll(options.match);
31
- for (const match of matches) {
32
- const from = match.index!;
33
- const to = from + match[0].length;
34
- const cursor = Cursor.getCursorFromRange(state, { from, to });
35
- annotations.push({ cursor });
36
- }
37
- }
38
-
39
- return annotations;
40
- };
41
-
42
- const annotationsState = StateField.define<Annotation[]>({
43
- create: (state) => {
44
- return match(state);
45
- },
46
- update: (value, tr) => {
47
- if (!tr.changes.empty) {
48
- return match(tr.state);
49
- }
50
-
51
- return value;
52
- },
53
- });
54
-
14
+ /**
15
+ *
16
+ */
17
+ export const annotations = ({ match }: AnnotationOptions = {}): Extension => {
55
18
  return [
56
- annotationsState,
57
- EditorView.decorations.compute([annotationsState], (state) => {
58
- const annotations = state.field(annotationsState);
59
- const decorations = annotations
60
- .map((annotation) => {
61
- const range = Cursor.getRangeFromCursor(state, annotation.cursor);
62
- return range && annotationMark.range(range.from, range.to);
63
- })
64
- .filter(isNotFalsy);
65
-
66
- return Decoration.set(decorations);
19
+ ViewPlugin.fromClass(
20
+ class {
21
+ decorations: DecorationSet = Decoration.none;
22
+ update(update: ViewUpdate) {
23
+ const builder = new RangeSetBuilder<Decoration>();
24
+ if (match) {
25
+ // Only process visible lines.
26
+ const { from, to } = update.view.viewport;
27
+ const text = update.state.doc.sliceString(from, to);
28
+ const matches = text.matchAll(match);
29
+ for (const m of matches) {
30
+ if (m.index !== undefined) {
31
+ // Adjust match position relative to viewport.
32
+ const start = from + m.index;
33
+ const end = start + m[0].length;
34
+ builder.add(start, end, annotationMark);
35
+ }
36
+ }
37
+ }
38
+
39
+ this.decorations = builder.finish();
40
+ }
41
+ },
42
+ {
43
+ decorations: (v) => v.decorations,
44
+ },
45
+ ),
46
+
47
+ EditorView.theme({
48
+ '.cm-annotation': {
49
+ textDecoration: 'underline',
50
+ textDecorationStyle: 'wavy',
51
+ textDecorationColor: 'var(--dx-errorText)',
52
+ },
67
53
  }),
68
- styles,
69
54
  ];
70
55
  };
71
-
72
- const styles = EditorView.theme({
73
- '.cm-annotation': {
74
- textDecoration: 'underline',
75
- textDecorationStyle: 'wavy',
76
- textDecorationColor: 'var(--dx-error)',
77
- },
78
- });
@@ -17,7 +17,6 @@ import { keymap } from '@codemirror/view';
17
17
  export type AutocompleteResult = Completion;
18
18
 
19
19
  export type AutocompleteOptions = {
20
- debug?: boolean;
21
20
  activateOnTyping?: boolean;
22
21
  override?: CompletionSource[];
23
22
  onSearch?: (text: string) => Completion[];
@@ -30,7 +29,7 @@ export type AutocompleteOptions = {
30
29
  /**
31
30
  * Autocomplete extension.
32
31
  */
33
- export const autocomplete = ({ debug, activateOnTyping, override, onSearch }: AutocompleteOptions = {}): Extension => {
32
+ export const autocomplete = ({ activateOnTyping, override, onSearch }: AutocompleteOptions = {}): Extension => {
34
33
  const extensions: Extension[] = [
35
34
  // https://codemirror.net/docs/ref/#view.keymap
36
35
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -40,16 +39,16 @@ export const autocomplete = ({ debug, activateOnTyping, override, onSearch }: Au
40
39
  // https://codemirror.net/examples/autocompletion
41
40
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
42
41
  autocompletion({
43
- activateOnTyping,
44
42
  override,
45
- closeOnBlur: !debug,
46
- tooltipClass: () => 'shadow rounded',
43
+ activateOnTyping,
44
+ // closeOnBlur: false,
45
+ // tooltipClass: () => 'rounded-be pbe-1 border-separator',
47
46
  }),
48
47
  ];
49
48
 
50
49
  if (onSearch) {
51
50
  extensions.push(
52
- // TODO(burdon): Optional decoration via addToOptions
51
+ // TODO(burdon): Optional decoration via addToOptions.
53
52
  markdownLanguage.data.of({
54
53
  autocomplete: (context: CompletionContext): CompletionResult | null => {
55
54
  const match = context.matchBefore(/\w*/);
@@ -5,18 +5,19 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import '@preact/signals-react';
8
+
9
+ import { Repo } from '@automerge/automerge-repo';
10
+ import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
8
11
  import React, { useEffect, useState } from 'react';
9
12
 
10
- import { Repo } from '@dxos/automerge/automerge-repo';
11
- import { BroadcastChannelNetworkAdapter } from '@dxos/automerge/automerge-repo-network-broadcastchannel';
12
13
  import { Expando } from '@dxos/echo-schema';
13
- import { DocAccessor, Filter, live, createDocAccessor, useQuery, useSpace, type Space } from '@dxos/react-client/echo';
14
+ import { DocAccessor, live, createDocAccessor, useQuery, useSpace, type Space, Query } from '@dxos/react-client/echo';
14
15
  import { useIdentity, type Identity } from '@dxos/react-client/halo';
15
16
  import { ClientRepeater, type ClientRepeatedComponentProps } from '@dxos/react-client/testing';
16
17
  import { useThemeContext } from '@dxos/react-ui';
17
18
  import { withLayout, withTheme } from '@dxos/storybook-utils';
18
19
 
19
- import { editorContent } from '../../defaults';
20
+ import { editorSlots } from '../../defaults';
20
21
  import { useTextEditor } from '../../hooks';
21
22
  import translations from '../../translations';
22
23
  import { createBasicExtensions, createDataExtensions, createThemeExtensions } from '../factories';
@@ -41,12 +42,7 @@ const Editor = ({ source, autoFocus, space, identity }: EditorProps) => {
41
42
  initialValue: DocAccessor.getValue(source),
42
43
  extensions: [
43
44
  createBasicExtensions({ placeholder: 'Type here...' }),
44
- createThemeExtensions({
45
- themeMode,
46
- slots: {
47
- editor: { className: editorContent },
48
- },
49
- }),
45
+ createThemeExtensions({ themeMode, slots: editorSlots }),
50
46
  createDataExtensions({ id: 'test', text: source, space, identity }),
51
47
  ],
52
48
  autoFocus,
@@ -71,11 +67,12 @@ const Story = () => {
71
67
  doc.text = initialContent;
72
68
  });
73
69
 
74
- const object2 = repo2.find<TestObject>(object1.url);
70
+ const object2 = await repo2.find<TestObject>(object1.url);
75
71
  await object2.whenReady();
76
72
 
77
- setObject1({ handle: object1, path: ['text'] });
78
- setObject2({ handle: object2, path: ['text'] });
73
+ // TODO(mykola): Fix types.
74
+ setObject1({ handle: object1 as any, path: ['text'] });
75
+ setObject2({ handle: object2 as any, path: ['text'] });
79
76
  });
80
77
  }, []);
81
78
 
@@ -103,7 +100,7 @@ const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
103
100
  const identity = useIdentity();
104
101
  const space = useSpace(spaceKey);
105
102
  const [source, setSource] = useState<DocAccessor>();
106
- const objects = useQuery<Expando>(space, Filter.from({ type: 'test' }));
103
+ const objects = useQuery(space, Query.type(Expando, { type: 'test' }));
107
104
 
108
105
  useEffect(() => {
109
106
  if (!source && objects.length) {
@@ -2,17 +2,17 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type DocHandle, Repo } from '@automerge/automerge-repo';
5
6
  import { EditorState } from '@codemirror/state';
6
7
  import { EditorView } from '@codemirror/view';
7
8
  import { render, screen } from '@testing-library/react';
8
9
  // TODO(wittjosiah): Move to vitest expect (and remove from package.json).
9
10
  import chai, { expect } from 'chai';
10
11
  import chaiDom from 'chai-dom';
11
- import get from 'lodash.get';
12
12
  import React, { type FC, useEffect, useRef, useState } from 'react';
13
13
  import { describe, test } from 'vitest';
14
14
 
15
- import { type DocHandle, Repo } from '@dxos/automerge/automerge-repo';
15
+ import { get } from '@dxos/util';
16
16
 
17
17
  import { automerge } from './automerge';
18
18
 
@@ -24,7 +24,7 @@ const path = ['text'];
24
24
 
25
25
  class Generator {
26
26
  constructor(private readonly _handle: DocHandle<TestObject>) {}
27
- update(text: string) {
27
+ update(text: string): void {
28
28
  this._handle.change((doc: TestObject) => {
29
29
  doc.text = text;
30
30
  });
@@ -36,7 +36,8 @@ const Test: FC<{ handle: DocHandle<TestObject>; generator: Generator }> = ({ han
36
36
  const [view, setView] = useState<EditorView>();
37
37
  useEffect(() => {
38
38
  const extensions = [
39
- automerge({ handle, path }),
39
+ // TODO(mykola): Fix types.
40
+ automerge({ handle: handle as any, path }),
40
41
  EditorView.updateListener.of((update) => {
41
42
  if (view.state.doc.toString() === 'hello!') {
42
43
  // Update editor.
@@ -46,7 +47,7 @@ const Test: FC<{ handle: DocHandle<TestObject>; generator: Generator }> = ({ han
46
47
  ];
47
48
 
48
49
  const view = new EditorView({
49
- state: EditorState.create({ doc: get(handle.docSync()!, path), extensions }),
50
+ state: EditorState.create({ doc: get(handle.doc()!, path), extensions }),
50
51
  parent: ref.current!,
51
52
  });
52
53
 
@@ -4,10 +4,10 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A } from '@automerge/automerge';
7
8
  import { StateField, type Extension } from '@codemirror/state';
8
9
  import { EditorView, ViewPlugin } from '@codemirror/view';
9
10
 
10
- import { next as A } from '@dxos/automerge/automerge';
11
11
  import { type DocAccessor } from '@dxos/react-client/echo';
12
12
 
13
13
  import { cursorConverter } from './cursor';
@@ -19,7 +19,7 @@ export const automerge = (accessor: DocAccessor): Extension => {
19
19
  const syncState = StateField.define<State>({
20
20
  create: () => ({
21
21
  path: accessor.path.slice(),
22
- lastHeads: A.getHeads(accessor.handle.docSync()!),
22
+ lastHeads: A.getHeads(accessor.handle.doc()!),
23
23
  unreconciledTransactions: [],
24
24
  }),
25
25
 
@@ -4,10 +4,9 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { type Heads, type Prop } from '@automerge/automerge';
7
8
  import { Annotation, StateEffect, type StateField, type EditorState, type Transaction } from '@codemirror/state';
8
9
 
9
- import { type Heads, type Prop } from '@dxos/automerge/automerge';
10
-
11
10
  export type State = {
12
11
  path: Prop[];
13
12
  lastHeads: Heads;
@@ -4,10 +4,10 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A } from '@automerge/automerge';
7
8
  import { type StateField } from '@codemirror/state';
8
9
  import { type EditorView } from '@codemirror/view';
9
10
 
10
- import { next as A } from '@dxos/automerge/automerge';
11
11
  import { type IDocHandle } from '@dxos/react-client/echo';
12
12
 
13
13
  import { getLastHeads, getPath, isReconcile, reconcileAnnotation, type State, updateHeads } from './defs';
@@ -26,7 +26,7 @@ export class Syncer {
26
26
  private readonly _state: StateField<State>
27
27
  ) {}
28
28
 
29
- reconcile(view: EditorView, editor: boolean) {
29
+ reconcile(view: EditorView, editor: boolean): void {
30
30
  // TODO(burdon): Better way to do mutex?
31
31
  if (this._pending) {
32
32
  return;
@@ -41,7 +41,7 @@ export class Syncer {
41
41
  this._pending = false;
42
42
  }
43
43
 
44
- onEditorChange(view: EditorView) {
44
+ onEditorChange(view: EditorView): void {
45
45
  // Apply the unreconciled transactions to the document.
46
46
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
47
47
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
@@ -54,18 +54,18 @@ export class Syncer {
54
54
  }
55
55
  }
56
56
 
57
- onAutomergeChange(view: EditorView) {
57
+ onAutomergeChange(view: EditorView): void {
58
58
  // Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
59
59
  const oldHeads = getLastHeads(view.state, this._state);
60
- const newHeads = A.getHeads(this._handle.docSync()!);
61
- const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.docSync()!, oldHeads, newHeads);
60
+ const newHeads = A.getHeads(this._handle.doc()!);
61
+ const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.doc()!, oldHeads, newHeads);
62
62
 
63
63
  const selection = view.state.selection;
64
64
  const path = getPath(view.state, this._state);
65
65
  updateCodeMirror(view, selection, path, diff);
66
66
 
67
67
  // TODO(burdon): Test conflicts?
68
- // A.getConflicts(this._handle.docSync()!, path[0]);
68
+ // A.getConflicts(this._handle.doc()!, path[0]);
69
69
 
70
70
  view.dispatch({
71
71
  effects: updateHeads(newHeads),
@@ -4,9 +4,9 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A, type Heads } from '@automerge/automerge';
7
8
  import { type EditorState, type StateField, type Transaction, type Text } from '@codemirror/state';
8
9
 
9
- import { next as A, type Heads } from '@dxos/automerge/automerge';
10
10
  import { type IDocHandle } from '@dxos/react-client/echo';
11
11
 
12
12
  import { type State } from './defs';
@@ -4,9 +4,6 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
- import { ChangeSet, type ChangeSpec, type EditorSelection, type EditorState } from '@codemirror/state';
8
- import { type EditorView } from '@codemirror/view';
9
-
10
7
  import {
11
8
  type DelPatch,
12
9
  type InsertPatch,
@@ -14,7 +11,9 @@ import {
14
11
  type Prop,
15
12
  type PutPatch,
16
13
  type SpliceTextPatch,
17
- } from '@dxos/automerge/automerge';
14
+ } from '@automerge/automerge';
15
+ import { ChangeSet, type ChangeSpec, type EditorSelection, type EditorState } from '@codemirror/state';
16
+ import { type EditorView } from '@codemirror/view';
18
17
 
19
18
  import { reconcileAnnotation } from './defs';
20
19
 
@@ -53,7 +53,7 @@ export class SpaceAwarenessProvider implements AwarenessProvider {
53
53
  this._info = params.info;
54
54
  }
55
55
 
56
- open() {
56
+ open(): void {
57
57
  this._ctx = new Context();
58
58
  this._postTask = new DeferredTask(this._ctx, async () => {
59
59
  if (this._localState) {
@@ -92,7 +92,7 @@ export class SpaceAwarenessProvider implements AwarenessProvider {
92
92
  });
93
93
  }
94
94
 
95
- close() {
95
+ close(): void {
96
96
  void this._ctx?.dispose();
97
97
  this._ctx = undefined;
98
98
  this._postTask = undefined;
@@ -113,12 +113,12 @@ export class SpaceAwarenessProvider implements AwarenessProvider {
113
113
  this._postTask.schedule();
114
114
  }
115
115
 
116
- private _handleQueryMessage() {
116
+ private _handleQueryMessage(): void {
117
117
  invariant(this._postTask);
118
118
  this._postTask.schedule();
119
119
  }
120
120
 
121
- private _handlePostMessage(message: ProtocolMessage) {
121
+ private _handlePostMessage(message: ProtocolMessage): void {
122
122
  invariant(message.kind === 'post');
123
123
  // TODO(wittjosiah): Is it helpful or confusing to show cursors for self on other devices?
124
124
  this._remoteStates.set(message.state.peerId, message.state);
@@ -95,17 +95,17 @@ export class RemoteSelectionsDecorator implements PluginValue {
95
95
  });
96
96
  }
97
97
 
98
- destroy() {
98
+ destroy(): void {
99
99
  void this._ctx.dispose();
100
100
  this._provider.close();
101
101
  }
102
102
 
103
- update(update: ViewUpdate) {
103
+ update(update: ViewUpdate): void {
104
104
  this._updateLocalSelection(update.view);
105
105
  this._updateRemoteSelections(update.view);
106
106
  }
107
107
 
108
- private _updateLocalSelection(view: EditorView) {
108
+ private _updateLocalSelection(view: EditorView): void {
109
109
  const hasFocus = view.hasFocus && view.dom.ownerDocument.hasFocus();
110
110
  const { anchor = undefined, head = undefined } = hasFocus ? view.state.selection.main : {};
111
111
  if (this._lastAnchor === anchor && this._lastHead === head) {
@@ -125,7 +125,7 @@ export class RemoteSelectionsDecorator implements PluginValue {
125
125
  );
126
126
  }
127
127
 
128
- private _updateRemoteSelections(view: EditorView) {
128
+ private _updateRemoteSelections(view: EditorView): void {
129
129
  const decorations: Range<Decoration>[] = [
130
130
  // TODO(burdon): Factor out for testing.
131
131
  // {
@@ -239,11 +239,11 @@ class RemoteCaretWidget extends WidgetType {
239
239
  return span;
240
240
  }
241
241
 
242
- override updateDOM() {
242
+ override updateDOM(): boolean {
243
243
  return false;
244
244
  }
245
245
 
246
- override eq(widget: this) {
246
+ override eq(widget: this): boolean {
247
247
  return widget._color === this._color;
248
248
  }
249
249
 
@@ -251,7 +251,7 @@ class RemoteCaretWidget extends WidgetType {
251
251
  return -1;
252
252
  }
253
253
 
254
- override ignoreEvent() {
254
+ override ignoreEvent(): boolean {
255
255
  return true;
256
256
  }
257
257
  }