@dxos/react-ui-editor 0.8.4-main.3f58842 → 0.8.4-main.406dc2a

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 (310) 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 +4239 -3098
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +71 -1
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/browser/types/index.mjs +1 -1
  9. package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
  10. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
  11. package/dist/lib/node-esm/index.mjs +4239 -3098
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +71 -1
  15. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  16. package/dist/lib/node-esm/types/index.mjs +1 -1
  17. package/dist/types/src/components/Editor/Editor.d.ts +24 -9
  18. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  19. package/dist/types/src/components/Editor/Editor.stories.d.ts +30 -0
  20. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
  21. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  27. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/util.d.ts +3 -3
  29. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
  31. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  32. package/dist/types/src/components/index.d.ts +0 -1
  33. package/dist/types/src/components/index.d.ts.map +1 -1
  34. package/dist/types/src/defaults.d.ts.map +1 -1
  35. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts +26 -0
  36. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
  37. package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
  38. package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
  39. package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
  40. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
  41. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
  42. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
  43. package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
  44. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
  45. package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
  46. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  47. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +10 -19
  48. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  49. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  50. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  51. package/dist/types/src/extensions/automerge/sync.d.ts +2 -2
  52. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  53. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  54. package/dist/types/src/extensions/autoscroll.d.ts +10 -0
  55. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
  56. package/dist/types/src/extensions/comments.d.ts +1 -1
  57. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  58. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  59. package/dist/types/src/extensions/factories.d.ts +8 -8
  60. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  61. package/dist/types/src/extensions/focus.d.ts.map +1 -1
  62. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  63. package/dist/types/src/extensions/index.d.ts +4 -1
  64. package/dist/types/src/extensions/index.d.ts.map +1 -1
  65. package/dist/types/src/extensions/json.d.ts +1 -1
  66. package/dist/types/src/extensions/json.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
  69. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
  71. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  72. package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
  73. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  74. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  75. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  76. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
  77. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  78. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  79. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  80. package/dist/types/src/extensions/modes.d.ts +1 -1
  81. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  82. package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
  83. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
  84. package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
  85. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  86. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  87. package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
  88. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  89. package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts +36 -0
  90. package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts.map +1 -0
  91. package/dist/types/src/extensions/popover/index.d.ts +8 -0
  92. package/dist/types/src/extensions/popover/index.d.ts.map +1 -0
  93. package/dist/types/src/extensions/popover/menu-presets.d.ts +4 -0
  94. package/dist/types/src/extensions/popover/menu-presets.d.ts.map +1 -0
  95. package/dist/types/src/extensions/popover/menu.d.ts +24 -0
  96. package/dist/types/src/extensions/popover/menu.d.ts.map +1 -0
  97. package/dist/types/src/extensions/popover/modal.d.ts +7 -0
  98. package/dist/types/src/extensions/popover/modal.d.ts.map +1 -0
  99. package/dist/types/src/extensions/popover/popover.d.ts +47 -0
  100. package/dist/types/src/extensions/popover/popover.d.ts.map +1 -0
  101. package/dist/types/src/extensions/popover/usePopoverMenu.d.ts +34 -0
  102. package/dist/types/src/extensions/popover/usePopoverMenu.d.ts.map +1 -0
  103. package/dist/types/src/extensions/popover/util.d.ts +8 -0
  104. package/dist/types/src/extensions/popover/util.d.ts.map +1 -0
  105. package/dist/types/src/extensions/preview/preview.d.ts +2 -6
  106. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  107. package/dist/types/src/extensions/state.d.ts +2 -0
  108. package/dist/types/src/extensions/state.d.ts.map +1 -0
  109. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  110. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  111. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  112. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  113. package/dist/types/src/extensions/tags/index.d.ts +4 -0
  114. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  115. package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
  116. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
  117. package/dist/types/src/extensions/tags/xml-tags.d.ts +72 -0
  118. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  119. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  120. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  121. package/dist/types/src/hooks/useTextEditor.d.ts +4 -8
  122. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  123. package/dist/types/src/stories/CommandDialog.stories.d.ts +14 -0
  124. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
  125. package/dist/types/src/stories/Comments.stories.d.ts +21 -10
  126. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  127. package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -3
  128. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  129. package/dist/types/src/stories/Experimental.stories.d.ts +22 -13
  130. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  131. package/dist/types/src/stories/Markdown.stories.d.ts +32 -43
  132. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  133. package/dist/types/src/stories/Outliner.stories.d.ts +15 -21
  134. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  135. package/dist/types/src/stories/Popover.stories.d.ts +20 -0
  136. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
  137. package/dist/types/src/stories/Preview.stories.d.ts +21 -7
  138. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  139. package/dist/types/src/stories/Tags.stories.d.ts +16 -0
  140. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
  141. package/dist/types/src/stories/TextEditor.stories.d.ts +37 -52
  142. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  143. package/dist/types/src/stories/components/EditorStory.d.ts +6 -9
  144. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  145. package/dist/types/src/styles/theme.d.ts.map +1 -1
  146. package/dist/types/src/testing/PreviewPopover.d.ts +20 -0
  147. package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -0
  148. package/dist/types/src/testing/index.d.ts +1 -0
  149. package/dist/types/src/testing/index.d.ts.map +1 -1
  150. package/dist/types/src/testing/util.d.ts +1 -0
  151. package/dist/types/src/testing/util.d.ts.map +1 -1
  152. package/dist/types/src/translations.d.ts +1 -1
  153. package/dist/types/src/types/types.d.ts +2 -2
  154. package/dist/types/src/types/types.d.ts.map +1 -1
  155. package/dist/types/src/util/cursor.d.ts.map +1 -1
  156. package/dist/types/src/util/debug.d.ts +1 -1
  157. package/dist/types/src/util/debug.d.ts.map +1 -1
  158. package/dist/types/src/util/decorations.d.ts +4 -0
  159. package/dist/types/src/util/decorations.d.ts.map +1 -0
  160. package/dist/types/src/util/dom.d.ts +2 -12
  161. package/dist/types/src/util/dom.d.ts.map +1 -1
  162. package/dist/types/src/util/index.d.ts +1 -0
  163. package/dist/types/src/util/index.d.ts.map +1 -1
  164. package/dist/types/src/util/react.d.ts +1 -1
  165. package/dist/types/src/util/react.d.ts.map +1 -1
  166. package/dist/types/tsconfig.tsbuildinfo +1 -1
  167. package/package.json +69 -61
  168. package/src/components/Editor/Editor.stories.tsx +72 -0
  169. package/src/components/Editor/Editor.tsx +58 -15
  170. package/src/components/EditorToolbar/EditorToolbar.tsx +41 -30
  171. package/src/components/EditorToolbar/blocks.ts +21 -24
  172. package/src/components/EditorToolbar/formatting.ts +22 -25
  173. package/src/components/EditorToolbar/headings.ts +10 -5
  174. package/src/components/EditorToolbar/image.ts +8 -4
  175. package/src/components/EditorToolbar/lists.ts +16 -19
  176. package/src/components/EditorToolbar/search.ts +8 -4
  177. package/src/components/EditorToolbar/util.ts +16 -5
  178. package/src/components/EditorToolbar/view-mode.ts +11 -6
  179. package/src/components/index.ts +0 -1
  180. package/src/defaults.ts +5 -2
  181. package/src/extensions/autocomplete/autocomplete.ts +220 -0
  182. package/src/extensions/autocomplete/index.ts +8 -0
  183. package/src/extensions/autocomplete/match.ts +46 -0
  184. package/src/extensions/{command → autocomplete}/placeholder.ts +22 -18
  185. package/src/extensions/{command → autocomplete}/typeahead.ts +8 -50
  186. package/src/extensions/automerge/automerge.stories.tsx +31 -24
  187. package/src/extensions/automerge/automerge.ts +31 -11
  188. package/src/extensions/automerge/defs.ts +1 -1
  189. package/src/extensions/automerge/sync.ts +8 -4
  190. package/src/extensions/automerge/update-automerge.ts +1 -1
  191. package/src/extensions/autoscroll.ts +157 -0
  192. package/src/extensions/awareness/awareness.ts +2 -2
  193. package/src/extensions/comments.ts +18 -13
  194. package/src/extensions/dnd.ts +1 -1
  195. package/src/extensions/factories.ts +48 -31
  196. package/src/extensions/focus.ts +5 -4
  197. package/src/extensions/folding.tsx +4 -6
  198. package/src/extensions/hashtag.tsx +2 -2
  199. package/src/extensions/index.ts +4 -1
  200. package/src/extensions/json.ts +1 -1
  201. package/src/extensions/markdown/action.ts +2 -1
  202. package/src/extensions/markdown/bundle.ts +29 -5
  203. package/src/extensions/markdown/changes.ts +1 -1
  204. package/src/extensions/markdown/decorate.ts +24 -14
  205. package/src/extensions/markdown/formatting.test.ts +6 -6
  206. package/src/extensions/markdown/formatting.ts +3 -3
  207. package/src/extensions/markdown/highlight.ts +1 -1
  208. package/src/extensions/markdown/image.ts +3 -4
  209. package/src/extensions/markdown/link.ts +3 -0
  210. package/src/extensions/markdown/table.ts +7 -1
  211. package/src/extensions/mention.ts +1 -1
  212. package/src/extensions/modes.ts +2 -2
  213. package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +16 -21
  214. package/src/extensions/outliner/outliner.test.ts +3 -2
  215. package/src/extensions/outliner/outliner.ts +7 -6
  216. package/src/extensions/outliner/selection.ts +1 -1
  217. package/src/extensions/outliner/tree.test.ts +2 -1
  218. package/src/extensions/outliner/tree.ts +2 -2
  219. package/src/extensions/popover/PopoverMenuProvider.tsx +221 -0
  220. package/src/extensions/popover/index.ts +12 -0
  221. package/src/extensions/popover/menu-presets.ts +124 -0
  222. package/src/extensions/popover/menu.ts +67 -0
  223. package/src/extensions/popover/modal.ts +24 -0
  224. package/src/extensions/popover/popover.ts +293 -0
  225. package/src/extensions/popover/usePopoverMenu.ts +173 -0
  226. package/src/extensions/popover/util.ts +29 -0
  227. package/src/extensions/preview/index.ts +1 -1
  228. package/src/extensions/preview/preview.ts +57 -62
  229. package/src/extensions/selection.ts +2 -2
  230. package/src/extensions/state.ts +7 -0
  231. package/src/extensions/tags/extended-markdown.test.ts +261 -0
  232. package/src/extensions/tags/extended-markdown.ts +78 -0
  233. package/src/extensions/tags/index.ts +7 -0
  234. package/src/extensions/tags/streamer.ts +243 -0
  235. package/src/extensions/tags/xml-tags.ts +393 -0
  236. package/src/extensions/tags/xml-util.ts +94 -0
  237. package/src/hooks/useTextEditor.ts +27 -39
  238. package/src/stories/CommandDialog.stories.tsx +78 -0
  239. package/src/stories/Comments.stories.tsx +14 -10
  240. package/src/stories/EditorToolbar.stories.tsx +13 -12
  241. package/src/stories/Experimental.stories.tsx +17 -13
  242. package/src/stories/Markdown.stories.tsx +25 -21
  243. package/src/stories/Outliner.stories.tsx +54 -35
  244. package/src/stories/Popover.stories.tsx +163 -0
  245. package/src/stories/Preview.stories.tsx +34 -33
  246. package/src/stories/Tags.stories.tsx +81 -0
  247. package/src/stories/TextEditor.stories.tsx +39 -58
  248. package/src/stories/components/EditorStory.tsx +16 -15
  249. package/src/styles/theme.ts +16 -12
  250. package/src/testing/PreviewPopover.tsx +80 -0
  251. package/src/testing/index.ts +1 -0
  252. package/src/testing/util.ts +2 -0
  253. package/src/translations.ts +1 -1
  254. package/src/types/types.ts +1 -1
  255. package/src/util/cursor.ts +2 -1
  256. package/src/util/debug.ts +2 -2
  257. package/src/util/decorations.ts +21 -0
  258. package/src/util/dom.ts +5 -27
  259. package/src/util/index.ts +1 -0
  260. package/src/util/react.tsx +1 -1
  261. package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
  262. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
  263. package/dist/types/src/components/Popover/CommandMenu.d.ts +0 -34
  264. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
  265. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
  266. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
  267. package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
  268. package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
  269. package/dist/types/src/components/Popover/index.d.ts +0 -4
  270. package/dist/types/src/components/Popover/index.d.ts.map +0 -1
  271. package/dist/types/src/extensions/autocomplete.d.ts +0 -13
  272. package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
  273. package/dist/types/src/extensions/command/action.d.ts +0 -17
  274. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  275. package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
  276. package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
  277. package/dist/types/src/extensions/command/command.d.ts +0 -6
  278. package/dist/types/src/extensions/command/command.d.ts.map +0 -1
  279. package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
  280. package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
  281. package/dist/types/src/extensions/command/hint.d.ts +0 -24
  282. package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
  283. package/dist/types/src/extensions/command/index.d.ts +0 -7
  284. package/dist/types/src/extensions/command/index.d.ts.map +0 -1
  285. package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
  286. package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
  287. package/dist/types/src/extensions/command/state.d.ts +0 -16
  288. package/dist/types/src/extensions/command/state.d.ts.map +0 -1
  289. package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
  290. package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
  291. package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
  292. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
  293. package/dist/types/src/stories/Command.stories.d.ts +0 -7
  294. package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
  295. package/dist/types/src/stories/CommandMenu.stories.d.ts +0 -13
  296. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
  297. package/src/components/Popover/CommandMenu.tsx +0 -279
  298. package/src/components/Popover/RefDropdownMenu.tsx +0 -85
  299. package/src/components/Popover/RefPopover.tsx +0 -99
  300. package/src/components/Popover/index.ts +0 -7
  301. package/src/extensions/autocomplete.ts +0 -69
  302. package/src/extensions/command/action.ts +0 -56
  303. package/src/extensions/command/command-menu.ts +0 -210
  304. package/src/extensions/command/command.ts +0 -34
  305. package/src/extensions/command/hint.ts +0 -102
  306. package/src/extensions/command/index.ts +0 -10
  307. package/src/extensions/command/state.ts +0 -89
  308. package/src/extensions/command/useCommandMenu.ts +0 -118
  309. package/src/stories/Command.stories.tsx +0 -97
  310. package/src/stories/CommandMenu.stories.tsx +0 -159
