@dxos/react-ui-editor 0.8.4-main.ef1bc66f44 → 0.8.4-main.effb148878

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 (149) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +796 -755
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/translations.mjs +39 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +796 -755
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/translations.mjs +41 -0
  12. package/dist/lib/node-esm/translations.mjs.map +7 -0
  13. package/dist/types/src/components/Editor/Editor.d.ts +36 -25
  14. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  15. package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
  16. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
  18. package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
  19. package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
  20. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
  21. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
  25. package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  27. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
  29. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -18
  31. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -18
  33. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -18
  35. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  36. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
  37. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  38. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -2
  39. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  40. package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
  41. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
  42. package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
  43. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  44. package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
  45. package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
  46. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +5 -19
  47. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  48. package/dist/types/src/components/index.d.ts +0 -2
  49. package/dist/types/src/components/index.d.ts.map +1 -1
  50. package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
  51. package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
  52. package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
  53. package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
  54. package/dist/types/src/extensions/index.d.ts +2 -0
  55. package/dist/types/src/extensions/index.d.ts.map +1 -0
  56. package/dist/types/src/hooks/index.d.ts +1 -0
  57. package/dist/types/src/hooks/index.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
  59. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
  60. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  61. package/dist/types/src/index.d.ts +1 -2
  62. package/dist/types/src/index.d.ts.map +1 -1
  63. package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
  64. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  65. package/dist/types/src/stories/Comments.stories.d.ts +2 -2
  66. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/EditorToolbar.stories.d.ts +28 -26
  68. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
  70. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Markdown.stories.d.ts +2 -2
  72. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  73. package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
  74. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  75. package/dist/types/src/stories/Popover.stories.d.ts +2 -2
  76. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  77. package/dist/types/src/stories/Preview.stories.d.ts +2 -2
  78. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  79. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  80. package/dist/types/src/stories/TextEditor.stories.d.ts +2 -2
  81. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  82. package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
  83. package/dist/types/src/stories/components/EditorStory.d.ts +4 -4
  84. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  85. package/dist/types/src/stories/components/util.d.ts +3 -2
  86. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  87. package/dist/types/src/translations.d.ts +24 -24
  88. package/dist/types/src/translations.d.ts.map +1 -1
  89. package/dist/types/src/util/react.d.ts +2 -5
  90. package/dist/types/src/util/react.d.ts.map +1 -1
  91. package/dist/types/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +77 -68
  93. package/src/components/Editor/Editor.stories.tsx +15 -21
  94. package/src/components/Editor/Editor.tsx +54 -53
  95. package/src/components/Editor/EditorView.tsx +102 -0
  96. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +19 -24
  97. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  98. package/src/components/EditorMenuProvider/popover.ts +3 -1
  99. package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
  100. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +1 -1
  101. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -65
  102. package/src/components/EditorToolbar/blocks.ts +54 -46
  103. package/src/components/EditorToolbar/formatting.ts +44 -45
  104. package/src/components/EditorToolbar/headings.ts +44 -50
  105. package/src/components/EditorToolbar/image.ts +16 -21
  106. package/src/components/EditorToolbar/index.ts +2 -3
  107. package/src/components/EditorToolbar/lists.ts +58 -0
  108. package/src/components/EditorToolbar/search.ts +16 -21
  109. package/src/components/EditorToolbar/types.ts +8 -0
  110. package/src/components/EditorToolbar/view-mode.ts +37 -43
  111. package/src/components/index.ts +0 -3
  112. package/src/extensions/Assistant.stories.tsx +112 -0
  113. package/src/extensions/assistant-extension.tsx +223 -0
  114. package/src/extensions/index.ts +5 -0
  115. package/src/hooks/index.ts +1 -0
  116. package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
  117. package/src/index.ts +1 -4
  118. package/src/stories/Automerge.stories.tsx +18 -16
  119. package/src/stories/Comments.stories.tsx +6 -6
  120. package/src/stories/EditorToolbar.stories.tsx +37 -65
  121. package/src/stories/Experimental.stories.tsx +12 -12
  122. package/src/stories/Markdown.stories.tsx +2 -2
  123. package/src/stories/Outliner.stories.tsx +4 -5
  124. package/src/stories/Popover.stories.tsx +10 -11
  125. package/src/stories/Preview.stories.tsx +51 -43
  126. package/src/stories/Tags.stories.tsx +5 -5
  127. package/src/stories/TextEditor.stories.tsx +2 -2
  128. package/src/stories/Theme.stories.tsx +4 -4
  129. package/src/stories/components/EditorStory.tsx +19 -12
  130. package/src/stories/components/util.tsx +49 -50
  131. package/src/translations.ts +29 -24
  132. package/src/util/react.tsx +4 -13
  133. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  134. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  135. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  136. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  137. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
  138. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  139. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  140. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  141. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  142. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  143. package/src/components/EditorContent/EditorContent.tsx +0 -83
  144. package/src/components/EditorContent/index.ts +0 -6
  145. package/src/components/EditorToolbar/actions.ts +0 -87
  146. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  147. package/src/stories/CommandDialog.stories.tsx +0 -81
  148. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  149. /package/src/components/{EditorContent → Editor}/controller.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.8.4-main.ef1bc66f44",
