@dxos/react-ui-editor 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446

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 (193) hide show
  1. package/dist/lib/browser/index.mjs +5088 -5521
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +4915 -5354
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +5088 -5521
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/{components/EditorToolbar/EditorToolbar.stories.d.ts → InputMode.stories.d.ts} +7 -3
  11. package/dist/types/src/InputMode.stories.d.ts.map +1 -0
  12. package/dist/types/src/{stories/TextEditorBasic.stories.d.ts → TextEditor.stories.d.ts} +35 -5
  13. package/dist/types/src/TextEditor.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  15. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -4
  17. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/comment.d.ts +3 -4
  19. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -4
  21. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -4
  23. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/lists.d.ts +3 -4
  25. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/util.d.ts +20 -14
  27. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/{view-mode.d.ts → viewMode.d.ts} +4 -5
  29. package/dist/types/src/components/EditorToolbar/viewMode.d.ts.map +1 -0
  30. package/dist/types/src/defaults.d.ts +0 -1
  31. package/dist/types/src/defaults.d.ts.map +1 -1
  32. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  33. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  34. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  36. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  37. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  38. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  39. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  40. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  41. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  42. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  43. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  44. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  45. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  46. package/dist/types/src/extensions/command/command.d.ts +10 -5
  47. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  48. package/dist/types/src/extensions/command/hint.d.ts +2 -4
  49. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  50. package/dist/types/src/extensions/command/index.d.ts +0 -1
  51. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  52. package/dist/types/src/extensions/command/menu.d.ts +2 -7
  53. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  54. package/dist/types/src/extensions/command/preview.d.ts +12 -0
  55. package/dist/types/src/extensions/command/preview.d.ts.map +1 -0
  56. package/dist/types/src/extensions/command/state.d.ts +11 -9
  57. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  58. package/dist/types/src/extensions/comments.d.ts +7 -9
  59. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  60. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  61. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  62. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  63. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  64. package/dist/types/src/extensions/index.d.ts +0 -1
  65. package/dist/types/src/extensions/index.d.ts.map +1 -1
  66. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  69. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -5
  71. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  72. package/dist/types/src/extensions/markdown/formatting.d.ts +3 -3
  73. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  74. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  75. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  76. package/dist/types/src/extensions/markdown/index.d.ts +0 -1
  77. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  78. package/dist/types/src/extensions/markdown/link.d.ts +1 -4
  79. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  80. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  81. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  82. package/dist/types/src/extensions/modes.d.ts +5 -5
  83. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  84. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  85. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  86. package/dist/types/src/fragments.d.ts +3 -0
  87. package/dist/types/src/fragments.d.ts.map +1 -0
  88. package/dist/types/src/hooks/index.d.ts +1 -0
  89. package/dist/types/src/hooks/index.d.ts.map +1 -1
  90. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  91. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  92. package/dist/types/src/hooks/useTextEditor.d.ts +1 -2
  93. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  94. package/dist/types/src/styles/theme.d.ts.map +1 -1
  95. package/dist/types/src/types.d.ts +0 -5
  96. package/dist/types/src/types.d.ts.map +1 -1
  97. package/dist/types/src/util/cursor.d.ts.map +1 -1
  98. package/dist/types/src/util/debug.d.ts.map +1 -1
  99. package/dist/types/src/util/dom.d.ts.map +1 -1
  100. package/dist/types/src/util/facet.d.ts.map +1 -1
  101. package/dist/types/src/util/react.d.ts +1 -6
  102. package/dist/types/src/util/react.d.ts.map +1 -1
  103. package/dist/types/tsconfig.tsbuildinfo +1 -1
  104. package/package.json +27 -37
  105. package/src/InputMode.stories.tsx +124 -0
  106. package/src/TextEditor.stories.tsx +856 -0
  107. package/src/components/EditorToolbar/EditorToolbar.tsx +34 -33
  108. package/src/components/EditorToolbar/blocks.ts +6 -27
  109. package/src/components/EditorToolbar/comment.ts +4 -11
  110. package/src/components/EditorToolbar/formatting.ts +7 -34
  111. package/src/components/EditorToolbar/headings.ts +8 -9
  112. package/src/components/EditorToolbar/lists.ts +7 -26
  113. package/src/components/EditorToolbar/util.ts +17 -17
  114. package/src/components/EditorToolbar/{view-mode.ts → viewMode.ts} +8 -9
  115. package/src/defaults.ts +3 -5
  116. package/src/extensions/automerge/automerge.stories.tsx +17 -11
  117. package/src/extensions/automerge/automerge.test.tsx +4 -4
  118. package/src/extensions/automerge/automerge.ts +2 -2
  119. package/src/extensions/automerge/defs.ts +2 -1
  120. package/src/extensions/automerge/sync.ts +4 -4
  121. package/src/extensions/automerge/update-automerge.ts +1 -1
  122. package/src/extensions/automerge/update-codemirror.ts +4 -3
  123. package/src/extensions/command/command.ts +27 -9
  124. package/src/extensions/command/hint.ts +30 -33
  125. package/src/extensions/command/index.ts +0 -1
  126. package/src/extensions/command/menu.ts +8 -11
  127. package/src/extensions/command/preview.ts +79 -0
  128. package/src/extensions/command/state.ts +61 -41
  129. package/src/extensions/comments.ts +9 -9
  130. package/src/extensions/folding.tsx +1 -1
  131. package/src/extensions/index.ts +0 -1
  132. package/src/extensions/markdown/changes.ts +2 -3
  133. package/src/extensions/markdown/decorate.ts +10 -12
  134. package/src/extensions/markdown/formatting.ts +6 -6
  135. package/src/extensions/markdown/image.ts +11 -12
  136. package/src/extensions/markdown/index.ts +0 -1
  137. package/src/extensions/markdown/link.ts +24 -33
  138. package/src/extensions/markdown/styles.ts +2 -2
  139. package/src/extensions/modes.ts +6 -5
  140. package/src/fragments.ts +19 -0
  141. package/src/hooks/index.ts +1 -0
  142. package/src/hooks/useActionHandler.ts +12 -0
  143. package/src/hooks/useTextEditor.ts +3 -4
  144. package/src/styles/theme.ts +0 -3
  145. package/src/types.ts +0 -7
  146. package/src/util/react.tsx +2 -20
  147. package/dist/lib/browser/testing/index.mjs +0 -67
  148. package/dist/lib/browser/testing/index.mjs.map +0 -7
  149. package/dist/lib/node/testing/index.cjs +0 -101
  150. package/dist/lib/node/testing/index.cjs.map +0 -7
  151. package/dist/lib/node-esm/testing/index.mjs +0 -69
  152. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  153. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +0 -1
  154. package/dist/types/src/components/EditorToolbar/image.d.ts +0 -16
  155. package/dist/types/src/components/EditorToolbar/image.d.ts.map +0 -1
  156. package/dist/types/src/components/EditorToolbar/search.d.ts +0 -17
  157. package/dist/types/src/components/EditorToolbar/search.d.ts.map +0 -1
  158. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +0 -1
  159. package/dist/types/src/extensions/command/action.d.ts +0 -17
  160. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  161. package/dist/types/src/extensions/markdown/outliner.d.ts +0 -12
  162. package/dist/types/src/extensions/markdown/outliner.d.ts.map +0 -1
  163. package/dist/types/src/extensions/preview/index.d.ts +0 -2
  164. package/dist/types/src/extensions/preview/index.d.ts.map +0 -1
  165. package/dist/types/src/extensions/preview/preview.d.ts +0 -39
  166. package/dist/types/src/extensions/preview/preview.d.ts.map +0 -1
  167. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +0 -1
  168. package/dist/types/src/stories/TextEditorComments.stories.d.ts +0 -13
  169. package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +0 -1
  170. package/dist/types/src/stories/TextEditorPreview.stories.d.ts +0 -13
  171. package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +0 -1
  172. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts +0 -19
  173. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +0 -1
  174. package/dist/types/src/stories/story-utils.d.ts +0 -53
  175. package/dist/types/src/stories/story-utils.d.ts.map +0 -1
  176. package/dist/types/src/testing/RefPopover.d.ts +0 -21
  177. package/dist/types/src/testing/RefPopover.d.ts.map +0 -1
  178. package/dist/types/src/testing/index.d.ts +0 -2
  179. package/dist/types/src/testing/index.d.ts.map +0 -1
  180. package/src/components/EditorToolbar/EditorToolbar.stories.tsx +0 -90
  181. package/src/components/EditorToolbar/image.ts +0 -16
  182. package/src/components/EditorToolbar/search.ts +0 -19
  183. package/src/extensions/command/action.ts +0 -49
  184. package/src/extensions/markdown/outliner.ts +0 -235
  185. package/src/extensions/preview/index.ts +0 -5
  186. package/src/extensions/preview/preview.ts +0 -271
  187. package/src/stories/TextEditorBasic.stories.tsx +0 -333
  188. package/src/stories/TextEditorComments.stories.tsx +0 -99
  189. package/src/stories/TextEditorPreview.stories.tsx +0 -239
  190. package/src/stories/TextEditorSpecial.stories.tsx +0 -107
  191. package/src/stories/story-utils.tsx +0 -327
  192. package/src/testing/RefPopover.tsx +0 -74
  193. package/src/testing/index.ts +0 -5