@@ -0,0 +1,293 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Extension, Prec, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
6
+ import {
7
+ Decoration,
8
+ type DecorationSet,
9
+ EditorView,
10
+ type KeyBinding,
11
+ ViewPlugin,
12
+ type ViewUpdate,
13
+ keymap,
14
+ } from '@codemirror/view';
15
+
16
+ import { isNonNullable, isTruthy } from '@dxos/util';
17
+
18
+ import { type Range } from '../../types';
19
+ import { type PlaceholderOptions, placeholder } from '../autocomplete';
20
+
21
+ import { modalStateField } from './modal';
22
+
23
+ const DELIMITERS = [' ', ':'];
24
+
25
+ export type PopoverOptions = {
26
+ trigger?: string | string[];
27
+ triggerKey?: string;
28
+ placeholder?: Partial<PlaceholderOptions>;
29
+ delimiters?: string[];
30
+
31
+ // TODO(burdon): Auto.
32
+ // activateOnTyping?: boolean;
33
+
34
+ // Trigger update.
35
+ onTextChange?: (event: { view: EditorView; pos: number; text: string; trigger?: string }) => void;
36
+ onClose?: (event: { view: EditorView }) => void;
37
+
38
+ // Menu specific.
39
+ onEnter?: (event: { view: EditorView }) => void;
40
+ onArrowUp?: (event: { view: EditorView }) => void;
41
+ onArrowDown?: (event: { view: EditorView }) => void;
42
+ };
43
+
44
+ /**
45
+ * Creates a popover that appears when the trigger character is inserted.
46
+ * This can be used for context menus or autocompletion.
47
+ */
48
+ export const popover = (options: PopoverOptions = {}): Extension => {
49
+ return [
50
+ Prec.highest(popoverKeymap(options)),
51
+ popoverStateField,
52
+ popoverTriggerListener(options),
53
+ popoverAnchorDecoration(options),
54
+ modalStateField,
55
+ options.trigger &&
56
+ placeholder({
57
+ // TODO(burdon): Translations.
58
+ content: `Press '${Array.isArray(options.trigger) ? options.trigger[0] : options.trigger}' for commands`,
59
+ ...options.placeholder,
60
+ }),
61
+ ].filter(isTruthy);
62
+ };
63
+
64
+ /**
65
+ * Listen for selection and document changes.
66
+ */
67
+ const popoverTriggerListener = (options: PopoverOptions) =>
68
+ EditorView.updateListener.of(({ view, docChanged }) => {
69
+ const { range: activeRange, trigger } = view.state.field(popoverStateField) ?? {};
70
+ if (!activeRange) {
71
+ return;
72
+ }
73
+
74
+ const text = view.state.doc.sliceString(activeRange.from, activeRange.to);
75
+ const selection = view.state.selection.main;
76
+ const shouldClose =
77
+ // Trigger deleted.
78
+ (trigger ? trigger !== text[0] : false) ||
79
+ // Whitespace in text.
80
+ /\s/.test(trigger ? text.slice(1) : text) ||
81
+ // Cursor moved before the range.
82
+ selection.head < activeRange.from ||
83
+ // Cursor moved after the range (+1 to handle selection changing before doc).
84
+ selection.head > activeRange.to + 1;
85
+
86
+ const nextRange = shouldClose ? null : docChanged ? { from: activeRange.from, to: selection.head } : activeRange;
87
+ if (nextRange !== activeRange) {
88
+ view.dispatch({
89
+ effects: popoverRangeEffect.of(nextRange ? { range: nextRange, trigger } : null),
90
+ });
91
+ }
92
+
93
+ if (shouldClose) {
94
+ options.onClose?.({ view });
95
+ }
96
+ });
97
+
98
+ /**
99
+ * Popover navigation.
100
+ */
101
+ const popoverKeymap = (options: PopoverOptions) => {
102
+ const triggers = Array.isArray(options.trigger) ? options.trigger : [options.trigger];
103
+ return keymap.of(
104
+ [
105
+ // Prefix triggers.
106
+ ...triggers.filter(isNonNullable).map((trigger) => ({
107
+ key: trigger,
108
+ run: (view: EditorView) => {
109
+ // Determine if we should trigger the popover:
110
+ // 1. Empty lines or at the beginning of a line
111
+ // 2. When there's a preceding space
112
+ const selection = view.state.selection.main;
113
+ const line = view.state.doc.lineAt(selection.head);
114
+ if (
115
+ line.text.trim() === '' ||
116
+ selection.head === line.from ||
117
+ (selection.head > line.from && line.text[selection.head - line.from - 1] === ' ')
118
+ ) {
119
+ // Insert and select the trigger.
120
+ view.dispatch({
121
+ changes: { from: selection.head, insert: trigger },
122
+ selection: { anchor: selection.head + 1, head: selection.head + 1 },
123
+ effects: popoverRangeEffect.of({ trigger, range: { from: selection.head, to: selection.head + 1 } }),
124
+ });
125
+
126
+ return true;
127
+ }
128
+
129
+ return false;
130
+ },
131
+ })),
132
+
133
+ //
134
+ // Custom trigger.
135
+ //
136
+ options.triggerKey &&
137
+ ({
138
+ key: options.triggerKey,
139
+ run: (view: EditorView) => {
140
+ const selection = view.state.selection.main;
141
+ const line = view.state.doc.lineAt(selection.head);
142
+
143
+ // Get last word.
144
+ let str = line.text.slice(0, selection.head - line.from);
145
+ const idx = getLastIndexOf(str, options.delimiters ?? DELIMITERS);
146
+ if (idx !== -1) {
147
+ str = str.slice(idx + 1);
148
+ }
149
+
150
+ // Create anchor even if zero length (append space).
151
+ const from = line.from + idx;
152
+ console.log('effect', from + 1, selection.head);
153
+ view.dispatch({
154
+ effects: popoverRangeEffect.of({ range: { from: from + 1, to: selection.head } }),
155
+ changes:
156
+ selection.head === view.state.doc.length
157
+ ? { from: from + 1, to: selection.head, insert: ' ' }
158
+ : undefined,
159
+ });
160
+ return true;
161
+ },
162
+ } satisfies KeyBinding),
163
+
164
+ //
165
+ // Nav keys.
166
+ //
167
+ {
168
+ key: 'ArrowUp',
169
+ run: (view: EditorView) => {
170
+ const range = view.state.field(popoverStateField)?.range;
171
+ if (range) {
172
+ options.onArrowUp?.({ view });
173
+ return true;
174
+ }
175
+
176
+ return false;
177
+ },
178
+ },
179
+ {
180
+ key: 'ArrowDown',
181
+ run: (view: EditorView) => {
182
+ const range = view.state.field(popoverStateField)?.range;
183
+ if (range) {
184
+ options.onArrowDown?.({ view });
185
+ return true;
186
+ }
187
+
188
+ return false;
189
+ },
190
+ },
191
+ {
192
+ key: 'Enter',
193
+ run: (view: EditorView) => {
194
+ const range = view.state.field(popoverStateField)?.range;
195
+ if (range) {
196
+ view.dispatch({ changes: { from: range.from, to: range.to, insert: '' } });
197
+ options.onEnter?.({ view });
198
+ return true;
199
+ }
200
+
201
+ return false;
202
+ },
203
+ },
204
+ ].filter(isTruthy),
205
+ );
206
+ };
207
+
208
+ /**
209
+ * Creates a <dx-anchor> tag, which is used to anchor the Popver.
210
+ */
211
+ const popoverAnchorDecoration = (options: PopoverOptions) => {
212
+ return ViewPlugin.fromClass(
213
+ class {
214
+ _decorations: DecorationSet = Decoration.none;
215
+
216
+ constructor(readonly view: EditorView) {}
217
+
218
+ // TODO(wittjosiah): The decorations are repainted on every update, this occasionally causes menu to flicker.
219
+ update({ view, transactions }: ViewUpdate) {
220
+ const builder = new RangeSetBuilder<Decoration>();
221
+ const { range, trigger } = view.state.field(popoverStateField) ?? {};
222
+ if (range) {
223
+ // Check if we should show the widget (only if cursor is within the active command range).
224
+ const selection = view.state.selection.main;
225
+ const showWidget = selection.head >= range.from && selection.head <= range.to;
226
+ console.log('update', showWidget, range.from, range.to + 1);
227
+ if (showWidget) {
228
+ builder.add(
229
+ range.from,
230
+ range.to + 1,
231
+ Decoration.mark({
232
+ tagName: 'dx-anchor',
233
+ class: 'cm-popover-trigger',
234
+ attributes: {
235
+ 'data-visible-focus': 'false',
236
+ 'data-auto-trigger': 'true',
237
+ 'data-trigger': trigger ?? options.triggerKey ?? '',
238
+ },
239
+ }),
240
+ );
241
+ }
242
+
243
+ const rangeChanged = transactions.some((tr) => tr.effects.some((effect) => effect.is(popoverRangeEffect)));
244
+ if (rangeChanged) {
245
+ // NOTE: Content skips the trigger character.
246
+ const content = view.state.sliceDoc(range.from + (trigger ? trigger.length : 0), range.to);
247
+ options.onTextChange?.({ view, pos: selection.head, text: content, trigger });
248
+ }
249
+ } else {
250
+ console.log('remove');
251
+ }
252
+
253
+ this._decorations = builder.finish();
254
+ }
255
+ },
256
+ {
257
+ decorations: (v) => v._decorations,
258
+ },
259
+ );
260
+ };
261
+
262
+ type PopoverState = {
263
+ /**
264
+ * Trigger prefix (in document).
265
+ */
266
+ trigger?: string;
267
+
268
+ /**
269
+ * Current document completion range.
270
+ */
271
+ range: Range;
272
+ };
273
+
274
+ // State effects for managing popover state.
275
+ export const popoverRangeEffect = StateEffect.define<PopoverState | null>();
276
+
277
+ // State field to track the active popover trigger range.
278
+ export const popoverStateField = StateField.define<PopoverState | null>({
279
+ create: () => null,
280
+ update: (value, tr) => {
281
+ let newValue = value;
282
+ for (const effect of tr.effects) {
283
+ if (effect.is(popoverRangeEffect)) {
284
+ newValue = effect.value;
285
+ }
286
+ }
287
+
288
+ return newValue;
289
+ },
290
+ });
291
+
292
+ const getLastIndexOf = (str: string, delimiters: string[]) =>
293
+ Math.max(...delimiters.map((delim) => str.lastIndexOf(delim)));
@@ -0,0 +1,173 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Extension } from '@codemirror/state';
6
+ import { type EditorState } from '@codemirror/state';
7
+ import { type RefObject, useCallback, useMemo, useRef, useState } from 'react';
8
+
9
+ import { invariant } from '@dxos/invariant';
10
+ import { type MaybePromise } from '@dxos/util';
11
+
12
+ import { type PopoverMenuGroup, type PopoverMenuItem } from './menu';
13
+ import { filterMenuGroups, getMenuItem, getNextMenuItem, getPreviousMenuItem } from './menu';
14
+ import { modalStateEffect } from './modal';
15
+ import { type PopoverOptions, popover, popoverRangeEffect, popoverStateField } from './popover';
16
+ import { type PopoverMenuProviderProps } from './PopoverMenuProvider';
17
+
18
+ export type GetMenuContext = {
19
+ state: EditorState;
20
+ pos: number;
21
+ text: string;
22
+ trigger?: string;
23
+ };
24
+
25
+ export type UsePopoverMenuProps = {
26
+ filter?: boolean;
27
+ getMenu?: (context: GetMenuContext) => MaybePromise<PopoverMenuGroup[]>;
28
+ } & Pick<PopoverOptions, 'trigger' | 'triggerKey' | 'placeholder'>;
29
+
30
+ export type UsePopoverMenu = {
31
+ groupsRef: RefObject<PopoverMenuGroup[]>;
32
+ extension: Extension;
33
+ } & Pick<PopoverMenuProviderProps, 'currentItem' | 'open' | 'onOpenChange' | 'onActivate' | 'onSelect' | 'onCancel'>;
34
+
35
+ /**
36
+ * ```tsx
37
+ * const { groupsRef, extension, ...menuProps } = usePopoverMenu();
38
+ * const { parentRef, viewRef } = useTextEditor({ extensions: [extension] });
39
+ * return (
40
+ * <PopoverMenuProvider view={viewRef.current} groups={groupsRef.current} {...menuProps}>
41
+ * <div ref={parentRef} />
42
+ * </PopoverMenuProvider>
43
+ * );
44
+ * ```
45
+ */
46
+ export const usePopoverMenu = ({
47
+ trigger,
48
+ triggerKey,
49
+ placeholder,
50
+ filter = true,
51
+ getMenu,
52
+ }: UsePopoverMenuProps): UsePopoverMenu => {
53
+ const groupsRef = useRef<PopoverMenuGroup[]>([]);
54
+ const currentRef = useRef<PopoverMenuItem | null>(null);
55
+ const [currentItem, setCurrentItem] = useState<string>();
56
+ const [open, setOpen] = useState(false);
57
+ const [_, refresh] = useState({});
58
+
59
+ /**
60
+ * Get filtered options.
61
+ */
62
+ const getMenuOptions = useCallback<NonNullable<UsePopoverMenuProps['getMenu']>>(
63
+ async ({ text, trigger, ...props }) => {
64
+ const groups = (await getMenu?.({ text, trigger, ...props })) ?? [];
65
+ return filter
66
+ ? filterMenuGroups(groups, (item) =>
67
+ text ? (item.label as string).toLowerCase().startsWith(text.toLowerCase()) : true,
68
+ )
69
+ : groups;
70
+ },
71
+ [getMenu, filter],
72
+ );
73
+
74
+ const handleOpenChange = useCallback<NonNullable<UsePopoverMenu['onOpenChange']>>(
75
+ async ({ view, open }) => {
76
+ invariant(view);
77
+ setOpen(open);
78
+ if (!open) {
79
+ setCurrentItem(undefined);
80
+ view.dispatch({
81
+ effects: [popoverRangeEffect.of(null)],
82
+ });
83
+ }
84
+
85
+ // TODO(burdon): Possible race condition.
86
+ // useTextEditor.handleKeyDown will get called after this handler completes.
87
+ requestAnimationFrame(() => {
88
+ view.dispatch({
89
+ effects: [modalStateEffect.of(open)],
90
+ });
91
+ });
92
+ },
93
+ [getMenuOptions],
94
+ );
95
+
96
+ const handleActivate = useCallback<NonNullable<UsePopoverMenu['onActivate']>>(
97
+ async ({ view, trigger }) => {
98
+ const item = getMenuItem(groupsRef.current, currentItem);
99
+ if (item) {
100
+ currentRef.current = item;
101
+ }
102
+
103
+ if (!open) {
104
+ handleOpenChange({ view, open: true, trigger });
105
+ }
106
+ },
107
+ [open, handleOpenChange],
108
+ );
109
+
110
+ const handleSelect = useCallback<NonNullable<UsePopoverMenu['onSelect']>>(({ view, item }) => {
111
+ void item.onSelect?.(view, view.state.selection.main.head);
112
+ }, []);
113
+
114
+ const handleCancel = useCallback<NonNullable<UsePopoverMenu['onCancel']>>(({ view }) => {
115
+ // Delete trigger.
116
+ const { range, trigger } = view.state.field(popoverStateField) ?? {};
117
+ if (range && trigger) {
118
+ view.dispatch({
119
+ changes: { ...range, insert: '' },
120
+ });
121
+ }
122
+ }, []);
123
+
124
+ const serializedTrigger = Array.isArray(trigger) ? trigger.join(',') : trigger;
125
+ const extension = useMemo<Extension>(() => {
126
+ return popover({
127
+ trigger,
128
+ triggerKey,
129
+ placeholder,
130
+ onClose: ({ view }) => handleOpenChange({ view, open: false }),
131
+ onEnter: ({ view }) => {
132
+ if (currentRef.current) {
133
+ handleSelect({ view, item: currentRef.current });
134
+ }
135
+ },
136
+ onArrowUp: () => {
137
+ setCurrentItem((currentItem) => {
138
+ const previous = getPreviousMenuItem(groupsRef.current, currentItem);
139
+ currentRef.current = previous;
140
+ return previous.id;
141
+ });
142
+ },
143
+ onArrowDown: () => {
144
+ setCurrentItem((currentItem) => {
145
+ const next = getNextMenuItem(groupsRef.current, currentItem);
146
+ currentRef.current = next;
147
+ return next.id;
148
+ });
149
+ },
150
+ onTextChange: async ({ view, pos, text, trigger }) => {
151
+ groupsRef.current = (await getMenuOptions({ state: view.state, pos, text, trigger })) ?? [];
152
+ const firstItem = groupsRef.current.filter((group) => group.items.length > 0)[0]?.items[0];
153
+ if (firstItem) {
154
+ setCurrentItem(firstItem.id);
155
+ currentRef.current = firstItem;
156
+ }
157
+
158
+ refresh({});
159
+ },
160
+ });
161
+ }, [handleOpenChange, getMenuOptions, serializedTrigger, placeholder]);
162
+
163
+ return {
164
+ groupsRef,
165
+ extension,
166
+ currentItem,
167
+ open,
168
+ onOpenChange: handleOpenChange,
169
+ onActivate: handleActivate,
170
+ onSelect: handleSelect,
171
+ onCancel: handleCancel,
172
+ };
173
+ };
@@ -0,0 +1,29 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+
7
+ export const insertAtCursor = (view: EditorView, from: number, insert: string) => {
8
+ view.dispatch({
9
+ changes: { from, to: from, insert },
10
+ selection: { anchor: from + insert.length, head: from + insert.length },
11
+ });
12
+ };
13
+
14
+ /**
15
+ * If the cursor is at the start of a line, insert the text at the cursor.
16
+ * Otherwise, insert the text on a new line.
17
+ */
18
+ export const insertAtLineStart = (view: EditorView, from: number, insert: string) => {
19
+ const line = view.state.doc.lineAt(from);
20
+ if (line.from === from) {
21
+ insertAtCursor(view, from, insert);
22
+ } else {
23
+ insert = '\n' + insert;
24
+ view.dispatch({
25
+ changes: { from: line.to, to: line.to, insert },
26
+ selection: { anchor: line.to + insert.length, head: line.to + insert.length },
27
+ });
28
+ }
29
+ };
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright 2024 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
5
  export * from './preview';
