@dxos/react-ui-editor 0.8.4-main.5ea62a8 → 0.8.4-main.72ec0f3

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 (347) hide show
  1. package/dist/lib/browser/{chunk-22UMM3QJ.mjs → chunk-HL3YF6WC.mjs} +2 -2
  2. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5006 -3698
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +1 -1
  7. package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
  8. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
  9. package/dist/lib/node-esm/index.mjs +5006 -3698
  10. package/dist/lib/node-esm/index.mjs.map +4 -4
  11. package/dist/lib/node-esm/meta.json +1 -1
  12. package/dist/lib/node-esm/types/index.mjs +1 -1
  13. package/dist/types/src/components/Editor/Editor.d.ts +37 -15
  14. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  15. package/dist/types/src/components/Editor/Editor.stories.d.ts +20 -0
  16. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/EditorContent/EditorContent.d.ts +29 -0
  18. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +1 -0
  19. package/dist/types/src/components/EditorContent/EditorContent.stories.d.ts +26 -0
  20. package/dist/types/src/components/EditorContent/EditorContent.stories.d.ts.map +1 -0
  21. package/dist/types/src/components/EditorContent/controller.d.ts +10 -0
  22. package/dist/types/src/components/EditorContent/controller.d.ts.map +1 -0
  23. package/dist/types/src/components/EditorContent/index.d.ts +3 -0
  24. package/dist/types/src/components/EditorContent/index.d.ts.map +1 -0
  25. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +36 -0
  26. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -0
  27. package/dist/types/src/components/EditorMenuProvider/index.d.ts +7 -0
  28. package/dist/types/src/components/EditorMenuProvider/index.d.ts.map +1 -0
  29. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts +4 -0
  30. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -0
  31. package/dist/types/src/components/EditorMenuProvider/menu.d.ts +28 -0
  32. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -0
  33. package/dist/types/src/components/EditorMenuProvider/popover.d.ts +47 -0
  34. package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -0
  35. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts +34 -0
  36. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -0
  37. package/dist/types/src/components/EditorMenuProvider/util.d.ts +8 -0
  38. package/dist/types/src/components/EditorMenuProvider/util.d.ts.map +1 -0
  39. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts +16 -0
  40. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -0
  41. package/dist/types/src/components/EditorPreviewProvider/index.d.ts +2 -0
  42. package/dist/types/src/components/EditorPreviewProvider/index.d.ts.map +1 -0
  43. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +26 -2
  44. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  45. package/dist/types/src/components/EditorToolbar/actions.d.ts +39 -0
  46. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +1 -0
  47. package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -3
  48. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  49. package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -3
  50. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  51. package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -3
  52. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  53. package/dist/types/src/components/EditorToolbar/index.d.ts +2 -1
  54. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  55. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +11 -0
  56. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +1 -0
  57. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +3 -3
  58. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  59. package/dist/types/src/components/index.d.ts +4 -2
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts +17 -0
  62. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
  63. package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
  64. package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
  65. package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
  66. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
  67. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
  68. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
  69. package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
  70. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
  71. package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
  72. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  73. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
  74. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  75. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  76. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  77. package/dist/types/src/extensions/automerge/sync.d.ts +3 -3
  78. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  79. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  80. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  81. package/dist/types/src/extensions/autoscroll.d.ts +20 -0
  82. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
  83. package/dist/types/src/extensions/awareness/awareness-provider.d.ts +1 -1
  84. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  85. package/dist/types/src/extensions/blocks.d.ts +2 -0
  86. package/dist/types/src/extensions/blocks.d.ts.map +1 -0
  87. package/dist/types/src/extensions/bookmarks.d.ts +12 -0
  88. package/dist/types/src/extensions/bookmarks.d.ts.map +1 -0
  89. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  90. package/dist/types/src/extensions/factories.d.ts +11 -11
  91. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  92. package/dist/types/src/extensions/focus.d.ts.map +1 -1
  93. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  94. package/dist/types/src/extensions/index.d.ts +10 -1
  95. package/dist/types/src/extensions/index.d.ts.map +1 -1
  96. package/dist/types/src/extensions/json.d.ts +1 -1
  97. package/dist/types/src/extensions/json.d.ts.map +1 -1
  98. package/dist/types/src/extensions/listener.d.ts +8 -6
  99. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  100. package/dist/types/src/extensions/markdown/bundle.d.ts +6 -2
  101. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  102. package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
  103. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  104. package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
  105. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  106. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -3
  107. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  108. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  109. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  110. package/dist/types/src/extensions/modal.d.ts +7 -0
  111. package/dist/types/src/extensions/modal.d.ts.map +1 -0
  112. package/dist/types/src/extensions/modes.d.ts +1 -1
  113. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  114. package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
  115. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
  116. package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
  117. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  118. package/dist/types/src/extensions/outliner/tree.d.ts +1 -1
  119. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  120. package/dist/types/src/extensions/preview/preview.d.ts +8 -8
  121. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  122. package/dist/types/src/extensions/replacer.d.ts +21 -0
  123. package/dist/types/src/extensions/replacer.d.ts.map +1 -0
  124. package/dist/types/src/extensions/replacer.test.d.ts +2 -0
  125. package/dist/types/src/extensions/replacer.test.d.ts.map +1 -0
  126. package/dist/types/src/extensions/scrolling.d.ts +78 -0
  127. package/dist/types/src/extensions/scrolling.d.ts.map +1 -0
  128. package/dist/types/src/extensions/state.d.ts +2 -0
  129. package/dist/types/src/extensions/state.d.ts.map +1 -0
  130. package/dist/types/src/extensions/submit.d.ts +10 -0
  131. package/dist/types/src/extensions/submit.d.ts.map +1 -0
  132. package/dist/types/src/extensions/tab.d.ts +4 -0
  133. package/dist/types/src/extensions/tab.d.ts.map +1 -0
  134. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  135. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  136. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  137. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  138. package/dist/types/src/extensions/tags/index.d.ts +4 -0
  139. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  140. package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
  141. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
  142. package/dist/types/src/extensions/tags/xml-tags.d.ts +97 -0
  143. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  144. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  145. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  146. package/dist/types/src/hooks/useTextEditor.d.ts +5 -9
  147. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  148. package/dist/types/src/stories/{Command.stories.d.ts → CommandDialog.stories.d.ts} +2 -3
  149. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
  150. package/dist/types/src/stories/Comments.stories.d.ts +3 -4
  151. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  152. package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -2
  153. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  154. package/dist/types/src/stories/Experimental.stories.d.ts +3 -4
  155. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  156. package/dist/types/src/stories/Markdown.stories.d.ts +3 -4
  157. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  158. package/dist/types/src/stories/Outliner.stories.d.ts +0 -1
  159. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  160. package/dist/types/src/stories/{CommandMenu.stories.d.ts → Popover.stories.d.ts} +6 -6
  161. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
  162. package/dist/types/src/stories/Preview.stories.d.ts +4 -4
  163. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  164. package/dist/types/src/stories/Tags.stories.d.ts +16 -0
  165. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
  166. package/dist/types/src/stories/TextEditor.stories.d.ts +3 -5
  167. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  168. package/dist/types/src/stories/components/EditorStory.d.ts +7 -5
  169. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  170. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  171. package/dist/types/src/styles/theme.d.ts.map +1 -1
  172. package/dist/types/src/types/types.d.ts +2 -2
  173. package/dist/types/src/types/types.d.ts.map +1 -1
  174. package/dist/types/src/util/debug.d.ts +5 -1
  175. package/dist/types/src/util/debug.d.ts.map +1 -1
  176. package/dist/types/src/util/decorations.d.ts +4 -0
  177. package/dist/types/src/util/decorations.d.ts.map +1 -0
  178. package/dist/types/src/util/dom.d.ts +2 -12
  179. package/dist/types/src/util/dom.d.ts.map +1 -1
  180. package/dist/types/src/util/index.d.ts +1 -0
  181. package/dist/types/src/util/index.d.ts.map +1 -1
  182. package/dist/types/src/util/react.d.ts +1 -1
  183. package/dist/types/src/util/react.d.ts.map +1 -1
  184. package/dist/types/tsconfig.tsbuildinfo +1 -1
  185. package/package.json +68 -66
  186. package/src/components/Editor/Editor.stories.tsx +89 -0
  187. package/src/components/Editor/Editor.tsx +160 -25
  188. package/src/components/EditorContent/EditorContent.stories.tsx +70 -0
  189. package/src/components/EditorContent/EditorContent.tsx +70 -0
  190. package/src/components/EditorContent/controller.ts +50 -0
  191. package/src/components/EditorContent/index.ts +6 -0
  192. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +233 -0
  193. package/src/components/EditorMenuProvider/index.ts +11 -0
  194. package/src/components/EditorMenuProvider/menu-presets.ts +123 -0
  195. package/src/components/EditorMenuProvider/menu.ts +71 -0
  196. package/src/components/EditorMenuProvider/popover.ts +287 -0
  197. package/src/components/EditorMenuProvider/useEditorMenu.ts +175 -0
  198. package/src/components/EditorMenuProvider/util.ts +31 -0
  199. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +82 -0
  200. package/src/components/EditorPreviewProvider/index.ts +5 -0
  201. package/src/components/EditorToolbar/EditorToolbar.tsx +101 -91
  202. package/src/components/EditorToolbar/{lists.ts → actions.ts} +46 -16
  203. package/src/components/EditorToolbar/blocks.ts +2 -1
  204. package/src/components/EditorToolbar/formatting.ts +2 -1
  205. package/src/components/EditorToolbar/headings.ts +8 -5
  206. package/src/components/EditorToolbar/image.ts +1 -1
  207. package/src/components/EditorToolbar/index.ts +3 -7
  208. package/src/components/EditorToolbar/search.ts +1 -1
  209. package/src/components/EditorToolbar/useEditorToolbar.ts +20 -0
  210. package/src/components/EditorToolbar/view-mode.ts +2 -1
  211. package/src/components/index.ts +8 -2
  212. package/src/extensions/autocomplete/autocomplete.ts +151 -0
  213. package/src/extensions/autocomplete/index.ts +8 -0
  214. package/src/extensions/autocomplete/match.ts +46 -0
  215. package/src/extensions/{command → autocomplete}/placeholder.ts +21 -17
  216. package/src/extensions/{command → autocomplete}/typeahead.ts +6 -48
  217. package/src/extensions/automerge/automerge.stories.tsx +9 -9
  218. package/src/extensions/automerge/automerge.ts +28 -9
  219. package/src/extensions/automerge/cursor.ts +1 -1
  220. package/src/extensions/automerge/sync.ts +8 -4
  221. package/src/extensions/automerge/update-automerge.ts +1 -1
  222. package/src/extensions/autoscroll.ts +163 -0
  223. package/src/extensions/awareness/awareness-provider.ts +2 -2
  224. package/src/extensions/blocks.ts +131 -0
  225. package/src/extensions/bookmarks.ts +75 -0
  226. package/src/extensions/comments.ts +7 -2
  227. package/src/extensions/factories.ts +50 -32
  228. package/src/extensions/focus.ts +5 -4
  229. package/src/extensions/folding.tsx +3 -6
  230. package/src/extensions/hashtag.tsx +2 -2
  231. package/src/extensions/index.ts +10 -1
  232. package/src/extensions/json.ts +1 -1
  233. package/src/extensions/listener.ts +14 -20
  234. package/src/extensions/markdown/bundle.ts +37 -6
  235. package/src/extensions/markdown/decorate.ts +26 -17
  236. package/src/extensions/markdown/formatting.ts +8 -10
  237. package/src/extensions/markdown/highlight.ts +1 -1
  238. package/src/extensions/markdown/image.ts +5 -6
  239. package/src/extensions/markdown/link.ts +3 -0
  240. package/src/extensions/markdown/table.ts +13 -7
  241. package/src/extensions/modal.ts +24 -0
  242. package/src/extensions/modes.ts +2 -2
  243. package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +16 -21
  244. package/src/extensions/outliner/outliner.test.ts +1 -1
  245. package/src/extensions/outliner/outliner.ts +5 -5
  246. package/src/extensions/outliner/tree.test.ts +1 -1
  247. package/src/extensions/outliner/tree.ts +1 -1
  248. package/src/extensions/preview/index.ts +1 -1
  249. package/src/extensions/preview/preview.ts +69 -69
  250. package/src/extensions/replacer.test.ts +75 -0
  251. package/src/extensions/replacer.ts +93 -0
  252. package/src/extensions/scrolling.ts +189 -0
  253. package/src/extensions/selection.ts +3 -3
  254. package/src/extensions/state.ts +7 -0
  255. package/src/extensions/submit.ts +62 -0
  256. package/src/extensions/tab.ts +29 -0
  257. package/src/extensions/tags/extended-markdown.test.ts +262 -0
  258. package/src/extensions/tags/extended-markdown.ts +78 -0
  259. package/src/extensions/tags/index.ts +7 -0
  260. package/src/extensions/tags/streamer.ts +243 -0
  261. package/src/extensions/tags/xml-tags.ts +500 -0
  262. package/src/extensions/tags/xml-util.ts +94 -0
  263. package/src/extensions/typewriter.ts +1 -1
  264. package/src/hooks/useTextEditor.ts +31 -43
  265. package/src/stories/CommandDialog.stories.tsx +83 -0
  266. package/src/stories/Comments.stories.tsx +8 -9
  267. package/src/stories/EditorToolbar.stories.tsx +15 -14
  268. package/src/stories/Experimental.stories.tsx +7 -7
  269. package/src/stories/Markdown.stories.tsx +6 -6
  270. package/src/stories/Outliner.stories.tsx +40 -32
  271. package/src/stories/Popover.stories.tsx +162 -0
  272. package/src/stories/Preview.stories.tsx +46 -43
  273. package/src/stories/Tags.stories.tsx +95 -0
  274. package/src/stories/TextEditor.stories.tsx +10 -33
  275. package/src/stories/components/EditorStory.tsx +30 -17
  276. package/src/stories/components/util.tsx +40 -8
  277. package/src/styles/markdown.ts +1 -1
  278. package/src/styles/theme.ts +13 -11
  279. package/src/types/types.ts +1 -1
  280. package/src/util/debug.ts +7 -2
  281. package/src/util/decorations.ts +21 -0
  282. package/src/util/dom.ts +5 -27
  283. package/src/util/index.ts +1 -0
  284. package/src/util/react.tsx +1 -1
  285. package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
  286. package/dist/lib/browser/testing/index.mjs +0 -6
  287. package/dist/lib/browser/testing/index.mjs.map +0 -7
  288. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
  289. package/dist/lib/node-esm/testing/index.mjs +0 -8
  290. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  291. package/dist/types/src/components/EditorToolbar/lists.d.ts +0 -19
  292. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +0 -1
  293. package/dist/types/src/components/EditorToolbar/util.d.ts +0 -51
  294. package/dist/types/src/components/EditorToolbar/util.d.ts.map +0 -1
  295. package/dist/types/src/components/Popover/CommandMenu.d.ts +0 -34
  296. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
  297. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
  298. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
  299. package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
  300. package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
  301. package/dist/types/src/components/Popover/index.d.ts +0 -4
  302. package/dist/types/src/components/Popover/index.d.ts.map +0 -1
  303. package/dist/types/src/extensions/autocomplete.d.ts +0 -13
  304. package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
  305. package/dist/types/src/extensions/command/action.d.ts +0 -17
  306. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  307. package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
  308. package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
  309. package/dist/types/src/extensions/command/command.d.ts +0 -6
  310. package/dist/types/src/extensions/command/command.d.ts.map +0 -1
  311. package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
  312. package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
  313. package/dist/types/src/extensions/command/hint.d.ts +0 -24
  314. package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
  315. package/dist/types/src/extensions/command/index.d.ts +0 -7
  316. package/dist/types/src/extensions/command/index.d.ts.map +0 -1
  317. package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
  318. package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
  319. package/dist/types/src/extensions/command/state.d.ts +0 -16
  320. package/dist/types/src/extensions/command/state.d.ts.map +0 -1
  321. package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
  322. package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
  323. package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
  324. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
  325. package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
  326. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
  327. package/dist/types/src/testing/index.d.ts +0 -2
  328. package/dist/types/src/testing/index.d.ts.map +0 -1
  329. package/dist/types/src/testing/util.d.ts +0 -3
  330. package/dist/types/src/testing/util.d.ts.map +0 -1
  331. package/src/components/EditorToolbar/util.ts +0 -76
  332. package/src/components/Popover/CommandMenu.tsx +0 -279
  333. package/src/components/Popover/RefDropdownMenu.tsx +0 -85
  334. package/src/components/Popover/RefPopover.tsx +0 -99
  335. package/src/components/Popover/index.ts +0 -7
  336. package/src/extensions/autocomplete.ts +0 -69
  337. package/src/extensions/command/action.ts +0 -56
  338. package/src/extensions/command/command-menu.ts +0 -211
  339. package/src/extensions/command/command.ts +0 -34
  340. package/src/extensions/command/hint.ts +0 -103
  341. package/src/extensions/command/index.ts +0 -10
  342. package/src/extensions/command/state.ts +0 -90
  343. package/src/extensions/command/useCommandMenu.ts +0 -119
  344. package/src/stories/Command.stories.tsx +0 -101
  345. package/src/stories/CommandMenu.stories.tsx +0 -161
  346. package/src/testing/index.ts +0 -5
  347. package/src/testing/util.ts +0 -7