3
+ "version": "0.8.4-main.effb148878",
4
4
  "description": "Text editor components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -8,86 +8,94 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/dxos/dxos"
10
10
  },
11
- "license": "MIT",
11
+ "license": "FSL-1.1-Apache-2.0",
12
12
  "author": "DXOS.org",
13
13
  "sideEffects": false,
14
14
  "type": "module",
15
+ "imports": {
16
+ "#translations": "./src/translations.ts"
17
+ },
15
18
  "exports": {
16
19
  ".": {
17
20
  "source": "./src/index.ts",
18
21
  "types": "./dist/types/src/index.d.ts",
19
22
  "browser": "./dist/lib/browser/index.mjs",
20
23
  "node": "./dist/lib/node-esm/index.mjs"
24
+ },
25
+ "./translations": {
26
+ "source": "./src/translations.ts",
27
+ "types": "./dist/types/src/translations.d.ts",
28
+ "browser": "./dist/lib/browser/translations.mjs",
29
+ "node": "./dist/lib/node-esm/translations.mjs"
21
30
  }
22
31
  },
23
32
  "types": "dist/types/src/index.d.ts",
24
- "typesVersions": {
25
- "*": {}
26
- },
27
33
  "files": [
28
34
  "dist",
29
35
  "src"
30
36
  ],
