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

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 (210) hide show
  1. package/dist/lib/browser/index.mjs +2074 -888
  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.map +2 -2
  5. package/dist/lib/node-esm/index.mjs +2074 -888
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  9. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  10. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  11. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  12. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  13. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  14. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  15. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  17. package/dist/types/src/components/EditorToolbar/util.d.ts +2 -2
  18. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
  20. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  21. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +1 -1
  22. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +2 -9
  23. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -1
  24. package/dist/types/src/components/Popover/RefPopover.d.ts +20 -17
  25. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -1
  26. package/dist/types/src/defaults.d.ts.map +1 -1
  27. package/dist/types/src/extensions/autocomplete.d.ts +20 -7
  28. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  29. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  30. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +9 -18
  31. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  32. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  33. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  34. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  36. package/dist/types/src/extensions/autoscroll.d.ts +10 -0
  37. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
  38. package/dist/types/src/extensions/command/action.d.ts +1 -1
  39. package/dist/types/src/extensions/command/action.d.ts.map +1 -1
  40. package/dist/types/src/extensions/command/command-menu.d.ts +1 -1
  41. package/dist/types/src/extensions/command/command-menu.d.ts.map +1 -1
  42. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  43. package/dist/types/src/extensions/command/hint.d.ts +2 -7
  44. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  45. package/dist/types/src/extensions/command/state.d.ts +1 -1
  46. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  47. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -1
  48. package/dist/types/src/extensions/command/useCommandMenu.d.ts +4 -4
  49. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +1 -1
  50. package/dist/types/src/extensions/comments.d.ts +1 -1
  51. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  52. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  53. package/dist/types/src/extensions/factories.d.ts +2 -7
  54. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  55. package/dist/types/src/extensions/index.d.ts +2 -0
  56. package/dist/types/src/extensions/index.d.ts.map +1 -1
  57. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  58. package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
  59. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  60. package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
  61. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  62. package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
  63. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  64. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  65. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  66. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  69. package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
  70. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  71. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  72. package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
  73. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  74. package/dist/types/src/extensions/preview/preview.d.ts +3 -5
  75. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  76. package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
  77. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
  78. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
  79. package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
  80. package/dist/types/src/extensions/tags/index.d.ts +4 -0
  81. package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
  82. package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
  83. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
  84. package/dist/types/src/extensions/tags/xml-tags.d.ts +71 -0
  85. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
  86. package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
  87. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
  88. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  89. package/dist/types/src/stories/Command.stories.d.ts +12 -4
  90. package/dist/types/src/stories/Command.stories.d.ts.map +1 -1
  91. package/dist/types/src/stories/CommandMenu.stories.d.ts +10 -3
  92. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
  93. package/dist/types/src/stories/Comments.stories.d.ts +21 -9
  94. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  95. package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -2
  96. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  97. package/dist/types/src/stories/Experimental.stories.d.ts +22 -12
  98. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  99. package/dist/types/src/stories/Markdown.stories.d.ts +32 -42
  100. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  101. package/dist/types/src/stories/Outliner.stories.d.ts +15 -20
  102. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  103. package/dist/types/src/stories/Preview.stories.d.ts +21 -6
  104. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  105. package/dist/types/src/stories/Tags.stories.d.ts +17 -0
  106. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
  107. package/dist/types/src/stories/TextEditor.stories.d.ts +38 -51
  108. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  109. package/dist/types/src/stories/components/EditorStory.d.ts +3 -6
  110. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  111. package/dist/types/src/styles/theme.d.ts.map +1 -1
  112. package/dist/types/src/testing/util.d.ts +1 -0
  113. package/dist/types/src/testing/util.d.ts.map +1 -1
  114. package/dist/types/src/translations.d.ts +1 -1
  115. package/dist/types/src/types/types.d.ts +1 -1
  116. package/dist/types/src/util/cursor.d.ts.map +1 -1
  117. package/dist/types/src/util/debug.d.ts +1 -1
  118. package/dist/types/src/util/debug.d.ts.map +1 -1
  119. package/dist/types/src/util/decorations.d.ts +4 -0
  120. package/dist/types/src/util/decorations.d.ts.map +1 -0
  121. package/dist/types/src/util/dom.d.ts +2 -12
  122. package/dist/types/src/util/dom.d.ts.map +1 -1
  123. package/dist/types/src/util/domino.d.ts +18 -0
  124. package/dist/types/src/util/domino.d.ts.map +1 -0
  125. package/dist/types/src/util/index.d.ts +2 -0
  126. package/dist/types/src/util/index.d.ts.map +1 -1
  127. package/dist/types/src/util/react.d.ts +1 -1
  128. package/dist/types/src/util/react.d.ts.map +1 -1
  129. package/dist/types/tsconfig.tsbuildinfo +1 -1
  130. package/package.json +57 -51
  131. package/src/components/Editor/Editor.tsx +1 -1
  132. package/src/components/EditorToolbar/EditorToolbar.tsx +40 -30
  133. package/src/components/EditorToolbar/blocks.ts +21 -24
  134. package/src/components/EditorToolbar/formatting.ts +22 -25
  135. package/src/components/EditorToolbar/headings.ts +10 -5
  136. package/src/components/EditorToolbar/image.ts +8 -4
  137. package/src/components/EditorToolbar/lists.ts +16 -19
  138. package/src/components/EditorToolbar/search.ts +8 -4
  139. package/src/components/EditorToolbar/util.ts +16 -5
  140. package/src/components/EditorToolbar/view-mode.ts +11 -6
  141. package/src/components/Popover/CommandMenu.tsx +3 -3
  142. package/src/components/Popover/RefDropdownMenu.tsx +20 -16
  143. package/src/components/Popover/RefPopover.tsx +63 -45
  144. package/src/defaults.ts +5 -2
  145. package/src/extensions/autocomplete.ts +204 -54
  146. package/src/extensions/automerge/automerge.stories.tsx +25 -18
  147. package/src/extensions/automerge/automerge.ts +4 -3
  148. package/src/extensions/automerge/defs.ts +1 -1
  149. package/src/extensions/automerge/sync.ts +1 -1
  150. package/src/extensions/automerge/update-automerge.ts +1 -1
  151. package/src/extensions/autoscroll.ts +151 -0
  152. package/src/extensions/awareness/awareness.ts +2 -2
  153. package/src/extensions/command/action.ts +1 -1
  154. package/src/extensions/command/command-menu.ts +6 -5
  155. package/src/extensions/command/command.ts +3 -3
  156. package/src/extensions/command/floating-menu.ts +1 -1
  157. package/src/extensions/command/hint.ts +2 -1
  158. package/src/extensions/command/placeholder.ts +1 -1
  159. package/src/extensions/command/state.ts +4 -3
  160. package/src/extensions/command/typeahead.ts +2 -2
  161. package/src/extensions/command/useCommandMenu.ts +5 -4
  162. package/src/extensions/comments.ts +18 -13
  163. package/src/extensions/dnd.ts +1 -1
  164. package/src/extensions/factories.ts +9 -21
  165. package/src/extensions/folding.tsx +2 -2
  166. package/src/extensions/index.ts +2 -0
  167. package/src/extensions/markdown/action.ts +2 -1
  168. package/src/extensions/markdown/bundle.ts +25 -3
  169. package/src/extensions/markdown/changes.ts +1 -1
  170. package/src/extensions/markdown/decorate.ts +23 -14
  171. package/src/extensions/markdown/formatting.test.ts +6 -6
  172. package/src/extensions/markdown/formatting.ts +3 -3
  173. package/src/extensions/markdown/highlight.ts +1 -1
  174. package/src/extensions/markdown/image.ts +3 -4
  175. package/src/extensions/markdown/table.ts +7 -1
  176. package/src/extensions/mention.ts +1 -1
  177. package/src/extensions/outliner/outliner.test.ts +3 -2
  178. package/src/extensions/outliner/outliner.ts +5 -4
  179. package/src/extensions/outliner/selection.ts +1 -1
  180. package/src/extensions/outliner/tree.test.ts +2 -1
  181. package/src/extensions/outliner/tree.ts +2 -2
  182. package/src/extensions/preview/preview.ts +59 -59
  183. package/src/extensions/tags/extended-markdown.test.ts +261 -0
  184. package/src/extensions/tags/extended-markdown.ts +78 -0
  185. package/src/extensions/tags/index.ts +7 -0
  186. package/src/extensions/tags/streamer.ts +244 -0
  187. package/src/extensions/tags/xml-tags.ts +335 -0
  188. package/src/extensions/tags/xml-util.ts +94 -0
  189. package/src/hooks/useTextEditor.ts +3 -3
  190. package/src/stories/Command.stories.tsx +24 -31
  191. package/src/stories/CommandMenu.stories.tsx +23 -22
  192. package/src/stories/Comments.stories.tsx +10 -6
  193. package/src/stories/EditorToolbar.stories.tsx +8 -8
  194. package/src/stories/Experimental.stories.tsx +12 -8
  195. package/src/stories/Markdown.stories.tsx +21 -17
  196. package/src/stories/Outliner.stories.tsx +17 -14
  197. package/src/stories/Preview.stories.tsx +27 -26
  198. package/src/stories/Tags.stories.tsx +81 -0
  199. package/src/stories/TextEditor.stories.tsx +40 -34
  200. package/src/stories/components/EditorStory.tsx +9 -10
  201. package/src/styles/theme.ts +8 -6
  202. package/src/testing/util.ts +2 -0
  203. package/src/translations.ts +1 -1
  204. package/src/util/cursor.ts +2 -1
  205. package/src/util/debug.ts +2 -2
  206. package/src/util/decorations.ts +21 -0
  207. package/src/util/dom.ts +5 -27
  208. package/src/util/domino.ts +51 -0
  209. package/src/util/index.ts +2 -0
  210. package/src/util/react.tsx +1 -1
