@dxos/ui-editor 0.10.0 → 0.11.1

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 (551) hide show
  1. package/dist/lib/chunk-diff.mjs +935 -0
  2. package/dist/lib/chunk-diff.mjs.map +1 -0
  3. package/dist/lib/headless.mjs +2 -0
  4. package/dist/lib/index.mjs +9937 -0
  5. package/dist/lib/index.mjs.map +1 -0
  6. package/dist/lib/types.mjs +18 -0
  7. package/dist/lib/types.mjs.map +1 -0
  8. package/dist/types/src/extensions/{bookmarks.d.ts → behavior/bookmarks.d.ts} +3 -0
  9. package/dist/types/src/extensions/behavior/bookmarks.d.ts.map +1 -0
  10. package/dist/types/src/extensions/behavior/dnd.d.ts.map +1 -0
  11. package/dist/types/src/extensions/behavior/index.d.ts +6 -0
  12. package/dist/types/src/extensions/behavior/index.d.ts.map +1 -0
  13. package/dist/types/src/extensions/{listener.d.ts → behavior/listener.d.ts} +3 -0
  14. package/dist/types/src/extensions/behavior/listener.d.ts.map +1 -0
  15. package/dist/types/src/extensions/{spacing.d.ts → behavior/spacing.d.ts} +3 -0
  16. package/dist/types/src/extensions/behavior/spacing.d.ts.map +1 -0
  17. package/dist/types/src/extensions/behavior/submit.d.ts.map +1 -0
  18. package/dist/types/src/extensions/collab/automerge/automerge.d.ts.map +1 -0
  19. package/dist/types/src/extensions/collab/automerge/automerge.test.d.ts.map +1 -0
  20. package/dist/types/src/extensions/{automerge → collab/automerge}/cursor.d.ts +1 -1
  21. package/dist/types/src/extensions/collab/automerge/cursor.d.ts.map +1 -0
  22. package/dist/types/src/extensions/{automerge → collab/automerge}/defs.d.ts +3 -1
  23. package/dist/types/src/extensions/collab/automerge/defs.d.ts.map +1 -0
  24. package/dist/types/src/extensions/collab/automerge/index.d.ts +3 -0
  25. package/dist/types/src/extensions/collab/automerge/index.d.ts.map +1 -0
  26. package/dist/types/src/extensions/collab/automerge/sync.d.ts.map +1 -0
  27. package/dist/types/src/extensions/{automerge → collab/automerge}/update-automerge.d.ts +4 -0
  28. package/dist/types/src/extensions/collab/automerge/update-automerge.d.ts.map +1 -0
  29. package/dist/types/src/extensions/{automerge → collab/automerge}/update-codemirror.d.ts +4 -0
  30. package/dist/types/src/extensions/collab/automerge/update-codemirror.d.ts.map +1 -0
  31. package/dist/types/src/extensions/collab/awareness/awareness-provider.d.ts.map +1 -0
  32. package/dist/types/src/extensions/{awareness → collab/awareness}/awareness.d.ts +4 -1
  33. package/dist/types/src/extensions/collab/awareness/awareness.d.ts.map +1 -0
  34. package/dist/types/src/extensions/collab/awareness/index.d.ts.map +1 -0
  35. package/dist/types/src/extensions/collab/index.d.ts +3 -0
  36. package/dist/types/src/extensions/collab/index.d.ts.map +1 -0
  37. package/dist/types/src/extensions/completion/autocomplete.d.ts.map +1 -0
  38. package/dist/types/src/extensions/{autocomplete → completion}/index.d.ts +1 -0
  39. package/dist/types/src/extensions/completion/index.d.ts.map +1 -0
  40. package/dist/types/src/extensions/{autocomplete → completion}/match.d.ts +2 -2
  41. package/dist/types/src/extensions/completion/match.d.ts.map +1 -0
  42. package/dist/types/src/extensions/{mention.d.ts → completion/mention.d.ts} +3 -0
  43. package/dist/types/src/extensions/completion/mention.d.ts.map +1 -0
  44. package/dist/types/src/extensions/completion/placeholder.d.ts.map +1 -0
  45. package/dist/types/src/extensions/{autocomplete → completion}/typeahead.d.ts +2 -2
  46. package/dist/types/src/extensions/completion/typeahead.d.ts.map +1 -0
  47. package/dist/types/src/extensions/{factories.d.ts → core/factories.d.ts} +23 -2
  48. package/dist/types/src/extensions/core/factories.d.ts.map +1 -0
  49. package/dist/types/src/extensions/core/factories.test.d.ts.map +1 -0
  50. package/dist/types/src/extensions/core/index.d.ts +3 -0
  51. package/dist/types/src/extensions/core/index.d.ts.map +1 -0
  52. package/dist/types/src/extensions/{modes.d.ts → core/modes.d.ts} +3 -0
  53. package/dist/types/src/extensions/core/modes.d.ts.map +1 -0
  54. package/dist/types/src/extensions/{debug.d.ts → debug/debug.d.ts} +3 -0
  55. package/dist/types/src/extensions/debug/debug.d.ts.map +1 -0
  56. package/dist/types/src/extensions/debug/index.d.ts +2 -0
  57. package/dist/types/src/extensions/debug/index.d.ts.map +1 -0
  58. package/dist/types/src/extensions/{annotations.d.ts → decoration/annotations.d.ts} +1 -1
  59. package/dist/types/src/extensions/decoration/annotations.d.ts.map +1 -0
  60. package/dist/types/src/extensions/decoration/assistant.d.ts +27 -0
  61. package/dist/types/src/extensions/decoration/assistant.d.ts.map +1 -0
  62. package/dist/types/src/extensions/decoration/fader.d.ts.map +1 -0
  63. package/dist/types/src/extensions/decoration/hashtag.d.ts +6 -0
  64. package/dist/types/src/extensions/decoration/hashtag.d.ts.map +1 -0
  65. package/dist/types/src/extensions/decoration/index.d.ts +8 -0
  66. package/dist/types/src/extensions/decoration/index.d.ts.map +1 -0
  67. package/dist/types/src/extensions/decoration/marker.d.ts.map +1 -0
  68. package/dist/types/src/extensions/{pos.d.ts → decoration/pos.d.ts} +40 -14
  69. package/dist/types/src/extensions/decoration/pos.d.ts.map +1 -0
  70. package/dist/types/src/extensions/decoration/pos.test.d.ts.map +1 -0
  71. package/dist/types/src/extensions/{replacer.d.ts → decoration/replacer.d.ts} +8 -8
  72. package/dist/types/src/extensions/decoration/replacer.d.ts.map +1 -0
  73. package/dist/types/src/extensions/decoration/replacer.test.d.ts.map +1 -0
  74. package/dist/types/src/extensions/{blast.d.ts → demo/blast.d.ts} +3 -0
  75. package/dist/types/src/extensions/demo/blast.d.ts.map +1 -0
  76. package/dist/types/src/extensions/demo/index.d.ts +3 -0
  77. package/dist/types/src/extensions/demo/index.d.ts.map +1 -0
  78. package/dist/types/src/extensions/demo/snippets.d.ts.map +1 -0
  79. package/dist/types/src/extensions/index.d.ts +10 -33
  80. package/dist/types/src/extensions/index.d.ts.map +1 -1
  81. package/dist/types/src/extensions/language/index.d.ts +5 -0
  82. package/dist/types/src/extensions/language/index.d.ts.map +1 -0
  83. package/dist/types/src/extensions/language/json/index.d.ts +2 -0
  84. package/dist/types/src/extensions/language/json/index.d.ts.map +1 -0
  85. package/dist/types/src/extensions/language/json/json.d.ts +12 -0
  86. package/dist/types/src/extensions/language/json/json.d.ts.map +1 -0
  87. package/dist/types/src/extensions/language/markdown/action.d.ts.map +1 -0
  88. package/dist/types/src/extensions/language/markdown/bundle.d.ts.map +1 -0
  89. package/dist/types/src/extensions/{markdown → language/markdown}/changes.d.ts +1 -1
  90. package/dist/types/src/extensions/language/markdown/changes.d.ts.map +1 -0
  91. package/dist/types/src/extensions/language/markdown/changes.test.d.ts.map +1 -0
  92. package/dist/types/src/extensions/{markdown → language/markdown}/debug.d.ts +3 -0
  93. package/dist/types/src/extensions/language/markdown/debug.d.ts.map +1 -0
  94. package/dist/types/src/extensions/{markdown → language/markdown}/decorate.d.ts +5 -1
  95. package/dist/types/src/extensions/language/markdown/decorate.d.ts.map +1 -0
  96. package/dist/types/src/extensions/{markdown → language/markdown}/formatting.d.ts +11 -2
  97. package/dist/types/src/extensions/language/markdown/formatting.d.ts.map +1 -0
  98. package/dist/types/src/extensions/language/markdown/formatting.test.d.ts.map +1 -0
  99. package/dist/types/src/extensions/{markdown → language/markdown}/highlight.d.ts +16 -12
  100. package/dist/types/src/extensions/language/markdown/highlight.d.ts.map +1 -0
  101. package/dist/types/src/extensions/language/markdown/image.d.ts.map +1 -0
  102. package/dist/types/src/extensions/language/markdown/image.test.d.ts.map +1 -0
  103. package/dist/types/src/extensions/language/markdown/index.d.ts.map +1 -0
  104. package/dist/types/src/extensions/language/markdown/link.d.ts +25 -0
  105. package/dist/types/src/extensions/language/markdown/link.d.ts.map +1 -0
  106. package/dist/types/src/extensions/language/markdown/link.test.d.ts +2 -0
  107. package/dist/types/src/extensions/language/markdown/link.test.d.ts.map +1 -0
  108. package/dist/types/src/extensions/language/markdown/parser.test.d.ts.map +1 -0
  109. package/dist/types/src/extensions/{markdown → language/markdown}/styles.d.ts +3 -0
  110. package/dist/types/src/extensions/language/markdown/styles.d.ts.map +1 -0
  111. package/dist/types/src/extensions/language/markdown/table.d.ts.map +1 -0
  112. package/dist/types/src/extensions/language/mermaid/index.d.ts +2 -0
  113. package/dist/types/src/extensions/language/mermaid/index.d.ts.map +1 -0
  114. package/dist/types/src/extensions/language/mermaid/mermaid.d.ts +18 -0
  115. package/dist/types/src/extensions/language/mermaid/mermaid.d.ts.map +1 -0
  116. package/dist/types/src/extensions/language/xml/extended-markdown.d.ts.map +1 -0
  117. package/dist/types/src/extensions/language/xml/extended-markdown.test.d.ts.map +1 -0
  118. package/dist/types/src/extensions/language/xml/index.d.ts.map +1 -0
  119. package/dist/types/src/extensions/language/xml/stub.d.ts +76 -0
  120. package/dist/types/src/extensions/language/xml/stub.d.ts.map +1 -0
  121. package/dist/types/src/extensions/language/xml/widgets/anchor.d.ts.map +1 -0
  122. package/dist/types/src/extensions/language/xml/widgets/index.d.ts.map +1 -0
  123. package/dist/types/src/extensions/language/xml/xml-block-decoration.d.ts +48 -0
  124. package/dist/types/src/extensions/language/xml/xml-block-decoration.d.ts.map +1 -0
  125. package/dist/types/src/extensions/language/xml/xml-formatting.d.ts.map +1 -0
  126. package/dist/types/src/extensions/{tags → language/xml}/xml-tags.d.ts +12 -1
  127. package/dist/types/src/extensions/language/xml/xml-tags.d.ts.map +1 -0
  128. package/dist/types/src/extensions/language/xml/xml-tags.test.d.ts +2 -0
  129. package/dist/types/src/extensions/language/xml/xml-tags.test.d.ts.map +1 -0
  130. package/dist/types/src/extensions/language/xml/xml-util.d.ts.map +1 -0
  131. package/dist/types/src/extensions/language/xml/xml-util.test.d.ts.map +1 -0
  132. package/dist/types/src/extensions/review/change-bar.d.ts +29 -0
  133. package/dist/types/src/extensions/review/change-bar.d.ts.map +1 -0
  134. package/dist/types/src/extensions/{comments.d.ts → review/comments.d.ts} +56 -38
  135. package/dist/types/src/extensions/review/comments.d.ts.map +1 -0
  136. package/dist/types/src/extensions/review/comments.test.d.ts +2 -0
  137. package/dist/types/src/extensions/review/comments.test.d.ts.map +1 -0
  138. package/dist/types/src/extensions/review/diff.d.ts +131 -0
  139. package/dist/types/src/extensions/review/diff.d.ts.map +1 -0
  140. package/dist/types/src/extensions/review/diff.test.d.ts +2 -0
  141. package/dist/types/src/extensions/review/diff.test.d.ts.map +1 -0
  142. package/dist/types/src/extensions/review/index.d.ts +7 -0
  143. package/dist/types/src/extensions/review/index.d.ts.map +1 -0
  144. package/dist/types/src/extensions/review/suggest.d.ts +89 -0
  145. package/dist/types/src/extensions/review/suggest.d.ts.map +1 -0
  146. package/dist/types/src/extensions/review/suggest.test.d.ts +2 -0
  147. package/dist/types/src/extensions/review/suggest.test.d.ts.map +1 -0
  148. package/dist/types/src/extensions/review/suggestions-overlay.d.ts +25 -0
  149. package/dist/types/src/extensions/review/suggestions-overlay.d.ts.map +1 -0
  150. package/dist/types/src/extensions/review/suggestions-overlay.test.d.ts +2 -0
  151. package/dist/types/src/extensions/review/suggestions-overlay.test.d.ts.map +1 -0
  152. package/dist/types/src/extensions/review/track-changes.d.ts +27 -0
  153. package/dist/types/src/extensions/review/track-changes.d.ts.map +1 -0
  154. package/dist/types/src/extensions/review/track-changes.test.d.ts +2 -0
  155. package/dist/types/src/extensions/review/track-changes.test.d.ts.map +1 -0
  156. package/dist/types/src/extensions/{busy.d.ts → state/busy.d.ts} +1 -0
  157. package/dist/types/src/extensions/state/busy.d.ts.map +1 -0
  158. package/dist/types/src/extensions/{focus.d.ts → state/focus.d.ts} +4 -0
  159. package/dist/types/src/extensions/state/focus.d.ts.map +1 -0
  160. package/dist/types/src/extensions/state/index.d.ts +5 -0
  161. package/dist/types/src/extensions/state/index.d.ts.map +1 -0
  162. package/dist/types/src/extensions/state/modal.d.ts.map +1 -0
  163. package/dist/types/src/extensions/{selection.d.ts → state/selection.d.ts} +1 -1
  164. package/dist/types/src/extensions/state/selection.d.ts.map +1 -0
  165. package/dist/types/src/extensions/state/selection.test.d.ts.map +1 -0
  166. package/dist/types/src/extensions/streaming/index.d.ts +4 -0
  167. package/dist/types/src/extensions/streaming/index.d.ts.map +1 -0
  168. package/dist/types/src/extensions/streaming/pending/index.d.ts.map +1 -0
  169. package/dist/types/src/extensions/streaming/pending/pending-text-stream.d.ts.map +1 -0
  170. package/dist/types/src/extensions/streaming/pending/pending-text-stream.test.d.ts.map +1 -0
  171. package/dist/types/src/extensions/{pending → streaming/pending}/pending-text.d.ts +1 -0
  172. package/dist/types/src/extensions/streaming/pending/pending-text.d.ts.map +1 -0
  173. package/dist/types/src/extensions/streaming/pending/pending-text.test.d.ts.map +1 -0
  174. package/dist/types/src/extensions/streaming/scrolling/auto-scroll.d.ts.map +1 -0
  175. package/dist/types/src/extensions/streaming/scrolling/crawler.d.ts.map +1 -0
  176. package/dist/types/src/extensions/streaming/scrolling/index.d.ts.map +1 -0
  177. package/dist/types/src/extensions/streaming/scrolling/scroll-past-end.d.ts +4 -0
  178. package/dist/types/src/extensions/streaming/scrolling/scroll-past-end.d.ts.map +1 -0
  179. package/dist/types/src/extensions/streaming/scrolling/scrollbar-autohide.d.ts.map +1 -0
  180. package/dist/types/src/extensions/streaming/scrolling/scroller.d.ts.map +1 -0
  181. package/dist/types/src/extensions/{typewriter.d.ts → streaming/typewriter.d.ts} +12 -12
  182. package/dist/types/src/extensions/streaming/typewriter.d.ts.map +1 -0
  183. package/dist/types/src/extensions/streaming/typewriter.test.d.ts.map +1 -0
  184. package/dist/types/src/extensions/structure/blocks/blocks.d.ts +52 -0
  185. package/dist/types/src/extensions/structure/blocks/blocks.d.ts.map +1 -0
  186. package/dist/types/src/extensions/structure/blocks/blocks.test.d.ts +2 -0
  187. package/dist/types/src/extensions/structure/blocks/blocks.test.d.ts.map +1 -0
  188. package/dist/types/src/extensions/structure/blocks/drag.d.ts +57 -0
  189. package/dist/types/src/extensions/structure/blocks/drag.d.ts.map +1 -0
  190. package/dist/types/src/extensions/structure/blocks/index.d.ts +5 -0
  191. package/dist/types/src/extensions/structure/blocks/index.d.ts.map +1 -0
  192. package/dist/types/src/extensions/structure/blocks/selection.d.ts +47 -0
  193. package/dist/types/src/extensions/structure/blocks/selection.d.ts.map +1 -0
  194. package/dist/types/src/extensions/structure/blocks/selection.test.d.ts +2 -0
  195. package/dist/types/src/extensions/structure/blocks/selection.test.d.ts.map +1 -0
  196. package/dist/types/src/extensions/structure/blocks/types.d.ts +17 -0
  197. package/dist/types/src/extensions/structure/blocks/types.d.ts.map +1 -0
  198. package/dist/types/src/extensions/structure/folding/folding.d.ts.map +1 -0
  199. package/dist/types/src/extensions/structure/folding/index.d.ts +3 -0
  200. package/dist/types/src/extensions/structure/folding/index.d.ts.map +1 -0
  201. package/dist/types/src/extensions/structure/folding/turns.d.ts +55 -0
  202. package/dist/types/src/extensions/structure/folding/turns.d.ts.map +1 -0
  203. package/dist/types/src/extensions/structure/folding/turns.test.d.ts +2 -0
  204. package/dist/types/src/extensions/structure/folding/turns.test.d.ts.map +1 -0
  205. package/dist/types/src/extensions/structure/index.d.ts +4 -0
  206. package/dist/types/src/extensions/structure/index.d.ts.map +1 -0
  207. package/dist/types/src/extensions/structure/outliner/commands.d.ts +31 -0
  208. package/dist/types/src/extensions/structure/outliner/commands.d.ts.map +1 -0
  209. package/dist/types/src/extensions/structure/outliner/dnd.d.ts +43 -0
  210. package/dist/types/src/extensions/structure/outliner/dnd.d.ts.map +1 -0
  211. package/dist/types/src/extensions/structure/outliner/dnd.test.d.ts +2 -0
  212. package/dist/types/src/extensions/structure/outliner/dnd.test.d.ts.map +1 -0
  213. package/dist/types/src/extensions/structure/outliner/editor.d.ts.map +1 -0
  214. package/dist/types/src/extensions/structure/outliner/editor.test.d.ts.map +1 -0
  215. package/dist/types/src/extensions/{outliner → structure/outliner}/index.d.ts +1 -0
  216. package/dist/types/src/extensions/structure/outliner/index.d.ts.map +1 -0
  217. package/dist/types/src/extensions/{outliner → structure/outliner}/menu.d.ts +1 -0
  218. package/dist/types/src/extensions/structure/outliner/menu.d.ts.map +1 -0
  219. package/dist/types/src/extensions/structure/outliner/outliner.d.ts.map +1 -0
  220. package/dist/types/src/extensions/structure/outliner/outliner.test.d.ts.map +1 -0
  221. package/dist/types/src/extensions/{outliner → structure/outliner}/tree.d.ts +1 -1
  222. package/dist/types/src/extensions/structure/outliner/tree.d.ts.map +1 -0
  223. package/dist/types/src/extensions/structure/outliner/tree.test.d.ts.map +1 -0
  224. package/dist/types/src/headless.d.ts +4 -0
  225. package/dist/types/src/headless.d.ts.map +1 -0
  226. package/dist/types/src/styles/theme.d.ts +1 -1
  227. package/dist/types/src/styles/theme.d.ts.map +1 -1
  228. package/dist/types/src/util/util.d.ts +2 -0
  229. package/dist/types/src/util/util.d.ts.map +1 -1
  230. package/dist/types/tsconfig.tsbuildinfo +1 -1
  231. package/package.json +48 -42
  232. package/src/defaults.ts +1 -1
  233. package/src/extensions/{bookmarks.ts → behavior/bookmarks.ts} +24 -14
  234. package/src/extensions/behavior/index.ts +9 -0
  235. package/src/extensions/{listener.ts → behavior/listener.ts} +4 -1
  236. package/src/extensions/{spacing.ts → behavior/spacing.ts} +8 -2
  237. package/src/extensions/{submit.ts → behavior/submit.ts} +2 -10
  238. package/src/extensions/{automerge → collab/automerge}/automerge.test.tsx +4 -4
  239. package/src/extensions/{automerge → collab/automerge}/automerge.ts +7 -4
  240. package/src/extensions/{automerge → collab/automerge}/cursor.ts +1 -1
  241. package/src/extensions/{automerge → collab/automerge}/defs.ts +4 -1
  242. package/src/extensions/{automerge → collab/automerge}/index.ts +1 -0
  243. package/src/extensions/{automerge → collab/automerge}/sync.ts +1 -2
  244. package/src/extensions/{automerge → collab/automerge}/update-automerge.ts +4 -0
  245. package/src/extensions/{automerge → collab/automerge}/update-codemirror.ts +5 -1
  246. package/src/extensions/{awareness → collab/awareness}/awareness-provider.ts +3 -4
  247. package/src/extensions/{awareness → collab/awareness}/awareness.ts +19 -16
  248. package/src/extensions/collab/index.ts +6 -0
  249. package/src/extensions/{autocomplete → completion}/autocomplete.ts +7 -5
  250. package/src/extensions/{autocomplete → completion}/index.ts +2 -1
  251. package/src/extensions/{autocomplete → completion}/match.ts +2 -2
  252. package/src/extensions/{mention.ts → completion/mention.ts} +3 -0
  253. package/src/extensions/{autocomplete → completion}/placeholder.ts +1 -1
  254. package/src/extensions/completion/typeahead.ts +97 -0
  255. package/src/extensions/{factories.ts → core/factories.ts} +35 -13
  256. package/src/extensions/core/index.ts +6 -0
  257. package/src/extensions/{modes.ts → core/modes.ts} +4 -1
  258. package/src/extensions/{debug.ts → debug/debug.ts} +3 -0
  259. package/src/extensions/debug/index.ts +5 -0
  260. package/src/extensions/{annotations.ts → decoration/annotations.ts} +12 -10
  261. package/src/extensions/decoration/assistant.ts +225 -0
  262. package/src/extensions/{fader.ts → decoration/fader.ts} +17 -19
  263. package/src/extensions/{hashtag.ts → decoration/hashtag.ts} +35 -28
  264. package/src/extensions/decoration/index.ts +11 -0
  265. package/src/extensions/{marker.ts → decoration/marker.ts} +18 -18
  266. package/src/extensions/{pos.test.ts → decoration/pos.test.ts} +57 -1
  267. package/src/extensions/{pos.ts → decoration/pos.ts} +165 -31
  268. package/src/extensions/{blast.ts → demo/blast.ts} +3 -0
  269. package/src/extensions/demo/index.ts +6 -0
  270. package/src/extensions/{snippets.ts → demo/snippets.ts} +5 -1
  271. package/src/extensions/index.ts +10 -33
  272. package/src/extensions/language/index.ts +8 -0
  273. package/src/extensions/language/json/index.ts +5 -0
  274. package/src/extensions/{json.ts → language/json/json.ts} +11 -6
  275. package/src/extensions/{markdown → language/markdown}/action.ts +1 -1
  276. package/src/extensions/{markdown → language/markdown}/bundle.ts +7 -1
  277. package/src/extensions/{markdown → language/markdown}/changes.test.ts +2 -2
  278. package/src/extensions/{markdown → language/markdown}/changes.ts +2 -2
  279. package/src/extensions/{markdown → language/markdown}/debug.ts +3 -0
  280. package/src/extensions/{markdown → language/markdown}/decorate.ts +96 -85
  281. package/src/extensions/{markdown → language/markdown}/formatting.test.ts +88 -1
  282. package/src/extensions/{markdown → language/markdown}/formatting.ts +193 -74
  283. package/src/extensions/{markdown → language/markdown}/highlight.ts +42 -41
  284. package/src/extensions/{markdown → language/markdown}/image.test.ts +1 -1
  285. package/src/extensions/{markdown → language/markdown}/image.ts +6 -6
  286. package/src/extensions/language/markdown/link.test.ts +58 -0
  287. package/src/extensions/language/markdown/link.ts +109 -0
  288. package/src/extensions/{markdown → language/markdown}/parser.test.ts +0 -28
  289. package/src/extensions/{markdown → language/markdown}/styles.ts +38 -7
  290. package/src/extensions/{markdown → language/markdown}/table.ts +5 -5
  291. package/src/extensions/language/mermaid/index.ts +5 -0
  292. package/src/extensions/language/mermaid/mermaid.ts +44 -0
  293. package/src/extensions/{tags → language/xml}/DESIGN.md +3 -3
  294. package/src/extensions/{tags → language/xml}/extended-markdown.test.ts +2 -1
  295. package/src/extensions/{tags → language/xml}/extended-markdown.ts +2 -4
  296. package/src/extensions/language/xml/stub.ts +233 -0
  297. package/src/extensions/language/xml/xml-block-decoration.ts +165 -0
  298. package/src/extensions/{tags → language/xml}/xml-formatting.ts +54 -38
  299. package/src/extensions/language/xml/xml-tags.test.ts +588 -0
  300. package/src/extensions/{tags → language/xml}/xml-tags.ts +165 -66
  301. package/src/extensions/{tags → language/xml}/xml-util.test.ts +2 -2
  302. package/src/extensions/review/change-bar.ts +94 -0
  303. package/src/extensions/review/comments.test.ts +72 -0
  304. package/src/extensions/{comments.ts → review/comments.ts} +395 -267
  305. package/src/extensions/review/diff.test.ts +405 -0
  306. package/src/extensions/review/diff.ts +502 -0
  307. package/src/extensions/review/index.ts +10 -0
  308. package/src/extensions/review/suggest.test.ts +170 -0
  309. package/src/extensions/review/suggest.ts +512 -0
  310. package/src/extensions/review/suggestions-overlay.test.ts +58 -0
  311. package/src/extensions/review/suggestions-overlay.ts +50 -0
  312. package/src/extensions/review/track-changes.test.ts +101 -0
  313. package/src/extensions/review/track-changes.ts +215 -0
  314. package/src/extensions/{busy.ts → state/busy.ts} +1 -0
  315. package/src/extensions/{focus.ts → state/focus.ts} +4 -0
  316. package/src/extensions/state/index.ts +8 -0
  317. package/src/extensions/{selection.ts → state/selection.ts} +3 -3
  318. package/src/extensions/streaming/index.ts +7 -0
  319. package/src/extensions/{pending → streaming/pending}/pending-text-stream.ts +10 -3
  320. package/src/extensions/{pending → streaming/pending}/pending-text.test.ts +1 -1
  321. package/src/extensions/{pending → streaming/pending}/pending-text.ts +20 -19
  322. package/src/extensions/{scrolling → streaming/scrolling}/auto-scroll.ts +9 -6
  323. package/src/extensions/{scrolling → streaming/scrolling}/scroll-past-end.ts +1 -0
  324. package/src/extensions/{scrolling → streaming/scrolling}/scrollbar-autohide.ts +13 -11
  325. package/src/extensions/{typewriter.ts → streaming/typewriter.ts} +46 -47
  326. package/src/extensions/structure/blocks/DESIGN.md +134 -0
  327. package/src/extensions/structure/blocks/blocks.test.ts +147 -0
  328. package/src/extensions/structure/blocks/blocks.ts +219 -0
  329. package/src/extensions/structure/blocks/drag.ts +996 -0
  330. package/src/extensions/structure/blocks/index.ts +8 -0
  331. package/src/extensions/structure/blocks/selection.test.ts +56 -0
  332. package/src/extensions/structure/blocks/selection.ts +279 -0
  333. package/src/extensions/structure/blocks/types.ts +22 -0
  334. package/src/extensions/{folding.ts → structure/folding/folding.ts} +12 -10
  335. package/src/extensions/structure/folding/index.ts +6 -0
  336. package/src/extensions/structure/folding/turns.test.ts +72 -0
  337. package/src/extensions/structure/folding/turns.ts +209 -0
  338. package/src/extensions/structure/index.ts +7 -0
  339. package/src/extensions/structure/outliner/DESIGN.md +183 -0
  340. package/src/extensions/structure/outliner/SPEC.mdl +305 -0
  341. package/src/extensions/{outliner → structure/outliner}/commands.ts +125 -42
  342. package/src/extensions/structure/outliner/dnd.test.ts +142 -0
  343. package/src/extensions/structure/outliner/dnd.ts +375 -0
  344. package/src/extensions/structure/outliner/editor.test.ts +85 -0
  345. package/src/extensions/{outliner → structure/outliner}/editor.ts +14 -9
  346. package/src/extensions/{outliner → structure/outliner}/index.ts +1 -0
  347. package/src/extensions/{outliner → structure/outliner}/menu.ts +48 -25
  348. package/src/extensions/structure/outliner/outliner.test.ts +193 -0
  349. package/src/extensions/structure/outliner/outliner.ts +183 -0
  350. package/src/extensions/{outliner → structure/outliner}/tree.test.ts +2 -2
  351. package/src/extensions/{outliner → structure/outliner}/tree.ts +1 -1
  352. package/src/headless.ts +13 -0
  353. package/src/styles/theme.ts +3 -1
  354. package/src/util/util.ts +3 -0
  355. package/dist/lib/browser/index.mjs +0 -9594
  356. package/dist/lib/browser/index.mjs.map +0 -7
  357. package/dist/lib/browser/meta.json +0 -1
  358. package/dist/lib/browser/types/index.mjs +0 -33
  359. package/dist/lib/browser/types/index.mjs.map +0 -7
  360. package/dist/lib/node-esm/index.mjs +0 -9596
  361. package/dist/lib/node-esm/index.mjs.map +0 -7
  362. package/dist/lib/node-esm/meta.json +0 -1
  363. package/dist/lib/node-esm/types/index.mjs +0 -35
  364. package/dist/lib/node-esm/types/index.mjs.map +0 -7
  365. package/dist/types/src/extensions/annotations.d.ts.map +0 -1
  366. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +0 -1
  367. package/dist/types/src/extensions/autocomplete/index.d.ts.map +0 -1
  368. package/dist/types/src/extensions/autocomplete/match.d.ts.map +0 -1
  369. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +0 -1
  370. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +0 -1
  371. package/dist/types/src/extensions/automerge/automerge.d.ts.map +0 -1
  372. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +0 -1
  373. package/dist/types/src/extensions/automerge/cursor.d.ts.map +0 -1
  374. package/dist/types/src/extensions/automerge/defs.d.ts.map +0 -1
  375. package/dist/types/src/extensions/automerge/index.d.ts +0 -2
  376. package/dist/types/src/extensions/automerge/index.d.ts.map +0 -1
  377. package/dist/types/src/extensions/automerge/sync.d.ts.map +0 -1
  378. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +0 -1
  379. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +0 -1
  380. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +0 -1
  381. package/dist/types/src/extensions/awareness/awareness.d.ts.map +0 -1
  382. package/dist/types/src/extensions/awareness/index.d.ts.map +0 -1
  383. package/dist/types/src/extensions/blast.d.ts.map +0 -1
  384. package/dist/types/src/extensions/blocks.d.ts +0 -2
  385. package/dist/types/src/extensions/blocks.d.ts.map +0 -1
  386. package/dist/types/src/extensions/bookmarks.d.ts.map +0 -1
  387. package/dist/types/src/extensions/busy.d.ts.map +0 -1
  388. package/dist/types/src/extensions/comments.d.ts.map +0 -1
  389. package/dist/types/src/extensions/debug.d.ts.map +0 -1
  390. package/dist/types/src/extensions/dnd.d.ts.map +0 -1
  391. package/dist/types/src/extensions/factories.d.ts.map +0 -1
  392. package/dist/types/src/extensions/factories.test.d.ts.map +0 -1
  393. package/dist/types/src/extensions/fader.d.ts.map +0 -1
  394. package/dist/types/src/extensions/focus.d.ts.map +0 -1
  395. package/dist/types/src/extensions/folding.d.ts.map +0 -1
  396. package/dist/types/src/extensions/hashtag.d.ts +0 -3
  397. package/dist/types/src/extensions/hashtag.d.ts.map +0 -1
  398. package/dist/types/src/extensions/json.d.ts +0 -7
  399. package/dist/types/src/extensions/json.d.ts.map +0 -1
  400. package/dist/types/src/extensions/listener.d.ts.map +0 -1
  401. package/dist/types/src/extensions/markdown/action.d.ts.map +0 -1
  402. package/dist/types/src/extensions/markdown/bundle.d.ts.map +0 -1
  403. package/dist/types/src/extensions/markdown/changes.d.ts.map +0 -1
  404. package/dist/types/src/extensions/markdown/changes.test.d.ts.map +0 -1
  405. package/dist/types/src/extensions/markdown/debug.d.ts.map +0 -1
  406. package/dist/types/src/extensions/markdown/decorate.d.ts.map +0 -1
  407. package/dist/types/src/extensions/markdown/formatting.d.ts.map +0 -1
  408. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +0 -1
  409. package/dist/types/src/extensions/markdown/highlight.d.ts.map +0 -1
  410. package/dist/types/src/extensions/markdown/image.d.ts.map +0 -1
  411. package/dist/types/src/extensions/markdown/image.test.d.ts.map +0 -1
  412. package/dist/types/src/extensions/markdown/index.d.ts.map +0 -1
  413. package/dist/types/src/extensions/markdown/link.d.ts +0 -7
  414. package/dist/types/src/extensions/markdown/link.d.ts.map +0 -1
  415. package/dist/types/src/extensions/markdown/parser.test.d.ts.map +0 -1
  416. package/dist/types/src/extensions/markdown/styles.d.ts.map +0 -1
  417. package/dist/types/src/extensions/markdown/table.d.ts.map +0 -1
  418. package/dist/types/src/extensions/marker.d.ts.map +0 -1
  419. package/dist/types/src/extensions/mention.d.ts.map +0 -1
  420. package/dist/types/src/extensions/modal.d.ts.map +0 -1
  421. package/dist/types/src/extensions/modes.d.ts.map +0 -1
  422. package/dist/types/src/extensions/outliner/commands.d.ts +0 -10
  423. package/dist/types/src/extensions/outliner/commands.d.ts.map +0 -1
  424. package/dist/types/src/extensions/outliner/editor.d.ts.map +0 -1
  425. package/dist/types/src/extensions/outliner/editor.test.d.ts.map +0 -1
  426. package/dist/types/src/extensions/outliner/index.d.ts.map +0 -1
  427. package/dist/types/src/extensions/outliner/menu.d.ts.map +0 -1
  428. package/dist/types/src/extensions/outliner/outliner.d.ts.map +0 -1
  429. package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +0 -1
  430. package/dist/types/src/extensions/outliner/selection.d.ts +0 -12
  431. package/dist/types/src/extensions/outliner/selection.d.ts.map +0 -1
  432. package/dist/types/src/extensions/outliner/tree.d.ts.map +0 -1
  433. package/dist/types/src/extensions/outliner/tree.test.d.ts.map +0 -1
  434. package/dist/types/src/extensions/pending/index.d.ts.map +0 -1
  435. package/dist/types/src/extensions/pending/pending-text-stream.d.ts.map +0 -1
  436. package/dist/types/src/extensions/pending/pending-text-stream.test.d.ts.map +0 -1
  437. package/dist/types/src/extensions/pending/pending-text.d.ts.map +0 -1
  438. package/dist/types/src/extensions/pending/pending-text.test.d.ts.map +0 -1
  439. package/dist/types/src/extensions/pos.d.ts.map +0 -1
  440. package/dist/types/src/extensions/pos.test.d.ts.map +0 -1
  441. package/dist/types/src/extensions/replacer.d.ts.map +0 -1
  442. package/dist/types/src/extensions/replacer.test.d.ts.map +0 -1
  443. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +0 -1
  444. package/dist/types/src/extensions/scrolling/crawler.d.ts.map +0 -1
  445. package/dist/types/src/extensions/scrolling/index.d.ts.map +0 -1
  446. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts +0 -3
  447. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +0 -1
  448. package/dist/types/src/extensions/scrolling/scrollbar-autohide.d.ts.map +0 -1
  449. package/dist/types/src/extensions/scrolling/scroller.d.ts.map +0 -1
  450. package/dist/types/src/extensions/selection.d.ts.map +0 -1
  451. package/dist/types/src/extensions/selection.test.d.ts.map +0 -1
  452. package/dist/types/src/extensions/snippets.d.ts.map +0 -1
  453. package/dist/types/src/extensions/spacing.d.ts.map +0 -1
  454. package/dist/types/src/extensions/state.d.ts +0 -2
  455. package/dist/types/src/extensions/state.d.ts.map +0 -1
  456. package/dist/types/src/extensions/submit.d.ts.map +0 -1
  457. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +0 -1
  458. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +0 -1
  459. package/dist/types/src/extensions/tags/index.d.ts.map +0 -1
  460. package/dist/types/src/extensions/tags/stub.d.ts +0 -35
  461. package/dist/types/src/extensions/tags/stub.d.ts.map +0 -1
  462. package/dist/types/src/extensions/tags/widgets/anchor.d.ts.map +0 -1
  463. package/dist/types/src/extensions/tags/widgets/index.d.ts.map +0 -1
  464. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +0 -31
  465. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +0 -1
  466. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +0 -1
  467. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +0 -1
  468. package/dist/types/src/extensions/tags/xml-util.d.ts.map +0 -1
  469. package/dist/types/src/extensions/tags/xml-util.test.d.ts.map +0 -1
  470. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  471. package/dist/types/src/extensions/typewriter.test.d.ts.map +0 -1
  472. package/src/extensions/autocomplete/typeahead.ts +0 -87
  473. package/src/extensions/blocks.ts +0 -131
  474. package/src/extensions/markdown/link.ts +0 -49
  475. package/src/extensions/outliner/editor.test.ts +0 -33
  476. package/src/extensions/outliner/outliner.test.ts +0 -99
  477. package/src/extensions/outliner/outliner.ts +0 -166
  478. package/src/extensions/outliner/selection.ts +0 -50
  479. package/src/extensions/state.ts +0 -7
  480. package/src/extensions/tags/stub.ts +0 -80
  481. package/src/extensions/tags/xml-block-decoration.ts +0 -123
  482. package/dist/types/src/extensions/{dnd.d.ts → behavior/dnd.d.ts} +0 -0
  483. package/dist/types/src/extensions/{submit.d.ts → behavior/submit.d.ts} +0 -0
  484. package/dist/types/src/extensions/{automerge → collab/automerge}/automerge.d.ts +0 -0
  485. package/dist/types/src/extensions/{automerge → collab/automerge}/automerge.test.d.ts +0 -0
  486. package/dist/types/src/extensions/{automerge → collab/automerge}/sync.d.ts +0 -0
  487. package/dist/types/src/extensions/{awareness → collab/awareness}/awareness-provider.d.ts +0 -0
  488. package/dist/types/src/extensions/{awareness → collab/awareness}/index.d.ts +0 -0
  489. package/dist/types/src/extensions/{autocomplete → completion}/autocomplete.d.ts +0 -0
  490. package/dist/types/src/extensions/{autocomplete → completion}/placeholder.d.ts +0 -0
  491. package/dist/types/src/extensions/{factories.test.d.ts → core/factories.test.d.ts} +0 -0
  492. package/dist/types/src/extensions/{fader.d.ts → decoration/fader.d.ts} +0 -0
  493. package/dist/types/src/extensions/{marker.d.ts → decoration/marker.d.ts} +0 -0
  494. package/dist/types/src/extensions/{pos.test.d.ts → decoration/pos.test.d.ts} +0 -0
  495. package/dist/types/src/extensions/{replacer.test.d.ts → decoration/replacer.test.d.ts} +0 -0
  496. package/dist/types/src/extensions/{snippets.d.ts → demo/snippets.d.ts} +0 -0
  497. package/dist/types/src/extensions/{markdown → language/markdown}/action.d.ts +0 -0
  498. package/dist/types/src/extensions/{markdown → language/markdown}/bundle.d.ts +0 -0
  499. package/dist/types/src/extensions/{markdown → language/markdown}/changes.test.d.ts +0 -0
  500. package/dist/types/src/extensions/{markdown → language/markdown}/formatting.test.d.ts +0 -0
  501. package/dist/types/src/extensions/{markdown → language/markdown}/image.d.ts +0 -0
  502. package/dist/types/src/extensions/{markdown → language/markdown}/image.test.d.ts +0 -0
  503. package/dist/types/src/extensions/{markdown → language/markdown}/index.d.ts +0 -0
  504. package/dist/types/src/extensions/{markdown → language/markdown}/parser.test.d.ts +0 -0
  505. package/dist/types/src/extensions/{markdown → language/markdown}/table.d.ts +0 -0
  506. package/dist/types/src/extensions/{tags → language/xml}/extended-markdown.d.ts +0 -0
  507. package/dist/types/src/extensions/{tags → language/xml}/extended-markdown.test.d.ts +0 -0
  508. package/dist/types/src/extensions/{tags → language/xml}/index.d.ts +0 -0
  509. package/dist/types/src/extensions/{tags → language/xml}/widgets/anchor.d.ts +0 -0
  510. package/dist/types/src/extensions/{tags → language/xml}/widgets/index.d.ts +0 -0
  511. package/dist/types/src/extensions/{tags → language/xml}/xml-formatting.d.ts +0 -0
  512. package/dist/types/src/extensions/{tags → language/xml}/xml-util.d.ts +0 -0
  513. package/dist/types/src/extensions/{tags → language/xml}/xml-util.test.d.ts +0 -0
  514. package/dist/types/src/extensions/{modal.d.ts → state/modal.d.ts} +0 -0
  515. package/dist/types/src/extensions/{selection.test.d.ts → state/selection.test.d.ts} +0 -0
  516. package/dist/types/src/extensions/{pending → streaming/pending}/index.d.ts +0 -0
  517. package/dist/types/src/extensions/{pending → streaming/pending}/pending-text-stream.d.ts +0 -0
  518. package/dist/types/src/extensions/{pending → streaming/pending}/pending-text-stream.test.d.ts +0 -0
  519. package/dist/types/src/extensions/{pending → streaming/pending}/pending-text.test.d.ts +0 -0
  520. package/dist/types/src/extensions/{scrolling → streaming/scrolling}/auto-scroll.d.ts +0 -0
  521. package/dist/types/src/extensions/{scrolling → streaming/scrolling}/crawler.d.ts +0 -0
  522. package/dist/types/src/extensions/{scrolling → streaming/scrolling}/index.d.ts +0 -0
  523. package/dist/types/src/extensions/{scrolling → streaming/scrolling}/scrollbar-autohide.d.ts +0 -0
  524. package/dist/types/src/extensions/{scrolling → streaming/scrolling}/scroller.d.ts +0 -0
  525. package/dist/types/src/extensions/{typewriter.test.d.ts → streaming/typewriter.test.d.ts} +0 -0
  526. package/dist/types/src/extensions/{folding.d.ts → structure/folding/folding.d.ts} +0 -0
  527. package/dist/types/src/extensions/{outliner → structure/outliner}/editor.d.ts +0 -0
  528. package/dist/types/src/extensions/{outliner → structure/outliner}/editor.test.d.ts +0 -0
  529. package/dist/types/src/extensions/{outliner → structure/outliner}/outliner.d.ts +0 -0
  530. package/dist/types/src/extensions/{outliner → structure/outliner}/outliner.test.d.ts +0 -0
  531. package/dist/types/src/extensions/{outliner → structure/outliner}/tree.test.d.ts +0 -0
  532. package/src/extensions/{dnd.ts → behavior/dnd.ts} +29 -29
  533. package/src/extensions/{awareness → collab/awareness}/index.ts +0 -0
  534. package/src/extensions/{factories.test.ts → core/factories.test.ts} +0 -0
  535. package/src/extensions/{replacer.test.ts → decoration/replacer.test.ts} +0 -0
  536. package/src/extensions/{replacer.ts → decoration/replacer.ts} +25 -25
  537. /package/src/extensions/{markdown → language/markdown}/index.ts +0 -0
  538. /package/src/extensions/{tags → language/xml}/index.ts +0 -0
  539. /package/src/extensions/{tags → language/xml}/testing/text.md +0 -0
  540. /package/src/extensions/{tags → language/xml}/testing/text.txt +0 -0
  541. /package/src/extensions/{tags → language/xml}/widgets/anchor.ts +0 -0
  542. /package/src/extensions/{tags → language/xml}/widgets/index.ts +0 -0
  543. /package/src/extensions/{tags → language/xml}/xml-util.ts +0 -0
  544. /package/src/extensions/{modal.ts → state/modal.ts} +0 -0
  545. /package/src/extensions/{selection.test.ts → state/selection.test.ts} +0 -0
  546. /package/src/extensions/{pending → streaming/pending}/index.ts +0 -0
  547. /package/src/extensions/{pending → streaming/pending}/pending-text-stream.test.ts +0 -0
  548. /package/src/extensions/{scrolling → streaming/scrolling}/crawler.ts +0 -0
  549. /package/src/extensions/{scrolling → streaming/scrolling}/index.ts +0 -0
  550. /package/src/extensions/{scrolling → streaming/scrolling}/scroller.ts +0 -0
  551. /package/src/extensions/{typewriter.test.ts → streaming/typewriter.test.ts} +0 -0