31
37
  "dependencies": {
32
- "@automerge/automerge": "3.2.3",
33
- "@codemirror/autocomplete": "^6.19.0",
34
- "@codemirror/commands": "^6.8.1",
38
+ "@automerge/automerge": "3.2.6",
39
+ "@codemirror/autocomplete": "^6.20.2",
40
+ "@codemirror/commands": "^6.10.3",
35
41
  "@codemirror/lang-html": "^6.4.11",
36
- "@codemirror/lang-javascript": "^6.2.4",
42
+ "@codemirror/lang-javascript": "^6.2.5",
37
43
  "@codemirror/lang-json": "^6.0.2",
38
- "@codemirror/lang-markdown": "6.3.4",
44
+ "@codemirror/lang-markdown": "6.5.0",
39
45
  "@codemirror/lang-xml": "^6.1.0",
40
- "@codemirror/lang-yaml": "^6.1.2",
41
- "@codemirror/language": "^6.11.3",
42
- "@codemirror/language-data": "^6.5.1",
43
- "@codemirror/lint": "6.8.5",
44
- "@codemirror/search": "^6.5.11",
45
- "@codemirror/state": "^6.5.2",
46
+ "@codemirror/lang-yaml": "^6.1.3",
47
+ "@codemirror/language": "^6.12.3",
48
+ "@codemirror/language-data": "^6.5.2",
49
+ "@codemirror/lint": "6.9.6",
50
+ "@codemirror/search": "^6.7.0",
51
+ "@codemirror/state": "^6.6.0",
46
52
  "@codemirror/theme-one-dark": "^6.1.3",
47
- "@codemirror/view": "^6.38.4",
53
+ "@codemirror/view": "^6.43.0",
48
54
  "@fluentui/react-tabster": "9.26.11",
49
- "@lezer/common": "^1.2.2",
50
- "@lezer/generator": "^1.7.1",
51
- "@lezer/highlight": "^1.2.1",
52
- "@lezer/markdown": "^1.3.1",
55
+ "@lezer/common": "^1.5.2",
56
+ "@lezer/generator": "^1.8.0",
57
+ "@lezer/highlight": "^1.2.3",
58
+ "@lezer/markdown": "^1.6.3",
53
59
  "@radix-ui/react-context": "1.1.1",
54
60
  "@radix-ui/react-use-controllable-state": "1.1.0",
55
- "@replit/codemirror-vim": "^6.2.1",
61
+ "@replit/codemirror-vim": "^6.3.0",
56
62
  "@replit/codemirror-vscode-keymap": "^6.0.2",
57
63
  "@uiw/codemirror-theme-vscode": "^4.25.2",
58
- "ajv": "^8.17.1",
64
+ "ajv": "^8.18.0",
59
65
  "bind-event-listener": "^3.0.0",
60
- "codemirror": "^6.0.1",
66
+ "codemirror": "^6.0.2",
61
67
  "lib0": "^0.2.65",
62
68
  "lodash.defaultsdeep": "^4.6.1",
63
69
  "lodash.merge": "^4.6.2",
64
70
  "lodash.sortby": "^4.7.0",
65
71
  "style-mod": "^4.1.0",
66
- "@dxos/app-graph": "0.8.4-main.ef1bc66f44",
67
- "@dxos/async": "0.8.4-main.ef1bc66f44",
68
- "@dxos/client": "0.8.4-main.ef1bc66f44",
69
- "@dxos/context": "0.8.4-main.ef1bc66f44",
70
- "@dxos/echo": "0.8.4-main.ef1bc66f44",
71
- "@dxos/echo-db": "0.8.4-main.ef1bc66f44",
72
- "@dxos/debug": "0.8.4-main.ef1bc66f44",
73
- "@dxos/invariant": "0.8.4-main.ef1bc66f44",
74
- "@dxos/lit-ui": "0.8.4-main.ef1bc66f44",
75
- "@dxos/log": "0.8.4-main.ef1bc66f44",
76
- "@dxos/react-hooks": "0.8.4-main.ef1bc66f44",
77
- "@dxos/display-name": "0.8.4-main.ef1bc66f44",
78
- "@dxos/react-ui-menu": "0.8.4-main.ef1bc66f44",
79
- "@dxos/protocols": "0.8.4-main.ef1bc66f44",
80
- "@dxos/react-ui-mosaic": "0.8.4-main.ef1bc66f44",
81
- "@dxos/ui": "0.8.4-main.ef1bc66f44",
82
- "@dxos/ui-theme": "0.8.4-main.ef1bc66f44",
83
- "@dxos/ui-editor": "0.8.4-main.ef1bc66f44",
84
- "@dxos/util": "0.8.4-main.ef1bc66f44"
72
+ "@dxos/app-graph": "0.8.4-main.effb148878",
73
+ "@dxos/async": "0.8.4-main.effb148878",
74
+ "@dxos/client": "0.8.4-main.effb148878",
75
+ "@dxos/context": "0.8.4-main.effb148878",
76
+ "@dxos/display-name": "0.8.4-main.effb148878",
77
+ "@dxos/echo": "0.8.4-main.effb148878",
78
+ "@dxos/echo-db": "0.8.4-main.effb148878",
79
+ "@dxos/effect": "0.8.4-main.effb148878",
80
+ "@dxos/debug": "0.8.4-main.effb148878",
81
+ "@dxos/invariant": "0.8.4-main.effb148878",
82
+ "@dxos/log": "0.8.4-main.effb148878",
83
+ "@dxos/protocols": "0.8.4-main.effb148878",
84
+ "@dxos/react-hooks": "0.8.4-main.effb148878",
85
+ "@dxos/react-ui-menu": "0.8.4-main.effb148878",
86
+ "@dxos/react-ui-mosaic": "0.8.4-main.effb148878",
87
+ "@dxos/ui-editor": "0.8.4-main.effb148878",
88
+ "@dxos/ui": "0.8.4-main.effb148878",
89
+ "@dxos/ui-theme": "0.8.4-main.effb148878",
90
+ "@dxos/util": "0.8.4-main.effb148878",
91
+ "@dxos/lit-ui": "0.8.4-main.effb148878"
85
92
  },
