@dxos/react-ui-editor 0.8.4-main.67995b8 → 0.8.4-main.a4bbb77

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 (261) hide show
  1. package/dist/lib/browser/index.mjs +3067 -1955
  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 +71 -1
  5. package/dist/lib/browser/testing/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +3067 -1955
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/testing/index.mjs +71 -1
  10. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  11. package/dist/types/src/components/{Popover → CommandMenu}/CommandMenu.d.ts +10 -6
  12. package/dist/types/src/components/CommandMenu/CommandMenu.d.ts.map +1 -0
  13. package/dist/types/src/components/CommandMenu/index.d.ts +2 -0
  14. package/dist/types/src/components/CommandMenu/index.d.ts.map +1 -0
  15. package/dist/types/src/components/Editor/Editor.d.ts +15 -9
  16. package/dist/types/src/components/Editor/Editor.d.ts.map +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.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/util.d.ts +5 -5
  25. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
  27. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  28. package/dist/types/src/components/index.d.ts +1 -1
  29. package/dist/types/src/components/index.d.ts.map +1 -1
  30. package/dist/types/src/defaults.d.ts.map +1 -1
  31. package/dist/types/src/extensions/autocomplete.d.ts +20 -7
  32. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  33. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  34. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +10 -19
  35. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  36. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  37. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  38. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  39. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  40. package/dist/types/src/extensions/autoscroll.d.ts +10 -0
  41. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
  42. package/dist/types/src/extensions/{command → command-dialog}/action.d.ts +1 -1
  43. package/dist/types/src/extensions/command-dialog/action.d.ts.map +1 -0
  44. package/dist/types/src/extensions/{command/command.d.ts → command-dialog/command-dialog.d.ts} +2 -2
  45. package/dist/types/src/extensions/command-dialog/command-dialog.d.ts.map +1 -0
  46. package/dist/types/src/extensions/{command → command-dialog}/hint.d.ts +2 -7
  47. package/dist/types/src/extensions/command-dialog/hint.d.ts.map +1 -0
  48. package/dist/types/src/extensions/command-dialog/index.d.ts +4 -0
  49. package/dist/types/src/extensions/command-dialog/index.d.ts.map +1 -0
  50. package/dist/types/src/extensions/{command → command-dialog}/state.d.ts +1 -1
  51. package/dist/types/src/extensions/command-dialog/state.d.ts.map +1 -0
  52. package/dist/types/src/extensions/command-dialog/typeahead.d.ts.map +1 -0
  53. package/dist/types/src/extensions/{command → command-menu}/command-menu.d.ts +3 -3
  54. package/dist/types/src/extensions/command-menu/command-menu.d.ts.map +1 -0
  55. package/dist/types/src/extensions/command-menu/index.d.ts +3 -0
  56. package/dist/types/src/extensions/command-menu/index.d.ts.map +1 -0
  57. package/dist/types/src/extensions/command-menu/placeholder.d.ts.map +1 -0
  58. package/dist/types/src/extensions/command-menu/useCommandMenu.d.ts +24 -0
  59. package/dist/types/src/extensions/command-menu/useCommandMenu.d.ts.map +1 -0
  60. package/dist/types/src/extensions/comments.d.ts +1 -1
  61. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  62. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  63. package/dist/types/src/extensions/factories.d.ts +3 -8
  64. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  65. package/dist/types/src/extensions/floating-menu.d.ts.map +1 -0
  66. package/dist/types/src/extensions/focus.d.ts.map +1 -1
  67. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  68. package/dist/types/src/extensions/index.d.ts +5 -1
  69. package/dist/types/src/extensions/index.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  71. package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
  72. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  73. package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
  74. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  75. package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
  76. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  77. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  78. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  79. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
  80. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  81. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  82. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  83. package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
  84. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  85. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  86. package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
  87. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  88. package/dist/types/src/extensions/preview/preview.d.ts +3 -6
  89. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  90. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  91. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  92. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  93. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  94. package/dist/types/src/extensions/tags/index.d.ts +4 -0
  95. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  96. package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
  97. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
  98. package/dist/types/src/extensions/tags/xml-tags.d.ts +72 -0
  99. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  100. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  101. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  102. package/dist/types/src/hooks/useTextEditor.d.ts +2 -2
  103. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  104. package/dist/types/src/stories/CommandDialog.stories.d.ts +14 -0
  105. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
  106. package/dist/types/src/stories/CommandMenu.stories.d.ts +10 -4
  107. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
  108. package/dist/types/src/stories/Comments.stories.d.ts +21 -10
  109. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  110. package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -3
  111. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  112. package/dist/types/src/stories/Experimental.stories.d.ts +22 -13
  113. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  114. package/dist/types/src/stories/Markdown.stories.d.ts +32 -43
  115. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  116. package/dist/types/src/stories/Outliner.stories.d.ts +15 -21
  117. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  118. package/dist/types/src/stories/Preview.stories.d.ts +21 -7
  119. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  120. package/dist/types/src/stories/Tags.stories.d.ts +16 -0
  121. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
  122. package/dist/types/src/stories/TextEditor.stories.d.ts +38 -52
  123. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  124. package/dist/types/src/stories/components/EditorStory.d.ts +5 -8
  125. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  126. package/dist/types/src/styles/theme.d.ts.map +1 -1
  127. package/dist/types/src/testing/PreviewPopover.d.ts +20 -0
  128. package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -0
  129. package/dist/types/src/testing/index.d.ts +1 -0
  130. package/dist/types/src/testing/index.d.ts.map +1 -1
  131. package/dist/types/src/testing/util.d.ts +1 -0
  132. package/dist/types/src/testing/util.d.ts.map +1 -1
  133. package/dist/types/src/translations.d.ts +1 -1
  134. package/dist/types/src/types/types.d.ts +1 -1
  135. package/dist/types/src/util/cursor.d.ts.map +1 -1
  136. package/dist/types/src/util/debug.d.ts +1 -1
  137. package/dist/types/src/util/debug.d.ts.map +1 -1
  138. package/dist/types/src/util/decorations.d.ts +4 -0
  139. package/dist/types/src/util/decorations.d.ts.map +1 -0
  140. package/dist/types/src/util/dom.d.ts +2 -12
  141. package/dist/types/src/util/dom.d.ts.map +1 -1
  142. package/dist/types/src/util/index.d.ts +1 -0
  143. package/dist/types/src/util/index.d.ts.map +1 -1
  144. package/dist/types/src/util/react.d.ts +1 -1
  145. package/dist/types/src/util/react.d.ts.map +1 -1
  146. package/dist/types/tsconfig.tsbuildinfo +1 -1
  147. package/package.json +70 -66
  148. package/src/components/{Popover → CommandMenu}/CommandMenu.tsx +95 -26
  149. package/src/components/{Popover → CommandMenu}/index.ts +0 -2
  150. package/src/components/Editor/Editor.tsx +50 -15
  151. package/src/components/EditorToolbar/EditorToolbar.tsx +40 -30
  152. package/src/components/EditorToolbar/blocks.ts +21 -24
  153. package/src/components/EditorToolbar/formatting.ts +22 -25
  154. package/src/components/EditorToolbar/headings.ts +10 -5
  155. package/src/components/EditorToolbar/image.ts +8 -4
  156. package/src/components/EditorToolbar/lists.ts +16 -19
  157. package/src/components/EditorToolbar/search.ts +8 -4
  158. package/src/components/EditorToolbar/util.ts +16 -5
  159. package/src/components/EditorToolbar/view-mode.ts +11 -6
  160. package/src/components/index.ts +1 -1
  161. package/src/defaults.ts +5 -2
  162. package/src/extensions/autocomplete.ts +204 -54
  163. package/src/extensions/automerge/automerge.stories.tsx +31 -24
  164. package/src/extensions/automerge/automerge.ts +4 -3
  165. package/src/extensions/automerge/defs.ts +1 -1
  166. package/src/extensions/automerge/sync.ts +1 -1
  167. package/src/extensions/automerge/update-automerge.ts +1 -1
  168. package/src/extensions/autoscroll.ts +157 -0
  169. package/src/extensions/awareness/awareness.ts +2 -2
  170. package/src/extensions/{command → command-dialog}/action.ts +2 -3
  171. package/src/extensions/{command/command.ts → command-dialog/command-dialog.ts} +4 -4
  172. package/src/extensions/{command → command-dialog}/hint.ts +2 -1
  173. package/src/extensions/command-dialog/index.ts +7 -0
  174. package/src/extensions/{command → command-dialog}/state.ts +4 -3
  175. package/src/extensions/{command → command-dialog}/typeahead.ts +2 -2
  176. package/src/extensions/{command → command-menu}/command-menu.ts +9 -9
  177. package/src/extensions/command-menu/index.ts +6 -0
  178. package/src/extensions/{command → command-menu}/placeholder.ts +1 -1
  179. package/src/extensions/{command → command-menu}/useCommandMenu.ts +35 -19
  180. package/src/extensions/comments.ts +18 -13
  181. package/src/extensions/dnd.ts +1 -1
  182. package/src/extensions/factories.ts +37 -27
  183. package/src/extensions/{command/floating-menu.ts → floating-menu.ts} +12 -19
  184. package/src/extensions/focus.ts +5 -4
  185. package/src/extensions/folding.tsx +4 -6
  186. package/src/extensions/index.ts +5 -1
  187. package/src/extensions/markdown/action.ts +2 -1
  188. package/src/extensions/markdown/bundle.ts +27 -5
  189. package/src/extensions/markdown/changes.ts +1 -1
  190. package/src/extensions/markdown/decorate.ts +24 -14
  191. package/src/extensions/markdown/formatting.test.ts +6 -6
  192. package/src/extensions/markdown/formatting.ts +3 -3
  193. package/src/extensions/markdown/highlight.ts +1 -1
  194. package/src/extensions/markdown/image.ts +3 -4
  195. package/src/extensions/markdown/link.ts +3 -0
  196. package/src/extensions/markdown/table.ts +7 -1
  197. package/src/extensions/mention.ts +1 -1
  198. package/src/extensions/outliner/outliner.test.ts +3 -2
  199. package/src/extensions/outliner/outliner.ts +6 -5
  200. package/src/extensions/outliner/selection.ts +1 -1
  201. package/src/extensions/outliner/tree.test.ts +2 -1
  202. package/src/extensions/outliner/tree.ts +2 -2
  203. package/src/extensions/preview/preview.ts +59 -62
  204. package/src/extensions/selection.ts +2 -2
  205. package/src/extensions/tags/extended-markdown.test.ts +261 -0
  206. package/src/extensions/tags/extended-markdown.ts +78 -0
  207. package/src/extensions/tags/index.ts +7 -0
  208. package/src/extensions/tags/streamer.ts +243 -0
  209. package/src/extensions/tags/xml-tags.ts +393 -0
  210. package/src/extensions/tags/xml-util.ts +94 -0
  211. package/src/hooks/useTextEditor.ts +8 -20
  212. package/src/stories/CommandDialog.stories.tsx +89 -0
  213. package/src/stories/CommandMenu.stories.tsx +33 -34
  214. package/src/stories/Comments.stories.tsx +14 -10
  215. package/src/stories/EditorToolbar.stories.tsx +13 -12
  216. package/src/stories/Experimental.stories.tsx +17 -13
  217. package/src/stories/Markdown.stories.tsx +25 -21
  218. package/src/stories/Outliner.stories.tsx +46 -34
  219. package/src/stories/Preview.stories.tsx +34 -33
  220. package/src/stories/Tags.stories.tsx +81 -0
  221. package/src/stories/TextEditor.stories.tsx +45 -38
  222. package/src/stories/components/EditorStory.tsx +12 -13
  223. package/src/styles/theme.ts +15 -12
  224. package/src/testing/PreviewPopover.tsx +78 -0
  225. package/src/testing/index.ts +1 -0
  226. package/src/testing/util.ts +2 -0
  227. package/src/translations.ts +1 -1
  228. package/src/util/cursor.ts +2 -1
  229. package/src/util/debug.ts +2 -2
  230. package/src/util/decorations.ts +21 -0
  231. package/src/util/dom.ts +5 -27
  232. package/src/util/index.ts +1 -0
  233. package/src/util/react.tsx +1 -1
  234. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
  235. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
  236. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
  237. package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
  238. package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
  239. package/dist/types/src/components/Popover/index.d.ts +0 -4
  240. package/dist/types/src/components/Popover/index.d.ts.map +0 -1
  241. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  242. package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
  243. package/dist/types/src/extensions/command/command.d.ts.map +0 -1
  244. package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
  245. package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
  246. package/dist/types/src/extensions/command/index.d.ts +0 -7
  247. package/dist/types/src/extensions/command/index.d.ts.map +0 -1
  248. package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
  249. package/dist/types/src/extensions/command/state.d.ts.map +0 -1
  250. package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
  251. package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
  252. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
  253. package/dist/types/src/stories/Command.stories.d.ts +0 -7
  254. package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
  255. package/src/components/Popover/RefDropdownMenu.tsx +0 -85
  256. package/src/components/Popover/RefPopover.tsx +0 -99
  257. package/src/extensions/command/index.ts +0 -10
  258. package/src/stories/Command.stories.tsx +0 -97
  259. /package/dist/types/src/extensions/{command → command-dialog}/typeahead.d.ts +0 -0
  260. /package/dist/types/src/extensions/{command → command-menu}/placeholder.d.ts +0 -0
  261. /package/dist/types/src/extensions/{command/floating-menu.d.ts → floating-menu.d.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorState } from '@codemirror/state';