@@ -0,0 +1,233 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { EditorView, WidgetType } from '@codemirror/view';
6
+ import { type FunctionComponent } from 'react';
7
+
8
+ import { invariant } from '@dxos/invariant';
9
+ import { log } from '@dxos/log';
10
+ import { Domino } from '@dxos/ui';
11
+
12
+ import { type XmlWidgetProps, type XmlWidgetState } from './xml-tags';
13
+
14
+ export interface XmlWidgetNotifier {
15
+ mounted(widget: XmlWidgetState): void;
16
+ unmounted(id: string): void;
17
+ /**
18
+ * Re-render the mounted widget for `id` with updated props. Keyed by id rather than by widget
19
+ * instance: a rebuild constructs fresh widgets, but `StubWidget.eq` (id equality) makes CodeMirror
20
+ * keep the previously-rendered DOM, so the instance in the decoration set is not the one holding
21
+ * the mounted root.
22
+ */
23
+ updated(id: string, widgetState: any): void;
24
+ /**
25
+ * Drop any mounted widgets whose id is not in `liveIds`. Needed because CM reuses a widget's DOM
26
+ * via `updateDOM` (without calling `destroy`) when a decoration's widget changes in place, so an
27
+ * id that is no longer present — e.g. a position-keyed id after an edit shifted the node — would
28
+ * otherwise leak a stale portal.
29
+ */
30
+ reconcile(liveIds: Set<string>): void;
31
+ }
32
+
33
+ /**
34
+ * Mounts a DOM placeholder that React portals render into.
35
+ * Uses a block <div> for block widgets and an inline <span> for inline widgets.
36
+ *
37
+ * Culling a large block widget out of the viewport and re-showing it tears down and rebuilds the
38
+ * portal, causing a blank frame + flicker. `#keepAlive` (for known-height blocks) caches `#root` and
39
+ * keeps the portal mounted across culls so CM re-shows the same populated node; `toDOM` also gives the
40
+ * placeholder a fixed height so CM's measurement is authoritative and content-independent.
41
+ *
42
+ * NOTE: This does NOT address the separate scroll-*jump* on large/fast scroll deltas. That was verified
43
+ * (Playwright, both async and plain-sync block stories) to be widget-independent: CM's measure-phase
44
+ * scroll re-anchor correcting its content-height *estimate* when a big scroll delta reveals a lot of
45
+ * not-yet-measured content. Gradual scrolling does not jump. That is general CM heightmap behavior, not
46
+ * fixable here — see upstream CM #1727 (https://code.haverbeke.berlin/codemirror/dev/issues/1727).
47
+ */
48
+ export class StubWidget<TProps extends XmlWidgetProps> extends WidgetType {
49
+ #root: HTMLElement | null = null;
50
+ #view: EditorView | undefined;
51
+ // Throttle the (hot) coordsAt trace to at most once per frame.
52
+ #coordsLoggedThisFrame = false;
53
+
54
+ constructor(
55
+ readonly id: string,
56
+ readonly Component: FunctionComponent<TProps>,
57
+ readonly props: TProps,
58
+ readonly notifier: XmlWidgetNotifier,
59
+ readonly streaming?: boolean,
60
+ readonly block?: boolean,
61
+ /**
62
+ * Reserved block height (px). Feeds CodeMirror's off-screen viewport estimate and pre-sizes the
63
+ * placeholder so the block occupies its final height before the portaled content resolves —
64
+ * otherwise it collapses to the 24px minimum, causing scroll jitter and a blank on scroll-back.
65
+ */
66
+ readonly blockHeight?: number,
67
+ /** When true, trace the widget's DOM lifecycle to diagnose scroll-cull jitter/jump (see PreviewScrollSurface). */
68
+ readonly debug?: boolean,
69
+ ) {
70
+ super();
71
+ invariant(id);
72
+ }
73
+
74
+ get root(): HTMLElement | null {
75
+ return this.#root;
76
+ }
77
+
78
+ /** Gated lifecycle trace. Enable per-tag via `XmlWidgetDef.debug` to watch the scroll-cull sequence. */
79
+ #trace(event: string, data?: Record<string, unknown>): void {
80
+ if (this.debug) {
81
+ log.info(`stub-widget: ${event}`, { id: this.id, block: this.block, ...data });
82
+ }
83
+ }
84
+
85
+ // CodeMirror reserves this height for the block while it is outside the rendered viewport.
86
+ override get estimatedHeight() {
87
+ return this.block && this.blockHeight != null ? this.blockHeight : -1;
88
+ }
89
+
90
+ // Report per-position screen coordinates inside the block so CM can anchor scroll correctly. Large
91
+ // block widgets need this alongside `estimatedHeight` (codemirror/dev#761); returning the same rect
92
+ // for every position confuses CM's geometry, so interpolate a vertical position across the widget's
93
+ // range (offset `pos` within `to - from`).
94
+ override coordsAt(dom: HTMLElement, pos: number, side: number) {
95
+ if (!this.block) {
96
+ return null;
97
+ }
98
+ const rect = dom.getBoundingClientRect();
99
+ const range = (this.props as XmlWidgetProps).range;
100
+ const length = range ? range.to - range.from : 0;
101
+ const fraction = length > 0 ? Math.min(1, Math.max(0, pos / length)) : side > 0 ? 1 : 0;
102
+ const y = rect.top + rect.height * fraction;
103
+ if (this.debug) {
104
+ const detached = rect.height === 0 || (rect.top === 0 && rect.bottom === 0);
105
+ // A detached / re-parented node reports a zero rect; feeding that to CM's scroll anchor snaps the
106
+ // viewport to the document top. Flag it so the jump-to-top is attributable in the trace.
107
+ if (detached) {
108
+ log.warn('stub-widget: coordsAt on detached node (zero rect)', {
109
+ id: this.id,
110
+ pos,
111
+ side,
112
+ connected: dom.isConnected,
113
+ rect: { top: rect.top, height: rect.height },
114
+ y,
115
+ });
116
+ } else if (!this.#coordsLoggedThisFrame) {
117
+ // CM consults coordsAt to anchor the scroll; log one sample per frame to correlate with jumps.
118
+ this.#coordsLoggedThisFrame = true;
119
+ requestAnimationFrame(() => {
120
+ this.#coordsLoggedThisFrame = false;
121
+ });
122
+ log.info('stub-widget: coordsAt', {
123
+ id: this.id,
124
+ pos,
125
+ side,
126
+ rect: { top: Math.round(rect.top), height: Math.round(rect.height) },
127
+ y: Math.round(y),
128
+ scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1),
129
+ });
130
+ }
131
+ }
132
+ return { left: rect.left, right: rect.right, top: y, bottom: y };
133
+ }
134
+
135
+ override eq(other: this) {
136
+ if (this.streaming) {
137
+ return false;
138
+ }
139
+ // Context too, not just the id: props are captured at build time, so an id-only comparison makes
140
+ // CodeMirror keep the existing widget (and its stale props) when the host publishes the context
141
+ // after the first build — leaving every widget callback bound to `undefined`.
142
+ const context = (props: TProps) => (props as XmlWidgetProps).context;
143
+ return this.id === other.id && context(this.props) === context(other.props);
144
+ }
145
+
146
+ override ignoreEvent() {
147
+ return true;
148
+ }
149
+
150
+ /**
151
+ * Keep the root DOM (and its portal) alive across CM viewport culls, for block widgets whose height
152
+ * we know up front. A cull calls `destroy()` then `toDOM()` on the SAME widget instance; reusing the
153
+ * cached, already-rendered node instead of rebuilding it keeps the block's measured geometry stable
154
+ * across the cull boundary, which sidesteps CM's measure-phase scroll re-anchor (upstream #1727) and
155
+ * the blank/flicker from the portal remounting. Genuine removals (the tag edited out) are pruned by
156
+ * `notifier.reconcile` on the next document change, not by cull. Scoped to known-height blocks so
157
+ * ordinary widgets keep CM's normal virtualization (no unbounded retained portals).
158
+ */
159
+ get #keepAlive(): boolean {
160
+ return !!this.block && this.blockHeight != null;
161
+ }
162
+
163
+ override toDOM(view: EditorView) {
164
+ this.#view = view;
165
+ const cached = this.#keepAlive && this.#root != null;
166
+ if (!this.#root) {
167
+ this.#root = this.block ? Domino.of('div').classNames('min-h-[24px]').root : Domino.of('span').root;
168
+ if (this.block && this.blockHeight != null) {
169
+ // Fixed (not min) height: give CM an authoritative, content-independent measurement so an async
170
+ // widget that mounts / re-lays-out later cannot perturb the heightmap (we know the height up
171
+ // front). `overflow: hidden` keeps content that briefly overshoots from changing the measured box.
172
+ this.#root.style.height = `${this.blockHeight}px`;
173
+ this.#root.style.overflow = 'hidden';
174
+ }
175
+ }
176
+ const props = Object.assign({}, this.props, { view }) as TProps;
177
+ this.notifier.mounted({ id: this.id, root: this.#root, props, Component: this.Component });
178
+ this.#trace(cached ? 'toDOM (reuse cached root)' : 'toDOM (create)', {
179
+ blockHeight: this.blockHeight,
180
+ scrollTop: Math.round(view.scrollDOM.scrollTop),
181
+ });
182
+ this.#measureAfterPaint('toDOM');
183
+ return this.#root;
184
+ }
185
+
186
+ override updateDOM(dom: HTMLElement) {
187
+ this.#root = dom;
188
+ const props = Object.assign({}, this.props, { view: this.#view }) as TProps;
189
+ this.notifier.mounted({ id: this.id, root: this.#root, props, Component: this.Component });
190
+ this.#trace('updateDOM (reuse/re-parent)', { connected: dom.isConnected });
191
+ this.#measureAfterPaint('updateDOM');
192
+ return true;
193
+ }
194
+
195
+ override destroy(_dom: HTMLElement) {
196
+ if (this.#keepAlive) {
197
+ // Cull, not removal: keep #root and the portal mounted so the next toDOM re-shows the identical,
198
+ // already-rendered node. `notifier.reconcile` drops it if the tag is genuinely removed.
199
+ this.#trace('destroy (cull, keep-alive)', { scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1) });
200
+ return;
201
+ }
202
+ this.#trace('destroy (cull)', { scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1) });
203
+ this.notifier.unmounted(this.id);
204
+ this.#root = null;
205
+ this.#view = undefined;
206
+ }
207
+
208
+ /**
209
+ * After the next paint, compare the measured block height against the reserved `estimatedHeight`.
210
+ * A large discrepancy is what shifts CM's total-height model and drives the scroll jump; logging it
211
+ * pinpoints when (and by how much) the portaled content diverges from the reserved space.
212
+ */
213
+ #measureAfterPaint(source: string): void {
214
+ if (!this.debug || !this.block) {
215
+ return;
216
+ }
217
+ const root = this.#root;
218
+ requestAnimationFrame(() => {
219
+ if (!root) {
220
+ return;
221
+ }
222
+ const measured = root.getBoundingClientRect().height;
223
+ const reserved = this.blockHeight ?? -1;
224
+ log.info(`stub-widget: measured after ${source}`, {
225
+ id: this.id,
226
+ reserved,
227
+ measured,
228
+ delta: reserved > 0 ? measured - reserved : undefined,
229
+ connected: root.isConnected,
230
+ });
231
+ });
232
+ }
233
+ }
@@ -0,0 +1,165 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Extension, type Range } from '@codemirror/state';
6
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
7
+
8
+ import { escapeRegExpSource } from '../../../util';
9
+
10
+ export type XmlBlockDecorationOptions = {
11
+ /**
12
+ * Tag name to match (e.g. `'prompt'`).
13
+ */
14
+ tag: string;
15
+
16
+ /**
17
+ * Class added via `Decoration.line` to each line that intersects the element's content
18
+ * range. Use to style the bubble container (e.g. flex alignment, vertical margin).
19
+ */
20
+ lineClass?: string;
21
+
22
+ /**
23
+ * Additional class added only to the first line of the content range. Use to round the top
24
+ * of a block bubble and pad its top edge (single line receives both first and last classes).
25
+ */
26
+ firstLineClass?: string;
27
+
28
+ /**
29
+ * Additional class added only to the last line of the content range. Use to round the
30
+ * bottom of a block bubble and pad its bottom edge.
31
+ */
32
+ lastLineClass?: string;
33
+
34
+ /**
35
+ * Class added via `Decoration.mark` covering the inner content (between the open and
36
+ * close tags). Use to style the bubble surface (background, padding, rounding).
37
+ */
38
+ contentClass?: string;
39
+
40
+ /**
41
+ * When true, the open and close tag delimiters are hidden via `Decoration.replace`
42
+ * with no widget, so the rendered text is the inner content only.
43
+ */
44
+ hideTags?: boolean;
45
+ };
46
+
47
+ /**
48
+ * Scans the doc for `<tag>…</tag>` blocks and decorates them without replacing them with a
49
+ * widget — the source text remains in the document and can still be matched by other extensions
50
+ * (e.g. `xmlFormatting`). Use this for "bubble"-style styling of XML blocks (chat prompts,
51
+ * callouts, etc.) where the inner content should stay editable/copyable rather than living inside
52
+ * a CodeMirror widget.
53
+ *
54
+ * Matching is done by direct text scan rather than a full-document XML parse: the transcript is
55
+ * mostly markdown, and a single stray `<` anywhere upstream (code, `a < b`, an unclosed streaming
56
+ * widget tag) corrupts a whole-document XML parse — which previously caused a well-formed prompt's
57
+ * `</tag>` to go unrecognised, extending the bubble over the rest of the conversation. An
58
+ * unterminated open tag (no matching `</tag>`, e.g. mid-stream) is left undecorated rather than
59
+ * painting to the end of the document.
60
+ */
61
+ // TODO(burdon): Change to array to support multiple tags.
62
+ export const xmlBlockDecoration = ({
63
+ tag,
64
+ lineClass,
65
+ firstLineClass,
66
+ lastLineClass,
67
+ contentClass,
68
+ hideTags,
69
+ }: XmlBlockDecorationOptions): Extension => {
70
+ const contentDecoration = contentClass ? Decoration.mark({ class: contentClass }) : undefined;
71
+ const hideDecoration = hideTags ? Decoration.replace({}) : undefined;
72
+
73
+ // Match an opening `<tag>` or `<tag …attrs>` (not self-closing); the paired `</tag>` is located
74
+ // by scanning forward so unrelated markup between blocks cannot break matching. `tag` is escaped
75
+ // so a name containing regex metacharacters (e.g. `foo.bar`) matches literally.
76
+ const escapedTag = escapeRegExpSource(tag);
77
+ const openTagRegExp = new RegExp(`<${escapedTag}(?:\\s[^>]*)?>`, 'g');
78
+ const closeTagStr = `</${tag}>`;
79
+
80
+ const buildDecorations = (view: EditorView): DecorationSet => {
81
+ const docLength = view.state.doc.length;
82
+ const text = view.state.sliceDoc(0, docLength);
83
+ if (!text.includes(`<${tag}`)) {
84
+ return Decoration.none;
85
+ }
86
+
87
+ const ranges: Range<Decoration>[] = [];
88
+ openTagRegExp.lastIndex = 0;
89
+ let match: RegExpExecArray | null;
90
+ while ((match = openTagRegExp.exec(text))) {
91
+ const openFrom = match.index;
92
+ const openTo = openFrom + match[0].length;
93
+ const closeFrom = text.indexOf(closeTagStr, openTo);
94
+ // Skip unterminated blocks: extending decoration to the document end would paint the bubble
95
+ // over everything that follows (e.g. the assistant response streaming in after the prompt).
96
+ if (closeFrom === -1) {
97
+ break;
98
+ }
99
+ const closeTo = closeFrom + closeTagStr.length;
100
+ const contentFrom = openTo;
101
+ const contentTo = closeFrom;
102
+
103
+ if (hideDecoration) {
104
+ ranges.push(hideDecoration.range(openFrom, openTo));
105
+ ranges.push(hideDecoration.range(closeFrom, closeTo));
106
+ }
107
+
108
+ if (contentDecoration && contentFrom < contentTo) {
109
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
110
+ }
111
+
112
+ if (lineClass || firstLineClass || lastLineClass) {
113
+ // Collect every line that intersects the content range so that the first and last
114
+ // can be styled distinctly (e.g. rounding the top/bottom of a single block bubble).
115
+ const lines = [];
116
+ let pos = contentFrom;
117
+ while (pos < contentTo && pos <= docLength) {
118
+ const line = view.state.doc.lineAt(pos);
119
+ // Only lines that intersect visible content: a hidden close tag on its own line would
120
+ // otherwise get an empty rounded bubble line after the tag is replaced.
121
+ if (line.to > contentFrom && line.from < contentTo) {
122
+ lines.push(line);
123
+ }
124
+ if (line.to >= contentTo) {
125
+ break;
126
+ }
127
+ pos = line.to + 1;
128
+ }
129
+
130
+ lines.forEach((line, index) => {
131
+ const cls = [lineClass, index === 0 && firstLineClass, index === lines.length - 1 && lastLineClass]
132
+ .filter(Boolean)
133
+ .join(' ');
134
+ if (cls) {
135
+ ranges.push(Decoration.line({ class: cls }).range(line.from));
136
+ }
137
+ });
138
+ }
139
+
140
+ // Resume scanning past the close tag so content is never re-matched.
141
+ openTagRegExp.lastIndex = closeTo;
142
+ }
143
+
144
+ return Decoration.set(ranges, true);
145
+ };
146
+
147
+ return ViewPlugin.fromClass(
148
+ class {
149
+ decorations: DecorationSet;
150
+
151
+ constructor(view: EditorView) {
152
+ this.decorations = buildDecorations(view);
153
+ }
154
+
155
+ update(update: ViewUpdate) {
156
+ if (update.docChanged) {
157
+ this.decorations = buildDecorations(update.view);
158
+ }
159
+ }
160
+ },
161
+ {
162
+ decorations: (instance) => instance.decorations,
163
+ },
164
+ );
165
+ };
@@ -6,15 +6,6 @@ import { xmlLanguage } from '@codemirror/lang-xml';
6
6
  import { type Extension, type Range } from '@codemirror/state';
7
7
  import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
8
8
 
9
- /**
10
- * Lezer XML node names that represent the angle-bracket delimited portions of an element.
11
- */
12
- const XML_TAG_NODES = new Set(['OpenTag', 'CloseTag', 'SelfClosingTag', 'MismatchedCloseTag']);
13
-
14
- const xmlElementMark = Decoration.mark({ class: 'cm-xml-element' });
15
- const xmlTagMark = Decoration.mark({ class: 'cm-xml-tag' });
16
- const xmlContentMark = Decoration.mark({ class: 'cm-xml-content' });
17
-
18
9
  export type XmlFormattingOptions = {
19
10
  /**
20
11
  * Tag names whose elements should NOT receive xmlFormatting decorations. Use to
@@ -47,6 +38,17 @@ export const xmlFormatting = ({ skip }: XmlFormattingOptions = {}): Extension =>
47
38
  }
48
39
 
49
40
  const tagNameAt = (node: { from: number; to: number }) => text.slice(node.from, node.to);
41
+ // A tag is only decorated when its `TagName` child parses as a real name. This gates out the
42
+ // malformed elements Lezer synthesises from a stray `<` in prose, which would otherwise span
43
+ // to the next `>` and paint the intervening text with `cm-xml-element`/`cm-xml-tag`.
44
+ const tagNameOf = (tagNode: { getChild: (name: string) => { from: number; to: number } | null }) => {
45
+ const tagNameNode = tagNode.getChild('TagName');
46
+ if (!tagNameNode) {
47
+ return undefined;
48
+ }
49
+ const value = tagNameAt(tagNameNode);
50
+ return VALID_TAG_NAME.test(value) ? value : undefined;
51
+ };
50
52
 
51
53
  const tree = xmlLanguage.parser.parse(text);
52
54
  const ranges: Range<Decoration>[] = [];
@@ -54,33 +56,36 @@ export const xmlFormatting = ({ skip }: XmlFormattingOptions = {}): Extension =>
54
56
  enter: (node) => {
55
57
  const name = node.type.name;
56
58
  if (name === 'SelfClosingTag' && node.from < node.to) {
57
- if (skipSet) {
58
- const tagNameNode = node.node.getChild('TagName');
59
- if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
60
- return false;
61
- }
59
+ const tagName = tagNameOf(node.node);
60
+ if (!tagName) {
61
+ return;
62
+ }
63
+ if (skipSet?.has(tagName)) {
64
+ return false;
62
65
  }
63
66
  // Self-closing tag is its own outer block and tag delimiter.
64
67
  ranges.push(xmlElementMark.range(node.from, node.to));
65
68
  ranges.push(xmlTagMark.range(node.from, node.to));
66
69
  return;
67
70
  }
68
- if (XML_TAG_NODES.has(name) && node.from < node.to) {
69
- ranges.push(xmlTagMark.range(node.from, node.to));
70
- return;
71
- }
72
71
  if (name === 'Element' && node.from < node.to) {
73
72
  const openTag = node.node.getChild('OpenTag');
74
- if (openTag && skipSet) {
75
- const tagNameNode = openTag.getChild('TagName');
76
- if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
77
- // Skip this element AND its descendants — another extension owns rendering.
78
- return false;
79
- }
73
+ // Require a real matching `CloseTag` (not `MismatchedCloseTag`) with a valid name: an
74
+ // unterminated or malformed element is left undecorated. Keep descending so any
75
+ // well-formed nested elements still get styled.
76
+ const closeTag = node.node.getChild('CloseTag');
77
+ const tagName = openTag ? tagNameOf(openTag) : undefined;
78
+ if (!openTag || !closeTag || !tagName) {
79
+ return;
80
+ }
81
+ if (skipSet?.has(tagName)) {
82
+ // Skip this element AND its descendants — another extension owns rendering.
83
+ return false;
80
84
  }
81
- const closeTag = node.node.getChild('CloseTag') ?? node.node.getChild('MismatchedCloseTag');
82
85
  ranges.push(xmlElementMark.range(node.from, node.to));
83
- if (openTag && closeTag && openTag.to < closeTag.from) {
86
+ ranges.push(xmlTagMark.range(openTag.from, openTag.to));
87
+ ranges.push(xmlTagMark.range(closeTag.from, closeTag.to));
88
+ if (openTag.to < closeTag.from) {
84
89
  ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
85
90
  }
86
91
  }
@@ -109,17 +114,28 @@ export const xmlFormatting = ({ skip }: XmlFormattingOptions = {}): Extension =>
109
114
  },
110
115
  ),
111
116
 
112
- EditorView.baseTheme({
113
- '.cm-xml-element': {
114
- backgroundColor: 'var(--color-current-surface)',
115
- borderRadius: '0.25rem',
116
- padding: '0.25rem',
117
- },
118
- '.cm-xml-tag': {
119
- color: 'var(--color-blue-500)',
120
- fontFamily: 'var(--font-mono)',
121
- },
122
- '.cm-xml-content': {},
123
- }),
117
+ styles,
124
118
  ];
125
119
  };
120
+
121
+ /**
122
+ * Well-formed XML tag name (a stray `<` in prose — e.g. `a < b` — has none, so it stays undecorated).
123
+ */
124
+ const VALID_TAG_NAME = /^[A-Za-z_][\w.-]*$/;
125
+
126
+ const xmlElementMark = Decoration.mark({ class: 'cm-xml-element' });
127
+ const xmlTagMark = Decoration.mark({ class: 'cm-xml-tag' });
128
+ const xmlContentMark = Decoration.mark({ class: 'cm-xml-content' });
129
+
130
+ const styles = EditorView.baseTheme({
131
+ '.cm-xml-element': {
132
+ backgroundColor: 'var(--color-current-surface)',
133
+ borderRadius: '0.25rem',
134
+ padding: '0.25rem',
135
+ },
136
+ '.cm-xml-tag': {
137
+ color: 'var(--color-blue-500)',
138
+ fontFamily: 'var(--font-mono)',
139
+ },
140
+ '.cm-xml-content': {},
141
+ });