@@ -2,17 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos/lit-ui/dx-ref-tag.pcss';
6
-
7
5
  import { syntaxTree } from '@codemirror/language';
8
- import {
9
- type EditorState,
10
- type Extension,
11
- type RangeSet,
12
- RangeSetBuilder,
13
- StateField,
14
- type Transaction,
15
- } from '@codemirror/state';
6
+ import { type EditorState, type Extension, RangeSetBuilder, StateField } from '@codemirror/state';
16
7
  import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
17
8
  import { type SyntaxNode } from '@lezer/common';
18
9
 
@@ -29,10 +20,6 @@ export type PreviewLinkTarget = {
29
20
  object?: any;
30
21
  };
31
22
 
32
- // TODO(wittjosiah): Remove.
33
- // TODO(burdon): Handle error.
34
- export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
35
-
36
23
  export type PreviewOptions = {
37
24
  addBlockContainer?: (link: PreviewLinkRef, el: HTMLElement) => void;
38
25
  removeBlockContainer?: (link: PreviewLinkRef) => void;
@@ -44,10 +31,16 @@ export type PreviewOptions = {
44
31
  export const preview = (options: PreviewOptions = {}): Extension => {
45
32
  return [
46
33
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
47
- // "Block decorations may not be specified via plugins"
34
+ // "Block decorations may not be specified via plugins".
48
35
  StateField.define<DecorationSet>({
49
36
  create: (state) => buildDecorations(state, options),
50
- update: (_: RangeSet<Decoration>, tr: Transaction) => buildDecorations(tr.state, options),
37
+ update: (decorations, tr) => {
38
+ if (tr.docChanged) {
39
+ return buildDecorations(tr.state, options);
40
+ }
41
+
42
+ return decorations.map(tr.changes);
43
+ },
51
44
  provide: (field) => [
52
45
  EditorView.decorations.from(field),
53
46
  EditorView.atomicRanges.of((view) => view.state.field(field)),
@@ -56,42 +49,19 @@ export const preview = (options: PreviewOptions = {}): Extension => {
56
49
  ];
57
50
  };
58
51
 
59
- /**
60
- * Link references.
61
- *
62
- * [Label][dxn:echo:123] Inline reference
63
- * ![Label][dxn:echo:123] Block reference
64
- * ![Label][?dxn:echo:123] Suggestion
65
- */
66
- export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
67
- const mark = node.getChild('LinkMark');
68
- const label = node.getChild('LinkLabel');
69
- if (mark && label) {
70
- const ref = state.sliceDoc(label.from + 1, label.to - 1);
71
- return {
72
- suggest: ref.startsWith('?'),
73
- block: state.sliceDoc(mark.from, mark.from + 1) === '!',
74
- label: state.sliceDoc(mark.to, label.from - 1),
75
- ref: ref.startsWith('?') ? ref.slice(1) : ref,
76
- };
77
- }
78
- };
79
-
80
52
  /**
81
53
  * Echo references are represented as markdown reference links.
82
54
  * https://www.markdownguide.org/basic-syntax/#reference-style-links
83
- * [Label|block][dxn:echo:123]
84
- * [Label|inline][dxn:echo:123]
85
55
  */
86
- const buildDecorations = (state: EditorState, options: PreviewOptions) => {
56
+ const buildDecorations = (state: EditorState, options: PreviewOptions): DecorationSet => {
87
57
  const builder = new RangeSetBuilder<Decoration>();
88
58
 
89
59
  syntaxTree(state).iterate({
90
60
  enter: (node) => {
91
61
  switch (node.name) {
92
62
  //
93
- // Decoration.
94
- // [Label][dxn:echo:123]
63
+ // Inline widget.
64
+ // [Label](dxn:echo:123)
95
65
  //
96
66
  case 'Link': {
97
67
  const link = getLinkRef(state, node.node);
@@ -101,29 +71,32 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
101
71
  node.to,
102
72
  Decoration.replace({
103
73
  widget: new PreviewInlineWidget(options, link),
74
+ side: 1,
104
75
  }),
105
76
  );
106
77
  }
107
- break;
78
+ return false;
108
79
  }
80
+
109
81
  //
110
- // Block widget.
111
- // ![Label][dxn:echo:123]
82
+ // Block widget (transclusion).
83
+ // ![Label](dxn:echo:123)
112
84
  //
113
85
  case 'Image': {
114
- const link = getLinkRef(state, node.node);
115
- if (options.addBlockContainer && options.removeBlockContainer && link) {
116
- builder.add(
117
- node.from,
118
- node.to,
119
- Decoration.replace({
120
- block: true,
121
- // atomic: true,
122
- widget: new PreviewBlockWidget(options, link),
123
- }),
124
- );
86
+ if (options.addBlockContainer && options.removeBlockContainer) {
87
+ const link = getLinkRef(state, node.node);
88
+ if (link) {
89
+ builder.add(
90
+ node.from,
91
+ node.to,
92
+ Decoration.replace({
93
+ block: true,
94
+ widget: new PreviewBlockWidget(options, link),
95
+ }),
96
+ );
97
+ }
125
98
  }
126
- break;
99
+ return false;
127
100
  }
128
101
  }
129
102
  },
@@ -132,9 +105,30 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
132
105
  return builder.finish();
133
106
  };
134
107
 
108
+ /**
109
+ * Link references.
110
+ * [Label](dxn:echo:123) Inline reference
111
+ * ![Label](dxn:echo:123) Block reference
112
+ */
113
+ export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
114
+ const mark = node.getChildren('LinkMark');
115
+ const urlNode = node.getChild('URL');
116
+ if (mark && urlNode) {
117
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
118
+ if (url.startsWith('dxn:')) {
119
+ const label = state.sliceDoc(mark[0].to, mark[1].from);
120
+ return {
121
+ block: state.sliceDoc(mark[0].from, mark[0].from + 1) === '!',
122
+ label,
123
+ ref: url,
124
+ };
125
+ }
126
+ }
127
+ };
128
+
135
129
  /**
136
130
  * Inline widget.
137
- * [Label][dxn:echo:123]
131
+ * [Label](dxn:echo:123)
138
132
  */
139
133
  class PreviewInlineWidget extends WidgetType {
140
134
  constructor(
@@ -152,8 +146,9 @@ class PreviewInlineWidget extends WidgetType {
152
146
  return this._link.ref === other._link.ref && this._link.label === other._link.label;
153
147
  }
154
148
 
155
- override toDOM(view: EditorView): HTMLElement {
156
- const root = document.createElement('dx-ref-tag');
149
+ override toDOM(_view: EditorView): HTMLElement {
150
+ const root = document.createElement('dx-anchor');
151
+ root.classList.add('dx-tag--anchor');
157
152
  root.textContent = this._link.label;
158
153
  root.setAttribute('refId', this._link.ref);
159
154
  return root;
@@ -161,7 +156,7 @@ class PreviewInlineWidget extends WidgetType {
161
156
  }
162
157
 
163
158
  /**
164
- * Block widget.
159
+ * Block widget (e.g., for surfaces).
165
160
  * ![Label][dxn:echo:123]
166
161
  */
167
162
  class PreviewBlockWidget extends WidgetType {
@@ -180,7 +175,7 @@ class PreviewBlockWidget extends WidgetType {
180
175
  return this._link.ref === other._link.ref;
181
176
  }
182
177
 
183
- override toDOM(view: EditorView): HTMLDivElement {
178
+ override toDOM(_view: EditorView): HTMLDivElement {
184
179
  const root = document.createElement('div');
185
180
  root.classList.add('cm-preview-block', 'density-coarse');
186
181
  this._options.addBlockContainer?.(this._link, root);