6
+ import { type SyntaxNode } from '@lezer/common';
7
+
8
+ import { invariant } from '@dxos/invariant';
9
+
10
+ export type Tag = Record<string, any> & {
11
+ _tag: string;
12
+ };
13
+
14
+ /**
15
+ * Parse XML Element.
16
+ */
17
+ export const nodeToJson = (state: EditorState, node: SyntaxNode): Tag | undefined => {
18
+ invariant(node.type.name === 'Element', 'Node is not an Element');
19
+
20
+ // Find the opening tag.
21
+ const openTag = node.node.getChild('OpenTag') || node.node.getChild('SelfClosingTag');
22
+ if (openTag) {
23
+ // Extract tag name.
24
+ const tagName = openTag.getChild('TagName');
25
+ if (!tagName) {
26
+ return;
27
+ }
28
+
29
+ const tag: Tag = {
30
+ _tag: state.doc.sliceString(tagName.from, tagName.to),
31
+ };
32
+
33
+ // Extract attributes.
34
+ let attributeNode = openTag.getChild('Attribute');
35
+ while (attributeNode) {
36
+ const attrName = attributeNode.getChild('AttributeName');
37
+ const attrValue = attributeNode.getChild('AttributeValue');
38
+ if (attrName) {
39
+ const attr = state.doc.sliceString(attrName.from, attrName.to);
40
+
41
+ // Default for attributes without values.
42
+ let value: string | boolean = true;
43
+
44
+ if (attrValue) {
45
+ const rawValue = state.doc.sliceString(attrValue.from, attrValue.to);
46
+ // Remove quotes if present.
47
+ if (
48
+ (rawValue.startsWith('"') && rawValue.endsWith('"')) ||
49
+ (rawValue.startsWith("'") && rawValue.endsWith("'"))
50
+ ) {
51
+ value = rawValue.slice(1, -1);
52
+ } else {
53
+ value = rawValue;
54
+ }
55
+ }
56
+
57
+ tag[attr] = value;
58
+ }
59
+
60
+ // Get next sibling attribute.
61
+ attributeNode = attributeNode.nextSibling;
62
+ }
63
+
64
+ // Extract children for non-self-closing tags.
65
+ if (node.type.name === 'Element' && openTag.type.name !== 'SelfClosingTag') {
66
+ const children: any[] = [];
67
+ let child = node.node.firstChild;
68
+
69
+ while (child) {
70
+ // Skip the opening and closing tags.
71
+ if (child.type.name !== 'OpenTag' && child.type.name !== 'CloseTag') {
72
+ if (child.type.name === 'Text') {
73
+ const text = state.doc.sliceString(child.from, child.to).trim();
74
+ if (text) {
75
+ children.push(text);
76
+ }
77
+ } else if (child.type.name === 'Element') {
78
+ const data = nodeToJson(state, child);
79
+ if (data) {
80
+ children.push(data);
81
+ }
82
+ }
83
+ }
84
+ child = child.nextSibling;
85
+ }
86
+
87
+ if (children.length > 0) {
88
+ tag.children = children;
89
+ }
90
+ }
91
+
92
+ return tag;
93
+ }
94
+ };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { EditorState, type EditorStateConfig, type Text } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
- import { useFocusableGroup, type TabsterTypes } from '@fluentui/react-tabster';
7
+ import { type TabsterTypes, useFocusableGroup } from '@fluentui/react-tabster';
8
8
  import {
9
9
  type DependencyList,
10
10
  type KeyboardEventHandler,
@@ -17,9 +17,9 @@ import {
17
17
  } from 'react';
18
18
 
19
19
  import { log } from '@dxos/log';
20
- import { getProviderValue, isNotFalsy, type MaybeProvider } from '@dxos/util';
20
+ import { type MaybeProvider, getProviderValue, isTruthy } from '@dxos/util';
21
21
 
22
- import { type EditorSelection, documentId, createEditorStateTransaction, editorInputMode } from '../extensions';
22
+ import { type EditorSelection, createEditorStateTransaction, documentId, editorInputMode } from '../extensions';
23
23
  import { debugDispatcher } from '../util';
24
24
 
25
25
  let instanceCount = 0;
@@ -35,8 +35,8 @@ export type CursorInfo = {
35
35
 
36
36
  export type UseTextEditor = {
37
37
  // TODO(burdon): Rename.
38
- parentRef: RefObject<HTMLDivElement>;
39
- view?: EditorView;
38
+ parentRef: RefObject<HTMLDivElement | null>;
39
+ view: EditorView | null;
40
40
  focusAttributes?: TabsterTypes.TabsterDOMAttribute & {
41
41
  tabIndex: 0;
42
42
  onKeyUp: KeyboardEventHandler<HTMLDivElement>;
@@ -66,11 +66,11 @@ export const useTextEditor = (
66
66
 
67
67
  // NOTE: Increments by 2 in strict mode.
68
68
  const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
69
- const [view, setView] = useState<EditorView>();
69
+ const [view, setView] = useState<EditorView | null>(null);
70
70
  const parentRef = useRef<HTMLDivElement>(null);
71
71
 
72
72
  useEffect(() => {
73
- let view: EditorView;
73
+ let view: EditorView | null = null;
74
74
  if (parentRef.current) {
75
75
  log('create', { id, instanceId, doc: initialValue?.length ?? 0 });
76
76
 
@@ -96,19 +96,7 @@ export const useTextEditor = (
96
96
  EditorView.exceptionSink.of((err) => {
97
97
  log.catch(err);
98
98
  }),
99
- // TODO(burdon): Factor out debug inspector.
100
- // ViewPlugin.fromClass(
101
- // class {
102
- // constructor(_view: EditorView) {
103
- // log('construct', { id });
104
- // }
105
- //
106
- // destroy() {
107
- // log('destroy', { id });
108
- // }
109
- // },
110
- // ),
111
- ].filter(isNotFalsy),
99
+ ].filter(isTruthy),
112
100
  });
113
101
 
114
102
  // https://codemirror.net/docs/ref/#view.EditorViewConfig
@@ -0,0 +1,89 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React, { type KeyboardEvent, useState } from 'react';
7
+
8
+ import { Button, Icon, Input } from '@dxos/react-ui';
9
+ import { withTheme } from '@dxos/react-ui/testing';
10
+ import { mx } from '@dxos/react-ui-theme';
11
+
12
+ import { editorWidth } from '../defaults';
13
+ import { type Action, commandDialog } from '../extensions';
14
+ import { str } from '../testing';
15
+ import { createRenderer } from '../util';
16
+
17
+ import { EditorStory } from './components';
18
+
19
+ const CommandDialog = ({ onAction }: { onAction: (action?: Action) => void }) => {
20
+ const [text, setText] = useState('');
21
+
22
+ const handleInsert = () => {
23
+ // TODO(burdon): Use queue ref.
24
+ const link = `![${text}](dxn:queue:data:123)`;
25
+ onAction(text.length ? { type: 'insert', text: link } : undefined);
26
+ };
27
+
28
+ const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
29
+ switch (event.key) {
30
+ case 'Enter': {
31
+ handleInsert();
32
+ break;
33
+ }
34
+ case 'Escape': {
35
+ onAction();
36
+ break;
37
+ }
38
+ }
39
+ };
40
+
41
+ return (
42
+ <div className='flex is-full justify-center'>
43
+ <div
44
+ className={mx(
45
+ 'flex is-full p-2 gap-2 items-center bg-modalSurface border border-separator rounded-md',
46
+ editorWidth,
47
+ )}
48
+ >
49
+ <Input.Root>
50
+ <Input.TextInput
51
+ autoFocus={true}
52
+ placeholder='Ask a question...'
53
+ value={text}
54
+ onChange={(ev) => setText(ev.target.value)}
55
+ onKeyDown={handleKeyDown}
56
+ />
57
+ </Input.Root>
58
+ <Button variant='ghost' classNames='pli-0' onClick={() => onAction({ type: 'cancel' })}>
59
+ <Icon icon='ph--x--regular' size={5} />
60
+ </Button>
61
+ </div>
62
+ </div>
63
+ );
64
+ };
65
+
66
+ const meta = {
67
+ title: 'ui/react-ui-editor/CommandDialog',
68
+ render: () => (
69
+ <EditorStory
70
+ text={str('# Command', '', '', '')}
71
+ extensions={[
72
+ commandDialog({
73
+ renderDialog: createRenderer(CommandDialog),
74
+ onHint: () => "Press '?' to ask a question",
75
+ }),
76
+ ]}
77
+ />
78
+ ),
79
+ decorators: [withTheme],
80
+ parameters: {
81
+ layout: 'fullscreen',
82
+ },
83
+ } satisfies Meta<typeof Button>;
84
+
85
+ export default meta;
86
+
87
+ type Story = StoryObj<typeof meta>;
88
+
89
+ export const Default: Story = {};
@@ -2,47 +2,44 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type EditorView } from '@codemirror/view';
8
- import { type StoryObj } from '@storybook/react-vite';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
9
7
  import React, { useCallback, useRef } from 'react';
10
8
 
11
9
  import { Obj, Query } from '@dxos/echo';
12
10
  import { faker } from '@dxos/random';
13
11
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
14
- import { createObjectFactory, Testing, type ValueGenerator } from '@dxos/schema/testing';
15
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
12
+ import { Domino } from '@dxos/react-ui';
13
+ import { withTheme } from '@dxos/react-ui/testing';
14
+ import { Testing, type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
16
15
 
17
- import { EditorStory, names } from './components';
18
16
  import {
19
- CommandMenu,
20
17
  type CommandMenuGroup,
21
18
  type CommandMenuItem,
22
- RefPopover,
19
+ CommandMenuProvider,
23
20
  coreSlashCommands,
24
21
  filterItems,
25
22
  insertAtCursor,
26
23
  insertAtLineStart,
27
24
  linkSlashCommands,
28
25
  } from '../components';
29
- import { useCommandMenu, type UseCommandMenuOptions } from '../extensions';
26
+ import { type UseCommandMenuOptions, useCommandMenu } from '../extensions';
30
27
  import { str } from '../testing';
31
- import { createElement } from '../util';
28
+
29
+ import { EditorStory, names } from './components';
32
30
 
33
31
  const generator: ValueGenerator = faker as any;
34
32
 
35
33
  type StoryProps = Omit<UseCommandMenuOptions, 'viewRef'> & { text: string };
36
34
 
37
35
  const DefaultStory = ({ text, ...options }: StoryProps) => {
38
- const viewRef = useRef<EditorView>();
39
- const { commandMenu, groupsRef, currentItem, onSelect, ...props } = useCommandMenu({ viewRef, ...options });
36
+ const viewRef = useRef<EditorView>(null);
37
+ const { groupsRef, commandMenu, ...commandMenuProps } = useCommandMenu({ viewRef, ...options });
40
38
 
41
39
  return (
42
- <RefPopover modal={false} {...props}>
40
+ <CommandMenuProvider groups={groupsRef.current} {...commandMenuProps}>
43
41
  <EditorStory ref={viewRef} text={text} placeholder={''} extensions={commandMenu} />
44
- <CommandMenu groups={groupsRef.current} currentItem={currentItem} onSelect={onSelect} />
45
- </RefPopover>
42
+ </CommandMenuProvider>
46
43
  );
47
44
  };
48
45
 
@@ -63,18 +60,18 @@ const groups: CommandMenuGroup[] = [
63
60
  },
64
61
  ];
65
62
 
66
- const meta: Meta<StoryProps> = {
63
+ const meta = {
67
64
  title: 'ui/react-ui-editor/CommandMenu',
68
- decorators: [withTheme, withLayout({ fullscreen: true })],
69
- render: (args) => <DefaultStory {...args} />,
65
+ render: DefaultStory,
66
+ decorators: [withTheme],
70
67
  parameters: {
71
68
  layout: 'fullscreen',
72
69
  },
73
- };
70
+ } satisfies Meta<typeof DefaultStory>;
74
71
 
75
72
  export default meta;
76
73
 
77
- type Story = StoryObj<StoryProps>;
74
+ type Story = StoryObj<typeof meta>;
78
75
 
79
76
  // TODO(burdon): Not working.
80
77
  export const Slash: Story = {
@@ -82,13 +79,14 @@ export const Slash: Story = {
82
79
  text: str('# Slash', '', names.join(' '), ''),
83
80
  trigger: '/',
84
81
  placeholder: {
85
- content: () => {
86
- return createElement('div', undefined, [
87
- createElement('span', { text: 'Press' }),
88
- createElement('span', { className: 'border border-separator rounded-sm mx-1 px-1', text: '/' }),
89
- createElement('span', { text: 'for commands' }),
90
- ]);
91
- },
82
+ content: () =>
83
+ Domino.of('div')
84
+ .children(
85
+ Domino.of('span').text('Press'),
86
+ Domino.of('span').text('/').classNames('border border-separator rounded-sm mx-1 px-1'),
87
+ Domino.of('span').text('for commands'),
88
+ )
89
+ .build(),
92
90
  },
93
91
  getMenu: (text) => {
94
92
  return filterItems(groups, (item) =>
@@ -99,7 +97,7 @@ export const Slash: Story = {
99
97
  };
100
98
 
101
99
  export const Link: Story = {
102
- render: (args) => {
100
+ render: (args: StoryProps) => {
103
101
  const { space } = useClientProvider();
104
102
  const getMenu = useCallback(
105
103
  async (trigger: string, query?: string): Promise<CommandMenuGroup[]> => {
@@ -123,7 +121,7 @@ export const Link: Story = {
123
121
  label: object.name,
124
122
  icon: 'ph--user--regular',
125
123
  onSelect: (view, head) => {
126
- const link = `[${object.name}][${Obj.getDXN(object)}]`;
124
+ const link = `[${object.name}](${Obj.getDXN(object)})`;
127
125
  if (query?.startsWith('@')) {
128
126
  insertAtLineStart(view, head, `!${link}\n`);
129
127
  } else {
@@ -139,21 +137,22 @@ export const Link: Story = {
139
137
 
140
138
  return <DefaultStory {...args} getMenu={getMenu} />;
141
139
  },
142
- args: {
143
- text: str('# Link', '', names.join(' '), ''),
144
- trigger: ['/', '@'],
145
- },
146
140
  decorators: [
147
141
  withClientProvider({
148
142
  createSpace: true,
149
143
  onInitialized: async (client) => {
150
144
  client.addTypes([Testing.Contact]);
151
145
  },
152
- onSpaceCreated: async ({ space }) => {
146
+ onCreateSpace: async ({ space }) => {
153
147
  const createObjects = createObjectFactory(space.db, generator);
154
148
  await createObjects([{ type: Testing.Contact, count: 10 }]);
155
149
  await space.db.flush({ indexes: true });
156
150
  },
157
151
  }),
158
152
  ],
153
+ args: {
154
+ text: str('# Link', '', names.join(' '), ''),
155
+ trigger: ['/', '@'],
156
+ getMenu: () => [],
157
+ },
159
158
  };
@@ -2,36 +2,40 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { effect, useSignal } from '@preact/signals-react';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
7
  import React, { type FC } from 'react';
9
8
 
10
9
  import { keySymbols, parseShortcut } from '@dxos/keyboard';
11
10
  import { PublicKey } from '@dxos/keys';
12
11
  import { log } from '@dxos/log';
13
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
12
+ import { withTheme } from '@dxos/react-ui/testing';
14
13
 
15
- import { EditorStory, content, longText } from './components';
16
14
  import { annotations, comments, createExternalCommentSync } from '../extensions';
17
15
  import { str } from '../testing';
18
16
  import { type Comment } from '../types';
19
17
  import { createRenderer } from '../util';
20
18
 
21
- const meta: Meta<typeof EditorStory> = {
19
+ import { EditorStory, content, longText } from './components';
20
+
21
+ const meta = {
22
22
  title: 'ui/react-ui-editor/Comments',
23
23
  component: EditorStory,
24
- decorators: [withTheme, withLayout({ fullscreen: true })],
25
- parameters: { layout: 'fullscreen' },
26
- };
24
+ decorators: [withTheme],
25
+ parameters: {
26
+ layout: 'fullscreen',
27
+ },
28
+ } satisfies Meta<typeof EditorStory>;
27
29
 
28
30
  export default meta;
29
31
 
32
+ type Story = StoryObj<typeof meta>;
33
+
30
34
  //
31
35
  // Comments
32
36
  //
33
37
 
34
- export const Comments = {
38
+ export const Comments: Story = {
35
39
  render: () => {
36
40
  const _comments = useSignal<Comment[]>([]);
37
41
  return (
@@ -91,7 +95,7 @@ const CommentTooltip: FC<{ shortcut: string }> = ({ shortcut }) => {
91
95
  // Annotations
92
96
  //
93
97
 
94
- export const Annotations = {
98
+ export const Annotations: Story = {
95
99
  render: () => (
96
100
  <EditorStory text={str('# Annotations', '', longText)} extensions={[annotations({ match: /volup/gi })]} />
97
101
  ),
@@ -2,28 +2,26 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type StoryObj } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useState } from 'react';
9
7
 
10
8
  import { invariant } from '@dxos/invariant';
11
9
  import { useThemeContext } from '@dxos/react-ui';
10
+ import { withTheme } from '@dxos/react-ui/testing';
12
11
  import { attentionSurface, mx } from '@dxos/react-ui-theme';
13
- import { type Meta, withLayout, withTheme } from '@dxos/storybook-utils';
14
12
 
15
13
  import { EditorToolbar, useEditorToolbarState } from '../components';
16
14
  import { editorWidth } from '../defaults';
17
15
  import {
18
16
  InputModeExtensions,
19
- createMarkdownExtensions,
20
17
  createBasicExtensions,
18
+ createMarkdownExtensions,
21
19
  createThemeExtensions,
22
20
  decorateMarkdown,
23
21
  formattingKeymap,
24
22
  useFormattingState,
25
23
  } from '../extensions';
26
- import { useTextEditor, type UseTextEditorProps } from '../hooks';
24
+ import { type UseTextEditorProps, useTextEditor } from '../hooks';
27
25
  import { translations } from '../translations';
28
26
  import { type EditorInputMode, type EditorViewMode } from '../types';
29
27
 
@@ -43,8 +41,8 @@ const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
43
41
  moveToEndOfLine: true,
44
42
  extensions: [
45
43
  editorInputMode ? InputModeExtensions[editorInputMode] : [],
46
- createBasicExtensions({ placeholder, lineWrapping: true, readOnly: viewMode === 'readonly' }),
47
- createMarkdownExtensions({ themeMode }),
44
+ createBasicExtensions({ placeholder, lineWrapping: true, readOnly: viewMode === 'readonly', search: true }),
45
+ createMarkdownExtensions(),
48
46
  createThemeExtensions({ themeMode, syntaxHighlighting: true }),
49
47
  viewMode === 'source' ? [] : decorateMarkdown(),
50
48
  formattingKeymap(),
@@ -75,12 +73,15 @@ const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
75
73
  );
76
74
  };
77
75
 
78
- const meta: Meta<StoryProps> = {
76
+ const meta = {
79
77
  title: 'ui/react-ui-editor/EditorToolbar',
80
78
  render: DefaultStory,
81
- decorators: [withTheme, withLayout({ fullscreen: true })],
82
- parameters: { translations, layout: 'fullscreen' },
83
- };
79
+ decorators: [withTheme],
80
+ parameters: {
81
+ layout: 'fullscreen',
82
+ translations,
83
+ },
84
+ } satisfies Meta<typeof DefaultStory>;
84
85
 
85
86
  export default meta;
86
87
 
@@ -2,35 +2,39 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
6
  import defaultsDeep from 'lodash.defaultsdeep';
8
7
  import React from 'react';
9
8
 
10
9
  import { log } from '@dxos/log';
11
10
  import { faker } from '@dxos/random';
12
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
11
+ import { withTheme } from '@dxos/react-ui/testing';
13
12
 
14
- import { EditorStory, content } from './components';
15
- import { typewriter, blast, defaultOptions, dropFile } from '../extensions';
13
+ import { blast, defaultOptions, dropFile, typewriter } from '../extensions';
16
14
  import { str } from '../testing';
17
15
 
18
- const meta: Meta<typeof EditorStory> = {
16
+ import { EditorStory, content } from './components';
17
+
18
+ const meta = {
19
19
  title: 'ui/react-ui-editor/Experimental',
20
20
  component: EditorStory,
21
- decorators: [withTheme, withLayout({ fullscreen: true })],
22
- parameters: { layout: 'fullscreen' },
23
- };
21
+ decorators: [withTheme],
22
+ parameters: {
23
+ layout: 'fullscreen',
24
+ },
25
+ } satisfies Meta<typeof EditorStory>;
24
26
 
25
27
  export default meta;
26
28
 
29
+ type Story = StoryObj<typeof meta>;
30
+
27
31
  //
28
32
  // Typewriter
29
33
  //
30
34
 
31
- const typewriterItems = localStorage.getItem('dxos.org/plugin/markdown/typewriter')?.split(',');
35
+ const typewriterItems = localStorage.getItem('dxos.org/testing/typewriter')?.split(',');
32
36
 
33
- export const Typewriter = {
37
+ export const Typewriter: Story = {
34
38
  render: () => (
35
39
  <EditorStory
36
40
  text={str('# Typewriter', '', content.paragraphs, content.footer)}
@@ -43,7 +47,7 @@ export const Typewriter = {
43
47
  // Blast
44
48
  //
45
49
 
46
- export const Blast = {
50
+ export const Blast: Story = {
47
51
  render: () => (
48
52
  <EditorStory
49
53
  text={str('# Blast', '', content.paragraphs, content.codeblocks, content.paragraphs)}
@@ -70,7 +74,7 @@ export const Blast = {
70
74
  // DND
71
75
  //
72
76
 
73
- export const DND = {
77
+ export const DND: Story = {
74
78
  render: () => (
75
79
  <EditorStory
76
80
  text={str('# DND', '')}