@dxos/react-ui-editor 0.8.4-main.dedc0f3 → 0.8.4-main.e8ec1fe

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 (317) 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 +5638 -5200
  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 +5638 -5200
  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 +14 -4
  82. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -1
  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 +10 -5
  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 +8 -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.map +1 -1
  101. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  102. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -3
  103. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  104. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  105. package/dist/types/src/extensions/modal.d.ts +7 -0
  106. package/dist/types/src/extensions/modal.d.ts.map +1 -0
  107. package/dist/types/src/extensions/modes.d.ts +1 -1
  108. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  109. package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
  110. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
  111. package/dist/types/src/extensions/preview/preview.d.ts +6 -4
  112. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  113. package/dist/types/src/extensions/replacer.d.ts +21 -0
  114. package/dist/types/src/extensions/replacer.d.ts.map +1 -0
  115. package/dist/types/src/extensions/replacer.test.d.ts +2 -0
  116. package/dist/types/src/extensions/replacer.test.d.ts.map +1 -0
  117. package/dist/types/src/extensions/scrolling.d.ts +78 -0
  118. package/dist/types/src/extensions/scrolling.d.ts.map +1 -0
  119. package/dist/types/src/extensions/state.d.ts +2 -0
  120. package/dist/types/src/extensions/state.d.ts.map +1 -0
  121. package/dist/types/src/extensions/submit.d.ts +10 -0
  122. package/dist/types/src/extensions/submit.d.ts.map +1 -0
  123. package/dist/types/src/extensions/tab.d.ts +4 -0
  124. package/dist/types/src/extensions/tab.d.ts.map +1 -0
  125. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -1
  126. package/dist/types/src/extensions/tags/xml-tags.d.ts +42 -16
  127. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  128. package/dist/types/src/hooks/useTextEditor.d.ts +5 -9
  129. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  130. package/dist/types/src/stories/{Command.stories.d.ts → CommandDialog.stories.d.ts} +2 -3
  131. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
  132. package/dist/types/src/stories/Comments.stories.d.ts +3 -4
  133. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  134. package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -2
  135. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  136. package/dist/types/src/stories/Experimental.stories.d.ts +3 -4
  137. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  138. package/dist/types/src/stories/Markdown.stories.d.ts +3 -4
  139. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  140. package/dist/types/src/stories/Outliner.stories.d.ts +0 -1
  141. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  142. package/dist/types/src/stories/{CommandMenu.stories.d.ts → Popover.stories.d.ts} +6 -6
  143. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
  144. package/dist/types/src/stories/Preview.stories.d.ts +4 -4
  145. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  146. package/dist/types/src/stories/Tags.stories.d.ts +0 -1
  147. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  148. package/dist/types/src/stories/TextEditor.stories.d.ts +3 -5
  149. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  150. package/dist/types/src/stories/components/EditorStory.d.ts +7 -5
  151. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  152. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  153. package/dist/types/src/styles/theme.d.ts.map +1 -1
  154. package/dist/types/src/types/types.d.ts +1 -1
  155. package/dist/types/src/types/types.d.ts.map +1 -1
  156. package/dist/types/src/util/debug.d.ts +4 -0
  157. package/dist/types/src/util/debug.d.ts.map +1 -1
  158. package/dist/types/src/util/index.d.ts +0 -1
  159. package/dist/types/src/util/index.d.ts.map +1 -1
  160. package/dist/types/tsconfig.tsbuildinfo +1 -1
  161. package/package.json +58 -59
  162. package/src/components/Editor/Editor.stories.tsx +89 -0
  163. package/src/components/Editor/Editor.tsx +160 -25
  164. package/src/components/EditorContent/EditorContent.stories.tsx +70 -0
  165. package/src/components/EditorContent/EditorContent.tsx +70 -0
  166. package/src/components/EditorContent/controller.ts +50 -0
  167. package/src/components/EditorContent/index.ts +6 -0
  168. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +233 -0
  169. package/src/components/EditorMenuProvider/index.ts +11 -0
  170. package/src/components/EditorMenuProvider/menu-presets.ts +123 -0
  171. package/src/components/EditorMenuProvider/menu.ts +71 -0
  172. package/src/components/EditorMenuProvider/popover.ts +287 -0
  173. package/src/components/EditorMenuProvider/useEditorMenu.ts +175 -0
  174. package/src/components/EditorMenuProvider/util.ts +31 -0
  175. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +82 -0
  176. package/src/components/EditorPreviewProvider/index.ts +5 -0
  177. package/src/components/EditorToolbar/EditorToolbar.tsx +101 -91
  178. package/src/components/EditorToolbar/{lists.ts → actions.ts} +46 -16
  179. package/src/components/EditorToolbar/blocks.ts +2 -1
  180. package/src/components/EditorToolbar/formatting.ts +2 -1
  181. package/src/components/EditorToolbar/headings.ts +8 -5
  182. package/src/components/EditorToolbar/image.ts +1 -1
  183. package/src/components/EditorToolbar/index.ts +3 -7
  184. package/src/components/EditorToolbar/search.ts +1 -1
  185. package/src/components/EditorToolbar/useEditorToolbar.ts +20 -0
  186. package/src/components/EditorToolbar/view-mode.ts +2 -1
  187. package/src/components/index.ts +8 -2
  188. package/src/extensions/{autocomplete.ts → autocomplete/autocomplete.ts} +8 -76
  189. package/src/extensions/autocomplete/index.ts +8 -0
  190. package/src/extensions/autocomplete/match.ts +46 -0
  191. package/src/extensions/{command → autocomplete}/placeholder.ts +21 -17
  192. package/src/extensions/{command → autocomplete}/typeahead.ts +6 -48
  193. package/src/extensions/automerge/automerge.stories.tsx +9 -9
  194. package/src/extensions/automerge/automerge.ts +28 -9
  195. package/src/extensions/automerge/cursor.ts +1 -1
  196. package/src/extensions/automerge/sync.ts +8 -4
  197. package/src/extensions/automerge/update-automerge.ts +1 -1
  198. package/src/extensions/autoscroll.ts +98 -86
  199. package/src/extensions/awareness/awareness-provider.ts +2 -2
  200. package/src/extensions/blocks.ts +131 -0
  201. package/src/extensions/bookmarks.ts +75 -0
  202. package/src/extensions/comments.ts +2 -1
  203. package/src/extensions/factories.ts +47 -16
  204. package/src/extensions/focus.ts +5 -4
  205. package/src/extensions/folding.tsx +3 -6
  206. package/src/extensions/hashtag.tsx +2 -2
  207. package/src/extensions/index.ts +8 -1
  208. package/src/extensions/json.ts +1 -1
  209. package/src/extensions/listener.ts +14 -20
  210. package/src/extensions/markdown/bundle.ts +16 -4
  211. package/src/extensions/markdown/decorate.ts +9 -8
  212. package/src/extensions/markdown/formatting.ts +8 -10
  213. package/src/extensions/markdown/highlight.ts +1 -1
  214. package/src/extensions/markdown/image.ts +2 -2
  215. package/src/extensions/markdown/link.ts +3 -0
  216. package/src/extensions/markdown/table.ts +6 -6
  217. package/src/extensions/modal.ts +24 -0
  218. package/src/extensions/modes.ts +2 -2
  219. package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +15 -20
  220. package/src/extensions/outliner/outliner.test.ts +1 -1
  221. package/src/extensions/outliner/outliner.ts +4 -4
  222. package/src/extensions/outliner/tree.test.ts +1 -1
  223. package/src/extensions/preview/index.ts +1 -1
  224. package/src/extensions/preview/preview.ts +14 -14
  225. package/src/extensions/replacer.test.ts +75 -0
  226. package/src/extensions/replacer.ts +93 -0
  227. package/src/extensions/scrolling.ts +189 -0
  228. package/src/extensions/selection.ts +3 -3
  229. package/src/extensions/state.ts +7 -0
  230. package/src/extensions/submit.ts +62 -0
  231. package/src/extensions/tab.ts +29 -0
  232. package/src/extensions/tags/extended-markdown.test.ts +2 -1
  233. package/src/extensions/tags/streamer.ts +4 -5
  234. package/src/extensions/tags/xml-tags.ts +320 -155
  235. package/src/extensions/typewriter.ts +1 -1
  236. package/src/hooks/useTextEditor.ts +31 -43
  237. package/src/stories/{Command.stories.tsx → CommandDialog.stories.tsx} +20 -27
  238. package/src/stories/Comments.stories.tsx +8 -9
  239. package/src/stories/EditorToolbar.stories.tsx +14 -13
  240. package/src/stories/Experimental.stories.tsx +7 -7
  241. package/src/stories/Markdown.stories.tsx +6 -6
  242. package/src/stories/Outliner.stories.tsx +40 -31
  243. package/src/stories/Popover.stories.tsx +162 -0
  244. package/src/stories/Preview.stories.tsx +46 -42
  245. package/src/stories/Tags.stories.tsx +24 -10
  246. package/src/stories/TextEditor.stories.tsx +10 -35
  247. package/src/stories/components/EditorStory.tsx +29 -16
  248. package/src/stories/components/util.tsx +40 -8
  249. package/src/styles/markdown.ts +1 -1
  250. package/src/styles/theme.ts +13 -11
  251. package/src/types/types.ts +1 -1
  252. package/src/util/debug.ts +5 -0
  253. package/src/util/index.ts +0 -1
  254. package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
  255. package/dist/lib/browser/testing/index.mjs +0 -6
  256. package/dist/lib/browser/testing/index.mjs.map +0 -7
  257. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
  258. package/dist/lib/node-esm/testing/index.mjs +0 -8
  259. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  260. package/dist/types/src/components/EditorToolbar/lists.d.ts +0 -19
  261. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +0 -1
  262. package/dist/types/src/components/EditorToolbar/util.d.ts +0 -51
  263. package/dist/types/src/components/EditorToolbar/util.d.ts.map +0 -1
  264. package/dist/types/src/components/Popover/CommandMenu.d.ts +0 -34
  265. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
  266. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -14
  267. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
  268. package/dist/types/src/components/Popover/RefPopover.d.ts +0 -37
  269. package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
  270. package/dist/types/src/components/Popover/index.d.ts +0 -4
  271. package/dist/types/src/components/Popover/index.d.ts.map +0 -1
  272. package/dist/types/src/extensions/autocomplete.d.ts +0 -26
  273. package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
  274. package/dist/types/src/extensions/command/action.d.ts +0 -17
  275. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  276. package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
  277. package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
  278. package/dist/types/src/extensions/command/command.d.ts +0 -6
  279. package/dist/types/src/extensions/command/command.d.ts.map +0 -1
  280. package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
  281. package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
  282. package/dist/types/src/extensions/command/hint.d.ts +0 -19
  283. package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
  284. package/dist/types/src/extensions/command/index.d.ts +0 -7
  285. package/dist/types/src/extensions/command/index.d.ts.map +0 -1
  286. package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
  287. package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
  288. package/dist/types/src/extensions/command/state.d.ts +0 -16
  289. package/dist/types/src/extensions/command/state.d.ts.map +0 -1
  290. package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
  291. package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
  292. package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
  293. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
  294. package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
  295. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
  296. package/dist/types/src/testing/index.d.ts +0 -2
  297. package/dist/types/src/testing/index.d.ts.map +0 -1
  298. package/dist/types/src/testing/util.d.ts +0 -3
  299. package/dist/types/src/testing/util.d.ts.map +0 -1
  300. package/dist/types/src/util/domino.d.ts +0 -18
  301. package/dist/types/src/util/domino.d.ts.map +0 -1
  302. package/src/components/EditorToolbar/util.ts +0 -76
  303. package/src/components/Popover/CommandMenu.tsx +0 -279
  304. package/src/components/Popover/RefDropdownMenu.tsx +0 -89
  305. package/src/components/Popover/RefPopover.tsx +0 -117
  306. package/src/components/Popover/index.ts +0 -7
  307. package/src/extensions/command/action.ts +0 -56
  308. package/src/extensions/command/command-menu.ts +0 -211
  309. package/src/extensions/command/command.ts +0 -34
  310. package/src/extensions/command/hint.ts +0 -103
  311. package/src/extensions/command/index.ts +0 -10
  312. package/src/extensions/command/state.ts +0 -90
  313. package/src/extensions/command/useCommandMenu.ts +0 -119
  314. package/src/stories/CommandMenu.stories.tsx +0 -160
  315. package/src/testing/index.ts +0 -5
  316. package/src/testing/util.ts +0 -7
  317. package/src/util/domino.ts +0 -51
