@dxos/react-ui-editor 0.8.4-main.c1de068 → 0.8.4-main.e098934

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 (231) hide show
  1. package/dist/lib/browser/index.mjs +2074 -996
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +71 -1
  5. package/dist/lib/browser/testing/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +2074 -996
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/testing/index.mjs +71 -1
  10. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  11. package/dist/types/src/components/{Popover → CommandMenu}/CommandMenu.d.ts +10 -6
  12. package/dist/types/src/components/CommandMenu/CommandMenu.d.ts.map +1 -0
  13. package/dist/types/src/components/CommandMenu/index.d.ts +2 -0
  14. package/dist/types/src/components/CommandMenu/index.d.ts.map +1 -0
  15. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  17. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/util.d.ts +2 -2
  24. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
  26. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  27. package/dist/types/src/components/index.d.ts +1 -1
  28. package/dist/types/src/components/index.d.ts.map +1 -1
  29. package/dist/types/src/defaults.d.ts.map +1 -1
  30. package/dist/types/src/extensions/autocomplete.d.ts +20 -7
  31. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  32. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  33. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +9 -18
  34. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  36. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  37. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  38. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  39. package/dist/types/src/extensions/autoscroll.d.ts +10 -0
  40. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
  41. package/dist/types/src/extensions/command/action.d.ts +1 -1
  42. package/dist/types/src/extensions/command/action.d.ts.map +1 -1
  43. package/dist/types/src/extensions/command/command-menu.d.ts +1 -1
  44. package/dist/types/src/extensions/command/command-menu.d.ts.map +1 -1
  45. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  46. package/dist/types/src/extensions/command/floating-menu.d.ts.map +1 -1
  47. package/dist/types/src/extensions/command/hint.d.ts +2 -7
  48. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  49. package/dist/types/src/extensions/command/state.d.ts +1 -1
  50. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  51. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -1
  52. package/dist/types/src/extensions/command/useCommandMenu.d.ts +3 -4
  53. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +1 -1
  54. package/dist/types/src/extensions/comments.d.ts +1 -1
  55. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  56. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  57. package/dist/types/src/extensions/factories.d.ts +2 -7
  58. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  59. package/dist/types/src/extensions/index.d.ts +2 -0
  60. package/dist/types/src/extensions/index.d.ts.map +1 -1
  61. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  62. package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
  63. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  64. package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
  65. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  66. package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
  67. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  69. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
  71. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  72. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  73. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  74. package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
  75. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  76. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  77. package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
  78. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  79. package/dist/types/src/extensions/preview/preview.d.ts +3 -6
  80. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  81. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  82. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  83. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  84. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  85. package/dist/types/src/extensions/tags/index.d.ts +4 -0
  86. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  87. package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
  88. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
  89. package/dist/types/src/extensions/tags/xml-tags.d.ts +71 -0
  90. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  91. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  92. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  93. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  94. package/dist/types/src/stories/Command.stories.d.ts +12 -4
  95. package/dist/types/src/stories/Command.stories.d.ts.map +1 -1
  96. package/dist/types/src/stories/CommandMenu.stories.d.ts +10 -3
  97. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
  98. package/dist/types/src/stories/Comments.stories.d.ts +21 -9
  99. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  100. package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -2
  101. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  102. package/dist/types/src/stories/Experimental.stories.d.ts +22 -12
  103. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  104. package/dist/types/src/stories/Markdown.stories.d.ts +32 -42
  105. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  106. package/dist/types/src/stories/Outliner.stories.d.ts +15 -20
  107. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  108. package/dist/types/src/stories/Preview.stories.d.ts +21 -6
  109. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  110. package/dist/types/src/stories/Tags.stories.d.ts +17 -0
  111. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
  112. package/dist/types/src/stories/TextEditor.stories.d.ts +38 -51
  113. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  114. package/dist/types/src/stories/components/EditorStory.d.ts +3 -6
  115. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  116. package/dist/types/src/styles/theme.d.ts.map +1 -1
  117. package/dist/types/src/testing/PreviewPopover.d.ts +20 -0
  118. package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -0
  119. package/dist/types/src/testing/index.d.ts +1 -0
  120. package/dist/types/src/testing/index.d.ts.map +1 -1
  121. package/dist/types/src/testing/util.d.ts +1 -0
  122. package/dist/types/src/testing/util.d.ts.map +1 -1
  123. package/dist/types/src/translations.d.ts +1 -1
  124. package/dist/types/src/types/types.d.ts +1 -1
  125. package/dist/types/src/util/cursor.d.ts.map +1 -1
  126. package/dist/types/src/util/debug.d.ts +1 -1
  127. package/dist/types/src/util/debug.d.ts.map +1 -1
  128. package/dist/types/src/util/decorations.d.ts +4 -0
  129. package/dist/types/src/util/decorations.d.ts.map +1 -0
  130. package/dist/types/src/util/dom.d.ts +2 -12
  131. package/dist/types/src/util/dom.d.ts.map +1 -1
  132. package/dist/types/src/util/domino.d.ts +18 -0
  133. package/dist/types/src/util/domino.d.ts.map +1 -0
  134. package/dist/types/src/util/index.d.ts +2 -0
  135. package/dist/types/src/util/index.d.ts.map +1 -1
  136. package/dist/types/src/util/react.d.ts +1 -1
  137. package/dist/types/src/util/react.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +57 -51
  140. package/src/components/{Popover → CommandMenu}/CommandMenu.tsx +93 -26
  141. package/src/components/{Popover → CommandMenu}/index.ts +0 -2
  142. package/src/components/Editor/Editor.tsx +1 -1
  143. package/src/components/EditorToolbar/EditorToolbar.tsx +40 -30
  144. package/src/components/EditorToolbar/blocks.ts +21 -24
  145. package/src/components/EditorToolbar/formatting.ts +22 -25
  146. package/src/components/EditorToolbar/headings.ts +10 -5
  147. package/src/components/EditorToolbar/image.ts +8 -4
  148. package/src/components/EditorToolbar/lists.ts +16 -19
  149. package/src/components/EditorToolbar/search.ts +8 -4
  150. package/src/components/EditorToolbar/util.ts +16 -5
  151. package/src/components/EditorToolbar/view-mode.ts +11 -6
  152. package/src/components/index.ts +1 -1
  153. package/src/defaults.ts +5 -2
  154. package/src/extensions/autocomplete.ts +204 -54
  155. package/src/extensions/automerge/automerge.stories.tsx +25 -18
  156. package/src/extensions/automerge/automerge.ts +4 -3
  157. package/src/extensions/automerge/defs.ts +1 -1
  158. package/src/extensions/automerge/sync.ts +1 -1
  159. package/src/extensions/automerge/update-automerge.ts +1 -1
  160. package/src/extensions/autoscroll.ts +157 -0
  161. package/src/extensions/awareness/awareness.ts +2 -2
  162. package/src/extensions/command/action.ts +1 -2
  163. package/src/extensions/command/command-menu.ts +7 -6
  164. package/src/extensions/command/command.ts +3 -3
  165. package/src/extensions/command/floating-menu.ts +10 -15
  166. package/src/extensions/command/hint.ts +2 -1
  167. package/src/extensions/command/placeholder.ts +1 -1
  168. package/src/extensions/command/state.ts +4 -3
  169. package/src/extensions/command/typeahead.ts +2 -2
  170. package/src/extensions/command/useCommandMenu.ts +6 -9
  171. package/src/extensions/comments.ts +18 -13
  172. package/src/extensions/dnd.ts +1 -1
  173. package/src/extensions/factories.ts +9 -21
  174. package/src/extensions/folding.tsx +2 -2
  175. package/src/extensions/index.ts +2 -0
  176. package/src/extensions/markdown/action.ts +2 -1
  177. package/src/extensions/markdown/bundle.ts +25 -3
  178. package/src/extensions/markdown/changes.ts +1 -1
  179. package/src/extensions/markdown/decorate.ts +23 -14
  180. package/src/extensions/markdown/formatting.test.ts +6 -6
  181. package/src/extensions/markdown/formatting.ts +3 -3
  182. package/src/extensions/markdown/highlight.ts +1 -1
  183. package/src/extensions/markdown/image.ts +3 -4
  184. package/src/extensions/markdown/link.ts +3 -0
  185. package/src/extensions/markdown/table.ts +7 -1
  186. package/src/extensions/mention.ts +1 -1
  187. package/src/extensions/outliner/outliner.test.ts +3 -2
  188. package/src/extensions/outliner/outliner.ts +6 -5
  189. package/src/extensions/outliner/selection.ts +1 -1
  190. package/src/extensions/outliner/tree.test.ts +2 -1
  191. package/src/extensions/outliner/tree.ts +2 -2
  192. package/src/extensions/preview/preview.ts +59 -62
  193. package/src/extensions/tags/extended-markdown.test.ts +261 -0
  194. package/src/extensions/tags/extended-markdown.ts +78 -0
  195. package/src/extensions/tags/index.ts +7 -0
  196. package/src/extensions/tags/streamer.ts +244 -0
  197. package/src/extensions/tags/xml-tags.ts +335 -0
  198. package/src/extensions/tags/xml-util.ts +94 -0
  199. package/src/hooks/useTextEditor.ts +3 -15
  200. package/src/stories/Command.stories.tsx +24 -31
  201. package/src/stories/CommandMenu.stories.tsx +28 -29
  202. package/src/stories/Comments.stories.tsx +10 -6
  203. package/src/stories/EditorToolbar.stories.tsx +8 -8
  204. package/src/stories/Experimental.stories.tsx +12 -8
  205. package/src/stories/Markdown.stories.tsx +21 -17
  206. package/src/stories/Outliner.stories.tsx +42 -30
  207. package/src/stories/Preview.stories.tsx +30 -29
  208. package/src/stories/Tags.stories.tsx +81 -0
  209. package/src/stories/TextEditor.stories.tsx +40 -34
  210. package/src/stories/components/EditorStory.tsx +9 -10
  211. package/src/styles/theme.ts +8 -6
  212. package/src/testing/PreviewPopover.tsx +78 -0
  213. package/src/testing/index.ts +1 -0
  214. package/src/testing/util.ts +2 -0
  215. package/src/translations.ts +1 -1
  216. package/src/util/cursor.ts +2 -1
  217. package/src/util/debug.ts +2 -2
  218. package/src/util/decorations.ts +21 -0
  219. package/src/util/dom.ts +5 -27
  220. package/src/util/domino.ts +51 -0
  221. package/src/util/index.ts +2 -0
  222. package/src/util/react.tsx +1 -1
  223. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
  224. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
  225. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
  226. package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
  227. package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
  228. package/dist/types/src/components/Popover/index.d.ts +0 -4
  229. package/dist/types/src/components/Popover/index.d.ts.map +0 -1
  230. package/src/components/Popover/RefDropdownMenu.tsx +0 -85
  231. package/src/components/Popover/RefPopover.tsx +0 -99
