@dxos/ui-editor 0.0.0 → 0.8.4-main.1c7ec43d41

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 (255) hide show
  1. package/dist/lib/browser/index.mjs +8633 -0
  2. package/dist/lib/browser/index.mjs.map +7 -0
  3. package/dist/lib/browser/meta.json +1 -0
  4. package/dist/lib/browser/types/index.mjs +33 -0
  5. package/dist/lib/browser/types/index.mjs.map +7 -0
  6. package/dist/lib/node-esm/index.mjs +8635 -0
  7. package/dist/lib/node-esm/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/meta.json +1 -0
  9. package/dist/lib/node-esm/types/index.mjs +35 -0
  10. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  11. package/dist/types/src/defaults.d.ts +6 -0
  12. package/dist/types/src/defaults.d.ts.map +1 -0
  13. package/dist/types/src/extensions/annotations.d.ts +9 -0
  14. package/dist/types/src/extensions/annotations.d.ts.map +1 -0
  15. package/dist/types/src/extensions/auto-scroll.d.ts +18 -0
  16. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -0
  17. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts +17 -0
  18. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
  19. package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
  20. package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
  21. package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
  22. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
  23. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +23 -0
  24. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
  25. package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
  26. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
  27. package/dist/types/src/extensions/automerge/automerge.d.ts +4 -0
  28. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
  29. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  30. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  31. package/dist/types/src/extensions/automerge/cursor.d.ts +4 -0
  32. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
  33. package/dist/types/src/extensions/automerge/defs.d.ts +17 -0
  34. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
  35. package/dist/types/src/extensions/automerge/index.d.ts +2 -0
  36. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
  37. package/dist/types/src/extensions/automerge/sync.d.ts +17 -0
  38. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -0
  39. package/dist/types/src/extensions/automerge/update-automerge.d.ts +6 -0
  40. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
  41. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +5 -0
  42. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
  43. package/dist/types/src/extensions/awareness/awareness-provider.d.ts +31 -0
  44. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -0
  45. package/dist/types/src/extensions/awareness/awareness.d.ts +46 -0
  46. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -0
  47. package/dist/types/src/extensions/awareness/index.d.ts +3 -0
  48. package/dist/types/src/extensions/awareness/index.d.ts.map +1 -0
  49. package/dist/types/src/extensions/blast.d.ts +25 -0
  50. package/dist/types/src/extensions/blast.d.ts.map +1 -0
  51. package/dist/types/src/extensions/blocks.d.ts +2 -0
  52. package/dist/types/src/extensions/blocks.d.ts.map +1 -0
  53. package/dist/types/src/extensions/bookmarks.d.ts +12 -0
  54. package/dist/types/src/extensions/bookmarks.d.ts.map +1 -0
  55. package/dist/types/src/extensions/comments.d.ts +90 -0
  56. package/dist/types/src/extensions/comments.d.ts.map +1 -0
  57. package/dist/types/src/extensions/debug.d.ts +3 -0
  58. package/dist/types/src/extensions/debug.d.ts.map +1 -0
  59. package/dist/types/src/extensions/dnd.d.ts +9 -0
  60. package/dist/types/src/extensions/dnd.d.ts.map +1 -0
  61. package/dist/types/src/extensions/factories.d.ts +88 -0
  62. package/dist/types/src/extensions/factories.d.ts.map +1 -0
  63. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  64. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  65. package/dist/types/src/extensions/focus.d.ts +7 -0
  66. package/dist/types/src/extensions/focus.d.ts.map +1 -0
  67. package/dist/types/src/extensions/folding.d.ts +6 -0
  68. package/dist/types/src/extensions/folding.d.ts.map +1 -0
  69. package/dist/types/src/extensions/hashtag.d.ts +3 -0
  70. package/dist/types/src/extensions/hashtag.d.ts.map +1 -0
  71. package/dist/types/src/extensions/index.d.ts +32 -0
  72. package/dist/types/src/extensions/index.d.ts.map +1 -0
  73. package/dist/types/src/extensions/json.d.ts +7 -0
  74. package/dist/types/src/extensions/json.d.ts.map +1 -0
  75. package/dist/types/src/extensions/listener.d.ts +13 -0
  76. package/dist/types/src/extensions/listener.d.ts.map +1 -0
  77. package/dist/types/src/extensions/markdown/action.d.ts +12 -0
  78. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  79. package/dist/types/src/extensions/markdown/bundle.d.ts +25 -0
  80. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
  81. package/dist/types/src/extensions/markdown/changes.d.ts +10 -0
  82. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -0
  83. package/dist/types/src/extensions/markdown/changes.test.d.ts +2 -0
  84. package/dist/types/src/extensions/markdown/changes.test.d.ts.map +1 -0
  85. package/dist/types/src/extensions/markdown/debug.d.ts +11 -0
  86. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -0
  87. package/dist/types/src/extensions/markdown/decorate.d.ts +25 -0
  88. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -0
  89. package/dist/types/src/extensions/markdown/formatting.d.ts +63 -0
  90. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  91. package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
  92. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
  93. package/dist/types/src/extensions/markdown/highlight.d.ts +37 -0
  94. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
  95. package/dist/types/src/extensions/markdown/image.d.ts +7 -0
  96. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  97. package/dist/types/src/extensions/markdown/index.d.ts +10 -0
  98. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
  99. package/dist/types/src/extensions/markdown/link.d.ts +7 -0
  100. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  101. package/dist/types/src/extensions/markdown/parser.test.d.ts +2 -0
  102. package/dist/types/src/extensions/markdown/parser.test.d.ts.map +1 -0
  103. package/dist/types/src/extensions/markdown/styles.d.ts +4 -0
  104. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -0
  105. package/dist/types/src/extensions/markdown/table.d.ts +8 -0
  106. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  107. package/dist/types/src/extensions/mention.d.ts +7 -0
  108. package/dist/types/src/extensions/mention.d.ts.map +1 -0
  109. package/dist/types/src/extensions/modal.d.ts +7 -0
  110. package/dist/types/src/extensions/modal.d.ts.map +1 -0
  111. package/dist/types/src/extensions/modes.d.ts +10 -0
  112. package/dist/types/src/extensions/modes.d.ts.map +1 -0
  113. package/dist/types/src/extensions/outliner/commands.d.ts +10 -0
  114. package/dist/types/src/extensions/outliner/commands.d.ts.map +1 -0
  115. package/dist/types/src/extensions/outliner/editor.d.ts +5 -0
  116. package/dist/types/src/extensions/outliner/editor.d.ts.map +1 -0
  117. package/dist/types/src/extensions/outliner/editor.test.d.ts +2 -0
  118. package/dist/types/src/extensions/outliner/editor.test.d.ts.map +1 -0
  119. package/dist/types/src/extensions/outliner/index.d.ts +4 -0
  120. package/dist/types/src/extensions/outliner/index.d.ts.map +1 -0
  121. package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
  122. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
  123. package/dist/types/src/extensions/outliner/outliner.d.ts +11 -0
  124. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -0
  125. package/dist/types/src/extensions/outliner/outliner.test.d.ts +2 -0
  126. package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +1 -0
  127. package/dist/types/src/extensions/outliner/selection.d.ts +12 -0
  128. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -0
  129. package/dist/types/src/extensions/outliner/tree.d.ts +79 -0
  130. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -0
  131. package/dist/types/src/extensions/outliner/tree.test.d.ts +2 -0
  132. package/dist/types/src/extensions/outliner/tree.test.d.ts.map +1 -0
  133. package/dist/types/src/extensions/preview/index.d.ts +2 -0
  134. package/dist/types/src/extensions/preview/index.d.ts.map +1 -0
  135. package/dist/types/src/extensions/preview/preview.d.ts +34 -0
  136. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -0
  137. package/dist/types/src/extensions/replacer.d.ts +21 -0
  138. package/dist/types/src/extensions/replacer.d.ts.map +1 -0
  139. package/dist/types/src/extensions/replacer.test.d.ts +2 -0
  140. package/dist/types/src/extensions/replacer.test.d.ts.map +1 -0
  141. package/dist/types/src/extensions/scroll-past-end.d.ts +3 -0
  142. package/dist/types/src/extensions/scroll-past-end.d.ts.map +1 -0
  143. package/dist/types/src/extensions/scroller.d.ts +68 -0
  144. package/dist/types/src/extensions/scroller.d.ts.map +1 -0
  145. package/dist/types/src/extensions/selection.d.ts +24 -0
  146. package/dist/types/src/extensions/selection.d.ts.map +1 -0
  147. package/dist/types/src/extensions/snippets.d.ts +10 -0
  148. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  149. package/dist/types/src/extensions/state.d.ts +2 -0
  150. package/dist/types/src/extensions/state.d.ts.map +1 -0
  151. package/dist/types/src/extensions/submit.d.ts +10 -0
  152. package/dist/types/src/extensions/submit.d.ts.map +1 -0
  153. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  154. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  155. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  156. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  157. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  158. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  159. package/dist/types/src/extensions/tags/index.d.ts +7 -0
  160. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  161. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  162. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  163. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  164. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  165. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  166. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  167. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  168. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  169. package/dist/types/src/extensions/tags/xml-tags.d.ts +117 -0
  170. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  171. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  172. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  173. package/dist/types/src/extensions/tags/xml-util.test.d.ts +2 -0
  174. package/dist/types/src/extensions/tags/xml-util.test.d.ts.map +1 -0
  175. package/dist/types/src/index.d.ts +8 -0
  176. package/dist/types/src/index.d.ts.map +1 -0
  177. package/dist/types/src/styles/index.d.ts +2 -0
  178. package/dist/types/src/styles/index.d.ts.map +1 -0
  179. package/dist/types/src/styles/theme.d.ts +58 -0
  180. package/dist/types/src/styles/theme.d.ts.map +1 -0
  181. package/dist/types/src/types/index.d.ts +2 -0
  182. package/dist/types/src/types/index.d.ts.map +1 -0
  183. package/dist/types/src/types/types.d.ts +21 -0
  184. package/dist/types/src/types/types.d.ts.map +1 -0
  185. package/dist/types/src/util/cursor.d.ts +31 -0
  186. package/dist/types/src/util/cursor.d.ts.map +1 -0
  187. package/dist/types/src/util/debug.d.ts +17 -0
  188. package/dist/types/src/util/debug.d.ts.map +1 -0
  189. package/dist/types/src/util/decorations.d.ts +4 -0
  190. package/dist/types/src/util/decorations.d.ts.map +1 -0
  191. package/dist/types/src/util/dom.d.ts +10 -0
  192. package/dist/types/src/util/dom.d.ts.map +1 -0
  193. package/dist/types/src/util/facet.d.ts +3 -0
  194. package/dist/types/src/util/facet.d.ts.map +1 -0
  195. package/dist/types/src/util/index.d.ts +7 -0
  196. package/dist/types/src/util/index.d.ts.map +1 -0
  197. package/dist/types/src/util/util.d.ts +8 -0
  198. package/dist/types/src/util/util.d.ts.map +1 -0
  199. package/dist/types/tsconfig.tsbuildinfo +1 -0
  200. package/package.json +42 -43
  201. package/src/defaults.ts +33 -20
  202. package/src/extensions/annotations.ts +1 -1
  203. package/src/extensions/auto-scroll.ts +234 -0
  204. package/src/extensions/autocomplete/placeholder.ts +37 -18
  205. package/src/extensions/automerge/automerge.test.tsx +37 -11
  206. package/src/extensions/automerge/automerge.ts +5 -7
  207. package/src/extensions/blocks.ts +5 -5
  208. package/src/extensions/comments.ts +5 -6
  209. package/src/extensions/dnd.ts +2 -2
  210. package/src/extensions/factories.test.ts +88 -0
  211. package/src/extensions/factories.ts +32 -15
  212. package/src/extensions/folding.ts +5 -22
  213. package/src/extensions/index.ts +4 -3
  214. package/src/extensions/markdown/action.ts +0 -1
  215. package/src/extensions/markdown/bundle.ts +23 -9
  216. package/src/extensions/markdown/decorate.ts +15 -12
  217. package/src/extensions/markdown/formatting.ts +5 -10
  218. package/src/extensions/markdown/highlight.ts +15 -7
  219. package/src/extensions/markdown/link.ts +27 -33
  220. package/src/extensions/markdown/parser.test.ts +0 -1
  221. package/src/extensions/markdown/styles.ts +42 -9
  222. package/src/extensions/markdown/table.ts +24 -2
  223. package/src/extensions/outliner/outliner.test.ts +0 -1
  224. package/src/extensions/outliner/outliner.ts +3 -4
  225. package/src/extensions/outliner/tree.test.ts +0 -1
  226. package/src/extensions/preview/preview.ts +62 -15
  227. package/src/extensions/scroll-past-end.ts +32 -0
  228. package/src/extensions/scroller.ts +256 -0
  229. package/src/extensions/selection.ts +1 -1
  230. package/src/extensions/snippets.ts +67 -0
  231. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  232. package/src/extensions/tags/extended-markdown.ts +80 -1
  233. package/src/extensions/tags/fader.ts +195 -0
  234. package/src/extensions/tags/index.ts +4 -1
  235. package/src/extensions/tags/testing/text.md +36 -0
  236. package/src/extensions/tags/testing/text.txt +35 -0
  237. package/src/extensions/tags/typewriter.test.ts +65 -0
  238. package/src/extensions/tags/typewriter.ts +594 -0
  239. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  240. package/src/extensions/tags/xml-formatting.ts +125 -0
  241. package/src/extensions/tags/xml-tags.ts +186 -35
  242. package/src/extensions/tags/xml-util.test.ts +199 -24
  243. package/src/extensions/tags/xml-util.ts +62 -5
  244. package/src/index.ts +0 -1
  245. package/src/styles/index.ts +0 -2
  246. package/src/styles/theme.ts +124 -33
  247. package/src/types/types.ts +10 -2
  248. package/src/typings.d.ts +8 -0
  249. package/src/util/cursor.ts +1 -2
  250. package/src/extensions/autoscroll.ts +0 -165
  251. package/src/extensions/scrolling.ts +0 -189
  252. package/src/extensions/tags/streamer.ts +0 -243
  253. package/src/extensions/typewriter.ts +0 -68
  254. package/src/styles/markdown.ts +0 -26
  255. package/src/styles/tokens.ts +0 -17