86
93
  "devDependencies": {
87
- "@automerge/automerge": "3.2.3",
88
- "@automerge/automerge-repo": "2.5.1",
89
- "@automerge/automerge-repo-network-broadcastchannel": "2.5.1",
94
+ "@automerge/automerge": "3.2.6",
95
+ "@automerge/automerge-repo": "2.6.0-subduction.20",
96
+ "@automerge/automerge-repo-network-broadcastchannel": "2.6.0-subduction.20",
90
97
  "@effect-atom/atom-react": "^0.5.0",
98
+ "@effect/ai": "0.33.2",
91
99
  "@effect/platform": "0.94.4",
92
100
  "@types/chai": "^4.2.15",
93
101
  "@types/chai-dom": "^1.11.0",
@@ -99,39 +107,40 @@
99
107
  "@types/react-test-renderer": "^17.0.2",
100
108
  "chai": "^4.4.1",
101
109
  "chai-dom": "^1.11.0",
102
- "effect": "3.19.16",
103
- "happy-dom": "^13.3.1",
104
- "jsdom": "^27.0.0",
110
+ "effect": "3.20.0",
111
+ "happy-dom": "^20.0.0",
112
+ "jsdom": "^29.1.1",
105
113
  "mocha": "^10.6.0",
106
114
  "react": "~19.2.3",
107
115
  "react-dom": "~19.2.3",
108
116
  "react-test-renderer": "~19.2.0",
109
- "vite": "7.1.9",
117
+ "vite": "^8.0.14",
110
118
  "vite-plugin-top-level-await": "^1.6.0",
111
- "vite-plugin-wasm": "^3.5.0",
112
- "@dxos/config": "0.8.4-main.ef1bc66f44",
113
- "@dxos/echo-atom": "0.8.4-main.ef1bc66f44",
114
- "@dxos/random": "0.8.4-main.ef1bc66f44",
115
- "@dxos/react-client": "0.8.4-main.ef1bc66f44",
116
- "@dxos/react-ui": "0.8.4-main.ef1bc66f44",
117
- "@dxos/keyboard": "0.8.4-main.ef1bc66f44",
118
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.ef1bc66f44",
119
- "@dxos/schema": "0.8.4-main.ef1bc66f44",
120
- "@dxos/react-ui-attention": "0.8.4-main.ef1bc66f44",
121
- "@dxos/ui-theme": "0.8.4-main.ef1bc66f44",
122
- "@dxos/ui-types": "0.8.4-main.ef1bc66f44",
123
- "@dxos/storybook-utils": "0.8.4-main.ef1bc66f44",
124
- "@dxos/echo": "0.8.4-main.ef1bc66f44"
119
+ "vite-plugin-wasm": "^3.6.0",
120
+ "@dxos/ai": "0.8.4-main.effb148878",
121
+ "@dxos/config": "0.8.4-main.effb148878",
122
+ "@dxos/echo": "0.8.4-main.effb148878",
123
+ "@dxos/echo-atom": "0.8.4-main.effb148878",
124
+ "@dxos/keyboard": "0.8.4-main.effb148878",
125
+ "@dxos/random": "0.8.4-main.effb148878",
126
+ "@dxos/react-ui": "0.8.4-main.effb148878",
127
+ "@dxos/react-client": "0.8.4-main.effb148878",
128
+ "@dxos/react-ui-attention": "0.8.4-main.effb148878",
129
+ "@dxos/storybook-utils": "0.8.4-main.effb148878",
130
+ "@dxos/schema": "0.8.4-main.effb148878",
131
+ "@dxos/ui-types": "0.8.4-main.effb148878",
132
+ "@dxos/ui-theme": "0.8.4-main.effb148878",
133
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.effb148878"
125
134
  },
126
135
  "peerDependencies": {
127
136
  "@effect-atom/atom-react": "^0.5.0",
128
137
  "@effect/platform": "0.94.4",
129
- "effect": "3.19.16",
138
+ "effect": "3.20.0",
130
139
  "react": "~19.2.3",
131
140
  "react-dom": "~19.2.3",
132
- "@dxos/react-ui": "0.8.4-main.ef1bc66f44",
133
- "@dxos/react-client": "0.8.4-main.ef1bc66f44",
134
- "@dxos/ui-theme": "0.8.4-main.ef1bc66f44"
141
+ "@dxos/react-client": "0.8.4-main.effb148878",
142
+ "@dxos/react-ui": "0.8.4-main.effb148878",
143
+ "@dxos/ui-theme": "0.8.4-main.effb148878"
135
144
  },