@@ -4,20 +4,21 @@
4
4
 
5
5
  import { completionKeymap } from '@codemirror/autocomplete';
6
6
  import { defaultKeymap, indentWithTab } from '@codemirror/commands';
7
- import { markdownLanguage, markdown } from '@codemirror/lang-markdown';
7
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
8
8
  import { syntaxHighlighting } from '@codemirror/language';
9
9
  import { languages } from '@codemirror/language-data';
10
10
  import { type Extension } from '@codemirror/state';
11
11
  import { keymap } from '@codemirror/view';
12
+ import { type MarkdownConfig } from '@lezer/markdown';
12
13
 
13
- import { type ThemeMode } from '@dxos/react-ui';
14
14
  import { isNotFalsy } from '@dxos/util';
15
15
 
16
16
  import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
17
17
 
18
18
  export type MarkdownBundleOptions = {
19
- themeMode?: ThemeMode;
19
+ extensions?: MarkdownConfig[];
20
20
  indentWithTab?: boolean;
21
+ setextHeading?: boolean;
21
22
  };
22
23
 
23
24
  /**
@@ -51,6 +52,7 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
51
52
  extensions: [
52
53
  // GFM provided by default.
53
54
  markdownTagsExtensions,
55
+ ...(options.extensions ?? defaultExtensions()),
54
56
  ],
55
57
  }),
56
58
 
@@ -69,3 +71,23 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
69
71
  ),
70
72
  ];
71
73
  };
74
+
75
+ /**
76
+ * Default customizations.
77
+ * https://github.com/lezer-parser/markdown/blob/main/src/markdown.ts
78
+ */
79
+ export const defaultExtensions = (): MarkdownConfig[] => [noSetExtHeading, noHtml];
80
+
81
+ /**
82
+ * Remove SetextHeading (e.g., headings created from "---").
83
+ */
84
+ const noSetExtHeading: MarkdownConfig = {
85
+ remove: ['SetextHeading'],
86
+ };
87
+
88
+ /**
89
+ * Remove HTML and XML parsing.
90
+ */
91
+ const noHtml: MarkdownConfig = {
92
+ remove: ['HTMLBlock', 'HTMLTag'],
93
+ };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
6
  import { type ChangeSpec, Transaction } from '@codemirror/state';