@@ -1,235 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { syntaxTree } from '@codemirror/language';
6
- import {
7
- type ChangeSpec,
8
- type Extension,
9
- type Line,
10
- StateField,
11
- type Transaction,
12
- EditorState,
13
- type Range,
14
- } from '@codemirror/state';
15
- import { Decoration, type DecorationSet, EditorView } from '@codemirror/view';
16
-
17
- import { log } from '@dxos/log';
18
- import { mx } from '@dxos/react-ui-theme';
19
-
20
- // TODO(burdon): Cut-and-paste.
21
- // TODO(burdon): Toggle list/task mode (in gutter?)
22
- // TODO(burdon): Convert to task object and insert link (menu button).
23
- // TOOD(burdon): Continuation lines and rich formatting?
24
-
25
- const indentLevel = 2;
26
- const matchTaskMarker = /^\s*- (\[ \]|\[x\])? /;
27
-
28
- /**
29
- * Get the starting position of text on the given line.
30
- */
31
- const getLineInfo = (line: Line) => {
32
- const match = line.text.match(matchTaskMarker);
33
- const start = line.from + (match?.[0]?.length ?? 0);
34
- return {
35
- match, // TODO(burdon): Indicate task or list marker.
36
- start,
37
- };
38
- };
39
-
40
- /**
41
- * Outliner extension.
42
- * - Store outline as a standard markdown document with task and list markers.
43
- * - Support continuation lines and rich formatting (with Shift+Enter).
44
- * - Constrain editor to outline structure.
45
- * - Support smart cut-and-paste.
46
- * - Support extracted links.
47
- * - Drag/drop lines or move them via shortcuts.
48
- */
49
- export const outliner = (): Extension => [
50
- EditorState.transactionFilter.of((tr) => {
51
- // Don't allow cursor before marker.
52
- if (!tr.docChanged) {
53
- const pos = tr.selection?.ranges[tr.selection?.mainIndex]?.from;
54
- if (pos != null) {
55
- const { match, start } = getLineInfo(tr.startState.doc.lineAt(pos));
56
- if (match) {
57
- if (pos < start) {
58
- return [{ selection: { anchor: start, head: start } }];
59
- }
60
- }
61
- }
62
-
63
- return tr;
64
- }
65
-
66
- const changes: ChangeSpec[] = [];
67
- tr.changes.iterChanges((fromA, toA, fromB, toB, insert) => {
68
- // NOTE: Task markers are atomic so will be deleted when backspace is pressed.
69
- // NOTE: CM inserts 2 or 6 spaces when deleting a list or task marker to create a continuation.
70
- // - [ ] <- backspace here deletes the task marker.
71
- // - [ ] <- backspace here inserts 6 spaces (creates continuation).
72
- // - [ ] <- backspace here deletes the task marker.
73
-
74
- const line = tr.startState.doc.lineAt(fromA);
75
- const isTaskMarker = line.text.match(matchTaskMarker);
76
- if (isTaskMarker) {
77
- const { start } = getLineInfo(line);
78
-
79
- // Detect and cancel replacement of task marker with continuation indent.
80
- const replace = start === toA && toA - fromA === insert.length;
81
- if (replace) {
82
- log.info('delete line');
83
- changes.push({ from: line.from - 1, to: toA });
84
- return;
85
- }
86
-
87
- // Detect deletion of marker.
88
- if (fromB === toB) {
89
- if (toA === line.to) {
90
- const line = tr.state.doc.lineAt(fromA);
91
- if (line.text.match(/^\s*$/)) {
92
- if (line.from === 0) {
93
- // Don't delete first line.
94
- log.info('skip');
95
- changes.push({ from: 0, to: 0 });
96
- return;
97
- } else {
98
- // Delete indent and marker.
99
- log.info('delete line');
100
- changes.push({ from: line.from - 1, to: toA });
101
- return;
102
- }
103
- }
104
- }
105
- return;
106
- }
107
-
108
- // Check appropriate indentation relative to previous line.
109
- if (insert.length === indentLevel) {
110
- if (line.number === 1) {
111
- log.info('skip');
112
- changes.push({ from: 0, to: 0 });
113
- return;
114
- } else {
115
- const getIndent = (text: string) => (text.match(/^\s*/)?.[0]?.length ?? 0) / indentLevel;
116
- const currentIndent = getIndent(line.text);
117
- const indentPrevious = getIndent(tr.state.doc.lineAt(fromA - 1).text);
118
- if (currentIndent > indentPrevious) {
119
- log.info('skip');
120
- changes.push({ from: 0, to: 0 });
121
- return;
122
- }
123
- }
124
- }
125
-
126
- // TODO(burdon): Detect pressing ENTER on empty line that is indented.
127
- // Don't allow empty line.
128
- // if (start === line.to && insert.toString() === '\n') {
129
- // log.info('skip');
130
- // changes.push({ from: 0, to: 0 });
131
- // return;
132
- // }
133
-
134
- log.info('change', {
135
- line: { from: line.from, to: line.to },
136
- start,
137
- a: [fromA, toA],
138
- b: [fromB, toB],
139
- insert: { text: insert.toString(), length: insert.length },
140
- });
141
- }
142
- });
143
-
144
- if (changes.length > 0) {
145
- return [{ changes }];
146
- }
147
-
148
- return tr;
149
- }),
150
-
151
- StateField.define<DecorationSet>({
152
- create: (state) => {
153
- return Decoration.set(buildDecorations(0, state.doc.length, state));
154
- },
155
- update: (value: DecorationSet, tr: Transaction) => {
156
- const from = 0;
157
- const to = tr.state.doc.length;
158
- return value.map(tr.changes).update({
159
- filterFrom: 0,
160
- filterTo: tr.state.doc.length,
161
- filter: () => false,
162
- add: buildDecorations(from, to, tr.state),
163
- });
164
- },
165
- provide: (field) => EditorView.decorations.from(field),
166
- }),
167
-
168
- // TODO(burdon): Increase indent padding by configuring decorate extension.
169
- // TODO(burdon): Hover to select entire group.
170
- EditorView.theme({
171
- '.cm-list-item-start': {
172
- borderTop: '1px solid var(--dx-separator)',
173
- borderLeft: '1px solid var(--dx-separator)',
174
- borderRight: '1px solid var(--dx-separator)',
175
- borderTopLeftRadius: '4px',
176
- borderTopRightRadius: '4px',
177
- paddingTop: '4px',
178
- marginTop: '8px',
179
- },
180
- '.cm-list-item-end': {
181
- borderLeft: '1px solid var(--dx-separator)',
182
- borderRight: '1px solid var(--dx-separator)',
183
- borderBottom: '1px solid var(--dx-separator)',
184
- borderBottomLeftRadius: '4px',
185
- borderBottomRightRadius: '4px',
186
- paddingBottom: '4px',
187
- marginBottom: '8px',
188
- },
189
- '.cm-list-item-continuation': {
190
- borderLeft: '1px solid var(--dx-separator)',
191
- borderRight: '1px solid var(--dx-separator)',
192
-
193
- // TODO(burdon): Should match parent indentation.
194
- paddingLeft: '24px',
195
- },
196
-
197
- // TODO(burdon): Set via options to decorate extension.
198
- '.cm-list-item-continuation.cm-codeblock-start': {
199
- borderRadius: '0',
200
- },
201
- }),
202
- ];
203
-
204
- /**
205
- * Add line decorations.
206
- */
207
- const buildDecorations = (from: number, to: number, state: EditorState) => {
208
- const decorations: Range<Decoration>[] = [];
209
- syntaxTree(state).iterate({
210
- enter: (node) => {
211
- if (node.name === 'ListItem') {
212
- const sub = node.node.getChild('BulletList');
213
- const lineStart = state.doc.lineAt(node.from);
214
- const lineEnd = sub ? state.doc.lineAt(state.doc.lineAt(sub.from).from - 1) : state.doc.lineAt(node.to);
215
-
216
- decorations.push(
217
- Decoration.line({
218
- class: mx('cm-list-item-start', lineStart.number === lineEnd.number && 'cm-list-item-end'),
219
- }).range(lineStart.from, lineStart.from),
220
- );
221
-
222
- for (let i = lineStart.from + 1; i < lineEnd.from; i++) {
223
- decorations.push(Decoration.line({ class: mx('cm-list-item-continuation') }).range(i, i));
224
- }
225
-
226
- // TODO(burdon): Need to sort.
227
- if (lineStart.number !== lineEnd.number) {
228
- decorations.push(Decoration.line({ class: mx('cm-list-item-end') }).range(lineEnd.from, lineEnd.from));
229
- }
230
- }
231
- },
232
- });
233
-
234
- return decorations;
235
- };
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- export * from './preview';
@@ -1,271 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos/lit-ui/dx-ref-tag.pcss';
6
-
7
- 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';
16
- import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
17
- import { type SyntaxNode } from '@lezer/common';
18
-
19
- import { type RenderCallback } from '../../types';
20
-
21
- export type PreviewLinkRef = {
22
- suggest?: boolean;
23
- block?: boolean;
24
- label: string;
25
- ref: string;
26
- };
27
-
28
- export type PreviewLinkTarget = {
29
- label: string;
30
- text?: string;
31
- object?: any;
32
- };
33
-
34
- export type PreviewAction =
35
- | {
36
- type: 'insert';
37
- link: PreviewLinkRef;
38
- target: PreviewLinkTarget;
39
- }
40
- | {
41
- type: 'delete';
42
- link: PreviewLinkRef;
43
- };
44
-
45
- // TODO(burdon): Handle error.
46
- export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
47
-
48
- export type PreviewActionHandler = (action: PreviewAction) => void;
49
-
50
- export type PreviewRenderProps = {
51
- readonly: boolean;
52
- link: PreviewLinkRef;
53
- onAction: PreviewActionHandler;
54
- onLookup?: PreviewLookup;
55
- };
56
-
57
- export type PreviewOptions = {
58
- renderBlock?: RenderCallback<PreviewRenderProps>;
59
- onLookup?: PreviewLookup;
60
- };
61
-
62
- /**
63
- * Create preview decorations.
64
- */
65
- export const preview = (options: PreviewOptions = {}): Extension => {
66
- return [
67
- // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
68
- // "Block decorations may not be specified via plugins"
69
- StateField.define<DecorationSet>({
70
- create: (state) => buildDecorations(state, options),
71
- update: (_: RangeSet<Decoration>, tr: Transaction) => buildDecorations(tr.state, options),
72
- provide: (field) => [
73
- EditorView.decorations.from(field),
74
- EditorView.atomicRanges.of((view) => view.state.field(field)),
75
- ],
76
- }),
77
-
78
- EditorView.theme({
79
- '.cm-preview-block': {
80
- marginLeft: '-1rem',
81
- marginRight: '-1rem',
82
- padding: '1rem',
83
- borderRadius: '0.5rem',
84
- background: 'var(--dx-modalSurface)',
85
- border: '1px solid var(--dx-separator)',
86
- },
87
- }),
88
- ];
89
- };
90
-
91
- /**
92
- * Link references.
93
- *
94
- * [Label][dxn:echo:123] Inline reference
95
- * ![Label][dxn:echo:123] Block reference
96
- * ![Label][?dxn:echo:123] Suggestion
97
- */
98
- const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
99
- const mark = node.getChild('LinkMark');
100
- const label = node.getChild('LinkLabel');
101
- if (mark && label) {
102
- const ref = state.sliceDoc(label.from + 1, label.to - 1);
103
- return {
104
- suggest: ref.startsWith('?'),
105
- block: state.sliceDoc(mark.from, mark.from + 1) === '!',
106
- label: state.sliceDoc(mark.to, label.from - 1),
107
- ref: ref.startsWith('?') ? ref.slice(1) : ref,
108
- };
109
- }
110
- };
111
-
112
- /**
113
- * Echo references are represented as markdown reference links.
114
- * https://www.markdownguide.org/basic-syntax/#reference-style-links
115
- * [Label|block][dxn:echo:123]
116
- * [Label|inline][dxn:echo:123]
117
- */
118
- const buildDecorations = (state: EditorState, options: PreviewOptions) => {
119
- const builder = new RangeSetBuilder<Decoration>();
120
-
121
- syntaxTree(state).iterate({
122
- enter: (node) => {
123
- switch (node.name) {
124
- //
125
- // Decoration.
126
- // [Label][dxn:echo:123]
127
- //
128
- case 'Link': {
129
- const link = getLinkRef(state, node.node);
130
- if (link) {
131
- builder.add(
132
- node.from,
133
- node.to,
134
- Decoration.replace({
135
- widget: new PreviewInlineWidget(options, link),
136
- }),
137
- );
138
- }
139
- break;
140
- }
141
- //
142
- // Block widget.
143
- // ![Label][dxn:echo:123]
144
- //
145
- case 'Image': {
146
- const link = getLinkRef(state, node.node);
147
- if (options.renderBlock && link) {
148
- builder.add(
149
- node.from,
150
- node.to,
151
- Decoration.replace({
152
- block: true,
153
- // atomic: true,
154
- widget: new PreviewBlockWidget(options, link),
155
- }),
156
- );
157
- }
158
- break;
159
- }
160
- }
161
- },
162
- });
163
-
164
- return builder.finish();
165
- };
166
-
167
- /**
168
- * Inline widget.
169
- * [Label][dxn:echo:123]
170
- */
171
- class PreviewInlineWidget extends WidgetType {
172
- constructor(
173
- readonly _options: PreviewOptions,
174
- readonly _link: PreviewLinkRef,
175
- ) {
176
- super();
177
- }
178
-
179
- // override ignoreEvent() {
180
- // return false;
181
- // }
182
-
183
- override eq(other: this) {
184
- return this._link.ref === other._link.ref && this._link.label === other._link.label;
185
- }
186
-
187
- override toDOM(view: EditorView) {
188
- const root = document.createElement('dx-ref-tag');
189
- root.textContent = this._link.label;
190
- root.setAttribute('ref', this._link.ref);
191
- return root;
192
- }
193
- }
194
-
195
- /**
196
- * Block widget.
197
- * ![Label][dxn:echo:123]
198
- */
199
- class PreviewBlockWidget extends WidgetType {
200
- constructor(
201
- readonly _options: PreviewOptions,
202
- readonly _link: PreviewLinkRef,
203
- ) {
204
- super();
205
- }
206
-
207
- // override ignoreEvent() {
208
- // return true;
209
- // }
210
-
211
- override eq(other: this) {
212
- return this._link.ref === other._link.ref;
213
- }
214
-
215
- override toDOM(view: EditorView) {
216
- const root = document.createElement('div');
217
- root.classList.add('cm-preview-block');
218
-
219
- // TODO(burdon): Inject handler.
220
- const handleAction: PreviewActionHandler = (action) => {
221
- const pos = view.posAtDOM(root);
222
- const node = syntaxTree(view.state).resolve(pos + 1).node.parent;
223
- if (!node) {
224
- return;
225
- }
226
-
227
- const link = getLinkRef(view.state, node);
228
- if (link?.ref !== action.link.ref) {
229
- return;
230
- }
231
-
232
- switch (action.type) {
233
- // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
234
- // Insert ref text.
235
- case 'insert': {
236
- view.dispatch({
237
- changes: {
238
- from: node.from,
239
- to: node.to,
240
- insert: action.target.text,
241
- },
242
- });
243
- break;
244
- }
245
- // Remove ref.
246
- case 'delete': {
247
- view.dispatch({
248
- changes: {
249
- from: node.from,
250
- to: node.to,
251
- },
252
- });
253
- break;
254
- }
255
- }
256
- };
257
-
258
- this._options.renderBlock!(
259
- root,
260
- {
261
- readonly: view.state.readOnly,
262
- link: this._link,
263
- onAction: handleAction,
264
- onLookup: this._options.onLookup,
265
- },
266
- view,
267
- );
268
-
269
- return root;
270
- }
271
- }