@dxos/ui-editor 0.0.0 → 0.8.4-main.05e74ebcff

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 (263) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +8657 -0
  4. package/dist/lib/browser/index.mjs.map +7 -0
  5. package/dist/lib/browser/meta.json +1 -0
  6. package/dist/lib/browser/types/index.mjs +33 -0
  7. package/dist/lib/browser/types/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +8659 -0
  9. package/dist/lib/node-esm/index.mjs.map +7 -0
  10. package/dist/lib/node-esm/meta.json +1 -0
  11. package/dist/lib/node-esm/types/index.mjs +35 -0
  12. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  13. package/dist/types/src/defaults.d.ts +6 -0
  14. package/dist/types/src/defaults.d.ts.map +1 -0
  15. package/dist/types/src/extensions/annotations.d.ts +9 -0
  16. package/dist/types/src/extensions/annotations.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 +30 -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/scrolling/auto-scroll.d.ts +18 -0
  142. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +1 -0
  143. package/dist/types/src/extensions/scrolling/crawler.d.ts +75 -0
  144. package/dist/types/src/extensions/scrolling/crawler.d.ts.map +1 -0
  145. package/dist/types/src/extensions/scrolling/index.d.ts +5 -0
  146. package/dist/types/src/extensions/scrolling/index.d.ts.map +1 -0
  147. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts +3 -0
  148. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +1 -0
  149. package/dist/types/src/extensions/scrolling/scroller.d.ts +16 -0
  150. package/dist/types/src/extensions/scrolling/scroller.d.ts.map +1 -0
  151. package/dist/types/src/extensions/selection.d.ts +24 -0
  152. package/dist/types/src/extensions/selection.d.ts.map +1 -0
  153. package/dist/types/src/extensions/snippets.d.ts +10 -0
  154. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  155. package/dist/types/src/extensions/state.d.ts +2 -0
  156. package/dist/types/src/extensions/state.d.ts.map +1 -0
  157. package/dist/types/src/extensions/submit.d.ts +10 -0
  158. package/dist/types/src/extensions/submit.d.ts.map +1 -0
  159. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  160. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  161. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  162. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  163. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  164. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  165. package/dist/types/src/extensions/tags/index.d.ts +7 -0
  166. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  167. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  168. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  169. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  170. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  171. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  172. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  173. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  174. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  175. package/dist/types/src/extensions/tags/xml-tags.d.ts +117 -0
  176. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  177. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  178. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  179. package/dist/types/src/extensions/tags/xml-util.test.d.ts +2 -0
  180. package/dist/types/src/extensions/tags/xml-util.test.d.ts.map +1 -0
  181. package/dist/types/src/index.d.ts +8 -0
  182. package/dist/types/src/index.d.ts.map +1 -0
  183. package/dist/types/src/styles/index.d.ts +2 -0
  184. package/dist/types/src/styles/index.d.ts.map +1 -0
  185. package/dist/types/src/styles/theme.d.ts +58 -0
  186. package/dist/types/src/styles/theme.d.ts.map +1 -0
  187. package/dist/types/src/types/index.d.ts +2 -0
  188. package/dist/types/src/types/index.d.ts.map +1 -0
  189. package/dist/types/src/types/types.d.ts +21 -0
  190. package/dist/types/src/types/types.d.ts.map +1 -0
  191. package/dist/types/src/util/cursor.d.ts +31 -0
  192. package/dist/types/src/util/cursor.d.ts.map +1 -0
  193. package/dist/types/src/util/debug.d.ts +17 -0
  194. package/dist/types/src/util/debug.d.ts.map +1 -0
  195. package/dist/types/src/util/decorations.d.ts +4 -0
  196. package/dist/types/src/util/decorations.d.ts.map +1 -0
  197. package/dist/types/src/util/dom.d.ts +10 -0
  198. package/dist/types/src/util/dom.d.ts.map +1 -0
  199. package/dist/types/src/util/facet.d.ts +3 -0
  200. package/dist/types/src/util/facet.d.ts.map +1 -0
  201. package/dist/types/src/util/index.d.ts +7 -0
  202. package/dist/types/src/util/index.d.ts.map +1 -0
  203. package/dist/types/src/util/util.d.ts +8 -0
  204. package/dist/types/src/util/util.d.ts.map +1 -0
  205. package/dist/types/tsconfig.tsbuildinfo +1 -0
  206. package/package.json +43 -44
  207. package/src/defaults.ts +33 -20
  208. package/src/extensions/annotations.ts +1 -1
  209. package/src/extensions/autocomplete/placeholder.ts +37 -18
  210. package/src/extensions/automerge/automerge.test.tsx +37 -11
  211. package/src/extensions/automerge/automerge.ts +5 -7
  212. package/src/extensions/blocks.ts +5 -5
  213. package/src/extensions/comments.ts +5 -6
  214. package/src/extensions/dnd.ts +2 -2
  215. package/src/extensions/factories.test.ts +88 -0
  216. package/src/extensions/factories.ts +32 -15
  217. package/src/extensions/folding.ts +5 -22
  218. package/src/extensions/index.ts +2 -3
  219. package/src/extensions/markdown/action.ts +0 -1
  220. package/src/extensions/markdown/bundle.ts +23 -9
  221. package/src/extensions/markdown/decorate.ts +15 -12
  222. package/src/extensions/markdown/formatting.ts +5 -10
  223. package/src/extensions/markdown/highlight.ts +15 -7
  224. package/src/extensions/markdown/link.ts +27 -33
  225. package/src/extensions/markdown/parser.test.ts +0 -1
  226. package/src/extensions/markdown/styles.ts +42 -9
  227. package/src/extensions/markdown/table.ts +24 -2
  228. package/src/extensions/outliner/outliner.test.ts +0 -1
  229. package/src/extensions/outliner/outliner.ts +3 -4
  230. package/src/extensions/outliner/tree.test.ts +0 -1
  231. package/src/extensions/preview/preview.ts +62 -15
  232. package/src/extensions/scrolling/auto-scroll.ts +244 -0
  233. package/src/extensions/scrolling/crawler.ts +263 -0
  234. package/src/extensions/scrolling/index.ts +8 -0
  235. package/src/extensions/scrolling/scroll-past-end.ts +32 -0
  236. package/src/extensions/scrolling/scroller.ts +27 -0
  237. package/src/extensions/selection.ts +1 -1
  238. package/src/extensions/snippets.ts +67 -0
  239. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  240. package/src/extensions/tags/extended-markdown.ts +80 -1
  241. package/src/extensions/tags/fader.ts +195 -0
  242. package/src/extensions/tags/index.ts +4 -1
  243. package/src/extensions/tags/testing/text.md +36 -0
  244. package/src/extensions/tags/testing/text.txt +35 -0
  245. package/src/extensions/tags/typewriter.test.ts +65 -0
  246. package/src/extensions/tags/typewriter.ts +594 -0
  247. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  248. package/src/extensions/tags/xml-formatting.ts +125 -0
  249. package/src/extensions/tags/xml-tags.ts +186 -35
  250. package/src/extensions/tags/xml-util.test.ts +199 -24
  251. package/src/extensions/tags/xml-util.ts +62 -5
  252. package/src/index.ts +0 -1
  253. package/src/styles/index.ts +0 -2
  254. package/src/styles/theme.ts +125 -33
  255. package/src/types/types.ts +10 -2
  256. package/src/typings.d.ts +8 -0
  257. package/src/util/cursor.ts +1 -2
  258. package/src/extensions/autoscroll.ts +0 -165
  259. package/src/extensions/scrolling.ts +0 -189
  260. package/src/extensions/tags/streamer.ts +0 -243
  261. package/src/extensions/typewriter.ts +0 -68
  262. package/src/styles/markdown.ts +0 -26
  263. package/src/styles/tokens.ts +0 -17