7
- import { ViewPlugin, type ViewUpdate, type PluginValue } from '@codemirror/view';
7
+ import { type PluginValue, ViewPlugin, type ViewUpdate } from '@codemirror/view';
8
8
 
9
9
  /**
10
10
  * Monitors and augments changes.
@@ -3,20 +3,21 @@
3
3
  //
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
- import { RangeSetBuilder, type EditorState, StateEffect } from '@codemirror/state';
7
- import { EditorView, Decoration, type DecorationSet, WidgetType, ViewPlugin, type ViewUpdate } from '@codemirror/view';
6
+ import { type EditorState, Prec, RangeSetBuilder, StateEffect } from '@codemirror/state';
7
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
8
8
  import { type SyntaxNodeRef } from '@lezer/common';
9
9
 
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { mx } from '@dxos/react-ui-theme';
12
12
 
13
+ import { type HeadingLevel, theme } from '../../styles';
14
+ import { type RenderCallback } from '../../types';
15
+ import { wrapWithCatch } from '../../util';
16
+
13
17
  import { adjustChanges } from './changes';
14
18
  import { image } from './image';
15
- import { formattingStyles, bulletListIndentationWidth, orderedListIndentationWidth } from './styles';
19
+ import { bulletListIndentationWidth, formattingStyles, orderedListIndentationWidth } from './styles';
16
20
  import { table } from './table';
17
- import { theme, type HeadingLevel } from '../../styles';
18
- import { type RenderCallback } from '../../types';
19
- import { wrapWithCatch } from '../../util';
20
21
 
21
22
  /**
22
23
  * Unicode characters.
@@ -233,7 +234,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
233
234
  const mark = node.node.firstChild!;
234
235
  if (mark?.name === 'HeaderMark') {
235
236
  const { from, to = 6 } = options.numberedHeadings ?? {};
236
- const text = view.state.sliceDoc(node.from, node.to);
237
+ const text = state.sliceDoc(node.from, node.to);
237
238
  const len = text.match(/[#\s]+/)![0].length;
238
239
  if (!from || level < from || level > to) {
239
240
  atomicDeco.add(mark.from, mark.from + len, hide);
@@ -426,6 +427,9 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
426
427
  const editing = editingRange(state, node, focus);
427
428
  if (urlNode && marks.length >= 2) {
428
429
  const url = state.sliceDoc(urlNode.from, urlNode.to);
430
+ if (options.skip?.({ name: 'Link', url })) {
431
+ break;
432
+ }
429
433
  if (!editing) {
430
434
  atomicDeco.add(node.from, marks[0].to, hide);
431
435
  }
@@ -493,15 +497,15 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
493
497
  tree.iterate({
494
498
  from,
495
499
  to,
496
- enter: wrapWithCatch(enterNode),
497
- leave: wrapWithCatch(leaveNode),
500
+ enter: wrapWithCatch(enterNode, 'decorate.enter'),
501
+ leave: wrapWithCatch(leaveNode, 'decorate.leave'),
498
502
  });
499
503
  }
500
504
  } else {
501
505
  // NOTE: If line numbering then we must iterate from the start of document.
502
506
  tree.iterate({
503
- enter: wrapWithCatch(enterNode),
504
- leave: wrapWithCatch(leaveNode),
507
+ enter: wrapWithCatch(enterNode, 'decorate.enter'),
508
+ leave: wrapWithCatch(leaveNode, 'decorate.leave'),
505
509
  });
506
510
  }
507
511
 
@@ -513,15 +517,20 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
513
517
 
514
518
  const forceUpdate = StateEffect.define<null>();
515
519
 
520
+ export type NodeData = { name: 'Link'; url: string } | { name: 'Image'; url: string };
521
+
516
522
  export interface DecorateOptions {
517
523
  /**
518
524
  * Prevents triggering decorations as the cursor moves through the document.
519
525
  */