@@ -0,0 +1,151 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { StateEffect } from '@codemirror/state';
6
+ import { EditorView, ViewPlugin } from '@codemirror/view';
7
+
8
+ import { Domino } from '../util';
9
+
10
+ const lineHeight = 24;
11
+
12
+ export const scrollToBottomEffect = StateEffect.define<any>();
13
+
14
+ export type AutoScrollOptions = {
15
+ overscroll: number;
16
+ throttle: number;
17
+ };
18
+
19
+ /**
20
+ * Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
21
+ */
22
+ // TODO(burdon): Reconcile with transcript-extension.
23
+ export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Partial<AutoScrollOptions> = {}) => {
24
+ let isThrottled = false;
25
+ let isPinned = true;
26
+ let lastScrollTop = 0;
27
+ let timeout: NodeJS.Timeout | undefined;
28
+ let buttonContainer: HTMLDivElement;
29
+
30
+ const hideScrollbar = (view: EditorView) => {
31
+ view.scrollDOM.classList.add('cm-hide-scrollbar');
32
+ clearTimeout(timeout);
33
+ timeout = setTimeout(() => {
34
+ view.scrollDOM.classList.remove('cm-hide-scrollbar');
35
+ }, 1_000);
36
+ };
37
+
38
+ const scrollToBottom = (view: EditorView) => {
39
+ isPinned = true;
40
+ buttonContainer?.classList.add('opacity-0');
41
+ requestAnimationFrame(() => {
42
+ hideScrollbar(view);
43
+ view.scrollDOM.scrollTo({
44
+ top: view.scrollDOM.scrollHeight,
45
+ behavior: 'smooth',
46
+ });
47
+ });
48
+ };
49
+
50
+ return [
51
+ // Scroll button.
52
+ ViewPlugin.fromClass(
53
+ class {
54
+ constructor(view: EditorView) {
55
+ const scroller = view.scrollDOM.parentElement;
56
+ buttonContainer = Domino.of('div')
57
+ .classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
58
+ .child(
59
+ Domino.of('button')
60
+ .classNames('dx-button bg-accentSurface')
61
+ .data('density', 'fine')
62
+ .child(Domino.of<any>('dx-icon').attr('icon', 'ph--arrow-down--regular'))
63
+ .on('click', () => {
64
+ scrollToBottom(view);
65
+ }),
66
+ )
67
+ .build();
68
+ scroller?.appendChild(buttonContainer);
69
+ }
70
+ },
71
+ ),
72
+
73
+ // Update listener for logging when scrolling is needed.
74
+ EditorView.updateListener.of((update) => {
75
+ // Listen for effects.
76
+ update.transactions.forEach((transaction) => {
77
+ for (const effect of transaction.effects) {
78
+ if (effect.is(scrollToBottomEffect)) {
79
+ scrollToBottom(update.view);
80
+ }
81
+ }
82
+ });
83
+
84
+ if (update.docChanged && isPinned && !isThrottled) {
85
+ const distanceFromBottom = calcDistance(update.view.scrollDOM);
86
+
87
+ // Hide scrollbar even if not scrolling to bottom.
88
+ // hideScrollbar(update.view);
89
+
90
+ // Keep pinned.
91
+ if (distanceFromBottom > overscroll) {
92
+ isThrottled = true;
93
+ requestAnimationFrame(() => {
94
+ scrollToBottom(update.view);
95
+ });
96
+
97
+ // Reset throttle.
98
+ setTimeout(() => {
99
+ isThrottled = false;
100
+ }, throttle);
101
+ }
102
+ }
103
+ }),
104
+
105
+ EditorView.domEventHandlers({
106
+ scroll: (event, view) => {
107
+ const scroller = view.scrollDOM;
108
+ const distanceFromBottom = calcDistance(scroller);
109
+ if (distanceFromBottom === 0) {
110
+ // Pin to bottom.
111
+ isPinned = true;
112
+ buttonContainer?.classList.add('opacity-0');
113
+ } else if (scroller.scrollTop < lastScrollTop) {
114
+ // Break pin if user scrolls up.
115
+ isPinned = false;
116
+ buttonContainer?.classList.remove('opacity-0');
117
+ }
118
+
119
+ lastScrollTop = scroller.scrollTop;
120
+ },
121
+ }),
122
+
123
+ EditorView.theme({
124
+ '.cm-scroller': {
125
+ paddingBottom: `${overscroll}px`,
126
+ scrollbarWidth: 'thin',
127
+ },
128
+ '.cm-scroller.cm-hide-scrollbar': {
129
+ scrollbarWidth: 'none',
130
+ },
131
+ '.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
132
+ display: 'none',
133
+ },
134
+
135
+ // TODO(burdon): IconButton.
136
+ '.cm-scroll-button': {
137
+ position: 'absolute',
138
+ bottom: '0.5rem',
139
+ right: '1rem',
140
+ },
141
+ }),
142
+ ];
143
+ };
144
+
145
+ const calcDistance = (scroller: HTMLElement) => {
146
+ const scrollTop = scroller.scrollTop;
147
+ const scrollHeight = scroller.scrollHeight;
148
+ const clientHeight = scroller.clientHeight;
149
+ const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
150
+ return distanceFromBottom;
151
+ };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Annotation, type Extension, RangeSet, type Range } from '@codemirror/state';
5
+ import { Annotation, type Extension, type Range, RangeSet } from '@codemirror/state';
6
6
  import {
7
7
  Decoration,
8
8
  type DecorationSet,
@@ -16,7 +16,7 @@ import {
16
16
  import { Event } from '@dxos/async';
17
17
  import { Context } from '@dxos/context';
18
18
 
19
- import { singleValueFacet, Cursor, type CursorConverter } from '../../util';
19
+ import { Cursor, type CursorConverter, singleValueFacet } from '../../util';
20
20
 
21
21
  export interface AwarenessProvider {
22
22
  remoteStateChange: Event<void>;
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { StateEffect } from '@codemirror/state';
6
- import { type KeyBinding, type Command, type EditorView } from '@codemirror/view';
6
+ import { type Command, type EditorView, type KeyBinding } from '@codemirror/view';
7
7
 
8
8
  import { commandState } from './state';
9
9
 
@@ -2,12 +2,13 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { RangeSetBuilder, StateField, StateEffect, Prec } from '@codemirror/state';
6
- import { EditorView, ViewPlugin, type ViewUpdate, Decoration, keymap, type DecorationSet } from '@codemirror/view';
5
+ import { Prec, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
6
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, keymap } from '@codemirror/view';
7
7
 
8
- import { placeholder, type PlaceholderOptions } from './placeholder';
9
8
  import { type Range } from '../../types';
10
9
 
10
+ import { type PlaceholderOptions, placeholder } from './placeholder';
11
+
11
12
  export type CommandMenuOptions = {
12
13
  trigger: string | string[];
13
14
  placeholder?: Partial<PlaceholderOptions>;
@@ -37,12 +38,12 @@ export const commandMenu = (options: CommandMenuOptions) => {
37
38
  // Check if we should show the widget - only if cursor is within the active command range.
38
39
  const shouldShowWidget = activeRange && selection.head >= activeRange.from && selection.head <= activeRange.to;
39
40
  if (shouldShowWidget) {
40
- // Create mark decoration that wraps the entire line content in a dx-ref-tag.
41
+ // Create mark decoration that wraps the entire line content in a dx-anchor.
41
42
  builder.add(
42
43
  activeRange.from,
43
44
  activeRange.to,
44
45
  Decoration.mark({
45
- tagName: 'dx-ref-tag',
46
+ tagName: 'dx-anchor',
46
47
  class: 'cm-ref-tag',
47
48
  attributes: {
48
49
  'data-auto-trigger': 'true',
@@ -2,14 +2,14 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Prec, type Extension } from '@codemirror/state';
5
+ import { type Extension, Prec } from '@codemirror/state';
6
6
  import { EditorView, keymap } from '@codemirror/view';
7
7
 
8
8
  import { isNonNullable } from '@dxos/util';
9
9
 
10
10
  import { closeEffect, commandKeyBindings } from './action';
11
- import { hint, type HintOptions } from './hint';
12
- import { commandConfig, commandState, type PopupOptions } from './state';
11
+ import { type HintOptions, hint } from './hint';
12
+ import { type PopupOptions, commandConfig, commandState } from './state';
13
13
 
14
14
  // TODO(burdon): Create knowledge base for CM notes and ideas.
15
15
  // https://discuss.codemirror.net/t/inline-code-hints-like-vscode/5533/4
@@ -37,7 +37,7 @@ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
37
37
  const button = document.createElement('button');
38
38
  button.appendChild(icon);
39
39
 
40
- this.tag = document.createElement('dx-ref-tag');
40
+ this.tag = document.createElement('dx-anchor');
41
41
  this.tag.classList.add('cm-ref-tag');
42
42
  this.tag.appendChild(button);
43
43
  }
@@ -6,9 +6,10 @@
6
6
  import { RangeSetBuilder } from '@codemirror/state';
7
7
  import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
8
8
 
9
- import { commandState } from './state';
10
9
  import { clientRectsFor, flattenRect } from '../../util';
11
10
 
11
+ import { commandState } from './state';
12
+
12
13
  export type HintOptions = {
13
14
  delay?: number;
14
15
  onHint?: () => string | undefined;
@@ -4,7 +4,7 @@
4
4
  //
5
5
 
6
6
  import { type Extension } from '@codemirror/state';
7
- import { Decoration, EditorView, WidgetType, ViewPlugin, type ViewUpdate } from '@codemirror/view';
7
+ import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
8
8
 
9
9
  import { clientRectsFor, flattenRect } from '../../util';
10
10
 
@@ -3,13 +3,14 @@
3
3
  //
4
4
 
5
5
  import { StateField } from '@codemirror/state';
6
- import { showTooltip, type EditorView, type Tooltip, type TooltipView } from '@codemirror/view';
6
+ import { type EditorView, type Tooltip, type TooltipView, showTooltip } from '@codemirror/view';
7
7
 
8
- import { closeEffect, type Action, openEffect } from './action';
9
- import { type CommandOptions } from './command';
10
8
  import { type RenderCallback } from '../../types';
11
9
  import { singleValueFacet } from '../../util';
12
10
 
11
+ import { type Action, closeEffect, openEffect } from './action';
12
+ import { type CommandOptions } from './command';
13
+
13
14
  export const commandConfig = singleValueFacet<CommandOptions>();
14
15
 
15
16
  export type PopupOptions = {
@@ -2,15 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { EditorSelection, Prec, RangeSetBuilder, type Extension } from '@codemirror/state';
5
+ import { EditorSelection, type Extension, Prec, RangeSetBuilder } from '@codemirror/state';
6
6
  import {
7
7
  type Command,
8
8
  Decoration,
9
9
  type DecorationSet,
10
10
  type EditorView,
11
- keymap,
12
11
  ViewPlugin,
13
12
  type ViewUpdate,
13
+ keymap,
14
14
  } from '@codemirror/view';
15
15
 
16
16
  import { Hint } from './hint';
@@ -5,12 +5,13 @@
5
5
  import { type EditorView } from '@codemirror/view';
6
6
  import { type RefObject, useCallback, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { type DxRefTag, type DxRefTagActivate } from '@dxos/lit-ui';
8
+ import { type DxAnchor, type DxAnchorActivate } from '@dxos/lit-ui';
9
9
  import { type MaybePromise } from '@dxos/util';
10
10
 
11
+ import { type CommandMenuGroup, type CommandMenuItem, getItem, getNextItem, getPreviousItem } from '../../components';
12
+
11
13
  import { commandMenu, commandRangeEffect } from './command-menu';
12
14
  import { type PlaceholderOptions } from './placeholder';
13
- import { getItem, getNextItem, getPreviousItem, type CommandMenuGroup, type CommandMenuItem } from '../../components';
14
15
 
15
16
  export type UseCommandMenuOptions = {
16
17
  viewRef: RefObject<EditorView | undefined>;
@@ -20,7 +21,7 @@ export type UseCommandMenuOptions = {
20
21
  };
21
22
 
22
23
  export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCommandMenuOptions) => {
23
- const triggerRef = useRef<DxRefTag | null>(null);
24
+ const triggerRef = useRef<DxAnchor | null>(null);
24
25
  const currentRef = useRef<CommandMenuItem | null>(null);
25
26
  const groupsRef = useRef<CommandMenuGroup[]>([]);
26
27
  const [currentItem, setCurrentItem] = useState<string>();
@@ -43,7 +44,7 @@ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCo
43
44
  );
44
45
 
45
46
  const handleActivate = useCallback(
46
- async (event: DxRefTagActivate) => {
47
+ async (event: DxAnchorActivate) => {
47
48
  const item = getItem(groupsRef.current, currentItem);
48
49
  if (item) {
49
50
  currentRef.current = item;
@@ -5,25 +5,26 @@
5
5
  import { invertedEffects } from '@codemirror/commands';
6
6
  import { type ChangeDesc, type Extension, StateEffect, StateField, type Text } from '@codemirror/state';
7
7
  import {
8
- hoverTooltip,
9
- keymap,
10
8
  type Command,
11
9
  Decoration,
12
10
  EditorView,
13
- type Rect,
14
11
  type PluginValue,
12
+ type Rect,
15
13
  ViewPlugin,
14
+ hoverTooltip,
15
+ keymap,
16
16
  } from '@codemirror/view';
17
17
  import sortBy from 'lodash.sortby';
18
18
  import { useEffect } from 'react';
19
19
 
20
- import { debounce, type CleanupFn } from '@dxos/async';
20
+ import { type CleanupFn, debounce } from '@dxos/async';
21
21
  import { log } from '@dxos/log';
22
22
  import { isNonNullable } from '@dxos/util';
23
23
 
24
+ import { type Comment, type Range, type RenderCallback } from '../types';
25
+ import { Cursor, callbackWrapper, singleValueFacet } from '../util';
26
+
24
27
  import { documentId } from './selection';
25
- import { type RenderCallback, type Comment, type Range } from '../types';
26
- import { Cursor, singleValueFacet, callbackWrapper } from '../util';
27
28
 
28
29
  //
29
30
  // State management.
@@ -57,7 +58,11 @@ const setCommentState = StateEffect.define<CommentsState>();
57
58
  * The ranges are tracked as Automerge cursors from which the absolute indexed ranges can be computed.
58
59
  */
59
60
  export const commentsState = StateField.define<CommentsState>({
60
- create: (state) => ({ id: state.facet(documentId), comments: [], selection: {} }),
61
+ create: (state) => ({
62
+ id: state.facet(documentId),
63
+ comments: [],
64
+ selection: {},
65
+ }),
61
66
  update: (value, tr) => {
62
67
  for (const effect of tr.effects) {
63
68
  // Update selection.
@@ -98,16 +103,16 @@ export const commentsState = StateField.define<CommentsState>({
98
103
  */
99
104
  const styles = EditorView.theme({
100
105
  '.cm-comment, .cm-comment-current': {
101
- margin: '0 -3px',
102
- padding: '3px',
103
- borderRadius: '3px',
106
+ padding: '3px 0',
107
+ backgroundColor: 'var(--dx-cmCommentSurface)',
108
+ },
109
+ '.cm-comment > span, .cm-comment-current > span': {
110
+ boxDecorationBreak: 'clone',
111
+ boxShadow: '0 0 1px 3px var(--dx-cmCommentSurface)',
104
112
  backgroundColor: 'var(--dx-cmCommentSurface)',
105
113
  color: 'var(--dx-cmComment)',
106
114
  cursor: 'pointer',
107
115
  },
108
- '.cm-comment:hover, .cm-comment-current': {
109
- textDecoration: 'underline',
110
- },
111
116
  });
112
117
 
113
118
  const createCommentMark = (id: string, isCurrent: boolean) =>
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import type { Extension } from '@codemirror/state';
6
- import { dropCursor, EditorView } from '@codemirror/view';
6
+ import { EditorView, dropCursor } from '@codemirror/view';
7
7
 
8
8
  export type DNDOptions = { onDrop?: (view: EditorView, event: { files: FileList }) => void };
9
9
 
@@ -31,11 +31,12 @@ import { type ThemeMode } from '@dxos/react-ui';
31
31
  import { type HuePalette } from '@dxos/react-ui-theme';
32
32
  import { hexToHue, isNotFalsy } from '@dxos/util';
33
33
 
34
+ import { editorGutter, editorMonospace } from '../defaults';
35
+ import { type ThemeStyles, defaultTheme } from '../styles';
36
+
34
37
  import { automerge } from './automerge';
35
38
  import { SpaceAwarenessProvider, awareness } from './awareness';
36
39
  import { focus } from './focus';
37
- import { editorGutter, editorMonospace } from '../defaults';
38
- import { type ThemeStyles, defaultTheme } from '../styles';
39
40
 
40
41
  //
41
42
  // Basic
@@ -47,6 +48,7 @@ export const preventNewline = EditorState.transactionFilter.of((tr) => (tr.newDo
47
48
  * https://codemirror.net/docs/extensions
48
49
  * https://github.com/codemirror/basic-setup
49
50
  * https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
51
+ * https://github.com/codemirror/theme-one-dark
50
52
  */
51
53
  export type BasicExtensionsOptions = {
52
54
  allowMultipleSelections?: boolean;
@@ -82,7 +84,7 @@ const defaultBasicOptions: BasicExtensionsOptions = {
82
84
  history: true,
83
85
  keymap: 'standard',
84
86
  lineWrapping: true,
85
- search: true,
87
+ search: false,
86
88
  } as const;
87
89
 
88
90
  const keymaps: { [key: string]: readonly KeyBinding[] } = {
@@ -157,9 +159,6 @@ export type ThemeExtensionsOptions = {
157
159
  scroll?: {
158
160
  className?: string;
159
161
  };
160
- scroller?: {
161
- className?: string;
162
- };
163
162
  content?: {
164
163
  className?: string;
165
164
  };
@@ -186,31 +185,21 @@ export const defaultThemeSlots = grow;
186
185
  export const createThemeExtensions = ({
187
186
  themeMode,
188
187
  styles,
189
- syntaxHighlighting: _syntaxHighlighting,
188
+ syntaxHighlighting: syntaxHighlightingProps,
190
189
  slots: _slots,
191
190
  }: ThemeExtensionsOptions = {}): Extension => {
192
191
  const slots = defaultsDeep({}, _slots, defaultThemeSlots);
193
192
  return [
194
193
  EditorView.darkTheme.of(themeMode === 'dark'),
195
194
  EditorView.baseTheme(styles ? merge({}, defaultTheme, styles) : defaultTheme),
196
- // https://github.com/codemirror/theme-one-dark
197
- _syntaxHighlighting &&
198
- (themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
195
+ syntaxHighlightingProps && syntaxHighlighting(themeMode === 'dark' ? oneDarkHighlightStyle : defaultHighlightStyle),
199
196
  slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
200
197
  slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
201
198
  slots.scroll?.className &&
202
199
  ViewPlugin.fromClass(
203
200
  class {
204
201
  constructor(view: EditorView) {
205
- view.scrollDOM.classList.add(slots.scroll.className);
206
- }
207
- },
208
- ),
209
- slots.scroller?.className &&
210
- ViewPlugin.fromClass(
211
- class {
212
- constructor(view: EditorView) {
213
- view.dom.querySelector('.cm-scroller')?.classList.add(...slots.scroller.className.split(' '));
202
+ view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
214
203
  }
215
204
  },
216
205
  ),
@@ -238,7 +227,6 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
238
227
  if (space && identity) {
239
228
  const peerId = identity?.identityKey.toHex();
240
229
  const hue = (identity?.profile?.data?.hue as HuePalette | undefined) ?? hexToHue(peerId ?? '0');
241
-
242
230
  extensions.push(
243
231
  awareness(
244
232
  new SpaceAwarenessProvider({
@@ -246,9 +234,9 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
246
234
  channel: `awareness.${id}`,
247
235
  peerId: identity.identityKey.toHex(),
248
236
  info: {
249
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
250
237
  darkColor: `var(--dx-${hue}Cursor)`,
251
238
  lightColor: `var(--dx-${hue}Cursor)`,
239
+ displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
252
240
  },
253
241
  }),
254
242
  ),
@@ -9,7 +9,7 @@ import React from 'react';
9
9
 
10
10
  import { Icon } from '@dxos/react-ui';
11
11
 
12
- import { createElement, renderRoot } from '../util';
12
+ import { Domino, renderRoot } from '../util';
13
13
 
14
14
  export type FoldingOptions = {};
15
15
 
@@ -26,7 +26,7 @@ export const folding = (_props: FoldingOptions = {}): Extension => [
26
26
  foldGutter({
27
27
  markerDOM: (open) => {
28
28
  // TODO(burdon): Use sprite directly.
29
- const el = createElement('div', { className: 'flex h-full items-center' });
29
+ const el = Domino.of('div').classNames('flex h-full items-center').build();
30
30
  return renderRoot(
31
31
  el,
32
32
  <Icon icon='ph--caret-right--bold' size={3} classNames={['mx-3 cursor-pointer', open && 'rotate-90']} />,
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './annotations';
6
6
  export * from './autocomplete';
7
+ export * from './autoscroll';
7
8
  export * from './automerge';
8
9
  export * from './awareness';
9
10
  export * from './blast';
@@ -23,4 +24,5 @@ export * from './modes';
23
24
  export * from './outliner';
24
25
  export * from './preview';
25
26
  export * from './selection';
27
+ export * from './tags';
26
28
  export * from './typewriter';
@@ -7,6 +7,8 @@ import { type EditorView } from '@codemirror/view';
7
7
  import { type Action } from '@dxos/app-graph';
8
8
  import { type MenuActionProperties } from '@dxos/react-ui-menu';
9
9
 
10
+ import { createComment } from '../comments';
11
+
10
12
  import {
11
13
  Inline,
12
14
  List,
@@ -25,7 +27,6 @@ import {
25
27
  toggleList,
26
28
  toggleStyle,
27
29
  } from './formatting';
28
- import { createComment } from '../comments';
29
30
 
30
31
  export type PayloadType =
31
32
  | 'view-mode'
@@ -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.