@@ -11,20 +11,23 @@ import { clientRectsFor, flattenRect } from '../../util';
11
11
  type Content = string | HTMLElement | ((view: EditorView) => HTMLElement);
12
12
 
13
13
  export type PlaceholderOptions = {
14
- delay?: number;
15
14
  content: Content;
15
+ delay?: number;
16
16
  };
17
17
 
18
- export const placeholder = ({ delay = 3_000, content }: PlaceholderOptions): Extension => {
18
+ /**
19
+ * Shows a transient placeholder at the current cursor position.
20
+ */
21
+ export const placeholder = ({ content, delay = 3_000 }: PlaceholderOptions): Extension => {
19
22
  const plugin = ViewPlugin.fromClass(
20
23
  class {
21
- decorations = Decoration.none;
22
- timeout: ReturnType<typeof setTimeout> | undefined;
24
+ _timeout: ReturnType<typeof setTimeout> | undefined;
25
+ _decorations = Decoration.none;
23
26
 
24
27
  update(update: ViewUpdate) {
25
- if (this.timeout) {
26
- window.clearTimeout(this.timeout);
27
- this.timeout = undefined;
28
+ if (this._timeout) {
29
+ window.clearTimeout(this._timeout);
30
+ this._timeout = undefined;
28
31
  }
29
32
 
30
33
  // Check if the active line (where cursor is) is empty.
@@ -33,10 +36,10 @@ export const placeholder = ({ delay = 3_000, content }: PlaceholderOptions): Ext
33
36
  if (isEmpty) {
34
37
  // Create widget decoration at the start of the current line.
35
38
  const lineStart = activeLine.from;
36
- this.timeout = setTimeout(() => {
37
- this.decorations = Decoration.set([
39
+ this._timeout = setTimeout(() => {
40
+ this._decorations = Decoration.set([
38
41
  Decoration.widget({
39
- widget: new Placeholder(content),
42
+ widget: new PlaceholderWidget(content),
40
43
  side: 1,
41
44
  }).range(lineStart),
42
45
  ]);
@@ -45,18 +48,18 @@ export const placeholder = ({ delay = 3_000, content }: PlaceholderOptions): Ext
45
48
  }, delay);
46
49
  }
47
50
 
48
- this.decorations = Decoration.none;
51
+ this._decorations = Decoration.none;
49
52
  }
50
53
 
51
54
  destroy() {
52
- if (this.timeout) {
53
- clearTimeout(this.timeout);
55
+ if (this._timeout) {
56
+ clearTimeout(this._timeout);
54
57
  }
55
58
  }
56
59
  },
57
60
  {
58
61
  provide: (plugin) => {
59
- return [EditorView.decorations.of((view) => view.plugin(plugin)?.decorations ?? Decoration.none)];
62
+ return [EditorView.decorations.of((view) => view.plugin(plugin)?._decorations ?? Decoration.none)];
60
63
  },
61
64
  },
62
65
  );
@@ -66,7 +69,7 @@ export const placeholder = ({ delay = 3_000, content }: PlaceholderOptions): Ext
66
69
  : plugin;
67
70
  };
68
71
 
69
- class Placeholder extends WidgetType {
72
+ export class PlaceholderWidget extends WidgetType {
70
73
  constructor(readonly content: Content) {
71
74
  super();
72
75
  }
@@ -75,6 +78,7 @@ class Placeholder extends WidgetType {
75
78
  const wrap = document.createElement('span');
76
79
  wrap.className = 'cm-placeholder';
77
80
  wrap.style.pointerEvents = 'none';
81
+ wrap.setAttribute('aria-hidden', 'true');
78
82
  wrap.appendChild(
79
83
  typeof this.content === 'string'
80
84
  ? document.createTextNode(this.content)
@@ -82,7 +86,7 @@ class Placeholder extends WidgetType {
82
86
  ? this.content(view)
83
87
  : this.content.cloneNode(true),
84
88
  );
85
- wrap.setAttribute('aria-hidden', 'true');
89
+
86
90
  return wrap;
87
91
  }
88
92
 
@@ -92,7 +96,7 @@ class Placeholder extends WidgetType {
92
96
  return null;
93
97
  }
94
98
 
95
- const style = window.getComputedStyle(dom.parentNode as HTMLElement);
99
+ const style = getComputedStyle(dom.parentNode as HTMLElement);
96
100
  const rect = flattenRect(rects[0], style.direction !== 'rtl');
97
101
  const lineHeight = parseInt(style.lineHeight);
98
102
  if (rect.bottom - rect.top > lineHeight * 1.5) {
@@ -13,17 +13,16 @@ import {
13
13
  keymap,
14
14
  } from '@codemirror/view';
15
15
 
16
- import { Hint } from './hint';
16
+ import { type CompoetionContext } from './match';
17
+ import { PlaceholderWidget } from './placeholder';
17
18
 
18
- export type TypeaheadContext = { line: string };
19
-
20
- // TODO(burdon): Option to complete only at end of line?
19
+ // TODO(burdon): Option to complete only at end of line.
21
20
  export type TypeaheadOptions = {
22
- onComplete?: (context: TypeaheadContext) => string | undefined;
21
+ onComplete?: (context: CompoetionContext) => string | undefined;
23
22
  };
24
23
 
25
24
  /**
26
- * CodeMirror extension for typeahead completion.
25
+ * Shows a completion placeholder.
27
26
  */
28
27
  export const typeahead = ({ onComplete }: TypeaheadOptions = {}): Extension => {
29
28
  let hint: string | undefined;
@@ -57,7 +56,7 @@ export const typeahead = ({ onComplete }: TypeaheadOptions = {}): Extension => {
57
56
  const str = update.state.sliceDoc(line.from, selection.from);
58
57
  hint = onComplete?.({ line: str });
59
58
  if (hint) {
60
- builder.add(selection.from, selection.to, Decoration.widget({ widget: new Hint(hint) }));
59
+ builder.add(selection.from, selection.to, Decoration.widget({ widget: new PlaceholderWidget(hint) }));
61
60
  }
62
61
  }
63
62
 
@@ -86,44 +85,3 @@ export const typeahead = ({ onComplete }: TypeaheadOptions = {}): Extension => {
86
85
  ),
87
86
  ];
88
87
  };
89
-
90
- type CompletionOptions = {
91
- default?: string;
92
- minLength?: number;
93
- };
94
-
95
- /**
96
- * Util to match current line to a static list of completions.
97
- */
98
- export const staticCompletion =
99
- (completions: string[], options: CompletionOptions = {}) =>
100
- ({ line }: TypeaheadContext) => {
101
- if (line.length === 0 && options.default) {
102
- return options.default;
103
- }
104
-
105
- const parts = line.split(/\s+/).filter(Boolean);
106
- if (parts.length) {
107
- const str = parts.at(-1)!;
108
- if (str.length >= (options.minLength ?? 0)) {
109
- for (const completion of completions) {
110
- const match = matchCompletion(completion, str);
111
- if (match) {
112
- return match;
113
- }
114
- }
115
- }
116
- }
117
- };
118
-
119
- export const matchCompletion = (completion: string, str: string, minLength = 0): string | undefined => {
120
- if (
121
- str.length >= minLength &&
122
- completion.length > str.length &&
123
- completion.startsWith(str)
124
- // TODO(burdon): If case insensitive, need to replace existing chars.
125
- // completion.toLowerCase().startsWith(str.toLowerCase())
126
- ) {
127
- return completion.slice(str.length);
128
- }
129
- };
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import '@preact/signals-react';
8
6
 
9
7
  import { Repo } from '@automerge/automerge-repo';
@@ -16,7 +14,8 @@ import { DocAccessor, Query, type Space, createDocAccessor, useQuery, useSpace }
16
14
  import { type Identity, useIdentity } from '@dxos/react-client/halo';
17
15
  import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
18
16
  import { useThemeContext } from '@dxos/react-ui';
19
- import { render, withLayout, withTheme } from '@dxos/storybook-utils';
17
+ import { withTheme } from '@dxos/react-ui/testing';
18
+ import { render } from '@dxos/storybook-utils';
20
19
 
21
20
  import { editorSlots } from '../../defaults';
22
21
  import { useTextEditor } from '../../hooks';
@@ -51,7 +50,7 @@ const Editor = ({ source, autoFocus, space, identity }: EditorProps) => {
51
50
  [source, themeMode],
52
51
  );
53
52
 
54
- return <div ref={parentRef} className='flex w-full' />;
53
+ return <div ref={parentRef} className='flex is-full' />;
55
54
  };
56
55
 
57
56
  const DefaultStory = () => {
@@ -96,8 +95,9 @@ const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
96
95
  const objects = useQuery(space, Query.type(Type.Expando, { type: 'test' }));
97
96
 
98
97
  useEffect(() => {
99
- if (!source && objects.length) {
100
- const source = createDocAccessor(objects[0].content, ['content']);
98
+ const content = objects[0]?.content.target;
99
+ if (!source && content) {
100
+ const source = createDocAccessor(content, ['content']);
101
101
  setSource(source);
102
102
  }
103
103
  }, [objects, source]);
@@ -113,8 +113,9 @@ const meta = {
113
113
  title: 'ui/react-ui-editor/Automerge',
114
114
  component: Editor as any,
115
115
  render: render(DefaultStory),
116
- decorators: [withTheme, withLayout({ fullscreen: true })],
116
+ decorators: [withTheme],
117
117
  parameters: {
118
+ layout: 'fullscreen',
118
119
  translations,
119
120
  },
120
121
  } satisfies Meta<typeof DefaultStory>;
@@ -128,14 +129,13 @@ export const Default: Story = {
128
129
  };
129
130
 
130
131
  export const WithEcho: Story = {
131
- decorators: [withTheme],
132
132
  render: () => {
133
133
  return (
134
134
  <ClientRepeater
135
135
  count={2}
136
136
  component={EchoStory}
137
137
  createSpace
138
- onSpaceCreated={async ({ space }) => {
138
+ onCreateSpace={async ({ space }) => {
139
139
  space.db.add(
140
140
  Obj.make(Type.Expando, {
141
141
  type: 'test',
@@ -5,12 +5,13 @@
5
5
  //
6
6
 
7
7
  import { next as A } from '@automerge/automerge';
8
- import { type Extension, StateField } from '@codemirror/state';
8
+ import { type Extension, StateField, Transaction } from '@codemirror/state';
9
9
  import { EditorView, ViewPlugin } from '@codemirror/view';
10
10
 
11
- import { type DocAccessor } from '@dxos/react-client/echo';
11
+ import { DocAccessor } from '@dxos/client/echo';
12
12
 
13
13
  import { Cursor } from '../../util';
14
+ import { initialSync } from '../state';
14
15
 
15
16
  import { cursorConverter } from './cursor';
16
17
  import { type State, isReconcile, updateHeadsEffect } from './defs';
@@ -18,11 +19,13 @@ import { Syncer } from './sync';
18
19
 
19
20
  export const automerge = (accessor: DocAccessor): Extension => {
20
21
  const syncState = StateField.define<State>({
21
- create: () => ({
22
- path: accessor.path.slice(),
23
- lastHeads: A.getHeads(accessor.handle.doc()!),
24
- unreconciledTransactions: [],
25
- }),
22
+ create: () => {
23
+ return {
24
+ path: accessor.path.slice(),
25
+ lastHeads: A.getHeads(accessor.handle.doc()!),
26
+ unreconciledTransactions: [],
27
+ };
28
+ },
26
29
 
27
30
  update: (value, tr) => {
28
31
  const result: State = {
@@ -64,6 +67,18 @@ export const automerge = (accessor: DocAccessor): Extension => {
64
67
  class {
65
68
  constructor(private readonly _view: EditorView) {
66
69
  accessor.handle.addListener('change', this._handleChange);
70
+
71
+ requestAnimationFrame(() => {
72
+ const value = DocAccessor.getValue<string>(accessor);
73
+ const current = this._view.state.doc.toString();
74
+ if (value !== current) {
75
+ // TODO(burdon): This attempts to set the initial state, but creates problems.
76
+ // this._view.dispatch({
77
+ // changes: { from: 0, to: this._view.state.doc.length, insert: value },
78
+ // annotations: initialSync,
79
+ // });
80
+ }
81
+ });
67
82
  }
68
83
 
69
84
  destroy() {
@@ -77,9 +92,13 @@ export const automerge = (accessor: DocAccessor): Extension => {
77
92
  ),
78
93
 
79
94
  // Reconcile local updates.
80
- EditorView.updateListener.of(({ view, changes }) => {
95
+ EditorView.updateListener.of(({ view, changes, transactions }) => {
81
96
  if (!changes.empty) {
82
- syncer.reconcile(view, true);
97
+ // Only reconcile if it's not an initial sync (to avoid loops)
98
+ const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
99
+ if (!isInitialSync) {
100
+ syncer.reconcile(view, true);
101
+ }
83
102
  }
84
103
  }),
85
104
  ];
@@ -2,8 +2,8 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type DocAccessor, fromCursor, toCursor } from '@dxos/client/echo';
5
6
  import { log } from '@dxos/log';
6
- import { type DocAccessor, fromCursor, toCursor } from '@dxos/react-client/echo';
7
7
 
8
8
  import { type CursorConverter } from '../../util';
9
9
 
@@ -8,7 +8,8 @@ import { next as A } from '@automerge/automerge';
8
8
  import { type StateField } from '@codemirror/state';
9
9
  import { type EditorView } from '@codemirror/view';
10
10
 
11
- import { type IDocHandle } from '@dxos/react-client/echo';
11
+ import { type IDocHandle } from '@dxos/client/echo';
12
+ import { log } from '@dxos/log';
12
13
 
13
14
  import { type State, getLastHeads, getPath, isReconcile, reconcileAnnotation, updateHeads } from './defs';
14
15
  import { updateAutomerge } from './update-automerge';
@@ -27,7 +28,6 @@ export class Syncer {
27
28
  ) {}
28
29
 
29
30
  reconcile(view: EditorView, editor: boolean): void {
30
- // TODO(burdon): Better way to do mutex?
31
31
  if (this._pending) {
32
32
  return;
33
33
  }
@@ -41,7 +41,9 @@ export class Syncer {
41
41
  this._pending = false;
42
42
  }
43
43
 
44
- onEditorChange(view: EditorView): void {
44
+ private onEditorChange(view: EditorView): void {
45
+ log('onEditorChange');
46
+
45
47
  // Apply the unreconciled transactions to the document.
46
48
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
47
49
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
@@ -54,7 +56,9 @@ export class Syncer {
54
56
  }
55
57
  }
56
58
 
57
- onAutomergeChange(view: EditorView): void {
59
+ private onAutomergeChange(view: EditorView): void {
60
+ log('onAutomergeChange');
61
+
58
62
  // Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
59
63
  const oldHeads = getLastHeads(view.state, this._state);
60
64
  const newHeads = A.getHeads(this._handle.doc()!);
@@ -7,7 +7,7 @@
7
7
  import { next as A, type Heads } from '@automerge/automerge';
8
8
  import { type EditorState, type StateField, type Text, type Transaction } from '@codemirror/state';
9
9
 
10
- import { type IDocHandle } from '@dxos/react-client/echo';
10
+ import { type IDocHandle } from '@dxos/client/echo';
11
11
 
12
12
  import { type State } from './defs';
13
13
 
@@ -0,0 +1,163 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { StateEffect } from '@codemirror/state';
6
+ import { EditorView, ViewPlugin } from '@codemirror/view';
7
+
8
+ import { debounce } from '@dxos/async';
9
+ import { Domino } from '@dxos/react-ui';
10
+
11
+ import { scrollToLineEffect } from './scrolling';
12
+
13
+ // TODO(burdon): Reconcile with scrollToLineEffect (scrolling).
14
+ export const scrollToBottomEffect = StateEffect.define<ScrollBehavior | undefined>();
15
+
16
+ export type AutoScrollOptions = {
17
+ /** Auto-scroll when reaches the bottom. */
18
+ autoScroll?: boolean;
19
+ /** Threshold in px to trigger scroll from bottom. */
20
+ threshold?: number;
21
+ /** Throttle time in ms. */
22
+ throttleDelay?: number;
23
+ /** Callback when auto-scrolling. */
24
+ onAutoScroll?: (props: { view: EditorView; distanceFromBottom: number }) => boolean | void;
25
+ };
26
+
27
+ /**
28
+ * Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
29
+ */
30
+ // TODO(burdon): Reconcile with transcript-extension.
31
+ export const autoScroll = ({
32
+ autoScroll = true,
33
+ threshold = 100,
34
+ throttleDelay = 1_000,
35
+ onAutoScroll,
36
+ }: Partial<AutoScrollOptions> = {}) => {
37
+ let buttonContainer: HTMLDivElement | undefined;
38
+ let hideTimeout: NodeJS.Timeout | undefined;
39
+ let lastScrollTop = 0;
40
+ let isPinned = true;
41
+
42
+ const setPinned = (pin: boolean) => {
43
+ isPinned = pin;
44
+ buttonContainer?.classList.toggle('opacity-0', pin);
45
+ };
46
+
47
+ // Temporarily hide the scrollbar while auto-scrolling.
48
+ const hideScrollbar = (view: EditorView) => {
49
+ view.scrollDOM.classList.add('cm-hide-scrollbar');
50
+ clearTimeout(hideTimeout);
51
+ hideTimeout = setTimeout(() => {
52
+ view.scrollDOM.classList.remove('cm-hide-scrollbar');
53
+ }, 1_000);
54
+ };
55
+
56
+ // Throttled scroll to bottom.
57
+ const scrollToBottom = (view: EditorView, behavior?: ScrollBehavior) => {
58
+ setPinned(true);
59
+ hideScrollbar(view);
60
+ const line = view.state.doc.lineAt(view.state.doc.length);
61
+ view.dispatch({
62
+ selection: { anchor: line.to, head: line.to },
63
+ effects: scrollToLineEffect.of({ line: line.number, options: { position: 'end', offset: threshold, behavior } }),
64
+ });
65
+ };
66
+
67
+ // Throttled check for distance from bottom (for downward scrolls only).
68
+ const checkDistance = debounce((view: EditorView) => {
69
+ const scrollerRect = view.scrollDOM.getBoundingClientRect();
70
+ const coords = view.coordsAtPos(view.state.doc.length);
71
+ const distanceFromBottom = coords ? coords.bottom - scrollerRect.bottom : 0;
72
+ setPinned(distanceFromBottom < 0);
73
+ }, 1_000);
74
+
75
+ // Debounce scroll updates so rapid edits don't cause clunky scrolling.
76
+ const triggerUpdate = debounce((view: EditorView) => scrollToBottom(view), throttleDelay);
77
+
78
+ return [
79
+ // Update listener for logging when scrolling is needed.
80
+ EditorView.updateListener.of(({ view, transactions, heightChanged }) => {
81
+ // TODO(burdon): Remove and use scrollToLineEffect instead.
82
+ transactions.forEach((transaction) => {
83
+ for (const effect of transaction.effects) {
84
+ if (effect.is(scrollToBottomEffect)) {
85
+ scrollToBottom(view, effect.value);
86
+ }
87
+ }
88
+ });
89
+
90
+ // Maybe scroll if doc changed and pinned.
91
+ // NOTE: Geometry changed is triggered when widgets change height (e.g., toggle tool block).
92
+ if (heightChanged && isPinned) {
93
+ const coords = view.coordsAtPos(view.state.doc.length);
94
+ const scrollerRect = view.scrollDOM.getBoundingClientRect();
95
+ const distanceFromBottom = coords ? scrollerRect.bottom - coords.bottom : 0;
96
+ if (autoScroll && distanceFromBottom < threshold) {
97
+ const shouldScroll = onAutoScroll?.({ view, distanceFromBottom }) ?? true;
98
+ if (shouldScroll) {
99
+ triggerUpdate(view);
100
+ }
101
+ } else if (distanceFromBottom < 0) {
102
+ setPinned(false);
103
+ }
104
+ }
105
+ }),
106
+
107
+ // Detect user scroll.
108
+ EditorView.domEventHandlers({
109
+ scroll: (event, view) => {
110
+ const currentScrollTop = view.scrollDOM.scrollTop;
111
+ const scrollingUp = currentScrollTop < lastScrollTop;
112
+ lastScrollTop = currentScrollTop;
113
+
114
+ // If user scrolls up, immediately unpin auto-scroll.
115
+ if (scrollingUp) {
116
+ setPinned(false);
117
+ } else {
118
+ checkDistance(view);
119
+ }
120
+ },
121
+ }),
122
+
123
+ // Scroll button.
124
+ ViewPlugin.fromClass(
125
+ class {
126
+ constructor(view: EditorView) {
127
+ buttonContainer = Domino.of('div')
128
+ .classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
129
+ .children(
130
+ Domino.of('button')
131
+ .classNames('dx-button bg-accentSurface')
132
+ .data('density', 'fine')
133
+ .children(Domino.of<any>('dx-icon').attributes({ icon: 'ph--arrow-down--regular' }))
134
+ .on('click', () => {
135
+ scrollToBottom(view);
136
+ }),
137
+ )
138
+ .build();
139
+
140
+ view.scrollDOM.parentElement!.appendChild(buttonContainer);
141
+ }
142
+ },
143
+ ),
144
+
145
+ // Styles.
146
+ EditorView.theme({
147
+ '.cm-scroller': {
148
+ scrollbarWidth: 'thin',
149
+ },
150
+ '.cm-scroller.cm-hide-scrollbar': {
151
+ scrollbarWidth: 'none',
152
+ },
153
+ '.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
154
+ display: 'none',
155
+ },
156
+ '.cm-scroll-button': {
157
+ position: 'absolute',
158
+ bottom: '0.5rem',
159
+ right: '1rem',
160
+ },
161
+ }),
162
+ ];
163
+ };
@@ -3,11 +3,11 @@
3
3
  //
4
4
 
5
5
  import { DeferredTask, Event, sleep } from '@dxos/async';
6
+ import { type Space } from '@dxos/client/echo';
7
+ import { type GossipMessage } from '@dxos/client/mesh';
6
8
  import { Context } from '@dxos/context';
7
9
  import { invariant } from '@dxos/invariant';
8
10
  import { log } from '@dxos/log';
9
- import { type Space } from '@dxos/react-client/echo';
10
- import { type GossipMessage } from '@dxos/react-client/mesh';
11
11
 
12
12
  import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from './awareness';
13
13
 
@@ -0,0 +1,131 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { RangeSetBuilder } from '@codemirror/state';
6
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
7
+
8
+ import { mx } from '@dxos/react-ui-theme';
9
+
10
+ const paragraphBlockPlugin = ViewPlugin.fromClass(
11
+ class {
12
+ decorations: DecorationSet;
13
+
14
+ constructor(view: EditorView) {
15
+ this.decorations = this.build(view);
16
+ }
17
+
18
+ update(update: ViewUpdate) {
19
+ if (update.docChanged || update.viewportChanged) {
20
+ this.decorations = this.build(update.view);
21
+ }
22
+ }
23
+
24
+ build({ state }: EditorView) {
25
+ const builder = new RangeSetBuilder<Decoration>();
26
+
27
+ // Helper: commit a block from blockStart to endLine (inclusive).
28
+ const pushBlock = (fromLine: number, toLine: number) => {
29
+ // Add line decorations for each line in the block.
30
+ for (let lineNum = fromLine; lineNum <= toLine; lineNum++) {
31
+ const line = state.doc.line(lineNum);
32
+ builder.add(
33
+ line.from,
34
+ line.from,
35
+ Decoration.line({
36
+ class: mx(
37
+ 'block-line',
38
+ fromLine === toLine && 'block-single',
39
+ lineNum === fromLine && 'block-first',
40
+ lineNum > fromLine && lineNum < toLine && 'block-middle',
41
+ lineNum === toLine && 'block-last',
42
+ ),
43
+ }),
44
+ );
45
+ }
46
+ };
47
+
48
+ let blockStart: number | null = null;
49
+ let consecutiveBlankLines = 0;
50
+ const totalLines = state.doc.lines;
51
+ for (let i = 1; i <= totalLines; i++) {
52
+ const line = state.doc.line(i);
53
+ const isBlank = /^\s*$/.test(line.text);
54
+
55
+ if (!isBlank) {
56
+ // Reset blank line counter.
57
+ consecutiveBlankLines = 0;
58
+ // Start a new block if we're not already in one.
59
+ if (blockStart === null) {
60
+ blockStart = i;
61
+ }
62
+ } else {
63
+ // Increment blank line counter.
64
+ consecutiveBlankLines++;
65
+
66
+ // End the current block if we have 2+ consecutive blank lines.
67
+ if (consecutiveBlankLines >= 2 && blockStart !== null) {
68
+ pushBlock(blockStart, i - consecutiveBlankLines);
69
+ blockStart = null;
70
+ }
71
+ }
72
+ }
73
+
74
+ // Handle any remaining block at the end of the document.
75
+ if (blockStart !== null) {
76
+ // Find the last non-blank line for the block end.
77
+ let lastNonBlankLine = totalLines;
78
+ while (lastNonBlankLine >= blockStart) {
79
+ const line = state.doc.line(lastNonBlankLine);
80
+ if (!/^\s*$/.test(line.text)) {
81
+ break;
82
+ }
83
+ lastNonBlankLine--;
84
+ }
85
+ if (lastNonBlankLine >= blockStart) {
86
+ pushBlock(blockStart, lastNonBlankLine);
87
+ }
88
+ }
89
+
90
+ return builder.finish();
91
+ }
92
+ },
93
+ {
94
+ decorations: (v) => v.decorations,
95
+ },
96
+ );
97
+
98
+ export const blocks = () => [
99
+ paragraphBlockPlugin,
100
+ EditorView.baseTheme({
101
+ '.cm-line.block-line': {
102
+ paddingLeft: '0.75rem',
103
+ paddingRight: '0.75rem',
104
+ borderLeft: '1px solid var(--dx-subduedSeparator)',
105
+ borderRight: '1px solid var(--dx-subduedSeparator)',
106
+ },
107
+ '.cm-line.block-single': {
108
+ border: '1px solid var(--dx-subduedSeparator)',
109
+ borderRadius: '6px',
110
+ paddingTop: '0.5rem',
111
+ paddingBottom: '0.5rem',
112
+ marginTop: '0.5rem',
113
+ marginBottom: '0.5rem',
114
+ },
115
+ '.cm-line.block-first': {
116
+ borderTop: '1px solid var(--dx-subduedSeparator)',
117
+ borderTopLeftRadius: '6px',
118
+ borderTopRightRadius: '6px',
119
+ paddingTop: '0.5rem',
120
+ marginTop: '0.5rem',
121
+ },
122
+ '.cm-line.block-middle': {},
123
+ '.cm-line.block-last': {
124
+ borderBottom: '1px solid var(--dx-subduedSeparator)',
125
+ borderBottomLeftRadius: '6px',
126
+ borderBottomRightRadius: '6px',
127
+ paddingBottom: '0.5rem',
128
+ marginBottom: '0.5rem',
129
+ },
130
+ }),
131
+ ];