520
526
  selectionChangeDelay?: number;
521
527
  numberedHeadings?: { from: number; to?: number };
522
- renderLinkButton?: RenderCallback<{ url: string }>;
523
528
  // TODO(burdon): Additional padding for each line.
524
529
  listPaddingLeft?: number;
530
+ // TODO(burdon): Use consistently.
531
+ skip?: (node: NodeData) => boolean;
532
+ // TODO(burdon): Remove.
533
+ renderLinkButton?: RenderCallback<{ url: string }>;
525
534
  }
526
535
 
527
536
  export const decorateMarkdown = (options: DecorateOptions = {}) => {
@@ -577,9 +586,9 @@ export const decorateMarkdown = (options: DecorateOptions = {}) => {
577
586
  },
578
587
  {
579
588
  provide: (plugin) => [
580
- EditorView.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration.none),
589
+ Prec.low(EditorView.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration.none)),
581
590
  EditorView.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration.none),
582
- EditorView.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration.none),
591
+ EditorView.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration.none),
583
592
  ],
584
593
  },
585
594
  ),
@@ -7,21 +7,21 @@ import { EditorState, type StateCommand } from '@codemirror/state';
7
7
  import { describe, expect, test } from 'vitest';
8
8
 
9
9
  import {
10
+ type Formatting,
11
+ Inline,
12
+ List,
10
13
  addBlockquote,
11
14
  addCodeblock,
12
15
  addLink,
13
16
  addList,
14
17
  addStyle,
15
18
  getFormatting,
16
- removeStyle,
17
- removeLink,
18
- removeList,
19
19
  removeBlockquote,
20
20
  removeCodeblock,
21
+ removeLink,
22
+ removeList,
23
+ removeStyle,
21
24
  setHeading,
22
- Inline,
23
- List,
24
- type Formatting,
25
25
  } from './formatting';