@@ -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 './scrolling';
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,6 @@
4
4
 
5
5
  export * from './annotations';
6
6
  export * from './autocomplete';
7
- export * from './autoscroll';
8
7
  export * from './automerge';
9
8
  export * from './awareness';
10
9
  export * from './blast';
@@ -26,9 +25,9 @@ export * from './modes';
26
25
  export * from './outliner';
27
26
  export * from './preview';
28
27
  export * from './replacer';
29
- export * from './selection';
30
28
  export * from './scrolling';
29
+ export * from './selection';
30
+ export * from './snippets';
31
31
  export * from './state';
32
32
  export * from './submit';
33
33
  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';
@@ -18,7 +18,7 @@ export const formattingStyles = EditorView.theme({
18
18
  width: '100%',
19
19
  height: '0',
20
20
  verticalAlign: 'middle',
21
- borderTop: '1px solid var(--dx-cmSeparator)',
21
+ borderTop: '1px solid var(--color-cm-separator)',
22
22
  opacity: 0.5,
23
23
  },
24
24
 
@@ -43,20 +43,45 @@ export const formattingStyles = EditorView.theme({
43
43
  * Blockquote.
44
44
  */
45
45
  '& .cm-blockquote': {
46
- background: 'var(--dx-cmCodeblock)',
47
- borderLeft: '2px solid var(--dx-cmSeparator)',
46
+ background: 'var(--color-cm-codeblock)',
47
+ borderLeft: '2px solid var(--color-cm-separator)',
48
48
  paddingLeft: '1rem',
49
- margin: '0',
49
+ margin: 0,
50
50
  },
51
51
 
52
52
  /**
53
53
  * Code and codeblocks.
54
54
  */
55
+ '& code': {
56
+ fontFamily: fontMono,
57
+ color: 'var(--color-cm-code)',
58
+ whiteSpace: 'nowrap',
59
+ },
55
60
  '& .cm-code': {
56
61
  fontFamily: fontMono,
62
+ color: 'var(--color-cm-code)',
63
+ },
64
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
65
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
66
+ // so constrain them to the target 24px height.
67
+ '& .cm-code-inline': {
68
+ fontFamily: fontMono,
69
+ height: '24px',
70
+ // display: 'inline-flex',
71
+ alignItems: 'center',
72
+ overflow: 'hidden',
73
+ whiteSpace: 'nowrap',
74
+ color: 'var(--color-cm-code-inline)',
75
+ },
76
+ '& .cm-code-mark': {
77
+ fontFamily: fontMono,
78
+ height: '24px',
79
+ display: 'inline-flex',
80
+ alignItems: 'center',
81
+ overflow: 'hidden',
57
82
  },
58
83
  '& .cm-codeblock-line': {
59
- background: 'var(--dx-cmCodeblock)',
84
+ background: 'var(--color-cm-codeblock)',
60
85
  paddingInline: '1rem !important',
61
86
  },
62
87
  '& .cm-codeblock-start': {
@@ -87,16 +112,24 @@ export const formattingStyles = EditorView.theme({
87
112
  */
88
113
  '.cm-table *': {
89
114
  fontFamily: fontMono,
115
+ lineHeight: 1.5,
90
116
  textDecoration: 'none !important',
91
117
  },
92
118
  '.cm-table-head': {
93
119
  padding: '2px 16px 2px 0px',
120
+ overflowWrap: 'break-word',
121
+ whiteSpace: 'pre-wrap',
122
+ wordBreak: 'keep-all',
94
123
  textAlign: 'left',
95
- borderBottom: '1px solid var(--dx-cmSeparator)',
96
- color: 'var(--dx-subdued)',
124
+ color: 'var(--color-subdued)',
125
+ borderBottom: '1px solid var(--color-cm-separator)',
97
126
  },
98
127
  '.cm-table-cell': {
99
128
  padding: '2px 16px 2px 0px',
129
+ overflowWrap: 'break-word',
130
+ whiteSpace: 'pre-wrap',
131
+ wordBreak: 'keep-all',
132
+ verticalAlign: 'top',
100
133
  },
101
134
 
102
135
  /**
@@ -113,12 +146,12 @@ export const formattingStyles = EditorView.theme({
113
146
  },
114
147
  '.cm-image-with-loader': {
115
148
  display: 'block',
116
- opacity: '0',
149
+ opacity: 0,
117
150
  transitionDuration: '350ms',
118
151
  transitionProperty: 'opacity',
119
152
  },
120
153
  '.cm-image-with-loader.cm-loaded-image': {
121
- opacity: '1',
154
+ opacity: 1,
122
155
  },
123
156
  '.cm-image-wrapper': {
124
157
  'grid-template-columns': '1fr',
@@ -107,6 +107,28 @@ const update = (state: EditorState, _options: TableOptions) => {
107
107
  return builder.finish();
108
108
  };
109
109
 
110
+ /** Renders cell text into el, processing inline markdown (bold, italic, code). */
111
+ const renderCellContent = (el: HTMLElement, text: string): void => {
112
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
113
+ for (const part of parts) {
114
+ if (part.length > 2 && part.startsWith('`') && part.endsWith('`')) {
115
+ const code = document.createElement('code');
116
+ code.textContent = part.slice(1, -1);
117
+ el.appendChild(code);
118
+ } else if ((part.startsWith('**') && part.endsWith('**')) || (part.startsWith('__') && part.endsWith('__'))) {
119
+ const strong = document.createElement('strong');
120
+ strong.textContent = part.slice(2, -2);
121
+ el.appendChild(strong);
122
+ } else if ((part.startsWith('*') && part.endsWith('*')) || (part.startsWith('_') && part.endsWith('_'))) {
123
+ const em = document.createElement('em');
124
+ em.textContent = part.slice(1, -1);
125
+ el.appendChild(em);
126
+ } else {
127
+ el.appendChild(document.createTextNode(part));
128
+ }
129
+ }
130
+ };
131
+
110
132
  class TableWidget extends WidgetType {
111
133
  constructor(readonly _table: Table) {
112
134
  super();
@@ -132,7 +154,7 @@ class TableWidget extends WidgetType {
132
154
  this._table.header?.forEach((cell) => {
133
155
  const th = document.createElement('th');
134
156
  th.setAttribute('class', 'cm-table-head');
135
- tr.appendChild(th).textContent = cell;
157
+ renderCellContent(tr.appendChild(th), cell);
136
158
  });
137
159
 
138
160
  const body = table.appendChild(document.createElement('tbody'));
@@ -141,7 +163,7 @@ class TableWidget extends WidgetType {
141
163
  row.forEach((cell) => {
142
164
  const td = document.createElement('td');
143
165
  td.setAttribute('class', 'cm-table-cell');
144
- tr.appendChild(td).textContent = cell;
166
+ renderCellContent(tr.appendChild(td), cell);
145
167
  });
146
168
  });
147
169
 
@@ -8,7 +8,6 @@ import { describe, test } from 'vitest';
8
8
 
9
9
  import { join } from '../../util';
10
10
  import { createMarkdownExtensions } from '../markdown';
11
-
12
11
  import { indentItemLess, indentItemMore, moveItemDown, moveItemUp } from './commands';
13
12
  import { listItemToString, outlinerTree, treeFacet } from './tree';
14
13