136
145
  "publishConfig": {
137
146
  "access": "public"
@@ -6,10 +6,10 @@ import { type Decorator, type Meta, type StoryObj } from '@storybook/react-vite'
6
6
  import React, { useMemo } from 'react';
7
7
 
8
8
  import { createDocAccessor, createObject } from '@dxos/echo-db';
9
- import { faker } from '@dxos/random';
9
+ import { random } from '@dxos/random';
10
10
  import { useThemeContext } from '@dxos/react-ui';
11
- import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
11
  import { withAttention } from '@dxos/react-ui-attention/testing';
12
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
13
13
  import { Text } from '@dxos/schema';
14
14
  import {
15
15
  automerge,
@@ -20,21 +20,15 @@ import {
20
20
  } from '@dxos/ui-editor';
21
21
 
22
22
  import { createMenuGroup } from '../EditorMenuProvider';
23
+ import { Editor, type EditorViewProps } from './Editor';
23
24
 
24
- import { Editor, type EditorContentProps } from './Editor';
25
-
26
- // TODO(burdon): PreviewPopoverProvider (MarkdownStream, Preview story).
27
- // TODO(burdon): Adapt Markdown plugin to use new Editor (plan first to check fit).
28
- // TODO(burdon): Remove redundant hooks and simplify props.
29
-
30
- faker.seed(1234);
25
+ random.seed(1234);
31
26
 
32
27
  const initialValue = ['# Blue Monday', '', 'How does it **feel**?', ''].join('\n');
33
28
 
34
- const items = faker.helpers.multiple(faker.commerce.productName, { count: 10 }).sort();
29
+ const items = random.helpers.multiple(random.commerce.productName, { count: 10 }).sort();
35
30
 
36
- // TODO(burdon): Adapter other tests in react-ui-editor/stories to use this pattern.
37
- const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
31
+ const withExtensions: Decorator<EditorViewProps> = (Story, { args }) => {
38
32
  const { themeMode } = useThemeContext();
39
33
  const extensions = useMemo(
40
34
  () => [
@@ -42,7 +36,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
42
36
  createThemeExtensions({ themeMode }),
43
37
  createMarkdownExtensions(),
44
38
  decorateMarkdown(),
45
- automerge(createDocAccessor(createObject(Text.make(args.initialValue)), ['content'])),
39
+ automerge(createDocAccessor(createObject(Text.make({ content: args.initialValue })), ['content'])),
46
40
  ],
47
41
  [themeMode],
48
42
  );
@@ -52,7 +46,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
52
46
 
53
47
  const meta = {
54
48
  title: 'ui/react-ui-editor/Editor',
55
- component: Editor.Content,
49
+ component: Editor.View,
56
50
  decorators: [withExtensions, withTheme(), withLayout({ layout: 'column' }), withAttention()],
57
51
  parameters: {
58
52
  layout: 'fullscreen',
@@ -60,16 +54,16 @@ const meta = {
60
54
  args: {
61
55
  initialValue,
62
56
  },
63
- } satisfies Meta<typeof Editor.Content>;
57
+ } satisfies Meta<typeof Editor.View>;
64
58
 
65
59
  export default meta;
66
60
 
67
- type Story = StoryObj<EditorContentProps>;
61
+ type Story = StoryObj<EditorViewProps>;
68
62
 
69
63
  export const Default: Story = {
70
64
  render: (args) => (
71
65
  <Editor.Root>
72
- <Editor.Content {...args} />
66
+ <Editor.View {...args} />
73
67
  </Editor.Root>
74
68
  ),
75
69
  };
@@ -78,7 +72,7 @@ export const WithToolbar: Story = {
78
72
  render: (args) => (
79
73
  <Editor.Root>
80
74
  <Editor.Toolbar />
81
- <Editor.Content {...args} />
75
+ <Editor.View {...args} />
82
76
  </Editor.Root>
83
77
  ),
84
78
  };
@@ -86,10 +80,10 @@ export const WithToolbar: Story = {
86
80
  export const WithPopover: Story = {
87
81
  render: (args) => (
88
82
  <Editor.Root trigger={['@']} getMenu={({ text }) => [createMenuGroup({ items, filter: text })]}>
89
- <Editor.Viewport>
83
+ <Editor.Content>
90
84
  <Editor.Toolbar />
91
- <Editor.Content {...args} />
92
- </Editor.Viewport>
85
+ <Editor.View {...args} />
86
+ </Editor.Content>
93
87
  </Editor.Root>
94
88
  ),
95
89
  };
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  import { type Extension } from '@codemirror/state';
6
+ import { Atom } from '@effect-atom/atom-react';
6
7
  import { createContext } from '@radix-ui/react-context';
7
8
  import React, { type PropsWithChildren, forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react';
8
9
 
@@ -12,18 +13,23 @@ import { mx } from '@dxos/ui-theme';
12
13
  import { isNonNullable } from '@dxos/util';
13
14
 
14
15
  import {
15
- type EditorController,
16
- EditorContent as NaturalEditorContent,
17
- type EditorContentProps as NaturalEditorContentProps,
18
- noopController,
19
- } from '../EditorContent';
20
- import { EditorMenuProvider, type UseEditorMenuProps, useEditorMenu } from '../EditorMenuProvider';
16
+ EditorMenuProvider,
17
+ type EditorMenuProviderProps,
18
+ type UseEditorMenuProps,
19
+ useEditorMenu,
20
+ } from '../EditorMenuProvider';
21
21
  import {
22
22
  type EditorToolbarState,
23
23
  EditorToolbar as NaturalEditorToolbar,
24
24
  type EditorToolbarProps as NaturalEditorToolbarProps,
25
- useEditorToolbar,
26
25
  } from '../EditorToolbar';
26
+ import {
27
+ type EditorController,
28
+ EditorView as NaturalEditorContent,
29
+ type EditorViewProps as NaturalEditorContentProps,
30
+ createEditorController,
31
+ noopController,
32
+ } from './EditorView';
27
33
 
28
34
  //
29
35
  // Context
@@ -33,16 +39,26 @@ type EditorContextValue = {
33
39
  controller?: EditorController;
34
40
  setController: (controller: EditorController) => void;
35
41
  extensions?: Extension[];
36
- } & Pick<NaturalEditorToolbarProps, 'state'>;
42
+ state: Atom.Writable<EditorToolbarState>;
43
+ };
37
44
 
38
45
  const [EditorContextProvider, useEditorContext] = createContext<EditorContextValue>('Editor');
39
46
 
47
+ /**
48
+ * Access the editor context. Must be used within `Editor.Root`.
49
+ */
50
+ export { useEditorContext };
51
+
40
52
  //
41
53
  // Root
42
54
  //
43
55
 
44
56
  type EditorRootProps = PropsWithChildren<
45
- Pick<EditorContextValue, 'extensions'> & Omit<UseEditorMenuProps, 'viewRef'> & Pick<EditorToolbarState, 'viewMode'>
57
+ Pick<EditorContextValue, 'extensions'> &
58
+ Omit<UseEditorMenuProps, 'viewRef'> &
59
+ Pick<EditorMenuProviderProps, 'numItems'> & {
60
+ viewMode?: EditorToolbarState['viewMode'];
61
+ }
46
62
  >;
47
63
 
48
64
  /**
@@ -50,11 +66,9 @@ type EditorRootProps = PropsWithChildren<
50
66
  * Provides context for all child components and manages the editor controller state.
51
67
  */
52
68
  const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
53
- ({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
54
- const state = useEditorToolbar({ viewMode });
55
-
56
- const [controller, setController] = useState<EditorController>();
57
- useImperativeHandle(forwardedRef, () => controller ?? noopController, [controller]);
69
+ ({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
70
+ // TODO(wittjosiah): Including initialState in the deps causes reactivity issues.
71
+ const state = useMemo(() => Atom.make<EditorToolbarState>({ viewMode }), [viewMode]);
58
72
 
59
73
  // TODO(burdon): Consider lighter-weight approach if EditorMenuProvider is not needed.
60
74
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
@@ -63,6 +77,10 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
63
77
  [extension, extensionsProp],
64
78
  );
65
79
 
80
+ // External controller.
81
+ const [controller, setController] = useState<EditorController>(noopController);
82
+ useImperativeHandle(forwardedRef, () => controller, [controller]);
83
+
66
84
  return (
67
85
  <EditorContextProvider
68
86
  controller={controller}
@@ -70,7 +88,7 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
70
88
  extensions={extensions}
71
89
  state={state}
72
90
  >
73
- <EditorMenuProvider view={controller?.view} groups={groupsRef.current} {...menuProps}>
91
+ <EditorMenuProvider view={controller?.view} groups={groupsRef.current} numItems={numItems} {...menuProps}>
74
92
  {children}
75
93
  </EditorMenuProvider>
76
94
  </EditorContextProvider>
@@ -81,41 +99,36 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
81
99
  EditorRoot.displayName = 'Editor.Root';
82
100
 
83
101
  //
84
- // Viewport
102
+ // Content
85
103
  //
86
104
 
87
- const EDITOR_VIEWPORT_NAME = 'Editor.Viewport';
105
+ const EDITOR_CONTENT_NAME = 'Editor.Content';
88
106
 
89
- type EditorViewportProps = ThemedClassName<PropsWithChildren<{}>>;
107
+ type EditorContentProps = ThemedClassName<PropsWithChildren<{}>>;
90
108
 
91
109
  /**
92
- * Viewport component that wraps the toolbar and editor content area.
110
+ * Content component that wraps the toolbar and editor view area.
93
111
  */
94
- const EditorViewport = ({ classNames, children }: EditorViewportProps) => {
95
- return (
96
- <div role='none' className={mx('grid grid-rows-[min-content_1fr] bs-full overflow-hidden', classNames)}>
97
- {children}
98
- </div>
99
- );
112
+ const EditorContent = ({ classNames, children }: EditorContentProps) => {
113
+ return <div className={mx('grid grid-rows-[min-content_1fr] h-full overflow-hidden', classNames)}>{children}</div>;
100
114
  };
101
115
 
102
- EditorViewport.displayName = EDITOR_VIEWPORT_NAME;
116
+ EditorContent.displayName = EDITOR_CONTENT_NAME;
103
117
 
104
118
  //
105
- // Content
119
+ // View
106
120
  //
107
121
 
108
- const EDITOR_CONTENT_NAME = 'Editor.Content';
122
+ const EDITOR_VIEW_NAME = 'Editor.View';
109
123
 
110
- type EditorContentProps = Omit<NaturalEditorContentProps, 'ref'>;
124
+ type EditorViewProps = Omit<NaturalEditorContentProps, 'ref'>;
111
125
 
112
126
  /**
113
- * Content component that renders the actual CodeMirror editor.
127
+ * View component that renders the actual CodeMirror editor.
114
128
  * Automatically registers the editor controller with the context.
115
129
  */
116
- const EditorContent = ({ extensions: providedExtensions, ...props }: EditorContentProps) => {
117
- const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_CONTENT_NAME);
118
-
130
+ const EditorView = ({ extensions: providedExtensions, ...props }: EditorViewProps) => {
131
+ const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
119
132
  const extensions = useMemo(
120
133
  () => [additionalExtensions, providedExtensions].filter(isNonNullable).flat(),
121
134
  [providedExtensions, additionalExtensions],
@@ -124,7 +137,7 @@ const EditorContent = ({ extensions: providedExtensions, ...props }: EditorConte
124
137
  return <NaturalEditorContent {...props} extensions={extensions} ref={setController} />;
125
138
  };
126
139
 
127
- EditorContent.displayName = EDITOR_CONTENT_NAME;
140
+ EditorView.displayName = EDITOR_VIEW_NAME;
128
141
 
129
142
  //
130
143
  // Toolbar
@@ -140,8 +153,6 @@ type EditorToolbarProps = Omit<NaturalEditorToolbarProps, 'getView' | 'state'>;
140
153
  */
141
154
  const EditorToolbar = (props: EditorToolbarProps) => {
142
155
  const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
143
-
144
- // TODO(burdon): Fix invariant.
145
156
  const getView = useCallback(() => {
146
157
  invariant(controller?.view);
147
158
  return controller?.view;
@@ -156,30 +167,20 @@ EditorToolbar.displayName = EDITOR_TOOLBAR_NAME;
156
167
  // Editor
157
168
  //
158
169
 
159
- /**
160
- * Compound editor component following the Radix UI pattern.
161
- *
162
- * @example
163
- * ```tsx
164
- * EditorMenuGroup.Root>
165
- * EditorMenuGroup.Toolbar />
166
- * EditorMenuGroup.Viewport>
167
- * EditorMenuGroup.Content extensions={[...]} />
168
- * </Editor.Viewport>
169
- * </Editor.Root>
170
- * ```
171
- */
172
170
  export const Editor = {
173
171
  Root: EditorRoot,
174
- Viewport: EditorViewport,
175
- Content: EditorContent,
176
172
  Toolbar: EditorToolbar,
173
+ Content: EditorContent,
174
+ View: EditorView,
177
175
  };
178
176
 
179
177
  export type {
180
178
  EditorController,
181
179
  EditorRootProps,
182
- EditorViewportProps,
183
180
  EditorContentProps,
184
- // EditorToolbarProps, // TODO(burdon): Restore once removed deprecated props.
181
+ EditorViewProps,
182
+ EditorToolbarProps,
183
+ EditorToolbarState,
185
184
  };
185
+
186
+ export { createEditorController };
@@ -0,0 +1,102 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Transaction } from '@codemirror/state';
6
+ import { EditorView as NaturalEditorView } from '@codemirror/view';
7
+ import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
8
+
9
+ import { type ThemedClassName } from '@dxos/react-ui';
10
+ import { initialSync } from '@dxos/ui-editor';
11
+ import { mx } from '@dxos/ui-theme';
12
+
13
+ import { type UseTextEditorProps, useTextEditor } from '../../hooks';
14
+ import { type EditorController, createEditorController, noopController } from './controller';
15
+
16
+ export type EditorViewProps = ThemedClassName<
17
+ {
18
+ focusable?: boolean;
19
+ value?: string;
20
+ onChange?: (value: string) => void;
21
+ } & UseTextEditorProps
22
+ >;
23
+
24
+ /**
25
+ * Minimal text editor.
26
+ * NOTE: This shouold not be used with the automerge extension.
27
+ */
28
+ // TODO(burdon): Move controller to Root component, then make composable.
29
+ export const EditorView = forwardRef<EditorController, EditorViewProps>(
30
+ ({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
31
+ // Hold the latest onChange in a ref so callers may pass an inline callback
32
+ // without forcing the underlying editor to be destroyed and recreated on
33
+ // every render — which would blur the focused input on each keystroke.
34
+ const onChangeRef = useRef(onChange);
35
+ onChangeRef.current = onChange;
36
+
37
+ const { parentRef, focusAttributes, view } = useTextEditor(
38
+ () => ({
39
+ id,
40
+ initialValue: value,
41
+ selectionEnd,
42
+ extensions: [
43
+ extensions ?? [],
44
+ NaturalEditorView.updateListener.of(({ view, docChanged, transactions }) => {
45
+ const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
46
+ if (!isInitialSync && docChanged) {
47
+ onChangeRef.current?.(view.state.doc.toString());
48
+ }
49
+ }),
50
+ ],
51
+ ...props,
52
+ }),
53
+ [id, extensions, selectionEnd],
54
+ );
55
+
56
+ // External controller.
57
+ useImperativeHandle(forwardedRef, () => {
58
+ return createEditorController(view);
59
+ }, [id, view]);
60
+
61
+ // Sync the editor doc to the controlled `value` prop, but only when they
62
+ // disagree. After internal typing the prop will already match the editor's
63
+ // doc, and dispatching anyway would race fast keystrokes — a stale rAF
64
+ // closure can replace doc content with an older value, dropping characters.
65
+ useEffect(() => {
66
+ if (!view) {
67
+ return;
68
+ }
69
+ const next = value ?? '';
70
+ if (view.state.doc.toString() === next) {
71
+ return;
72
+ }
73
+ requestAnimationFrame(() => {
74
+ if (view.state.doc.toString() === next) {
75
+ return;
76
+ }
77
+ view.dispatch({
78
+ annotations: initialSync,
79
+ changes: [{ from: 0, to: view.state.doc.length, insert: next }],
80
+ selection: selectionEnd ? { anchor: next.length } : undefined,
81
+ });
82
+
83
+ if (selectionEnd) {
84
+ view.focus();
85
+ }
86
+ });
87
+ }, [view, value, selectionEnd]);
88
+
89
+ return (
90
+ <div
91
+ className={mx(
92
+ 'w-full outline-hidden focus:border-accent-surface focus-within:border-focus-ring-subtle',
93
+ classNames,
94
+ )}
95
+ {...(focusable ? focusAttributes : {})}
96
+ ref={parentRef}
97
+ />
98
+ );
99
+ },
100
+ );
101
+
102
+ export { type EditorController, createEditorController, noopController };