26
26
 
27
27
  export const emptyFormatting: Formatting = {
@@ -7,14 +7,14 @@ import { syntaxTree } from '@codemirror/language';
7
7
  import {
8
8
  type ChangeSpec,
9
9
  EditorSelection,
10
- type Extension,
11
10
  type EditorState,
11
+ type Extension,
12
12
  type Line,
13
13
  type StateCommand,
14
14
  type Text,
15
15
  } from '@codemirror/state';
16
- import { EditorView, keymap, type ViewUpdate } from '@codemirror/view';
17
- import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
16
+ import { EditorView, type ViewUpdate, keymap } from '@codemirror/view';
17
+ import { type SyntaxNode, type SyntaxNodeRef } from '@lezer/common';
18
18
  import { useCallback, useMemo } from 'react';
19
19
 
20
20
  import { debounceAndThrottle } from '@dxos/async';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { markdownLanguage } from '@codemirror/lang-markdown';
6
6
  import { HighlightStyle } from '@codemirror/language';
7
- import { tags, styleTags, Tag } from '@lezer/highlight';
7
+ import { Tag, styleTags, tags } from '@lezer/highlight';
8
8
  import { type MarkdownConfig, Table } from '@lezer/markdown';
9
9
 
10
10
  import { fontBody, theme } from '../../styles';
@@ -17,8 +17,7 @@ export const image = (_options: ImageOptions = {}): Extension => {
17
17
  return [
18
18
  StateField.define<DecorationSet>({
19
19
  create: (state) => {
20
- // Process all images.
21
- return Decoration.set(buildDecorations(0, state.doc.length, state));
20
+ return Decoration.set(buildDecorations(state, 0, state.doc.length));
22
21
  },
23
22
  update: (value: DecorationSet, tr: Transaction) => {
24
23
  if (!tr.docChanged && !tr.selection) {
@@ -43,7 +42,7 @@ export const image = (_options: ImageOptions = {}): Extension => {
43
42
  filterFrom: from,
44
43
  filterTo: to,
45
44
  filter: () => false,
46
- add: buildDecorations(from, to, tr.state),
45
+ add: buildDecorations(tr.state, from, to),
47
46
  });
48
47
  },
49
48
  provide: (field) => EditorView.decorations.from(field),
@@ -51,7 +50,7 @@ export const image = (_options: ImageOptions = {}): Extension => {
51
50
  ];
52
51
  };
53
52
 
54
- const buildDecorations = (from: number, to: number, state: EditorState) => {
53
+ const buildDecorations = (state: EditorState, from: number, to: number) => {
55
54
  const decorations: Range<Decoration>[] = [];
56
55
  const cursor = state.selection.main.head;
57
56
  syntaxTree(state).iterate({
@@ -26,6 +26,9 @@ export const linkTooltip = (renderTooltip: RenderCallback<{ url: string }>) => {
26
26
  }
27
27
 
28
28
  const urlText = view.state.sliceDoc(url.from, url.to);
29
+ if (urlText.startsWith('dxn')) {
30
+ return null;
31
+ }
29
32
  return {
30
33
  pos: link.from,
31
34
  end: link.to,
@@ -94,7 +94,13 @@ const update = (state: EditorState, _options: TableOptions) => {
94
94
  } else {
95
95
  // Add class for styling.
96
96
  // TODO(burdon): Apply to each line?
97
- builder.add(table.from, table.to, Decoration.mark({ class: 'cm-table' }));
97
+ builder.add(
98
+ table.from,
99
+ table.to,
100
+ Decoration.mark({
101
+ class: 'cm-table',
102
+ }),
103
+ );
98
104
  }
99
105
  });
100
106
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { autocompletion, type CompletionContext, type CompletionResult } from '@codemirror/autocomplete';
5
+ import { type CompletionContext, type CompletionResult, autocompletion } from '@codemirror/autocomplete';
6
6
  import type { Extension } from '@codemirror/state';
7
7
 
8
8
  import { log } from '@dxos/log';
@@ -6,11 +6,12 @@ import { EditorSelection, EditorState } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
7
  import { describe, test } from 'vitest';
8
8
 
9
- import { indentItemLess, indentItemMore, moveItemDown, moveItemUp } from './commands';
10
- import { listItemToString, outlinerTree, treeFacet } from './tree';
11
9
  import { str } from '../../testing';
12
10
  import { createMarkdownExtensions } from '../markdown';
13
11
 
12
+ import { indentItemLess, indentItemMore, moveItemDown, moveItemUp } from './commands';
13
+ import { listItemToString, outlinerTree, treeFacet } from './tree';
14
+
14
15
  const lines = [
15
16
  '- [ ] 1',
16
17
  '- [ ] 2',
@@ -7,12 +7,13 @@ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate
7
7
 
8
8
  import { mx } from '@dxos/react-ui-theme';
9
9
 
10
+ import { floatingMenu } from '../command';
11
+ import { decorateMarkdown } from '../markdown';
12
+
10
13
  import { commands } from './commands';
11
14
  import { editor } from './editor';
12
- import { selectionCompartment, selectionFacet, selectionEquals } from './selection';
15
+ import { selectionCompartment, selectionEquals, selectionFacet } from './selection';
13
16
  import { outlinerTree, treeFacet } from './tree';
14
- import { floatingMenu } from '../command';
15
- import { decorateMarkdown } from '../markdown';
16
17
 
17
18
  // ISSUES:
18
19
  // TODO(burdon): Remove requirement for continuous lines to be indented (so that user's can't accidentally delete them and break the layout).
@@ -37,7 +38,7 @@ export type OutlinerProps = {};
37
38
  * - Constrains editor to outline structure.
38
39
  * - Supports smart cut-and-paste.
39
40
  */
40
- export const outliner = (options: OutlinerProps = {}): Extension => [
41
+ export const outliner = (_options: OutlinerProps = {}): Extension => [
41
42
  // Commands.
42
43
  Prec.highest(commands()),
43
44
 
@@ -158,7 +159,7 @@ const decorations = () => [
158
159
  '.cm-list-item-focused': {
159
160
  borderColor: 'var(--dx-accentFocusIndicator)',
160
161
  },
161
- '[data-has-focus] & .cm-list-item-selected': {
162
+ '&:focus-within .cm-list-item-selected': {
162
163
  borderColor: 'var(--dx-separator)',
163
164
  },
164
165
  }),
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Compartment, type EditorState, Facet, type SelectionRange } from '@codemirror/state';
6
- import { type EditorView, type Command } from '@codemirror/view';
6
+ import { type Command, type EditorView } from '@codemirror/view';
7
7
 
8
8
  import { treeFacet } from './tree';
9
9
 
@@ -6,10 +6,11 @@ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
6
6
  import { EditorState } from '@codemirror/state';
7
7
  import { beforeEach, describe, test } from 'vitest';
8
8
 
9
- import { outlinerTree, treeFacet, listItemToString, type Item } from './tree';
10
9
  import { str } from '../../testing';
11
10
  import { type Range } from '../../types';
12
11
 
12
+ import { type Item, listItemToString, outlinerTree, treeFacet } from './tree';
13
+
13
14
  const lines = [
14
15
  '- [ ] 1',
15
16
  '- [ ] 2',
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
- import { StateField, type Transaction, type Extension, type EditorState } from '@codemirror/state';
6
+ import { type EditorState, type Extension, StateField, type Transaction } from '@codemirror/state';
7
7
  import { Facet } from '@codemirror/state';
8
8
  import { type SyntaxNode } from '@lezer/common';
9
9
 
@@ -195,7 +195,7 @@ export type TreeOptions = {};
195
195
  * This adds overhead relative to the markdown AST, but allows for efficient traversal of the list items.
196
196
  * NOTE: Requires markdown parser to be enabled.
197
197
  */
198
- export const outlinerTree = (options: TreeOptions = {}): Extension => {
198
+ export const outlinerTree = (_options: TreeOptions = {}): Extension => {
199
199
  const buildTree = (state: EditorState): Tree => {
200
200
  let tree: Tree | undefined;
201
201
  let parent: Item | undefined;
@@ -2,21 +2,14 @@
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
 
19
10
  export type PreviewLinkRef = {
11
+ /** @deprecated */
12
+ // TODO(burdon): Remove?
20
13
  suggest?: boolean;
21
14
  block?: boolean;
22
15
  label: string;
@@ -29,10 +22,6 @@ export type PreviewLinkTarget = {
29
22
  object?: any;
30
23
  };
31
24
 
32
- // TODO(wittjosiah): Remove.
33
- // TODO(burdon): Handle error.
34
- export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
35
-
36
25
  export type PreviewOptions = {
37
26
  addBlockContainer?: (link: PreviewLinkRef, el: HTMLElement) => void;
38
27
  removeBlockContainer?: (link: PreviewLinkRef) => void;
@@ -44,10 +33,16 @@ export type PreviewOptions = {
44
33
  export const preview = (options: PreviewOptions = {}): Extension => {
45
34
  return [
46
35
  // 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"
36
+ // "Block decorations may not be specified via plugins".
48
37
  StateField.define<DecorationSet>({
49
38
  create: (state) => buildDecorations(state, options),
50
- update: (_: RangeSet<Decoration>, tr: Transaction) => buildDecorations(tr.state, options),
39
+ update: (decorations, tr) => {
40
+ if (tr.docChanged) {
41
+ return buildDecorations(tr.state, options);
42
+ }
43
+
44
+ return decorations.map(tr.changes);
45
+ },
51
46
  provide: (field) => [
52
47
  EditorView.decorations.from(field),
53
48
  EditorView.atomicRanges.of((view) => view.state.field(field)),
@@ -56,42 +51,19 @@ export const preview = (options: PreviewOptions = {}): Extension => {
56
51
  ];
57
52
  };
58
53
 
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
54
  /**
81
55
  * Echo references are represented as markdown reference links.
82
56
  * https://www.markdownguide.org/basic-syntax/#reference-style-links
83
- * [Label|block][dxn:echo:123]
84
- * [Label|inline][dxn:echo:123]
85
57
  */
86
- const buildDecorations = (state: EditorState, options: PreviewOptions) => {
58
+ const buildDecorations = (state: EditorState, options: PreviewOptions): DecorationSet => {
87
59
  const builder = new RangeSetBuilder<Decoration>();
88
60
 
89
61
  syntaxTree(state).iterate({
90
62
  enter: (node) => {
91
63
  switch (node.name) {
92
64
  //
93
- // Decoration.
94
- // [Label][dxn:echo:123]
65
+ // Inline widget.
66
+ // [Label](dxn:echo:123)
95
67
  //
96
68
  case 'Link': {
97
69
  const link = getLinkRef(state, node.node);
@@ -101,29 +73,32 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
101
73
  node.to,
102
74
  Decoration.replace({
103
75
  widget: new PreviewInlineWidget(options, link),
76
+ side: 1,
104
77
  }),
105
78
  );
106
79
  }
107
- break;
80
+ return false;
108
81
  }
82
+
109
83
  //
110
- // Block widget.
111
- // ![Label][dxn:echo:123]
84
+ // Block widget (transclusion).
85
+ // ![Label](dxn:echo:123)
112
86
  //
113
87
  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
- );
88
+ if (options.addBlockContainer && options.removeBlockContainer) {
89
+ const link = getLinkRef(state, node.node);
90
+ if (link) {
91
+ builder.add(
92
+ node.from,
93
+ node.to,
94
+ Decoration.replace({
95
+ block: true,
96
+ widget: new PreviewBlockWidget(options, link),
97
+ }),
98
+ );
99
+ }
125
100
  }
126
- break;
101
+ return false;
127
102
  }
128
103
  }
129
104
  },
@@ -132,9 +107,30 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
132
107
  return builder.finish();
133
108
  };
134
109
 
110
+ /**
111
+ * Link references.
112
+ * [Label](dxn:echo:123) Inline reference
113
+ * ![Label](dxn:echo:123) Block reference
114
+ */
115
+ export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
116
+ const mark = node.getChildren('LinkMark');
117
+ const urlNode = node.getChild('URL');
118
+ if (mark && urlNode) {
119
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
120
+ if (url.startsWith('dxn:')) {
121
+ const label = state.sliceDoc(mark[0].to, mark[1].from);
122
+ return {
123
+ block: state.sliceDoc(mark[0].from, mark[0].from + 1) === '!',
124
+ label,
125
+ ref: url,
126
+ };
127
+ }
128
+ }
129
+ };
130
+
135
131
  /**
136
132
  * Inline widget.
137
- * [Label][dxn:echo:123]
133
+ * [Label](dxn:echo:123)
138
134
  */
139
135
  class PreviewInlineWidget extends WidgetType {
140
136
  constructor(
@@ -152,8 +148,9 @@ class PreviewInlineWidget extends WidgetType {
152
148
  return this._link.ref === other._link.ref && this._link.label === other._link.label;
153
149
  }
154
150
 
155
- override toDOM(view: EditorView): HTMLElement {
156
- const root = document.createElement('dx-ref-tag');
151
+ override toDOM(_view: EditorView): HTMLElement {
152
+ const root = document.createElement('dx-anchor');
153
+ root.classList.add('dx-tag--anchor');
157
154
  root.textContent = this._link.label;
158
155
  root.setAttribute('refId', this._link.ref);
159
156
  return root;
@@ -161,7 +158,7 @@ class PreviewInlineWidget extends WidgetType {
161
158
  }
162
159
 
163
160
  /**
164
- * Block widget.
161
+ * Block widget (e.g., for surfaces).
165
162
  * ![Label][dxn:echo:123]
166
163
  */
167
164
  class PreviewBlockWidget extends WidgetType {
@@ -180,7 +177,7 @@ class PreviewBlockWidget extends WidgetType {
180
177
  return this._link.ref === other._link.ref;
181
178
  }
182
179
 
183
- override toDOM(view: EditorView): HTMLDivElement {
180
+ override toDOM(_view: EditorView): HTMLDivElement {
184
181
  const root = document.createElement('div');
185
182
  root.classList.add('cm-preview-block', 'density-coarse');
186
183
  this._options.addBlockContainer?.(this._link, root);