@@ -101,11 +101,11 @@ export const blocks = () => [
101
101
  '.cm-line.block-line': {
102
102
  paddingLeft: '0.75rem',
103
103
  paddingRight: '0.75rem',
104
- borderLeft: '1px solid var(--dx-subduedSeparator)',
105
- borderRight: '1px solid var(--dx-subduedSeparator)',
104
+ borderLeft: '1px solid var(--color-subdued-separator)',
105
+ borderRight: '1px solid var(--color-subdued-separator)',
106
106
  },
107
107
  '.cm-line.block-single': {
108
- border: '1px solid var(--dx-subduedSeparator)',
108
+ border: '1px solid var(--color-subdued-separator)',
109
109
  borderRadius: '6px',
110
110
  paddingTop: '0.5rem',
111
111
  paddingBottom: '0.5rem',
@@ -113,7 +113,7 @@ export const blocks = () => [
113
113
  marginBottom: '0.5rem',
114
114
  },
115
115
  '.cm-line.block-first': {
116
- borderTop: '1px solid var(--dx-subduedSeparator)',
116
+ borderTop: '1px solid var(--color-subdued-separator)',
117
117
  borderTopLeftRadius: '6px',
118
118
  borderTopRightRadius: '6px',
119
119
  paddingTop: '0.5rem',
@@ -121,7 +121,7 @@ export const blocks = () => [
121
121
  },
122
122
  '.cm-line.block-middle': {},
123
123
  '.cm-line.block-last': {
124
- borderBottom: '1px solid var(--dx-subduedSeparator)',
124
+ borderBottom: '1px solid var(--color-subdued-separator)',
125
125
  borderBottomLeftRadius: '6px',
126
126
  borderBottomRightRadius: '6px',
127
127
  paddingBottom: '0.5rem',
@@ -22,7 +22,6 @@ import { isNonNullable } from '@dxos/util';
22
22
 
23
23
  import { type Comment, type Range, type RenderCallback } from '../types';
24
24
  import { Cursor, singleValueFacet, wrapWithCatch } from '../util';
25
-
26
25
  import { documentId } from './selection';
27
26
 
28
27
  //
@@ -103,14 +102,14 @@ export const commentsState = StateField.define<CommentsState>({
103
102
  const styles = EditorView.theme({
104
103
  '.cm-comment, .cm-comment-current': {
105
104
  padding: '3px 0',
106
- color: 'var(--dx-cmCommentText)',
107
- backgroundColor: 'var(--dx-cmCommentSurface)',
105
+ color: 'var(--color-cm-comment-text)',
106
+ backgroundColor: 'var(--color-cm-comment-surface)',
108
107
  },
109
108
  '.cm-comment > span, .cm-comment-current > span': {
110
109
  boxDecorationBreak: 'clone',
111
- boxShadow: '0 0 1px 3px var(--dx-cmCommentSurface)',
112
- backgroundColor: 'var(--dx-cmCommentSurface)',
113
- color: 'var(--dx-cmCommentText)',
110
+ boxShadow: '0 0 1px 3px var(--color-cm-comment-surface)',
111
+ backgroundColor: 'var(--color-cm-comment-surface)',
112
+ color: 'var(--color-cm-comment-text)',
114
113
  cursor: 'pointer',
115
114
  },
116
115
  });
@@ -29,8 +29,8 @@ export const dropFile = (options: DropOptions = {}): Extension => {
29
29
 
30
30
  const styles = EditorView.theme({
31
31
  '.cm-dropCursor': {
32
- borderLeft: '2px solid var(--dx-accentText)',
33
- color: 'var(--dx-accentText)',
32
+ borderLeft: '2px solid var(--color-accent-text)',
33
+ color: 'var(--color-accent-text)',
34
34
  padding: '0 4px',
35
35
  },
36
36
  '.cm-dropCursor:after': {
@@ -0,0 +1,88 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { markdown, markdownLanguage, insertNewlineContinueMarkup } from '@codemirror/lang-markdown';
6
+ import { EditorSelection, EditorState } from '@codemirror/state';
7
+ import { describe, test } from 'vitest';
8
+
9
+ import { createBasicExtensions } from './factories';
10
+
11
+ describe('createBasicExtensions readOnly', () => {
12
+ test('drops user-initiated edits when readOnly is true', ({ expect }) => {
13
+ const state = EditorState.create({
14
+ doc: 'hello',
15
+ extensions: [createBasicExtensions({ readOnly: true })],
16
+ });
17
+ const tr = state.update({
18
+ changes: { from: state.doc.length, insert: ' world' },
19
+ userEvent: 'input.type',
20
+ });
21
+ expect(tr.state.doc.toString()).toBe('hello');
22
+ });
23
+
24
+ for (const userEvent of ['delete.forward', 'undo', 'redo']) {
25
+ test(`drops '${userEvent}' user events when readOnly is true`, ({ expect }) => {
26
+ const state = EditorState.create({
27
+ doc: 'hello',
28
+ extensions: [createBasicExtensions({ readOnly: true })],
29
+ });
30
+ const tr = state.update({
31
+ changes: { from: state.doc.length, insert: ' world' },
32
+ userEvent,
33
+ });
34
+ expect(tr.state.doc.toString()).toBe('hello');
35
+ });
36
+ }
37
+
38
+ test('allows programmatic dispatches (no userEvent) when readOnly is true', ({ expect }) => {
39
+ // Streaming consumers (e.g. MarkdownStream) populate the doc programmatically — those
40
+ // transactions must pass even though the editor is read-only to the user.
41
+ const state = EditorState.create({
42
+ doc: 'hello',
43
+ extensions: [createBasicExtensions({ readOnly: true })],
44
+ });
45
+ const tr = state.update({ changes: { from: state.doc.length, insert: ' world' } });
46
+ expect(tr.state.doc.toString()).toBe('hello world');
47
+ });
48
+
49
+ test('selection-only transactions still apply when readOnly', ({ expect }) => {
50
+ const state = EditorState.create({
51
+ doc: 'hello',
52
+ extensions: [createBasicExtensions({ readOnly: true })],
53
+ });
54
+ const tr = state.update({ selection: EditorSelection.cursor(2) });
55
+ expect(tr.state.selection.main.head).toBe(2);
56
+ });
57
+
58
+ test('doc-changing transactions apply normally when readOnly is false', ({ expect }) => {
59
+ const state = EditorState.create({
60
+ doc: 'hello',
61
+ extensions: [createBasicExtensions({ readOnly: false })],
62
+ });
63
+ const tr = state.update({ changes: { from: state.doc.length, insert: ' world' } });
64
+ expect(tr.state.doc.toString()).toBe('hello world');
65
+ });
66
+
67
+ // Regression: in MarkdownStream `readOnly: true` must also block the markdown extension's
68
+ // Enter handler (`insertNewlineContinueMarkup`), which programmatically dispatches a list
69
+ // continuation regardless of the readOnly facet.
70
+ test('markdown insertNewlineContinueMarkup is suppressed when readOnly', ({ expect }) => {
71
+ const doc = '- one\n- two';
72
+ const state = EditorState.create({
73
+ doc,
74
+ selection: EditorSelection.cursor(doc.length),
75
+ extensions: [createBasicExtensions({ readOnly: true }), markdown({ base: markdownLanguage })],
76
+ });
77
+ let dispatched: any;
78
+ insertNewlineContinueMarkup({
79
+ state,
80
+ dispatch: (tr) => {
81
+ dispatched = tr;
82
+ },
83
+ });
84
+ if (dispatched) {
85
+ expect(dispatched.state.doc.toString()).toBe(doc);
86
+ }
87
+ });
88
+ });
@@ -17,7 +17,6 @@ import {
17
17
  keymap,
18
18
  lineNumbers,
19
19
  placeholder,
20
- scrollPastEnd,
21
20
  } from '@codemirror/view';
22
21
  import { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';
23
22
  import defaultsDeep from 'lodash.defaultsdeep';
@@ -27,15 +26,14 @@ import { type DocAccessor } from '@dxos/echo-db';
27
26
  import { log } from '@dxos/log';
28
27
  import { type Messenger } from '@dxos/protocols';
29
28
  import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
30
- import { type HuePalette } from '@dxos/ui-theme';
31
- import { type ThemeMode } from '@dxos/ui-types';
29
+ import { type ChromaticPalette, type ThemeMode } from '@dxos/ui-types';
32
30
  import { hexToHue, isTruthy } from '@dxos/util';
33
31
 
34
32
  import { baseTheme, createFontTheme, editorGutter } from '../styles';
35
-
36
33
  import { automerge } from './automerge';
37
34
  import { SpaceAwarenessProvider, awareness } from './awareness';
38
35
  import { focus } from './focus';
36
+ import { scrollPastEnd } from './scroll-past-end';
39
37
 
40
38
  //
41
39
  // Basic
@@ -145,6 +143,19 @@ export const createBasicExtensions = (propsProp?: BasicExtensionsOptions): Exten
145
143
  props.lineWrapping && EditorView.lineWrapping,
146
144
  props.placeholder && placeholder(props.placeholder),
147
145
  props.readOnly !== undefined && EditorState.readOnly.of(props.readOnly),
146
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
147
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
148
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
149
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
150
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
151
+ // depend on being able to populate the doc themselves.
152
+ props.readOnly &&
153
+ EditorState.transactionFilter.of((tr) =>
154
+ tr.docChanged &&
155
+ (tr.isUserEvent('input') || tr.isUserEvent('delete') || tr.isUserEvent('undo') || tr.isUserEvent('redo'))
156
+ ? []
157
+ : tr,
158
+ ),
148
159
  props.scrollPastEnd && scrollPastEnd(),
149
160
  props.tabbable && tabbable,
150
161
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -181,12 +192,12 @@ export const createBasicExtensions = (propsProp?: BasicExtensionsOptions): Exten
181
192
  export type ThemeExtensionsOptions = {
182
193
  monospace?: boolean;
183
194
  themeMode?: ThemeMode;
195
+ scrollbarThin?: boolean;
184
196
  slots?: {
185
197
  editor?: {
186
198
  className?: string;
187
199
  };
188
- scroll?: {
189
- // NOTE: Do not apply vertical padding to scroll container.
200
+ scroller?: {
190
201
  className?: string;
191
202
  };
192
203
  content?: {
@@ -198,13 +209,13 @@ export type ThemeExtensionsOptions = {
198
209
 
199
210
  export const grow: ThemeExtensionsOptions['slots'] = {
200
211
  editor: {
201
- className: 'bs-full is-full',
212
+ className: 'h-full w-full',
202
213
  },
203
214
  } as const;
204
215
 
205
216
  export const fullWidth: ThemeExtensionsOptions['slots'] = {
206
217
  editor: {
207
- className: 'is-full',
218
+ className: 'w-full',
208
219
  },
209
220
  } as const;
210
221
 
@@ -220,11 +231,12 @@ export const defaultStyles = {
220
231
  */
221
232
  export const createThemeExtensions = ({
222
233
  monospace,
223
- themeMode,
234
+ scrollbarThin,
224
235
  slots: slotsProp,
225
236
  syntaxHighlighting: syntaxHighlightingProp,
237
+ themeMode,
226
238
  }: ThemeExtensionsOptions = {}): Extension => {
227
- const slots = defaultsDeep({}, slotsProp, defaultThemeSlots);
239
+ const slots: NonNullable<ThemeExtensionsOptions['slots']> = defaultsDeep({}, slotsProp, defaultThemeSlots);
228
240
  return [
229
241
  baseTheme,
230
242
  EditorView.darkTheme.of(themeMode === 'dark'),
@@ -233,11 +245,16 @@ export const createThemeExtensions = ({
233
245
  syntaxHighlighting(HighlightStyle.define(themeMode === 'dark' ? defaultStyles.dark : defaultStyles.light)),
234
246
  slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
235
247
  slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
236
- slots.scroll?.className &&
248
+ (slots.scroller?.className || scrollbarThin) &&
237
249
  ViewPlugin.fromClass(
238
250
  class {
239
251
  constructor(view: EditorView) {
240
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
252
+ if (slots.scroller?.className) {
253
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
254
+ }
255
+ if (scrollbarThin) {
256
+ view.scrollDOM.style.setProperty('--scrollbar-size', '4px');
257
+ }
241
258
  }
242
259
  },
243
260
  ),
@@ -263,7 +280,7 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
263
280
 
264
281
  if (messenger && identity) {
265
282
  const peerId = identity?.identityKey.toHex();
266
- const hue = (identity?.profile?.data?.hue as HuePalette | undefined) ?? hexToHue(peerId ?? '0');
283
+ const hue = (identity?.profile?.data?.hue as ChromaticPalette | undefined) ?? hexToHue(peerId ?? '0');
267
284
  extensions.push(
268
285
  awareness(
269
286
  new SpaceAwarenessProvider({
@@ -271,8 +288,8 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
271
288
  channel: `awareness.${id}`,
272
289
  peerId: identity.identityKey.toHex(),
273
290
  info: {
274
- darkColor: `var(--dx-${hue}Cursor)`,
275
- lightColor: `var(--dx-${hue}Cursor)`,
291
+ darkColor: `var(--color-${hue}-border)`,
292
+ lightColor: `var(--color-${hue}-border)`,
276
293
  displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
277
294
  },
278
295
  }),
@@ -17,37 +17,20 @@ export const folding = (): Extension => {
17
17
  placeholderDOM: () => Domino.of('span').root,
18
18
  }),
19
19
  foldGutter({
20
+ // NOTE: We can't animate since the element is remounted on state change.
20
21
  markerDOM: (open) => {
21
22
  return Domino.of('div')
22
- .classNames('flex bs-full justify-center items-center')
23
- .children(
23
+ .classNames('flex h-full justify-center items-center')
24
+ .append(
24
25
  Domino.of('svg', Domino.SVG)
25
- .classNames(mx('is-4 bs-4 cursor-pointer', open && 'rotate-90'))
26
- .children(
26
+ .classNames(mx('w-4 h-4 cursor-pointer', open && 'rotate-90'))
27
+ .append(
27
28
  Domino.of('use', Domino.SVG).attributes({
28
29
  href: Domino.icon('ph--caret-right--regular'),
29
30
  }),
30
31
  ),
31
32
  ).root;
32
33
  },
33
- // TODO(burdon): markerDOM is called either way, defeating the animation: transition-transform duration-200
34
- // domEventHandlers: {
35
- // click: (view, line: BlockInfo, event) => {
36
- // event.preventDefault();
37
- // event.stopPropagation();
38
- // const range = foldable(view.state, line.from, line.to);
39
- // if (range) {
40
- // view.dispatch({ effects: foldEffect.of(range) });
41
- // (event.target as HTMLElement)?.classList.add('rotate-90');
42
- // } else {
43
- // foldedRanges(view.state).between(line.from, line.to, (from, to) => {
44
- // view.dispatch({ effects: unfoldEffect.of({ from, to }) });
45
- // (event.target as HTMLElement)?.classList.remove('rotate-90');
46
- // });
47
- // }
48
- // return true;
49
- // },
50
- // },
51
34
  }),
52
35
  EditorView.theme({
53
36
  '.cm-foldGutter': {
@@ -4,7 +4,7 @@
4
4
 
5
5
  export * from './annotations';
6
6
  export * from './autocomplete';
7
- export * from './autoscroll';
7
+ export * from './auto-scroll';
8
8
  export * from './automerge';
9
9
  export * from './awareness';
10
10
  export * from './blast';
@@ -26,9 +26,10 @@ export * from './modes';
26
26
  export * from './outliner';
27
27
  export * from './preview';
28
28
  export * from './replacer';
29
+ export * from './scroll-past-end';
30
+ export * from './scroller';
29
31
  export * from './selection';
30
- export * from './scrolling';
32
+ export * from './snippets';
31
33
  export * from './state';
32
34
  export * from './submit';
33
35
  export * from './tags';
34
- export * from './typewriter';
@@ -8,7 +8,6 @@ import { type Node } from '@dxos/app-graph';
8
8
  import { type MenuActionProperties } from '@dxos/ui-types';
9
9
 
10
10
  import { createComment } from '../comments';
11
-
12
11
  import {
13
12
  Inline,
14
13
  List,
@@ -6,8 +6,7 @@ import { completionKeymap } from '@codemirror/autocomplete';
6
6
  import { defaultKeymap, indentWithTab } from '@codemirror/commands';
7
7
  import { jsonLanguage } from '@codemirror/lang-json';
8
8
  import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
9
- import { xml } from '@codemirror/lang-xml';
10
- import { LanguageDescription, syntaxHighlighting } from '@codemirror/language';
9
+ import { type LanguageDescription, foldNodeProp, syntaxHighlighting } from '@codemirror/language';
11
10
  import { languages } from '@codemirror/language-data';
12
11
  import { type Extension } from '@codemirror/state';
13
12
  import { keymap } from '@codemirror/view';
@@ -18,6 +17,8 @@ import { isTruthy } from '@dxos/util';
18
17
  import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
19
18
 
20
19
  export type MarkdownBundleOptions = {
20
+ /** Additional fenced-code languages prepended to the standard language-data list. */
21
+ codeLanguages?: LanguageDescription[];
21
22
  extensions?: MarkdownConfig[];
22
23
  indentWithTab?: boolean;
23
24
  setextHeading?: boolean;
@@ -45,8 +46,9 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
45
46
  base: markdownLanguage,
46
47
 
47
48
  // Languages for syntax highlighting fenced code blocks.
49
+ // Caller-supplied languages are checked first so they can override defaults.
48
50
  defaultCodeLanguage: jsonLanguage,
49
- codeLanguages: languages,
51
+ codeLanguages: [...(options.codeLanguages ?? []), ...languages],
50
52
 
51
53
  // Don't complete HTML tags.
52
54
  completeHTMLTags: false,
@@ -56,6 +58,10 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
56
58
  // GFM provided by default.
57
59
  markdownTagsExtensions,
58
60
  ...(options.extensions ?? defaultExtensions()),
61
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
62
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
63
+ // so we must override the node prop directly on the FencedCode node type.
64
+ noFencedCodeFolding,
59
65
  ],
60
66
  }),
61
67
 
@@ -77,12 +83,20 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
77
83
  ];
78
84
  };
79
85
 
80
- const xmlLanguageDesc = LanguageDescription.of({
81
- name: 'xml',
82
- alias: ['html', 'xhtml'],
83
- extensions: ['xml', 'xhtml'],
84
- load: async () => xml(),
85
- });
86
+ /**
87
+ * Disables folding for fenced code blocks.
88
+ *
89
+ * foldService cannot block folding because returning null just defers to the next service,
90
+ * and CodeMirror always falls back to syntaxFolding (which reads foldNodeProp).
91
+ * The only reliable fix is to override foldNodeProp on FencedCode to return null.
92
+ */
93
+ const noFencedCodeFolding: MarkdownConfig = {
94
+ props: [
95
+ foldNodeProp.add({
96
+ FencedCode: () => null,
97
+ }),
98
+ ],
99
+ };
86
100
 
87
101
  /**
88
102
  * Default customizations.
@@ -8,12 +8,10 @@ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate
8
8
  import { type SyntaxNodeRef } from '@lezer/common';
9
9
 
10
10
  import { invariant } from '@dxos/invariant';
11
- import { mx } from '@dxos/ui-theme';
12
11
 
13
12
  import { type HeadingLevel, markdownTheme } from '../../styles';
14
13
  import { type RenderCallback } from '../../types';
15
14
  import { wrapWithCatch } from '../../util';
16
-
17
15
  import { adjustChanges } from './changes';
18
16
  import { image } from './image';
19
17
  import { bulletListIndentationWidth, formattingStyles, orderedListIndentationWidth } from './styles';
@@ -56,7 +54,6 @@ class LinkButton extends WidgetType {
56
54
  return this.url === other.url;
57
55
  }
58
56
 
59
- // TODO(burdon): Create icon and link directly without react?
60
57
  override toDOM(view: EditorView) {
61
58
  const el = document.createElement('span');
62
59
  this.render(el, { url: this.url }, view);
@@ -132,8 +129,8 @@ class TextWidget extends WidgetType {
132
129
  const hide = Decoration.replace({});
133
130
  const blockQuote = Decoration.line({ class: 'cm-blockquote' });
134
131
  const fencedCodeLine = Decoration.line({ class: 'cm-code cm-codeblock-line' });
135
- const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-start') });
136
- const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-end') });
132
+ const fencedCodeLineFirst = Decoration.line({ class: 'cm-code cm-codeblock-line cm-codeblock-start' });
133
+ const fencedCodeLineLast = Decoration.line({ class: 'cm-code cm-codeblock-line cm-codeblock-end' });
137
134
  const commentBlockLine = fencedCodeLine;
138
135
  const commentBlockLineFirst = fencedCodeLineFirst;
139
136
  const commentBlockLineLast = fencedCodeLineLast;
@@ -244,14 +241,14 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
244
241
  headers
245
242
  .slice(from - 1)
246
243
  .map((level) => level?.number ?? 0)
247
- .join('.') + ' ';
244
+ .join('.') + '). ';
248
245
 
249
246
  if (num.length) {
250
247
  atomicDecoRanges.push({
251
248
  from: mark.from,
252
249
  to: mark.from + len,
253
250
  deco: Decoration.replace({
254
- widget: new TextWidget(num, markdownTheme.heading(level)),
251
+ widget: new TextWidget(num, markdownTheme.heading(level).className),
255
252
  }),
256
253
  });
257
254
  }
@@ -440,11 +437,11 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
440
437
 
441
438
  decoRanges.push({
442
439
  from: marks[0].to,
443
- to: marks[1].from,
440
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
444
441
  deco: Decoration.mark({
445
442
  tagName: 'a',
446
443
  attributes: {
447
- class: 'cm-link',
444
+ class: options.renderLinkButton ? 'cm-link cm-link-with-button' : 'cm-link',
448
445
  href: url,
449
446
  rel: 'noreferrer',
450
447
  target: '_blank',
@@ -457,7 +454,9 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
457
454
  from: marks[1].from,
458
455
  to: node.to,
459
456
  deco: options.renderLinkButton
460
- ? Decoration.replace({ widget: new LinkButton(url, options.renderLinkButton) })
457
+ ? Decoration.replace({
458
+ widget: new LinkButton(url, options.renderLinkButton),
459
+ })
461
460
  : hide,
462
461
  });
463
462
  }
@@ -527,8 +526,12 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
527
526
  }
528
527
 
529
528
  const atomicDeco = new RangeSetBuilder<Decoration>();
530
- for (const { from, to, deco: d } of atomicDecoRanges) {
531
- atomicDeco.add(from, to, d);
529
+ for (const { from, to, deco } of atomicDecoRanges) {
530
+ // Skip replace decorations that span line breaks (not allowed by CodeMirror plugins).
531
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
532
+ continue;
533
+ }
534
+ atomicDeco.add(from, to, deco);
532
535
  }
533
536
 
534
537
  return {
@@ -1245,20 +1245,15 @@ export const getFormatting = (state: EditorState): Formatting => {
1245
1245
  };
1246
1246
 
1247
1247
  /**
1248
- * Hook provides an extension to compute the current formatting state.
1248
+ * Extension to compute and publish the current formatting state.
1249
+ * @param onStateChange - Callback invoked with the current formatting state when the editor state changes.
1250
+ * @param delay - Debounce/throttle delay in ms.
1249
1251
  */
1250
- export const formattingListener = (stateProvider: () => Formatting | undefined, delay = 100): Extension => {
1252
+ export const formattingListener = (onStateChange: (state: Formatting) => void, delay = 100): Extension => {
1251
1253
  return EditorView.updateListener.of(
1252
1254
  debounceAndThrottle((update: ViewUpdate) => {
1253
1255
  if (update.docChanged || update.selectionSet) {
1254
- const state = stateProvider();
1255
- if (!state) {
1256
- return;
1257
- }
1258
-
1259
- Object.entries(getFormatting(update.state)).forEach(([key, active]) => {
1260
- state[key as keyof Formatting] = active as any;
1261
- });
1256
+ onStateChange(getFormatting(update.state));
1262
1257
  }
1263
1258
  }, delay),
1264
1259
  );
@@ -145,13 +145,21 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
145
145
  class: 'font-mono',
146
146
  },
147
147
 
148
- // Headings.
149
- { tag: tags.heading1, class: markdownTheme.heading(1) },
150
- { tag: tags.heading2, class: markdownTheme.heading(2) },
151
- { tag: tags.heading3, class: markdownTheme.heading(3) },
152
- { tag: tags.heading4, class: markdownTheme.heading(4) },
153
- { tag: tags.heading5, class: markdownTheme.heading(5) },
154
- { tag: tags.heading6, class: markdownTheme.heading(6) },
148
+ // Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via
149
+ // StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,
150
+ // HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).
151
+ // Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.
152
+ {
153
+ tag: tags.heading,
154
+ color: 'var(--color-cm-heading) !important',
155
+ fontWeight: '300',
156
+ },
157
+ { tag: tags.heading1, ...markdownTheme.heading(1) },
158
+ { tag: tags.heading2, ...markdownTheme.heading(2) },
159
+ { tag: tags.heading3, ...markdownTheme.heading(3) },
160
+ { tag: tags.heading4, ...markdownTheme.heading(4) },
161
+ { tag: tags.heading5, ...markdownTheme.heading(5) },
162
+ { tag: tags.heading6, ...markdownTheme.heading(6) },
155
163
 
156
164
  // Emphasis.
157
165
  { tag: tags.emphasis, class: 'italic' },
@@ -12,39 +12,33 @@ import { tooltipContent } from '@dxos/ui-theme';
12
12
  import { type RenderCallback } from '../../types';
13
13
 
14
14
  export const linkTooltip = (renderTooltip: RenderCallback<{ url: string }>) => {
15
- return hoverTooltip(
16
- (view, pos, side) => {
17
- const syntax = syntaxTree(view.state).resolveInner(pos, side);
18
- let link = null;
19
- for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
20
- link = node.name === 'Link' ? node : null;
21
- }
15
+ return hoverTooltip((view, pos, side) => {
16
+ const syntax = syntaxTree(view.state).resolveInner(pos, side);
17
+ let link = null;
18
+ for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
19
+ link = node.name === 'Link' ? node : null;
20
+ }
22
21
 
23
- const url = link && link.getChild('URL');
24
- if (!url || !link) {
25
- return null;
26
- }
22
+ const url = link && link.getChild('URL');
23
+ if (!url || !link) {
24
+ return null;
25
+ }
27
26
 
28
- const urlText = view.state.sliceDoc(url.from, url.to);
29
- if (urlText.startsWith('dxn')) {
30
- return null;
31
- }
32
- return {
33
- pos: link.from,
34
- end: link.to,
35
- // NOTE: Forcing above causes the tooltip to flicker.
36
- // above: true,
37
- create: () => {
38
- const el = document.createElement('div');
39
- el.className = tooltipContent({});
40
- renderTooltip(el, { url: urlText }, view);
41
- return { dom: el, offset: { x: 0, y: 4 } };
42
- },
43
- };
44
- },
45
- {
46
- // NOTE: 0 = default of 300ms.
47
- hoverTime: 1,
48
- },
49
- );
27
+ const urlText = view.state.sliceDoc(url.from, url.to);
28
+ if (urlText.startsWith('dxn')) {
29
+ return null;
30
+ }
31
+
32
+ return {
33
+ pos: link.from,
34
+ end: link.to,
35
+ above: true,
36
+ create: () => {
37
+ const el = document.createElement('div');
38
+ el.className = tooltipContent({});
39
+ renderTooltip(el, { url: urlText }, view);
40
+ return { dom: el, offset: { x: 0, y: 4 } };
41
+ },
42
+ };
43
+ });
50
44
  };
@@ -2,7 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- // @ts-ignore
6
5
  import { testTree } from '@lezer/generator/test';
7
6
  import { parser } from '@lezer/markdown';
8
7
  import { describe, test } from 'vitest';