@@ -3,17 +3,34 @@
3
3
  //
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
+ import { Prec } from '@codemirror/state';
6
7
  import { type EditorState, type Extension, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
7
- import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
8
+ import { keymap } from '@codemirror/view';
9
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
8
10
  import { type ComponentType, type FC } from 'react';
9
11
 
10
12
  import { invariant } from '@dxos/invariant';
11
13
  import { log } from '@dxos/log';
12
14
 
15
+ import { type Range } from '../../types';
13
16
  import { decorationSetToArray } from '../../util';
17
+ import { scrollToLineEffect } from '../scrolling';
14
18
 
15
19
  import { nodeToJson } from './xml-util';
16
20
 
21
+ /**
22
+ * StateEffect for navigating to previous bookmark.
23
+ */
24
+ export const navigatePreviousEffect = StateEffect.define<void>();
25
+
26
+ /**
27
+ * StateEffect for navigating to next bookmark.
28
+ */
29
+ export const navigateNextEffect = StateEffect.define<void>();
30
+
31
+ /**
32
+ * Dispatch function for updating state.
33
+ */
17
34
  export type StateDispatch<T> = T | ((state: T) => T);
18
35
 
19
36
  /**
@@ -28,9 +45,11 @@ export type XmlEventHandler<TEvent = any> = (event: TEvent) => void;
28
45
  /**
29
46
  * Widget component.
30
47
  */
31
- export type XmlWidgetProps<TContext = any, TProps = any> = TProps & {
48
+ export type XmlWidgetProps<TProps = any, TContext = any> = TProps & {
32
49
  _tag: string;
33
- context: TContext;
50
+ context?: TContext;
51
+ range?: { from: number; to: number };
52
+ view?: EditorView;
34
53
  onEvent?: XmlEventHandler;
35
54
  };
36
55
 
@@ -43,29 +62,37 @@ export type XmlWidgetFactory = (props: XmlWidgetProps, onEvent?: XmlEventHandler
43
62
  * Widget registry definition.
44
63
  */
45
64
  export type XmlWidgetDef = {
65
+ /** Block widget. */
46
66
  block?: boolean;
47
- /** Native widget. */
67
+
68
+ /** Native widget (rendered inline). */
48
69
  factory?: XmlWidgetFactory;
49
- /** React widget. */
70
+
71
+ /** React widget (rendered in portals outside of the editor). */
50
72
  Component?: FC<XmlWidgetProps>;
51
73
  };
52
74
 
53
75
  export type XmlWidgetRegistry = Record<string, XmlWidgetDef>;
54
76
 
77
+ export const getXmlTextChild = (children: any[]): string | null => {
78
+ const child = children?.[0];
79
+ return typeof child === 'string' ? child : null;
80
+ };
81
+
55
82
  /**
56
83
  * Update context.
57
84
  */
58
85
  export const xmlTagContextEffect = StateEffect.define<any>();
59
86
 
60
87
  /**
61
- * Update widget.
88
+ * Reset all state.
62
89
  */
63
- export const xmlTagUpdateEffect = StateEffect.define<{ id: string; value: any }>();
90
+ export const xmlTagResetEffect = StateEffect.define();
64
91
 
65
92
  /**
66
- * Reset all state.
93
+ * Update widget.
67
94
  */
68
- export const xmlTagResetEffect = StateEffect.define();
95
+ export const xmlTagUpdateEffect = StateEffect.define<{ id: string; value: any }>();
69
96
 
70
97
  type WidgetDecorationSet = {
71
98
  from: number;
@@ -76,9 +103,9 @@ type XmlWidgetStateMap = Record<string, any>;
76
103
 
77
104
  export type XmlWidgetState = {
78
105
  id: string;
79
- props: any;
80
106
  root: HTMLElement;
81
- Component: ComponentType<any>;
107
+ props: any;
108
+ Component: ComponentType<XmlWidgetProps>;
82
109
  };
83
110
 
84
111
  export interface XmlWidgetNotifier {
@@ -86,65 +113,243 @@ export interface XmlWidgetNotifier {
86
113
  unmounted(id: string): void;
87
114
  }
88
115
 
116
+ /**
117
+ * Context state.
118
+ */
119
+ const widgetContextStateField = StateField.define<any>({
120
+ create: () => undefined,
121
+ update: (value, tr) => {
122
+ for (const effect of tr.effects) {
123
+ if (effect.is(xmlTagContextEffect)) {
124
+ return effect.value;
125
+ }
126
+ }
127
+
128
+ return value;
129
+ },
130
+ });
131
+
132
+ /**
133
+ * Widget state management.
134
+ */
135
+ const widgetStateMapStateField = StateField.define<XmlWidgetStateMap>({
136
+ create: () => ({}),
137
+ update: (map, tr) => {
138
+ for (const effect of tr.effects) {
139
+ if (effect.is(xmlTagResetEffect)) {
140
+ return {};
141
+ }
142
+
143
+ if (effect.is(xmlTagUpdateEffect)) {
144
+ // Update accumulated widget props by id.
145
+ const { id, value } = effect.value;
146
+ log('widget updated', { id, value });
147
+ const state = typeof value === 'function' ? value(map[id]) : value;
148
+ return { ...map, [id]: state };
149
+ }
150
+ }
151
+
152
+ return map;
153
+ },
154
+ });
155
+
89
156
  export type XmlTagsOptions = {
157
+ /** Tag registry. */
90
158
  registry?: XmlWidgetRegistry;
91
- /**
92
- * Called when a widget is mounted or unmounted.
93
- */
159
+
160
+ /** Called when widgets are mounted or unmounted. */
94
161
  setWidgets?: (widgets: XmlWidgetState[]) => void;
162
+
163
+ /** Tags to bookmark. */
164
+ bookmarks?: string[];
95
165
  };
96
166
 
97
167
  /**
98
- * Extension that adds thread-related functionality including XML tag decorations.
168
+ * Implements custom XML tags via CodeMirror-native Widgets and portaled React components.
169
+ *
170
+ * Basic mechanism:
171
+ * - Decorations are created from XML tags that matched the provided Widget registry.
172
+ * - Native widgets are rendered inline.
173
+ * - React widgets are rendered in portals outside of the editor via the PlaceholderWidget.
174
+ * - Widget state can be update via effects.
175
+ * - NOTE: Widget state may be updated BEFORE the widget is mounted.
99
176
  */
100
- export const xmlTags = (options: XmlTagsOptions = {}): Extension => {
101
- //
102
- // Context state.
103
- //
104
- const contextState = StateField.define<any>({
105
- create: () => undefined,
106
- update: (value, tr) => {
107
- for (const effect of tr.effects) {
108
- if (effect.is(xmlTagContextEffect)) {
109
- return effect.value;
110
- }
111
- }
112
-
113
- return value;
114
- },
115
- });
177
+ export const xmlTags = ({ registry, setWidgets, bookmarks }: XmlTagsOptions): Extension => {
178
+ const notifier = createWidgetMap(setWidgets);
179
+ const widgetDecorationsField = createWidgetDecorationsField(registry, notifier);
180
+ return [
181
+ widgetContextStateField,
182
+ widgetStateMapStateField,
183
+ widgetDecorationsField,
184
+ createWidgetUpdatePlugin(widgetDecorationsField, notifier),
185
+ createNavigationEffectPlugin(widgetDecorationsField, bookmarks),
186
+ bookmarks?.length ? Prec.highest(keyHandlers) : [],
187
+ ];
188
+ };
116
189
 
117
- //
118
- // Active widgets.
119
- //
190
+ /**
191
+ * Manages the collection of widgets.
192
+ */
193
+ const createWidgetMap = (setWidgets?: (widgets: XmlWidgetState[]) => void): XmlWidgetNotifier => {
120
194
  const widgets = new Map<string, XmlWidgetState>();
195
+
196
+ // TODO(burdon): Batch updates?
121
197
  const notifier = {
122
- mounted: (widget: XmlWidgetState) => {
123
- widgets.set(widget.id, widget);
124
- options.setWidgets?.([...widgets.values()]);
198
+ mounted: (state: XmlWidgetState) => {
199
+ log('widget mounted', { id: state.id, tag: state.props._tag });
200
+ widgets.set(state.id, state);
201
+ setWidgets?.([...widgets.values()]);
125
202
  },
126
203
  unmounted: (id: string) => {
204
+ const state = widgets.get(id);
205
+ log('widget unmounted', { id, tag: state?.props._tag });
127
206
  widgets.delete(id);
128
- options.setWidgets?.([...widgets.values()]);
207
+ setWidgets?.([...widgets.values()]);
129
208
  },
130
209
  } satisfies XmlWidgetNotifier;
131
210
 
132
- //
133
- // Widget decorations.
134
- //
135
- const decorationsState = StateField.define<WidgetDecorationSet>({
211
+ return notifier;
212
+ };
213
+
214
+ /**
215
+ * Navigation keys.
216
+ */
217
+ const keyHandlers = keymap.of([
218
+ {
219
+ key: 'Mod-ArrowUp',
220
+ run: (view) => {
221
+ view.dispatch({ effects: navigatePreviousEffect.of() });
222
+ return true;
223
+ },
224
+ },
225
+ {
226
+ key: 'Mod-ArrowDown',
227
+ run: (view) => {
228
+ view.dispatch({ effects: navigateNextEffect.of() });
229
+ return true;
230
+ },
231
+ },
232
+ ]);
233
+
234
+ /**
235
+ * Effect processing plugin for navigation.
236
+ * Handles navigation up/down effects.
237
+ */
238
+ const createNavigationEffectPlugin = (
239
+ widgetDecorationsField: StateField<WidgetDecorationSet>,
240
+ bookmarks?: string[],
241
+ ) => {
242
+ return EditorView.updateListener.of((update) => {
243
+ update.transactions.forEach((transaction) => {
244
+ for (const effect of transaction.effects) {
245
+ if (effect.is(navigatePreviousEffect)) {
246
+ const view = update.view;
247
+ const cursorPos = view.state.doc.lineAt(view.state.selection.main.head).from;
248
+ let widget: { from: number; to: number; tag: string } | null = null;
249
+ const { decorations } = view.state.field(widgetDecorationsField);
250
+ for (const range of decorationSetToArray(decorations)) {
251
+ if (range.from < cursorPos) {
252
+ const tag = range.value.spec.tag;
253
+ if (bookmarks?.includes(tag)) {
254
+ if (!widget || range.from > widget.from) {
255
+ widget = { from: range.from, to: range.to, tag };
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ const line = view.state.doc.lineAt(widget?.from ?? 0);
262
+ view.dispatch({
263
+ selection: { anchor: line.from, head: line.from },
264
+ effects: scrollToLineEffect.of({ line: line.number, options: { offset: -16 } }),
265
+ });
266
+
267
+ continue;
268
+ }
269
+
270
+ if (effect.is(navigateNextEffect)) {
271
+ const view = update.view;
272
+ const cursorPos = view.state.doc.lineAt(view.state.selection.main.head).to;
273
+ let widget: { from: number; to: number; tag: string } | null = null;
274
+ const { decorations } = view.state.field(widgetDecorationsField);
275
+ for (const range of decorationSetToArray(decorations)) {
276
+ if (range.from > cursorPos) {
277
+ const tag = range.value.spec.tag;
278
+ if (bookmarks?.includes(tag)) {
279
+ if (!widget || range.from < widget.from) {
280
+ widget = { from: range.from, to: range.to, tag };
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ if (widget) {
287
+ const line = view.state.doc.lineAt(widget?.from);
288
+ view.dispatch({
289
+ selection: { anchor: line.to, head: line.to },
290
+ effects: scrollToLineEffect.of({ line: line.number, options: { offset: -16 } }),
291
+ });
292
+ } else {
293
+ const line = view.state.doc.lineAt(view.state.doc.length);
294
+ view.dispatch({
295
+ selection: { anchor: line.to, head: line.to },
296
+ effects: scrollToLineEffect.of({ line: line.number, options: { position: 'end' } }),
297
+ });
298
+ }
299
+
300
+ continue;
301
+ }
302
+ }
303
+ });
304
+ });
305
+ };
306
+
307
+ /**
308
+ * Handles effect that updates widget state.
309
+ */
310
+ const createWidgetUpdatePlugin = (
311
+ widgetDecorationsField: StateField<WidgetDecorationSet>,
312
+ notifier: XmlWidgetNotifier,
313
+ ) =>
314
+ ViewPlugin.fromClass(
315
+ class {
316
+ update(update: ViewUpdate) {
317
+ const widgetStateMap = update.state.field(widgetStateMapStateField);
318
+ const { decorations } = update.state.field(widgetDecorationsField);
319
+
320
+ // Check for widget update effects and re-render widgets.
321
+ for (const effect of update.transactions.flatMap((tr) => tr.effects)) {
322
+ if (effect.is(xmlTagUpdateEffect)) {
323
+ const widgetState = widgetStateMap[effect.value.id];
324
+
325
+ // Find and render widget.
326
+ for (const range of decorationSetToArray(decorations)) {
327
+ const deco = range.value;
328
+ const widget = deco?.spec?.widget;
329
+
330
+ // NOTE: If the widget has not yet been mounted, then the root will be null.
331
+ if (widget && widget instanceof PlaceholderWidget && widget.id === effect.value.id && widget.root) {
332
+ const props = { ...widget.props, ...widgetState };
333
+ notifier.mounted({ id: widget.id, props, root: widget.root, Component: widget.Component });
334
+ }
335
+ }
336
+ }
337
+ }
338
+ }
339
+ },
340
+ );
341
+
342
+ /**
343
+ * Builds and maintains decorations for XML widgets.
344
+ * Must be a StateField because block decorations cannot be provided via ViewPlugin.
345
+ */
346
+ const createWidgetDecorationsField = (registry: XmlWidgetRegistry = {}, notifier: XmlWidgetNotifier) =>
347
+ StateField.define<WidgetDecorationSet>({
136
348
  create: (state) => {
137
- return buildDecorations(
138
- state,
139
- 0,
140
- state.doc.length,
141
- state.field(contextState),
142
- state.field(widgetState),
143
- options,
144
- notifier,
145
- );
349
+ return buildDecorations(state, { from: 0, to: state.doc.length }, registry, notifier);
146
350
  },
147
351
  update: ({ from, decorations }, tr) => {
352
+ // Check for reset effect.
148
353
  for (const effect of tr.effects) {
149
354
  if (effect.is(xmlTagResetEffect)) {
150
355
  return { from: 0, decorations: Decoration.none };
@@ -153,32 +358,22 @@ export const xmlTags = (options: XmlTagsOptions = {}): Extension => {
153
358
 
154
359
  if (tr.docChanged) {
155
360
  const { state } = tr;
156
-
157
361
  // Flag if the transaction has modified the head of the document.
158
- // (i.e., any changes that touch before the current `from` position).
159
362
  const reset = tr.changes.touchesRange(0, from);
160
-
161
- // Since append-only, rebuild decorations from after the last widget.
162
- const result = buildDecorations(
163
- state,
164
- reset ? 0 : from,
165
- state.doc.length,
166
- state.field(contextState),
167
- state.field(widgetState),
168
- options,
169
- notifier,
170
- );
171
-
172
- // Merge with existing decorations.
173
- return {
174
- from: result.from,
175
- decorations: decorations.update({ add: decorationSetToArray(result.decorations) }),
176
- };
363
+ if (reset) {
364
+ log('document reset', { from, to: state.doc.length });
365
+ // Full rebuild from start.
366
+ return buildDecorations(state, { from: 0, to: state.doc.length }, registry, notifier);
367
+ } else {
368
+ // Append-only: rebuild decorations from after the last widget and merge with existing decorations.
369
+ const result = buildDecorations(state, { from, to: state.doc.length }, registry, notifier);
370
+ return {
371
+ from: result.from,
372
+ decorations: decorations.update({ add: decorationSetToArray(result.decorations) }),
373
+ };
374
+ }
177
375
  }
178
376
 
179
- // No document changes: avoid mapping decorations through an empty ChangeSet,
180
- // which can throw when the decoration set was created for a different base length.
181
- // Simply return the existing decorations unchanged.
182
377
  return { from, decorations };
183
378
  },
184
379
  provide: (field) => [
@@ -187,97 +382,66 @@ export const xmlTags = (options: XmlTagsOptions = {}): Extension => {
187
382
  ],
188
383
  });
189
384
 
190
- //
191
- // Widget state management.
192
- //
193
- const widgetState = StateField.define<XmlWidgetStateMap>({
194
- create: () => ({}),
195
- update: (map, tr) => {
196
- for (const effect of tr.effects) {
197
- if (effect.is(xmlTagResetEffect)) {
198
- return {};
199
- }
200
-
201
- if (effect.is(xmlTagUpdateEffect)) {
202
- // Update accumulated widget props by id.
203
- const { id, value } = effect.value;
204
- const state = typeof value === 'function' ? value(map[id]) : value;
205
-
206
- // Find and render widget.
207
- const { decorations } = tr.state.field(decorationsState);
208
- for (const range of decorationSetToArray(decorations)) {
209
- const deco = range.value;
210
- const widget = deco?.spec?.widget;
211
- if (widget && widget instanceof PlaceholderWidget && widget.id === effect.value.id && widget.root) {
212
- const props = { ...widget.props, ...state };
213
- notifier.mounted({ id: widget.id, props, root: widget.root, Component: widget.Component });
214
- }
215
- }
216
-
217
- return { ...map, [id]: state };
218
- }
219
- }
220
-
221
- return map;
222
- },
223
- });
224
-
225
- return [contextState, decorationsState, widgetState];
226
- };
227
-
228
385
  /**
229
386
  * Creates widget decorations for XML tags in the document using the syntax tree.
230
387
  */
231
388
  const buildDecorations = (
232
389
  state: EditorState,
233
- from: number,
234
- to: number,
235
- context: any,
236
- widgetState: XmlWidgetStateMap,
237
- options: XmlTagsOptions,
390
+ range: Range,
391
+ registry: XmlWidgetRegistry,
238
392
  notifier: XmlWidgetNotifier,
239
393
  ): WidgetDecorationSet => {
394
+ const context = state.field(widgetContextStateField, false);
395
+ const widgetStateMap = state.field(widgetStateMapStateField, false) ?? {};
396
+
240
397
  const builder = new RangeSetBuilder<Decoration>();
241
398
  const tree = syntaxTree(state);
242
399
  if (!tree || (tree.type.name === 'Program' && tree.length === 0)) {
243
- return { from, decorations: Decoration.none };
400
+ return { from: range.from, decorations: Decoration.none };
244
401
  }
245
402
 
403
+ let last = range.from;
246
404
  tree.iterate({
247
- from,
248
- to,
405
+ from: range.from,
406
+ to: range.to,
249
407
  enter: (node) => {
250
408
  switch (node.type.name) {
251
409
  // XML Element.
252
410
  case 'Element': {
253
411
  try {
254
- if (options.registry) {
255
- const props = nodeToJson(state, node.node);
256
- if (props) {
257
- const def = options.registry[props._tag];
258
- if (def) {
259
- const { block, factory, Component } = def;
260
- const state = props.id ? widgetState[props.id] : undefined;
261
- const args = { context, ...props, ...state };
262
- const widget: WidgetType | undefined = factory
263
- ? factory(args)
264
- : Component
265
- ? props.id && new PlaceholderWidget(props.id, Component, args, notifier)
266
- : undefined;
267
-
268
- if (widget) {
269
- from = node.node.to;
270
- builder.add(
271
- node.node.from,
272
- node.node.to,
273
- Decoration.replace({
274
- widget,
275
- block,
276
- atomic: true,
277
- inclusive: true,
278
- }),
279
- );
280
- }
412
+ const args = nodeToJson(state, node.node);
413
+ if (args) {
414
+ const def = registry[args._tag];
415
+ if (def) {
416
+ // NOTE: The widget state may already have been updated before the widget is mounted.
417
+ const { block, factory, Component } = def;
418
+ const widgetState = args.id ? widgetStateMap[args.id] : undefined;
419
+ const nodeRange = { from: node.node.from, to: node.node.to };
420
+ const props = { context, range: nodeRange, ...args, ...widgetState } satisfies XmlWidgetProps;
421
+
422
+ // Create widget.
423
+ const widget: WidgetType | undefined = factory
424
+ ? factory(props)
425
+ : Component
426
+ ? args.id && new PlaceholderWidget(args.id, Component, props, notifier)
427
+ : undefined;
428
+
429
+ // Add decoration.
430
+ if (widget) {
431
+ builder.add(
432
+ nodeRange.from,
433
+ nodeRange.to,
434
+ Decoration.replace({
435
+ widget,
436
+ block,
437
+ atomic: true,
438
+ inclusive: true,
439
+ tag: args._tag,
440
+ }),
441
+ );
442
+
443
+ // Track last widget (NOTE: range is inclusive).
444
+ last = nodeRange.to - 1;
281
445
  }
282
446
  }
283
447
  }
@@ -285,19 +449,20 @@ const buildDecorations = (
285
449
  log.catch(err);
286
450
  }
287
451
 
288
- return false; // Don't descend into children.
452
+ // Don't descend into children.
453
+ return false;
289
454
  }
290
455
  }
291
456
  },
292
457
  });
293
458
 
294
- return { from, decorations: builder.finish() };
459
+ return { from: last, decorations: builder.finish() };
295
460
  };
296
461
 
297
462
  /**
298
463
  * Placeholder for React widgets.
299
464
  */
300
- class PlaceholderWidget<TProps = {}> extends WidgetType {
465
+ class PlaceholderWidget<TProps extends XmlWidgetProps> extends WidgetType {
301
466
  private _root: HTMLElement | null = null;
302
467
 
303
468
  constructor(
@@ -310,25 +475,25 @@ class PlaceholderWidget<TProps = {}> extends WidgetType {
310
475
  invariant(id);
311
476
  }
312
477
 
313
- get root() {
478
+ get root(): HTMLElement | null {
314
479
  return this._root;
315
480
  }
316
481
 
317
- override eq(other: WidgetType): boolean {
318
- return other instanceof PlaceholderWidget && this.id === other.id;
482
+ override eq(other: this) {
483
+ return this.id === other.id;
319
484
  }
320
485
 
321
486
  override ignoreEvent() {
322
487
  return true;
323
488
  }
324
489
 
325
- override toDOM(_view: EditorView): HTMLElement {
490
+ override toDOM(_view: EditorView) {
326
491
  this._root = document.createElement('span');
327
- this.notifier.mounted({ id: this.id, props: this.props, root: this._root, Component: this.Component });
492
+ this.notifier.mounted({ id: this.id, root: this._root, props: this.props, Component: this.Component });
328
493
  return this._root;
329
494
  }
330
495
 
331
- override destroy(_dom: HTMLElement): void {
496
+ override destroy(_dom: HTMLElement) {
332
497
  this.notifier.unmounted(this.id);
333
498
  this._root = null;
334
499
  }
@@ -35,7 +35,7 @@ export const typewriter = ({ delay = 75, items = defaultItems }: DemoOptions = {
35
35
  {
36
36
  // Next prompt.
37
37
  // TODO(burdon): Press 1-9 to select prompt?
38
- key: "shift-meta-'",
38
+ key: "Shift-Meta-'",
39
39
  run: (view) => {
40
40
  clearTimeout(t);
41
41
  // TODO(burdon): Add space if needed.