@krainovsd/markdown-editor 0.1.3 → 0.2.0

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 (67) hide show
  1. package/lib/cjs/index-CDtGUxs-.js +52 -0
  2. package/lib/cjs/index-CDtGUxs-.js.map +1 -0
  3. package/lib/cjs/{index-DPZa3gzd.js → index-CiorogHq.js} +973 -368
  4. package/lib/cjs/index-CiorogHq.js.map +1 -0
  5. package/lib/cjs/index.js +6 -3
  6. package/lib/cjs/index.js.map +1 -1
  7. package/lib/esm/extensions/init-extensions.js +6 -2
  8. package/lib/esm/extensions/init-extensions.js.map +1 -1
  9. package/lib/esm/extensions/listeners/get-focus-event.js +1 -0
  10. package/lib/esm/extensions/listeners/get-focus-event.js.map +1 -1
  11. package/lib/esm/extensions/markdown/blockquote/blockquote-decoration.js +2 -2
  12. package/lib/esm/extensions/markdown/blockquote/blockquote-decoration.js.map +1 -1
  13. package/lib/esm/extensions/markdown/bold/bold-decoration.js +2 -2
  14. package/lib/esm/extensions/markdown/bold/bold-decoration.js.map +1 -1
  15. package/lib/esm/extensions/markdown/code/code-decoration.js +2 -2
  16. package/lib/esm/extensions/markdown/code/code-decoration.js.map +1 -1
  17. package/lib/esm/extensions/markdown/header/header-decoration.js +3 -3
  18. package/lib/esm/extensions/markdown/header/header-decoration.js.map +1 -1
  19. package/lib/esm/extensions/markdown/horizontal/horizontal-decoration.js +2 -2
  20. package/lib/esm/extensions/markdown/horizontal/horizontal-decoration.js.map +1 -1
  21. package/lib/esm/extensions/markdown/image/image-decoration.js +33 -26
  22. package/lib/esm/extensions/markdown/image/image-decoration.js.map +1 -1
  23. package/lib/esm/extensions/markdown/image/image-widget.js +76 -70
  24. package/lib/esm/extensions/markdown/image/image-widget.js.map +1 -1
  25. package/lib/esm/extensions/markdown/init-markdown.js +5 -2
  26. package/lib/esm/extensions/markdown/init-markdown.js.map +1 -1
  27. package/lib/esm/extensions/markdown/italic/italic-decoration.js +7 -7
  28. package/lib/esm/extensions/markdown/italic/italic-decoration.js.map +1 -1
  29. package/lib/esm/extensions/markdown/link/auto-link-decoration.js +24 -4
  30. package/lib/esm/extensions/markdown/link/auto-link-decoration.js.map +1 -1
  31. package/lib/esm/extensions/markdown/link/link-decoration.js +25 -5
  32. package/lib/esm/extensions/markdown/link/link-decoration.js.map +1 -1
  33. package/lib/esm/extensions/markdown/link/link-label-decoration.js +3 -2
  34. package/lib/esm/extensions/markdown/link/link-label-decoration.js.map +1 -1
  35. package/lib/esm/extensions/markdown/link/link-widget.js +137 -112
  36. package/lib/esm/extensions/markdown/link/link-widget.js.map +1 -1
  37. package/lib/esm/extensions/markdown/list/list-decoration.js +3 -3
  38. package/lib/esm/extensions/markdown/list/list-decoration.js.map +1 -1
  39. package/lib/esm/extensions/markdown/markdown-decoration.js +89 -44
  40. package/lib/esm/extensions/markdown/markdown-decoration.js.map +1 -1
  41. package/lib/esm/extensions/markdown/markdown-state.js +29 -0
  42. package/lib/esm/extensions/markdown/markdown-state.js.map +1 -0
  43. package/lib/esm/extensions/markdown/mention/mention-decoration.js +2 -2
  44. package/lib/esm/extensions/markdown/mention/mention-decoration.js.map +1 -1
  45. package/lib/esm/extensions/markdown/strike-through/strike-through-decoration.js +2 -2
  46. package/lib/esm/extensions/markdown/strike-through/strike-through-decoration.js.map +1 -1
  47. package/lib/esm/extensions/markdown/todo/todo-decoration.js +3 -3
  48. package/lib/esm/extensions/markdown/todo/todo-decoration.js.map +1 -1
  49. package/lib/esm/extensions/markdown/todo/todo-widget.js +1 -0
  50. package/lib/esm/extensions/markdown/todo/todo-widget.js.map +1 -1
  51. package/lib/esm/extensions/settings/init-settings.js +1 -0
  52. package/lib/esm/extensions/settings/init-settings.js.map +1 -1
  53. package/lib/esm/index.js +9 -0
  54. package/lib/esm/index.js.map +1 -1
  55. package/lib/esm/lib/utils/get-decoration.js +2 -1
  56. package/lib/esm/lib/utils/get-decoration.js.map +1 -1
  57. package/lib/esm/module/Editor/Editor.js +4 -0
  58. package/lib/esm/module/Editor/Editor.js.map +1 -1
  59. package/lib/esm/module/Editor/lib/init-editor.js +1 -0
  60. package/lib/esm/module/Editor/lib/init-editor.js.map +1 -1
  61. package/lib/index.d.ts +2 -1
  62. package/package.json +1 -1
  63. package/lib/cjs/index-Czx0hvPo.js +0 -528
  64. package/lib/cjs/index-Czx0hvPo.js.map +0 -1
  65. package/lib/cjs/index-DPZa3gzd.js.map +0 -1
  66. package/lib/esm/lib/utils/tick.js +0 -22
  67. package/lib/esm/lib/utils/tick.js.map +0 -1
@@ -1,15 +1,17 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
-
5
- const langMarkdown = require('@codemirror/lang-markdown');
6
- const language = require('@codemirror/language');
7
3
  const view = require('@codemirror/view');
4
+ const state = require('@codemirror/state');
5
+ const commands = require('@codemirror/commands');
6
+ const language = require('@codemirror/language');
7
+ const highlight = require('@lezer/highlight');
8
+ require('@codemirror/lang-markdown');
8
9
  const clsx = require('clsx');
9
- const index = require('./index-Czx0hvPo.js');
10
- require('@lezer/highlight');
11
- require('@codemirror/state');
12
- require('@codemirror/commands');
10
+ require('y-websocket');
11
+
12
+ const ReadonlyCompartment = new state.Compartment();
13
+ const VimModeCompartment = new state.Compartment();
14
+ const ThemeCompartment = new state.Compartment();
13
15
 
14
16
  function copyToClipboard(content) {
15
17
  if (navigator.clipboard && window.isSecureContext) {
@@ -44,34 +46,15 @@ function fallbackCopyTextToClipboard(content) {
44
46
  });
45
47
  }
46
48
 
47
- function tick({ deep = 1, maxDeep = 1, delay = 0, recursiveCondition, delayGetter, type = "macro", }) {
48
- return new Promise((resolve) => {
49
- if (type === "micro") {
50
- if (deep >= maxDeep || !recursiveCondition || recursiveCondition())
51
- resolve(1);
52
- else
53
- void tick({ deep: deep + 1, maxDeep, delay, recursiveCondition, type, delayGetter }).then(() => resolve(1));
54
- }
55
- if (type === "macro") {
56
- const currentDelay = delayGetter ? delayGetter(deep) : delay;
57
- setTimeout(() => {
58
- if (deep >= maxDeep || !recursiveCondition || recursiveCondition())
59
- resolve(1);
60
- else
61
- void tick({ deep: deep + 1, maxDeep, delay, recursiveCondition, type, delayGetter }).then(() => resolve(1));
62
- }, currentDelay);
63
- }
64
- });
65
- }
66
-
67
49
  function getLineDecoration({ style, range }) {
68
50
  return view.Decoration.line({
69
51
  class: style,
70
52
  }).range(range[0], range[1]);
71
53
  }
72
- function getMarkDecoration({ range, style }) {
54
+ function getMarkDecoration({ range, style, attributes }) {
73
55
  return view.Decoration.mark({
74
56
  class: style,
57
+ attributes,
75
58
  }).range(range[0], range[1]);
76
59
  }
77
60
  function getHideDecoration({ range }) {
@@ -92,6 +75,312 @@ function isInRange(ranges, text) {
92
75
  return ranges.some((range) => isRangeOverlap([range.from, range.to], text));
93
76
  }
94
77
 
78
+ function saveDispatch(dispatch) {
79
+ queueMicrotask(() => {
80
+ dispatch();
81
+ });
82
+ }
83
+
84
+ /**
85
+ * t.processingInstruction, t.meta - # () []
86
+ * t.url, t.link - links
87
+ */
88
+ function getHighlightTemplate(config) {
89
+ return language.syntaxHighlighting(language.HighlightStyle.define([
90
+ { tag: highlight.tags.keyword, color: config.keyword },
91
+ { tag: [highlight.tags.name, highlight.tags.deleted, highlight.tags.character, highlight.tags.macroName], color: config.variable },
92
+ { tag: [highlight.tags.propertyName], color: config.function },
93
+ {
94
+ tag: [highlight.tags.string, highlight.tags.inserted, highlight.tags.special(highlight.tags.string)],
95
+ color: config.string,
96
+ },
97
+ { tag: [highlight.tags.function(highlight.tags.variableName), highlight.tags.labelName], color: config.function },
98
+ { tag: [highlight.tags.color, highlight.tags.constant(highlight.tags.name), highlight.tags.standard(highlight.tags.name)], color: config.constant },
99
+ { tag: [highlight.tags.definition(highlight.tags.name), highlight.tags.separator], color: config.variable },
100
+ { tag: [highlight.tags.className], color: config.class },
101
+ {
102
+ tag: [highlight.tags.number, highlight.tags.changed, highlight.tags.annotation, highlight.tags.modifier, highlight.tags.self, highlight.tags.namespace],
103
+ color: config.number,
104
+ },
105
+ { tag: [highlight.tags.typeName], color: config.type, fontStyle: config.type },
106
+ { tag: [highlight.tags.operator, highlight.tags.operatorKeyword], color: config.keyword },
107
+ { tag: [highlight.tags.escape, highlight.tags.regexp], color: config.regexp },
108
+ { tag: [highlight.tags.comment], color: config.comment },
109
+ { tag: [highlight.tags.atom, highlight.tags.bool, highlight.tags.special(highlight.tags.variableName)], color: config.variable },
110
+ { tag: highlight.tags.invalid, color: config.invalid },
111
+ ]));
112
+ }
113
+
114
+ const CLASSES = {
115
+ listCommon: "cm-list-common",
116
+ code: "cm-code",
117
+ codeButton: "cm-code-button",
118
+ codeButtonSuccess: "cm-code-button-success",
119
+ codeButtonFail: "cm-code-button-fail",
120
+ codeButtonPending: "cm-code-button-pending",
121
+ codeButtonSpan: "cm-code-span",
122
+ horizontal: "cm-horizontal",
123
+ blockquote: "cm-blockquote",
124
+ blockquoteInner: "cm-blockquote-inner",
125
+ link: "cm-link",
126
+ mention: "cm-mention",
127
+ };
128
+
129
+ function getThemeTemplate(dark, config) {
130
+ return view.EditorView.theme({
131
+ "&": {
132
+ color: config.color,
133
+ backgroundColor: config.background,
134
+ },
135
+ ".cm-content": {
136
+ fontFamily: config.fontFamily || "Montserrat",
137
+ },
138
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer > .cm-selectionBackground": {
139
+ background: config.vimSelectionFocused,
140
+ },
141
+ "& .cm-selectionBackground": {
142
+ background: config.vimSelection,
143
+ },
144
+ "&.cm-editor.cm-focused": { outline: "none" },
145
+ "&.cm-editor": {
146
+ height: "100%",
147
+ width: "100%",
148
+ },
149
+ [`.${CLASSES.listCommon}:after`]: {
150
+ background: config.color,
151
+ },
152
+ [`.${CLASSES.code}`]: {
153
+ background: config.codeBackground,
154
+ color: config.codeColor,
155
+ },
156
+ [`.${CLASSES.horizontal}`]: {
157
+ borderBottomColor: config.horizontalColor,
158
+ },
159
+ [`.${CLASSES.link}`]: {
160
+ color: config.linkColor,
161
+ },
162
+ [`.${CLASSES.blockquote}`]: {
163
+ borderLeftColor: config.blockquoteColor,
164
+ },
165
+ [`.${CLASSES.blockquoteInner}:before`]: {
166
+ borderLeftColor: config.blockquoteColor,
167
+ },
168
+ [`.${CLASSES.codeButton}`]: {
169
+ color: config.codeButtonColor,
170
+ },
171
+ [`.${CLASSES.codeButton}:hover`]: {
172
+ background: config.codeButtonBackground,
173
+ },
174
+ [`.${CLASSES.codeButtonSuccess}:after`]: {
175
+ borderColor: config.codeButtonColor,
176
+ },
177
+ [`.${CLASSES.codeButtonFail}:after`]: {
178
+ background: config.codeButtonColor,
179
+ },
180
+ [`.${CLASSES.codeButtonFail}:before`]: {
181
+ background: config.codeButtonColor,
182
+ },
183
+ [`.${CLASSES.codeButtonPending}:before`]: {
184
+ borderColor: config.codeBackground,
185
+ borderTopColor: config.codeButtonColor,
186
+ },
187
+ [`.${CLASSES.mention}`]: {
188
+ color: config.mentionColor,
189
+ },
190
+ }, { dark });
191
+ }
192
+
193
+ const HIGHLIGHT_CONFIG$1 = {
194
+ keyword: "#f97583",
195
+ variable: "#ffab70",
196
+ function: "#79b8ff",
197
+ string: "#9ecbff",
198
+ constant: "#79b8ff",
199
+ type: "#79b8ff",
200
+ class: "#b392f0",
201
+ number: "#79b8ff",
202
+ comment: "#6a737d",
203
+ heading: "#79b8ff",
204
+ invalid: "#f97583",
205
+ regexp: "#9ecbff",
206
+ };
207
+ const THEME_CONFIG$1 = {
208
+ background: "#2E3235",
209
+ blockquoteColor: "#8A5CF5", // #6A8695
210
+ codeBackground: "#24292e",
211
+ codeButtonBackground: "#434C54FF",
212
+ codeButtonColor: "#DDDDDD",
213
+ codeColor: "#DDDDDD",
214
+ color: "#DDDDDD",
215
+ fontFamily: "Montserrat",
216
+ horizontalColor: "#DDDDDD",
217
+ linkColor: "#8A5CF5",
218
+ mentionColor: "#8A5CF5",
219
+ vimSelection: "#1A1919FF",
220
+ vimSelectionFocused: "#2E4B4BFF",
221
+ };
222
+ function getDarkTheme({ dark }) {
223
+ const highlightConfig = { ...HIGHLIGHT_CONFIG$1, ...(dark?.highlightConfig || {}) };
224
+ const themeConfig = { ...THEME_CONFIG$1, ...(dark?.themeConfig || {}) };
225
+ return [getThemeTemplate(true, themeConfig), getHighlightTemplate(highlightConfig)];
226
+ }
227
+
228
+ const HIGHLIGHT_CONFIG = {
229
+ keyword: "#9854f1",
230
+ variable: "#3760bf",
231
+ function: "#2e7de9",
232
+ string: "#587539",
233
+ constant: "#9854f1",
234
+ type: "#07879d",
235
+ class: "#3760bf",
236
+ number: "#b15c00",
237
+ comment: "#9da3c2",
238
+ heading: "#006a83",
239
+ invalid: "#ff3e64",
240
+ regexp: "#2e5857",
241
+ };
242
+ const THEME_CONFIG = {
243
+ background: "transparent",
244
+ blockquoteColor: "#8A5CF5", // #6A8695
245
+ codeBackground: "#e1e2e7",
246
+ codeButtonBackground: "#CCCCCDFF",
247
+ codeButtonColor: "#000000",
248
+ codeColor: "#000000",
249
+ color: "#000000",
250
+ fontFamily: "Montserrat",
251
+ horizontalColor: "#000000",
252
+ linkColor: "#8A5CF5",
253
+ mentionColor: "#8A5CF5",
254
+ vimSelection: "#d9d9d9",
255
+ vimSelectionFocused: "#d7d4f0",
256
+ };
257
+ function getLightTheme({ light }) {
258
+ const highlightConfig = { ...HIGHLIGHT_CONFIG, ...(light?.highlightConfig || {}) };
259
+ const themeConfig = { ...THEME_CONFIG, ...(light?.themeConfig || {}) };
260
+ return [getThemeTemplate(false, themeConfig), getHighlightTemplate(highlightConfig)];
261
+ }
262
+
263
+ const initTheme = (options) => {
264
+ const extensions = [
265
+ view.EditorView.lineWrapping,
266
+ ThemeCompartment.of(options.theme === "dark" ? getDarkTheme(options) : getLightTheme(options)),
267
+ ];
268
+ return extensions;
269
+ };
270
+
271
+ let vimMode = false;
272
+ let theme = "light";
273
+ const initKeyMaps = async ({ onEnter, onEscape, multiCursorMode, keyMaps, defaultKeyMaps, theme: initialTheme, vimMode: initialVimMode, dark, light, }) => {
274
+ vimMode = initialVimMode;
275
+ theme = initialTheme;
276
+ /** tab */
277
+ const keyBindings = [commands.indentWithTab];
278
+ /** standard */
279
+ keyBindings.push(...commands.standardKeymap.map((keyMap) => {
280
+ if (keyMap.key === "Enter" && onEnter) {
281
+ return {
282
+ key: "Enter",
283
+ shift: keyMap.run,
284
+ run: (view) => {
285
+ const response = onEnter(view);
286
+ if (response)
287
+ keyMap.run?.(view);
288
+ return response;
289
+ },
290
+ };
291
+ }
292
+ return keyMap;
293
+ }));
294
+ /** vim */
295
+ if (defaultKeyMaps?.vim)
296
+ keyBindings.push({
297
+ key: "Mod-Alt-v",
298
+ run: (view$1) => {
299
+ vimMode = !vimMode;
300
+ void import('@replit/codemirror-vim').then(({ vim }) => {
301
+ saveDispatch(() => {
302
+ view$1.dispatch({
303
+ effects: VimModeCompartment.reconfigure(vimMode ? [vim({ status: true }), view.drawSelection()] : []),
304
+ });
305
+ });
306
+ });
307
+ return true;
308
+ },
309
+ });
310
+ /** theme */
311
+ if (defaultKeyMaps?.theme)
312
+ keyBindings.push({
313
+ key: "Mod-Alt-a",
314
+ run: (view) => {
315
+ theme = theme === "light" ? "dark" : "light";
316
+ saveDispatch(() => {
317
+ view.dispatch({
318
+ effects: ThemeCompartment.reconfigure(theme === "dark"
319
+ ? getDarkTheme({
320
+ dark,
321
+ light,
322
+ theme,
323
+ })
324
+ : getLightTheme({
325
+ dark,
326
+ light,
327
+ theme,
328
+ })),
329
+ });
330
+ });
331
+ return true;
332
+ },
333
+ });
334
+ /** escape */
335
+ if (onEscape) {
336
+ keyBindings.push({
337
+ key: "Escape",
338
+ run: (view) => {
339
+ return onEscape(view);
340
+ },
341
+ });
342
+ }
343
+ /** custom */
344
+ if (keyMaps) {
345
+ keyBindings.push(...keyMaps);
346
+ }
347
+ /** history */
348
+ if (multiCursorMode) {
349
+ const { yUndoManagerKeymap } = await import('y-codemirror.next');
350
+ keyBindings.push(...yUndoManagerKeymap);
351
+ }
352
+ else {
353
+ keyBindings.push(...commands.historyKeymap);
354
+ }
355
+ return view.keymap.of(keyBindings);
356
+ };
357
+
358
+ function getChangeEvent({ onChange }) {
359
+ return onChange
360
+ ? view.EditorView.updateListener.of((e) => {
361
+ if (e.docChanged) {
362
+ onChange(e);
363
+ }
364
+ })
365
+ : [];
366
+ }
367
+
368
+ function getFocusEvent({ onBlur, onFocus }) {
369
+ return onFocus || onBlur
370
+ ? view.EditorView.focusChangeEffect.of((e, focus) => {
371
+ if (focus && onFocus)
372
+ onFocus(e);
373
+ else if (!focus && onBlur)
374
+ onBlur(e);
375
+ return null;
376
+ })
377
+ : [];
378
+ }
379
+
380
+ function initListeners({ onBlur, onChange, onFocus }) {
381
+ return [getChangeEvent({ onChange }), getFocusEvent({ onBlur, onFocus })];
382
+ }
383
+
95
384
  function styleInject(css, ref) {
96
385
  if ( ref === void 0 ) ref = {};
97
386
  var insertAt = ref.insertAt;
@@ -138,7 +427,7 @@ class BlockquoteWidget extends view.WidgetType {
138
427
  span.classList.add(styles.blockquote__inner);
139
428
  if (this.deep)
140
429
  span.classList.add(styles["blockquote__inner-deep"]);
141
- span.classList.add(index.CLASSES.blockquoteInner);
430
+ span.classList.add(CLASSES.blockquoteInner);
142
431
  return span;
143
432
  }
144
433
  }
@@ -173,12 +462,12 @@ function getBlockquoteDecorations({ decorations, node, view }) {
173
462
  }
174
463
  if (!isInner) {
175
464
  decorations.push(getLineDecoration({
176
- style: clsx(styles.blockquote, index.CLASSES.blockquote),
465
+ style: clsx(styles.blockquote, CLASSES.blockquote),
177
466
  range: [line.from],
178
467
  }));
179
468
  }
180
469
  }
181
- function getBlockquoteSelectionDecorations({ decorations, node, view, isReadonly, }) {
470
+ function getBlockquoteSelectionDecorations({ decorations, node, view, forceActive, }) {
182
471
  if (node.name !== NAME_OF_BLOCKQUOTE_MARK) {
183
472
  return;
184
473
  }
@@ -211,7 +500,7 @@ function getBlockquoteSelectionDecorations({ decorations, node, view, isReadonly
211
500
  pos = -1;
212
501
  }
213
502
  }
214
- if (isReadonly ||
503
+ if (forceActive ||
215
504
  !view.hasFocus ||
216
505
  !isInRange(view.state.selection.ranges, [line.from, line.to])) {
217
506
  if (!isInner)
@@ -244,7 +533,7 @@ function getBoldDecorations({ decorations, node, view }) {
244
533
  range: [node.from - step, node.to + step],
245
534
  }));
246
535
  }
247
- function getBoldSelectionDecorations({ decorations, node, view, isReadonly, }) {
536
+ function getBoldSelectionDecorations({ decorations, node, view, forceActive, }) {
248
537
  if (node.name !== NAME_OF_BOLD) {
249
538
  return;
250
539
  }
@@ -252,7 +541,7 @@ function getBoldSelectionDecorations({ decorations, node, view, isReadonly, }) {
252
541
  language.syntaxTree(view.state).resolve(node.from - 1).type.name !== "Emphasis") {
253
542
  return;
254
543
  }
255
- if (isReadonly ||
544
+ if (forceActive ||
256
545
  !view.hasFocus ||
257
546
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
258
547
  decorations.push(getHideDecoration({ range: [node.from, node.from + 2] }));
@@ -286,34 +575,34 @@ class CodeWidget extends view.WidgetType {
286
575
  const button = this.button;
287
576
  clearTimeout(this.timer);
288
577
  button.classList.remove(styles.pending);
289
- button.classList.remove(index.CLASSES.codeButtonPending);
578
+ button.classList.remove(CLASSES.codeButtonPending);
290
579
  button.classList.remove(styles.success);
291
- button.classList.remove(index.CLASSES.codeButtonSuccess);
580
+ button.classList.remove(CLASSES.codeButtonSuccess);
292
581
  button.classList.remove(styles.fail);
293
- button.classList.remove(index.CLASSES.codeButtonFail);
582
+ button.classList.remove(CLASSES.codeButtonFail);
294
583
  button.classList.add(styles.pending);
295
- button.classList.add(index.CLASSES.codeButtonPending);
584
+ button.classList.add(CLASSES.codeButtonPending);
296
585
  span.classList.add(styles.hide);
297
586
  void copyToClipboard(this.content)
298
587
  .then(() => {
299
588
  button.classList.remove(styles.pending);
300
- button.classList.remove(index.CLASSES.codeButtonPending);
589
+ button.classList.remove(CLASSES.codeButtonPending);
301
590
  button.classList.add(styles.success);
302
- button.classList.add(index.CLASSES.codeButtonSuccess);
591
+ button.classList.add(CLASSES.codeButtonSuccess);
303
592
  this.timer = setTimeout(() => {
304
593
  button.classList.remove(styles.success);
305
- button.classList.remove(index.CLASSES.codeButtonSuccess);
594
+ button.classList.remove(CLASSES.codeButtonSuccess);
306
595
  span.classList.remove(styles.hide);
307
596
  }, 500);
308
597
  })
309
598
  .catch(() => {
310
599
  button.classList.remove(styles.pending);
311
- button.classList.remove(index.CLASSES.codeButtonPending);
600
+ button.classList.remove(CLASSES.codeButtonPending);
312
601
  button.classList.add(styles.fail);
313
- button.classList.add(index.CLASSES.codeButtonFail);
602
+ button.classList.add(CLASSES.codeButtonFail);
314
603
  this.timer = setTimeout(() => {
315
604
  button.classList.remove(styles.fail);
316
- button.classList.remove(index.CLASSES.codeButtonFail);
605
+ button.classList.remove(CLASSES.codeButtonFail);
317
606
  span.classList.remove(styles.hide);
318
607
  }, 500);
319
608
  });
@@ -323,11 +612,11 @@ class CodeWidget extends view.WidgetType {
323
612
  this.view = view;
324
613
  const span = document.createElement("span");
325
614
  span.classList.add(styles.code__span);
326
- span.classList.add(index.CLASSES.codeButtonSpan);
615
+ span.classList.add(CLASSES.codeButtonSpan);
327
616
  span.textContent = this.language;
328
617
  const button = document.createElement("button");
329
618
  button.classList.add(styles.code__button);
330
- button.classList.add(index.CLASSES.codeButton);
619
+ button.classList.add(CLASSES.codeButton);
331
620
  if (this.content)
332
621
  button.addEventListener("click", this.onClick.bind(this));
333
622
  button.appendChild(span);
@@ -341,7 +630,7 @@ class CodeWidget extends view.WidgetType {
341
630
  }
342
631
  }
343
632
 
344
- function getCodeSelectionDecorations({ decorations, node, view, isReadonly, }) {
633
+ function getCodeSelectionDecorations({ decorations, node, view, forceActive, }) {
345
634
  if (node.name !== NAME_OF_FENCED_CODE && node.name !== NAME_OF_INLINE_CODE) {
346
635
  return;
347
636
  }
@@ -404,18 +693,18 @@ function getCodeSelectionDecorations({ decorations, node, view, isReadonly, }) {
404
693
  if (lines.length > 1) {
405
694
  lines.forEach((line) => {
406
695
  decorations.push(getLineDecoration({
407
- style: clsx(styles.code__line, index.CLASSES.code),
696
+ style: clsx(styles.code__line, CLASSES.code),
408
697
  range: [line.from],
409
698
  }));
410
699
  });
411
700
  }
412
701
  else {
413
702
  decorations.push(getMarkDecoration({
414
- style: clsx(styles.code__single, index.CLASSES.code),
703
+ style: clsx(styles.code__single, CLASSES.code),
415
704
  range: [node.from, node.to],
416
705
  }));
417
706
  }
418
- if (isReadonly ||
707
+ if (forceActive ||
419
708
  !view.hasFocus ||
420
709
  (lines.length > 1 && !isOverlapLine) ||
421
710
  (lines.length === 1 && !isInRange(view.state.selection.ranges, [node.from, node.to]))) {
@@ -461,7 +750,7 @@ function getHeaderDecorations({ decorations, node, view }) {
461
750
  range: [view.lineBlockAt(node.from).from],
462
751
  }));
463
752
  }
464
- function getHeaderSelectionDecorations({ decorations, node, view, isReadonly, }) {
753
+ function getHeaderSelectionDecorations({ decorations, node, view, forceActive, }) {
465
754
  const isHeader = node.name.startsWith(NAME_OF_HEADER);
466
755
  const isHeaderUnder = node.name.startsWith(NAME_OF_HEADER_UNDER);
467
756
  if (!isHeader && !isHeaderUnder) {
@@ -474,7 +763,7 @@ function getHeaderSelectionDecorations({ decorations, node, view, isReadonly, })
474
763
  const line = view.lineBlockAt(mark.from);
475
764
  if (line.length < mark.to - mark.from + 1)
476
765
  return;
477
- if (isReadonly ||
766
+ if (forceActive ||
478
767
  !view.hasFocus ||
479
768
  !isInRange(view.state.selection.ranges, [line.from, line.to])) {
480
769
  decorations.push(getHideDecoration({ range: [mark.from, mark.to + 1] }));
@@ -483,7 +772,7 @@ function getHeaderSelectionDecorations({ decorations, node, view, isReadonly, })
483
772
  else {
484
773
  const lineHeader = view.lineBlockAt(node.from);
485
774
  const lineMark = view.lineBlockAt(mark.from);
486
- if (isReadonly ||
775
+ if (forceActive ||
487
776
  !view.hasFocus ||
488
777
  !isInRange(view.state.selection.ranges, [lineHeader.from, lineMark.to])) {
489
778
  decorations.push(getHideDecoration({ range: [lineMark.from, lineMark.to] }));
@@ -497,15 +786,15 @@ const headerDecorationPlugin = {
497
786
 
498
787
  const NAME_OF_HORIZONTAL = "HorizontalRule";
499
788
 
500
- function getHorizontalSelectionDecoration({ decorations, isReadonly, node, view, }) {
789
+ function getHorizontalSelectionDecoration({ decorations, forceActive, node, view, }) {
501
790
  if (node.name !== NAME_OF_HORIZONTAL)
502
791
  return;
503
792
  const line = view.lineBlockAt(node.from);
504
- if (isReadonly ||
793
+ if (forceActive ||
505
794
  !view.hasFocus ||
506
795
  !isInRange(view.state.selection.ranges, [line.from, line.to])) {
507
796
  decorations.push(getLineDecoration({
508
- style: clsx(styles.horizontal, index.CLASSES.horizontal),
797
+ style: clsx(styles.horizontal, CLASSES.horizontal),
509
798
  range: [line.from],
510
799
  }));
511
800
  decorations.push(getHideDecoration({ range: [node.from, node.to] }));
@@ -515,6 +804,31 @@ const horizontalDecorationPlugin = {
515
804
  selectionDecorations: [getHorizontalSelectionDecoration],
516
805
  };
517
806
 
807
+ const openedImageEffect = state.StateEffect.define();
808
+ const openedLinkEffect = state.StateEffect.define();
809
+ const imageSrcGetterEffect = state.StateEffect.define();
810
+ const markdownState = state.StateField.define({
811
+ create() {
812
+ return {
813
+ openedImage: undefined,
814
+ imageSrcGetter: undefined,
815
+ openedLink: undefined,
816
+ };
817
+ },
818
+ update(value, transaction) {
819
+ const newValue = { ...value };
820
+ for (const effect of transaction.effects) {
821
+ if (effect.is(openedImageEffect))
822
+ newValue.openedImage = effect.value;
823
+ if (effect.is(imageSrcGetterEffect))
824
+ newValue.imageSrcGetter = effect.value;
825
+ if (effect.is(openedLinkEffect))
826
+ newValue.openedLink = effect.value;
827
+ }
828
+ return newValue;
829
+ },
830
+ });
831
+
518
832
  const NAME_OF_IMAGE = "Image";
519
833
  const CODE_OF_START_IMAGE_TEXT = 91; // [
520
834
  const CODE_OF_END_IMAGE_TEXT = 93; // ]
@@ -530,31 +844,38 @@ class ImageWidget extends view.WidgetType {
530
844
  link;
531
845
  from;
532
846
  to;
847
+ imageSrcGetter;
533
848
  view;
534
- constructor(text, link, from, to) {
849
+ constructor(text, link, from, to, imageSrcGetter, view) {
535
850
  super();
536
851
  this.text = text;
537
852
  this.link = link;
538
853
  this.from = from;
539
854
  this.to = to;
855
+ this.imageSrcGetter = imageSrcGetter;
856
+ this.view = view;
540
857
  }
541
858
  get key() {
542
859
  return `${this.link}:${this.text}:${this.from}:${this.to}`;
543
860
  }
861
+ get src() {
862
+ return this.imageSrcGetter ? this.imageSrcGetter(this.link) : this.link;
863
+ }
544
864
  eq(widget) {
545
865
  const image = IMAGE_NODES[this.key];
546
866
  if (!image)
547
867
  return false;
548
868
  delete IMAGE_NODES[this.key];
549
869
  EXISTING_WIDGETS.delete(this.key);
550
- if (image.src !== widget.link)
551
- image.src = widget.link;
870
+ if (image.src !== widget.src)
871
+ image.src = widget.src;
552
872
  if (image.alt !== widget.text)
553
873
  image.alt = widget.text;
554
874
  this.link = widget.link;
555
875
  this.text = widget.text;
556
876
  this.from = widget.from;
557
877
  this.to = widget.to;
878
+ this.registerListeners(image);
558
879
  IMAGE_NODES[this.key] = image;
559
880
  EXISTING_WIDGETS.add(this.key);
560
881
  return true;
@@ -562,24 +883,23 @@ class ImageWidget extends view.WidgetType {
562
883
  updateDOM() {
563
884
  return true;
564
885
  }
565
- toDOM(view) {
886
+ toDOM() {
566
887
  EXISTING_WIDGETS.add(this.key);
567
888
  let image = IMAGE_NODES[this.key];
568
889
  if (image) {
569
- if (image.src !== this.link) {
570
- image.src = this.link;
890
+ if (image.src !== this.src) {
891
+ image.src = this.src;
571
892
  }
572
893
  if (image.alt !== this.text)
573
894
  image.alt = this.text;
574
895
  return image;
575
896
  }
576
- this.view = view;
577
897
  image = document.createElement("img");
578
898
  image.classList.add(styles.image);
579
899
  image.alt = this.text;
580
- image.src = this.link;
581
- image.addEventListener("mousedown", handleClick);
582
- image.addEventListener("click", handleClick);
900
+ image.src = this.src;
901
+ image.style.maxWidth = "100%";
902
+ this.registerListeners(image);
583
903
  IMAGE_NODES[this.key] = image;
584
904
  if (!interval)
585
905
  interval = setInterval(garbageCollectorInterval, INTERVAL_DELAY);
@@ -588,15 +908,25 @@ class ImageWidget extends view.WidgetType {
588
908
  destroy() {
589
909
  EXISTING_WIDGETS.delete(this.key);
590
910
  }
911
+ registerListeners(image) {
912
+ image.clearListeners?.();
913
+ const abortController = new AbortController();
914
+ image.addEventListener("mousedown", (event) => handleClick$1(this.view, this.text, this.link, this.key, event), { signal: abortController.signal });
915
+ image.clearListeners = () => {
916
+ abortController.abort();
917
+ };
918
+ image.destroy = () => {
919
+ image.clearListeners?.();
920
+ image.remove();
921
+ };
922
+ }
591
923
  }
592
924
  function garbageCollectorInterval() {
593
925
  for (const [key, node] of Object.entries(IMAGE_NODES)) {
594
926
  if (EXISTING_WIDGETS.has(key) || !node)
595
927
  continue;
596
928
  delete IMAGE_NODES[key];
597
- node.removeEventListener("mousedown", handleClick);
598
- node.removeEventListener("click", handleClick);
599
- node.remove();
929
+ node.destroy?.();
600
930
  }
601
931
  if (Object.keys(IMAGE_NODES).length === 0 && interval) {
602
932
  clearInterval(interval);
@@ -604,44 +934,45 @@ function garbageCollectorInterval() {
604
934
  }
605
935
  }
606
936
  /** recursively find the link text node in line */
607
- function getTextNode(imageNode, line) {
937
+ function getTextNode$1(text, link, key, line) {
938
+ if (!line)
939
+ return null;
940
+ const textNodeContainer = getTextNodeContainer$1(text, link, key, line);
941
+ if (!textNodeContainer)
942
+ return null;
943
+ for (const node of Array.from(textNodeContainer.childNodes)) {
944
+ if (isCorrectNode$1(text, link, node)) {
945
+ return node;
946
+ }
947
+ }
948
+ return null;
949
+ }
950
+ function getTextNodeContainer$1(text, link, key, line) {
608
951
  if (!line)
609
952
  return null;
610
- const link = imageNode.src;
611
- let textNode = null;
612
953
  for (const node of Array.from(line.childNodes)) {
613
- if (node.nodeType !== 3) {
614
- const innerNode = getTextNode(imageNode, node);
615
- if (innerNode) {
616
- textNode = innerNode;
617
- break;
618
- }
619
- continue;
954
+ if (node instanceof HTMLElement && node.getAttribute("data-id") === key) {
955
+ return node;
620
956
  }
621
- const textContent = node.textContent;
622
- if (textContent && textContent.includes(link)) {
623
- textNode = node;
624
- break;
957
+ if (node.nodeType !== 3) {
958
+ const inner = getTextNodeContainer$1(text, link, key, node);
959
+ if (inner)
960
+ return inner;
625
961
  }
626
962
  }
627
- return textNode;
628
- }
629
- function getMinLength(imageNode) {
630
- const text = imageNode.alt || "";
631
- const link = imageNode.src;
632
- const startPosition = 4 + text.length;
633
- const endPosition = startPosition + link.length + 1;
634
- return endPosition;
963
+ return null;
635
964
  }
636
- function isCorrectNode(imageNode, node) {
965
+ function isCorrectNode$1(text, link, node) {
637
966
  if (!node)
638
967
  return false;
639
968
  const textContent = node?.textContent;
640
- const minLength = getMinLength(imageNode);
641
- return Boolean(node && textContent && node.nodeType === 3 && textContent.length >= minLength);
969
+ return Boolean(node &&
970
+ textContent &&
971
+ node.nodeType === 3 &&
972
+ textContent.includes(link) &&
973
+ textContent.includes(text));
642
974
  }
643
- function selectLink({ imageNode, node, selection, start }) {
644
- const link = imageNode.src;
975
+ function selectLink$1({ link, node, selection, start }) {
645
976
  const startPosition = start ?? (node.textContent?.indexOf?.(link) || 0);
646
977
  const endPosition = startPosition + link.length;
647
978
  const range = document.createRange();
@@ -650,7 +981,7 @@ function selectLink({ imageNode, node, selection, start }) {
650
981
  selection.removeAllRanges();
651
982
  selection.addRange(range);
652
983
  }
653
- function handleClick(event) {
984
+ function handleClick$1(view, text, link, key, event) {
654
985
  const selection = window.getSelection();
655
986
  if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
656
987
  return;
@@ -667,74 +998,66 @@ function handleClick(event) {
667
998
  const editor = Array.from(document.querySelectorAll(".cm-editor")).find((element) => element.contains(target));
668
999
  if (!selection || !editor || !parent)
669
1000
  return;
670
- const prevLine = parent.previousSibling;
671
- let textNode = getTextNode(target, prevLine);
672
- if (!textNode)
673
- textNode = getTextNode(target, parent);
1001
+ const textNode = getTextNode$1(text, link, key, line);
674
1002
  if (textNode) {
675
- if (isCorrectNode(target, textNode))
676
- selectLink({ selection, imageNode: target, node: textNode });
677
- return;
1003
+ return void selectLink$1({ selection, link, node: textNode });
678
1004
  }
679
- const range = document.createRange();
680
- range.selectNode(target);
681
- range.collapse(true);
682
- selection.removeAllRanges();
683
- selection.addRange(range);
684
- /** wait for the widget to disappear and link will be visible */
685
- void tick({
686
- delay: 0,
687
- maxDeep: 5,
688
- delayGetter: (deep) => {
689
- return deep > 1 ? 10 : 0;
690
- },
691
- recursiveCondition: () => {
692
- const textNode = getTextNode(target, line);
693
- return isCorrectNode(target, textNode);
694
- },
695
- })
696
- .then(() => {
697
- const textNode = getTextNode(target, line);
698
- if (isCorrectNode(target, textNode))
699
- selectLink({ selection, imageNode: target, node: textNode });
1005
+ saveDispatch(() => {
1006
+ if (!view)
1007
+ return;
1008
+ view.dispatch(view.state.update({ effects: openedImageEffect.of(key) }));
1009
+ const textNode = getTextNode$1(text, link, key, line);
1010
+ if (textNode) {
1011
+ selectLink$1({ selection, link, node: textNode });
1012
+ }
1013
+ requestAnimationFrame(() => {
1014
+ saveDispatch(() => {
1015
+ if (view)
1016
+ view.dispatch(view.state.update({ effects: openedImageEffect.of(undefined) }));
1017
+ });
1018
+ });
700
1019
  });
701
1020
  return false;
702
1021
  }
703
1022
 
704
- function getImageSelectionDecorations({ decorations, node, view, isReadonly, }) {
1023
+ function getImageDecorations({ decorations, node, view }) {
705
1024
  if (node.name !== NAME_OF_IMAGE) {
706
1025
  return;
707
1026
  }
708
1027
  const { text, url } = parseInfo(view, node);
709
- const line = view.lineBlockAt(node.from);
710
- if (line.from === node.from && line.to === node.to) {
711
- if (isReadonly ||
712
- !view.hasFocus ||
713
- !isInRange(view.state.selection.ranges, [line.from, line.to])) {
714
- decorations.push(getReplaceDecoration({
715
- range: [line.from, line.to],
716
- widget: new ImageWidget(text, url, node.from, node.to),
717
- }));
718
- }
719
- else {
720
- decorations.push(getWidgetDecorationOptions({
721
- range: [node.to + 1],
722
- widget: new ImageWidget(text, url, node.from, node.to),
723
- }));
724
- }
1028
+ const imageSrcGetter = view.state.field(markdownState).imageSrcGetter;
1029
+ decorations.push(getWidgetDecorationOptions({
1030
+ range: [node.to],
1031
+ widget: new ImageWidget(text, url, node.from, node.to, imageSrcGetter, view),
1032
+ }));
1033
+ }
1034
+ function getImageSelectionDecorations({ decorations, node, view, forceActive, }) {
1035
+ if (node.name !== NAME_OF_IMAGE) {
1036
+ return;
725
1037
  }
726
- else if (isReadonly ||
727
- !view.hasFocus ||
728
- !isInRange(view.state.selection.ranges, [line.from, line.to])) {
729
- decorations.push(getReplaceDecoration({
1038
+ const { text, url } = parseInfo(view, node);
1039
+ const openedImage = view.state.field(markdownState).openedImage;
1040
+ const key = `${url}:${text}:${node.from}:${node.to}`;
1041
+ const isOpened = openedImage && openedImage === key;
1042
+ if (isOpened) {
1043
+ return void decorations.push(getMarkDecoration({
730
1044
  range: [node.from, node.to],
731
- widget: new ImageWidget(text, url, node.from, node.to),
1045
+ attributes: {
1046
+ "data-id": key,
1047
+ },
732
1048
  }));
733
1049
  }
1050
+ if (forceActive ||
1051
+ !view.hasFocus ||
1052
+ !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1053
+ decorations.push(getHideDecoration({ range: [node.from, node.to] }));
1054
+ }
734
1055
  else {
735
- decorations.push(getWidgetDecorationOptions({
736
- range: [node.to],
737
- widget: new ImageWidget(text, url, node.from, node.to),
1056
+ decorations.push(getMarkDecoration({
1057
+ range: [node.from, node.to],
1058
+ attributes: {
1059
+ "data-id": key,
1060
+ },
738
1061
  }));
739
1062
  }
740
1063
  }
@@ -767,6 +1090,7 @@ function parseInfo(view, node) {
767
1090
  }
768
1091
  const imageDecorationPlugin = {
769
1092
  selectionDecorations: [getImageSelectionDecorations],
1093
+ decorations: [getImageDecorations],
770
1094
  };
771
1095
 
772
1096
  const NAME_OF_ITALIC = "Emphasis";
@@ -781,12 +1105,12 @@ function getItalicDecorations({ decorations, node }) {
781
1105
  range: [node.from, node.to],
782
1106
  }));
783
1107
  }
784
- function getItalicSelectionDecorations({ decorations, node, view, isReadonly, }) {
1108
+ function getItalicSelectionDecorations({ decorations, node, view, forceActive, }) {
785
1109
  if (node.name !== NAME_OF_ITALIC) {
786
1110
  return;
787
1111
  }
788
- if (checkIsSeveralEmphasis({ decorations, node, view, isReadonly })) {
789
- return void splitEmphasis({ decorations, node, view, isReadonly });
1112
+ if (checkIsSeveralEmphasis({ decorations, node, view, forceActive })) {
1113
+ return void splitEmphasis({ decorations, node, view, forceActive });
790
1114
  }
791
1115
  let step = 1;
792
1116
  const startText = view.state.doc.sliceString(node.from, node.from + 3);
@@ -794,7 +1118,7 @@ function getItalicSelectionDecorations({ decorations, node, view, isReadonly, })
794
1118
  LIST_OF_ITALIC_MARKS.has(startText.charCodeAt(1)) &&
795
1119
  LIST_OF_ITALIC_MARKS.has(startText.charCodeAt(2)))
796
1120
  step = 3;
797
- if (isReadonly ||
1121
+ if (forceActive ||
798
1122
  !view.hasFocus ||
799
1123
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
800
1124
  decorations.push(getHideDecoration({ range: [node.from, node.from + step] }));
@@ -815,7 +1139,7 @@ function checkIsSeveralEmphasis({ node, view }) {
815
1139
  return true;
816
1140
  return false;
817
1141
  }
818
- function splitEmphasis({ decorations, node, view, isReadonly }) {
1142
+ function splitEmphasis({ decorations, node, view, forceActive }) {
819
1143
  const text = view.state.doc.sliceString(node.from, node.to);
820
1144
  let marks = 0;
821
1145
  let pos = 0;
@@ -828,13 +1152,13 @@ function splitEmphasis({ decorations, node, view, isReadonly }) {
828
1152
  decorations,
829
1153
  view,
830
1154
  node: { ...node, name: node.name, from: node.from, to: node.from + pos },
831
- isReadonly,
1155
+ forceActive,
832
1156
  });
833
1157
  getItalicSelectionDecorations({
834
1158
  decorations,
835
1159
  view,
836
1160
  node: { ...node, name: node.name, from: node.from + pos, to: node.to },
837
- isReadonly,
1161
+ forceActive,
838
1162
  });
839
1163
  }
840
1164
  const italicDecorationPlugin = {
@@ -850,10 +1174,11 @@ const CODE_OF_END_LINK_URL = 41; // )
850
1174
  const NAME_OF_AUTO_LINK = "Autolink";
851
1175
  const CODE_OF_LINK_LABEL_END = 58; // :
852
1176
 
853
- function getLinkLabelSelectionDecoration({ decorations, node, view, isReadonly, }) {
1177
+ // [label]:
1178
+ function getLinkLabelSelectionDecoration({ decorations, node, view, forceActive, }) {
854
1179
  if (view.state.doc.sliceString(node.to, node.to + 1).charCodeAt(0) !== CODE_OF_LINK_LABEL_END)
855
1180
  return;
856
- if (isReadonly ||
1181
+ if (forceActive ||
857
1182
  !view.hasFocus ||
858
1183
  !isInRange(view.state.selection.ranges, [node.from, node.to + 1])) {
859
1184
  decorations.push(getMarkDecoration({
@@ -865,141 +1190,165 @@ function getLinkLabelSelectionDecoration({ decorations, node, view, isReadonly,
865
1190
  }
866
1191
  }
867
1192
 
1193
+ const LINK_NODES = {};
868
1194
  class LinkWidget extends view.WidgetType {
869
1195
  text;
870
1196
  link;
871
- type;
1197
+ from;
1198
+ to;
872
1199
  view;
873
- constructor(text, link, type) {
1200
+ constructor(text, link, from, to, view) {
874
1201
  super();
875
1202
  this.text = text;
876
1203
  this.link = link;
877
- this.type = type;
1204
+ this.from = from;
1205
+ this.to = to;
1206
+ this.view = view;
878
1207
  }
879
- /** recursively find the link text node in line */
880
- getTextNode(line) {
881
- if (!line)
882
- return null;
883
- let textNode = null;
884
- for (const node of Array.from(line.childNodes)) {
885
- if (node.nodeType !== 3) {
886
- const innerNode = this.getTextNode(node);
887
- if (innerNode) {
888
- textNode = innerNode;
889
- break;
890
- }
891
- continue;
892
- }
893
- const textContent = node.textContent;
894
- if (textContent && textContent.includes(this.link)) {
895
- textNode = node;
896
- break;
897
- }
898
- }
899
- return textNode;
900
- }
901
- getMinLength() {
902
- let startPosition = 0;
903
- if (this.type === "link")
904
- startPosition = 3 + this.text.length;
905
- if (this.type === "auto")
906
- startPosition = 1;
907
- const endPosition = startPosition + this.link.length;
908
- return endPosition;
909
- }
910
- /** check that the text node is correct node */
911
- isCorrectNode(node) {
912
- if (!node)
1208
+ get key() {
1209
+ return `${this.link}:${this.text}:${this.from}:${this.to}`;
1210
+ }
1211
+ eq(widget) {
1212
+ const anchor = LINK_NODES[this.key];
1213
+ if (!anchor)
913
1214
  return false;
914
- const textContent = node.textContent;
915
- const minLength = this.getMinLength();
916
- return Boolean(textContent && node.nodeType === 3 && textContent.length >= minLength);
917
- }
918
- selectLink(node, selection) {
919
- const startPosition = node.textContent?.indexOf?.(this.link) || 0;
920
- const endPosition = startPosition + this.link.length;
921
- const range = document.createRange();
922
- range.setStart(node, startPosition);
923
- range.setEnd(node, endPosition);
924
- selection.removeAllRanges();
925
- selection.addRange(range);
926
- }
927
- handleClick(event) {
928
- if (!this.view)
929
- return;
930
- /** open the link if has special key or the view is readonly */
931
- const contentEditable = this.view.contentDOM.getAttribute("contenteditable");
932
- const isReadonly = !contentEditable || contentEditable === "false";
933
- if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey || isReadonly) {
934
- if (event.type === "mousedown") {
935
- const target = event.target;
936
- window.open(target.href, "_blank");
937
- }
938
- return;
939
- }
940
- event.stopPropagation();
941
- event.preventDefault();
942
- const target = event.target;
943
- const parent = target.parentNode;
944
- let line = parent;
945
- /** recursively find line that contains link */
946
- while (line && !line.classList.contains("cm-line")) {
947
- line = line.parentNode;
948
- }
949
- const editor = this.view.dom.querySelector(".cm-content");
950
- const selection = window.getSelection();
951
- if (!selection || !editor || !parent)
952
- return;
953
- const range = document.createRange();
954
- range.selectNode(target);
955
- range.collapse(true);
956
- selection.removeAllRanges();
957
- selection.addRange(range);
958
- /** trick for correct select the link by click when the view is not focused */
959
- void tick({ delay: 0 }).then(() => {
960
- if (selection && selection.anchorNode?.nodeType !== 3) {
961
- selection.removeAllRanges();
962
- selection.addRange(range);
963
- }
964
- });
965
- /** wait for the widget to disappear and link will be visible */
966
- void tick({
967
- delay: 0,
968
- maxDeep: 5,
969
- delayGetter: (deep) => {
970
- return deep > 1 ? 10 : 5;
971
- },
972
- recursiveCondition: () => {
973
- const textNode = this.getTextNode(line);
974
- return this.isCorrectNode(textNode);
975
- },
976
- })
977
- .then(() => {
978
- const textNode = this.getTextNode(line);
979
- if (this.isCorrectNode(textNode))
980
- this.selectLink(textNode, selection);
981
- });
982
- return false;
1215
+ delete LINK_NODES[this.key];
1216
+ if (anchor.href !== widget.link)
1217
+ anchor.href = widget.link;
1218
+ if (anchor.textContent !== widget.text)
1219
+ anchor.textContent = widget.text;
1220
+ this.link = widget.link;
1221
+ this.text = widget.text;
1222
+ this.from = widget.from;
1223
+ this.to = widget.to;
1224
+ this.registerListeners(anchor);
1225
+ LINK_NODES[this.key] = anchor;
1226
+ return true;
983
1227
  }
984
- toDOM(view) {
985
- this.view = view;
1228
+ toDOM() {
986
1229
  const anchor = document.createElement("a");
987
1230
  anchor.classList.add(styles.link);
988
- anchor.classList.add(index.CLASSES.link);
1231
+ anchor.classList.add(CLASSES.link);
989
1232
  anchor.target = "_blank";
990
1233
  anchor.textContent = this.text;
991
1234
  anchor.href = this.link;
992
- anchor.addEventListener("mousedown", this.handleClick.bind(this));
993
- anchor.addEventListener("click", this.handleClick.bind(this));
1235
+ this.registerListeners(anchor);
1236
+ LINK_NODES[this.key] = anchor;
994
1237
  return anchor;
995
1238
  }
996
1239
  destroy(dom) {
997
- dom.removeEventListener("mousedown", this.handleClick.bind(this));
998
- dom.removeEventListener("click", this.handleClick.bind(this));
1240
+ delete LINK_NODES[this.key];
1241
+ dom.destroy?.();
1242
+ }
1243
+ registerListeners(anchor) {
1244
+ anchor.clearListeners?.();
1245
+ const abortController = new AbortController();
1246
+ anchor.addEventListener("mousedown", (event) => handleClick(this.view, this.text, this.link, this.key, event), { signal: abortController.signal });
1247
+ anchor.addEventListener("click", (e) => e.preventDefault(), { signal: abortController.signal });
1248
+ anchor.clearListeners = () => {
1249
+ abortController.abort();
1250
+ };
1251
+ anchor.destroy = () => {
1252
+ anchor.clearListeners?.();
1253
+ anchor.remove();
1254
+ };
1255
+ }
1256
+ }
1257
+ /** recursively find the link text node in line */
1258
+ function getTextNode(text, link, key, line) {
1259
+ if (!line)
1260
+ return null;
1261
+ const textNodeContainer = getTextNodeContainer(text, link, key, line);
1262
+ if (!textNodeContainer)
1263
+ return null;
1264
+ for (const node of Array.from(textNodeContainer.childNodes)) {
1265
+ if (isCorrectNode(text, link, node)) {
1266
+ return node;
1267
+ }
1268
+ }
1269
+ return null;
1270
+ }
1271
+ function getTextNodeContainer(text, link, key, line) {
1272
+ if (!line)
1273
+ return null;
1274
+ for (const node of Array.from(line.childNodes)) {
1275
+ if (node instanceof HTMLElement && node.getAttribute("data-id") === key) {
1276
+ return node;
1277
+ }
1278
+ if (node.nodeType !== 3) {
1279
+ const inner = getTextNodeContainer(text, link, key, node);
1280
+ if (inner)
1281
+ return inner;
1282
+ }
1283
+ }
1284
+ return null;
1285
+ }
1286
+ function isCorrectNode(text, link, node) {
1287
+ if (!node)
1288
+ return false;
1289
+ const textContent = node?.textContent;
1290
+ return Boolean(node &&
1291
+ textContent &&
1292
+ node.nodeType === 3 &&
1293
+ textContent.includes(link) &&
1294
+ textContent.includes(text));
1295
+ }
1296
+ function selectLink({ link, node, selection, start }) {
1297
+ const startPosition = start ?? (node.textContent?.indexOf?.(link) || 0);
1298
+ const endPosition = startPosition + link.length;
1299
+ const range = document.createRange();
1300
+ range.setStart(node, startPosition);
1301
+ range.setEnd(node, endPosition);
1302
+ selection.removeAllRanges();
1303
+ selection.addRange(range);
1304
+ }
1305
+ function handleClick(view, text, link, key, event) {
1306
+ /** open the link if has special key or the view is readonly */
1307
+ const contentEditable = view.contentDOM.getAttribute("contenteditable");
1308
+ const forceActive = !contentEditable || contentEditable === "false";
1309
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey || forceActive) {
1310
+ if (event.type === "mousedown") {
1311
+ const target = event.target;
1312
+ window.open(target.href, "_blank");
1313
+ }
1314
+ return;
999
1315
  }
1316
+ event.stopPropagation();
1317
+ event.preventDefault();
1318
+ const target = event.target;
1319
+ const parent = target.parentNode;
1320
+ let line = parent;
1321
+ /** recursively find line that contains link */
1322
+ while (line && !line.classList.contains("cm-line")) {
1323
+ line = line.parentNode;
1324
+ }
1325
+ const editor = Array.from(document.querySelectorAll(".cm-editor")).find((element) => element.contains(target));
1326
+ const selection = window.getSelection();
1327
+ if (!selection || !editor || !parent)
1328
+ return;
1329
+ const textNode = getTextNode(text, link, key, line);
1330
+ if (textNode) {
1331
+ return void selectLink({ selection, link, node: textNode });
1332
+ }
1333
+ saveDispatch(() => {
1334
+ if (!view)
1335
+ return;
1336
+ view.dispatch(view.state.update({ effects: openedLinkEffect.of(key) }));
1337
+ const textNode = getTextNode(text, link, key, line);
1338
+ if (textNode) {
1339
+ selectLink({ selection, link, node: textNode });
1340
+ }
1341
+ requestAnimationFrame(() => {
1342
+ saveDispatch(() => {
1343
+ if (view)
1344
+ view.dispatch(view.state.update({ effects: openedLinkEffect.of(undefined) }));
1345
+ });
1346
+ });
1347
+ });
1348
+ return false;
1000
1349
  }
1001
1350
 
1002
- function getLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1351
+ function getLinkSelectionDecorations({ decorations, node, view, forceActive, }) {
1003
1352
  if (node.name !== NAME_OF_LINK) {
1004
1353
  return;
1005
1354
  }
@@ -1026,15 +1375,34 @@ function getLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1026
1375
  urlCoordinates.to = pos;
1027
1376
  }
1028
1377
  if (urlCoordinates.from === -1 || urlCoordinates.to === -1)
1029
- return void getLinkLabelSelectionDecoration({ decorations, isReadonly, node, view });
1378
+ return void getLinkLabelSelectionDecoration({ decorations, forceActive, node, view });
1030
1379
  const text = content.substring(textCoordinates.from, textCoordinates.to);
1031
1380
  const url = content.substring(urlCoordinates.from, urlCoordinates.to);
1032
- if (isReadonly ||
1381
+ const openedLink = view.state.field(markdownState).openedLink;
1382
+ const key = `${url}:${text}:${node.from}:${node.to}`;
1383
+ const isOpened = openedLink && openedLink === key;
1384
+ if (isOpened) {
1385
+ return void decorations.push(getMarkDecoration({
1386
+ range: [node.from, node.to],
1387
+ attributes: {
1388
+ "data-id": key,
1389
+ },
1390
+ }));
1391
+ }
1392
+ if (forceActive ||
1033
1393
  !view.hasFocus ||
1034
1394
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1035
1395
  decorations.push(getReplaceDecoration({
1036
1396
  range: [node.from, node.to],
1037
- widget: new LinkWidget(text, url, "link"),
1397
+ widget: new LinkWidget(text, url, node.from, node.to, view),
1398
+ }));
1399
+ }
1400
+ else {
1401
+ decorations.push(getMarkDecoration({
1402
+ range: [node.from, node.to],
1403
+ attributes: {
1404
+ "data-id": key,
1405
+ },
1038
1406
  }));
1039
1407
  }
1040
1408
  }
@@ -1042,16 +1410,35 @@ const linkDecorationPlugin = {
1042
1410
  selectionDecorations: [getLinkSelectionDecorations],
1043
1411
  };
1044
1412
 
1045
- function getAutoLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1413
+ function getAutoLinkSelectionDecorations({ decorations, node, view, forceActive, }) {
1046
1414
  if (node.name !== NAME_OF_AUTO_LINK)
1047
1415
  return;
1048
1416
  const url = view.state.doc.sliceString(node.from + 1, node.to - 1);
1049
- if (isReadonly ||
1417
+ const openedLink = view.state.field(markdownState).openedLink;
1418
+ const key = `${url}:${url}:${node.from}:${node.to}`;
1419
+ const isOpened = openedLink && openedLink === key;
1420
+ if (isOpened) {
1421
+ return void decorations.push(getMarkDecoration({
1422
+ range: [node.from, node.to],
1423
+ attributes: {
1424
+ "data-id": key,
1425
+ },
1426
+ }));
1427
+ }
1428
+ if (forceActive ||
1050
1429
  !view.hasFocus ||
1051
1430
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1052
1431
  decorations.push(getReplaceDecoration({
1053
1432
  range: [node.from, node.to],
1054
- widget: new LinkWidget(url, url, "auto"),
1433
+ widget: new LinkWidget(url, url, node.from, node.to, view),
1434
+ }));
1435
+ }
1436
+ else {
1437
+ decorations.push(getMarkDecoration({
1438
+ range: [node.from, node.to],
1439
+ attributes: {
1440
+ "data-id": key,
1441
+ },
1055
1442
  }));
1056
1443
  }
1057
1444
  }
@@ -1078,7 +1465,7 @@ class ListPointWidget extends view.WidgetType {
1078
1465
  span.textContent = this.mark;
1079
1466
  if (LIST_OF_LIST_MARKS.has(this.lastCodePoint)) {
1080
1467
  span.classList.add(styles.common);
1081
- span.classList.add(index.CLASSES.listCommon);
1468
+ span.classList.add(CLASSES.listCommon);
1082
1469
  }
1083
1470
  if (CODE_OF_ORDERED_LIST_MARK === this.lastCodePoint) {
1084
1471
  span.classList.add(styles.ordered);
@@ -1087,7 +1474,7 @@ class ListPointWidget extends view.WidgetType {
1087
1474
  }
1088
1475
  }
1089
1476
 
1090
- function getListSelectionDecorations({ decorations, node, view, isReadonly, }) {
1477
+ function getListSelectionDecorations({ decorations, node, view, forceActive, }) {
1091
1478
  if (node.name !== NAME_OF_LIST) {
1092
1479
  return;
1093
1480
  }
@@ -1098,14 +1485,14 @@ function getListSelectionDecorations({ decorations, node, view, isReadonly, }) {
1098
1485
  }
1099
1486
  const nextSibling = node.node.nextSibling;
1100
1487
  if (nextSibling && nextSibling.name === NAME_OF_TODO$1) {
1101
- if (isReadonly ||
1488
+ if (forceActive ||
1102
1489
  !view.hasFocus ||
1103
1490
  !isInRange(view.state.selection.ranges, [node.from, nextSibling.from + 3])) {
1104
1491
  decorations.push(getHideDecoration({ range: [node.from, nextSibling.from] }));
1105
1492
  }
1106
1493
  return;
1107
1494
  }
1108
- if (isReadonly ||
1495
+ if (forceActive ||
1109
1496
  !view.hasFocus ||
1110
1497
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1111
1498
  decorations.push(getReplaceDecoration({
@@ -1127,15 +1514,15 @@ function getMentionDecorations({ decorations, node }) {
1127
1514
  return;
1128
1515
  }
1129
1516
  decorations.push(getMarkDecoration({
1130
- style: clsx(styles.mention, index.CLASSES.mention),
1517
+ style: clsx(styles.mention, CLASSES.mention),
1131
1518
  range: [node.from, node.to],
1132
1519
  }));
1133
1520
  }
1134
- function getMentionSelectionDecorations({ decorations, node, view, isReadonly, }) {
1521
+ function getMentionSelectionDecorations({ decorations, node, view, forceActive, }) {
1135
1522
  if (node.name !== NAME_OF_MENTION) {
1136
1523
  return;
1137
1524
  }
1138
- if (isReadonly ||
1525
+ if (forceActive ||
1139
1526
  !view.hasFocus ||
1140
1527
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1141
1528
  decorations.push(getHideDecoration({ range: [node.from, node.from + 1] }));
@@ -1157,11 +1544,11 @@ function getStrikeThroughDecorations({ decorations, node }) {
1157
1544
  range: [node.from, node.to],
1158
1545
  }));
1159
1546
  }
1160
- function getStrikeThroughSelectionDecorations({ decorations, node, view, isReadonly, }) {
1547
+ function getStrikeThroughSelectionDecorations({ decorations, node, view, forceActive, }) {
1161
1548
  if (node.name !== NAME_OF_STRIKE_THROUGH) {
1162
1549
  return;
1163
1550
  }
1164
- if (isReadonly ||
1551
+ if (forceActive ||
1165
1552
  !view.hasFocus ||
1166
1553
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1167
1554
  decorations.push(getHideDecoration({ range: [node.from, node.from + 2] }));
@@ -1195,7 +1582,7 @@ class TodoWidget extends view.WidgetType {
1195
1582
  to: this.position + 1,
1196
1583
  insert: this.checked ? " " : "x",
1197
1584
  };
1198
- index.saveDispatch(() => {
1585
+ saveDispatch(() => {
1199
1586
  if (!this.view)
1200
1587
  return;
1201
1588
  this.view.dispatch(this.view.state.update({ changes: change }));
@@ -1225,14 +1612,14 @@ class TodoWidget extends view.WidgetType {
1225
1612
  }
1226
1613
  }
1227
1614
 
1228
- function getTodoSelectionDecoration({ decorations, node, view, isReadonly, }) {
1615
+ function getTodoSelectionDecoration({ decorations, node, view, forceActive, }) {
1229
1616
  if (node.name !== NAME_OF_TODO)
1230
1617
  return;
1231
1618
  const prevSibling = node.node.prevSibling;
1232
1619
  if (!prevSibling || prevSibling.name !== NAME_OF_LIST_MARK)
1233
1620
  return;
1234
1621
  const isChecked = LIST_OF_TODO_MARKS.has(view.state.doc.sliceString(node.from + 1, node.from + 2).codePointAt(0) || 0);
1235
- if (isReadonly ||
1622
+ if (forceActive ||
1236
1623
  !view.hasFocus ||
1237
1624
  !isInRange(view.state.selection.ranges, [prevSibling.from, node.from + 3])) {
1238
1625
  decorations.push(getReplaceDecoration({
@@ -1242,7 +1629,7 @@ function getTodoSelectionDecoration({ decorations, node, view, isReadonly, }) {
1242
1629
  }
1243
1630
  if (isChecked) {
1244
1631
  const line = view.lineBlockAt(node.from);
1245
- if (isReadonly ||
1632
+ if (forceActive ||
1246
1633
  !view.hasFocus ||
1247
1634
  !isInRange(view.state.selection.ranges, [line.from, line.to]))
1248
1635
  decorations.push(getMarkDecoration({ style: styles.todo__checked, range: [node.from + 4, node.to] }));
@@ -1292,87 +1679,305 @@ const SKIP_MARKS = new Set([
1292
1679
  "HeaderMark",
1293
1680
  "TaskMarker",
1294
1681
  ]);
1295
- let markdownDecorations = [];
1296
- function getDecorations(view$1, isChanged) {
1297
- const decorations = isChanged ? [] : markdownDecorations;
1298
- const selectionDecorations = [];
1299
- const contentEditable = view$1.contentDOM.getAttribute("contenteditable");
1300
- const isReadonly = !contentEditable || contentEditable === "false";
1301
- for (const { from: fromVisible, to: toVisible } of view$1.visibleRanges) {
1302
- language.syntaxTree(view$1.state).iterate({
1303
- from: fromVisible,
1304
- to: toVisible,
1305
- enter: (node) => {
1306
- if (SKIP_MARKS.has(node.name))
1307
- return;
1308
- /** Decoration by change content */
1309
- if (isChanged) {
1310
- decorationFunctions.forEach((f) => f({ decorations, node, view: view$1 }));
1311
- }
1312
- /** Decoration by selection content */
1313
- selectionDecorationFunctions.forEach((f) => f({
1314
- decorations: selectionDecorations,
1315
- node,
1316
- view: view$1,
1317
- isReadonly,
1318
- }));
1319
- },
1320
- });
1321
- }
1322
- if (isChanged) {
1323
- markdownDecorations = decorations;
1682
+ function createDecorationsGetter() {
1683
+ let markdownDecorationsCache = [];
1684
+ let markdownSelectionDecorationsCache = [];
1685
+ function getDecorations(view$1, isChanged, mouseReleased) {
1686
+ const processDecorations = isChanged;
1687
+ const processSelectionDecorations = isChanged || mouseReleased;
1688
+ const decorations = processDecorations ? [] : markdownDecorationsCache;
1689
+ const selectionDecorations = processSelectionDecorations
1690
+ ? []
1691
+ : markdownSelectionDecorationsCache;
1692
+ const contentEditable = view$1.contentDOM.getAttribute("contenteditable");
1693
+ const isReadonly = !contentEditable || contentEditable === "false";
1694
+ for (const { from: fromVisible, to: toVisible } of view$1.visibleRanges) {
1695
+ language.syntaxTree(view$1.state).iterate({
1696
+ from: fromVisible,
1697
+ to: toVisible,
1698
+ enter: (node) => {
1699
+ if (SKIP_MARKS.has(node.name))
1700
+ return;
1701
+ /** Decoration by change content */
1702
+ if (processDecorations)
1703
+ decorationFunctions.forEach((f) => f({ decorations, node, view: view$1 }));
1704
+ /** Decoration by selection content */
1705
+ if (processSelectionDecorations)
1706
+ selectionDecorationFunctions.forEach((f) => f({
1707
+ decorations: selectionDecorations,
1708
+ node,
1709
+ view: view$1,
1710
+ forceActive: isReadonly,
1711
+ }));
1712
+ },
1713
+ });
1714
+ }
1715
+ if (processDecorations) {
1716
+ markdownDecorationsCache = decorations;
1717
+ }
1718
+ if (processSelectionDecorations) {
1719
+ markdownSelectionDecorationsCache = selectionDecorations;
1720
+ }
1721
+ return view.Decoration.set([...decorations, ...selectionDecorations], true);
1324
1722
  }
1325
- return view.Decoration.set([...decorations, ...selectionDecorations], true);
1723
+ return getDecorations;
1326
1724
  }
1327
- const markdownDecorationPlugin = view.ViewPlugin.fromClass(class DecorationMarkdown {
1328
- decorations;
1329
- constructor(view) {
1330
- this.decorations = getDecorations(view, true);
1331
- }
1332
- update(update) {
1333
- const isDocumentChanged = update.docChanged ||
1334
- update.viewportChanged ||
1335
- language.syntaxTree(update.startState) != language.syntaxTree(update.state);
1336
- this.decorations = getDecorations(update.view, isDocumentChanged);
1337
- }
1338
- }, {
1339
- decorations: (plugin) => plugin.decorations,
1340
- });
1341
-
1342
- const mentionParser = {
1343
- defineNodes: [{ name: NAME_OF_MENTION }],
1344
- parseInline: [
1345
- {
1346
- name: NAME_OF_MENTION,
1347
- parse(cx, next, pos) {
1348
- if (next != CODE_OF_START_MENTION)
1349
- return -1;
1350
- let end = pos + 1;
1351
- for (let i = pos + 1; i < cx.end; i++) {
1352
- const next = cx.char(i);
1353
- if (next === CODE_OF_SPACE)
1354
- break;
1355
- end++;
1356
- }
1357
- return cx.addElement(cx.elt(NAME_OF_MENTION, pos, end));
1358
- },
1359
- },
1360
- ],
1725
+ const markdownDecorationPlugin = (stateConfig) => {
1726
+ return view.ViewPlugin.fromClass(class DecorationMarkdown {
1727
+ decorations;
1728
+ mouseReleased = true;
1729
+ dom;
1730
+ view;
1731
+ decorationGetter;
1732
+ constructor(view) {
1733
+ this.decorationGetter = createDecorationsGetter();
1734
+ this.decorations = this.decorationGetter(view, true, this.mouseReleased);
1735
+ this.dom = view.dom;
1736
+ this.view = view;
1737
+ document.addEventListener("mousedown", this.onMouseDown.bind(this));
1738
+ document.addEventListener("mouseup", this.onMouseUp.bind(this));
1739
+ saveDispatch(() => {
1740
+ this.view.dispatch(this.view.state.update({
1741
+ effects: [imageSrcGetterEffect.of(stateConfig.imageSrcGetter)],
1742
+ }));
1743
+ });
1744
+ }
1745
+ update(update) {
1746
+ const isDocumentChanged = update.docChanged ||
1747
+ update.viewportChanged ||
1748
+ language.syntaxTree(update.startState) != language.syntaxTree(update.state);
1749
+ this.decorations = this.decorationGetter(update.view, isDocumentChanged, this.mouseReleased);
1750
+ }
1751
+ destroy() {
1752
+ document.removeEventListener("mousedown", this.onMouseDown.bind(this));
1753
+ document.removeEventListener("mouseup", this.onMouseUp.bind(this));
1754
+ }
1755
+ onMouseDown() {
1756
+ this.mouseReleased = false;
1757
+ }
1758
+ onMouseUp() {
1759
+ this.mouseReleased = true;
1760
+ if (this.view.state.selection.ranges[0].from !== this.view.state.selection.ranges[0].to) {
1761
+ saveDispatch(() => {
1762
+ this.view.dispatch(this.view.state.update({}));
1763
+ });
1764
+ }
1765
+ }
1766
+ }, {
1767
+ decorations: (plugin) => plugin.decorations,
1768
+ });
1361
1769
  };
1362
1770
 
1363
- const markdownParserPlugin = [mentionParser];
1364
-
1365
- const initMarkdown = ({ languages }) => {
1771
+ async function InitSettings({ readonly, vimMode, }) {
1772
+ let vimExtension = [];
1773
+ if (vimMode) {
1774
+ const { vim } = await import('@replit/codemirror-vim');
1775
+ vimExtension = [vim({ status: true }), view.drawSelection()];
1776
+ }
1366
1777
  return [
1367
- langMarkdown.markdown({
1368
- base: langMarkdown.markdownLanguage,
1369
- codeLanguages: languages,
1370
- addKeymap: true,
1371
- extensions: [markdownParserPlugin],
1778
+ ReadonlyCompartment.of(view.EditorView.editable.of(!readonly)),
1779
+ VimModeCompartment.of(vimExtension),
1780
+ commands.history(),
1781
+ ];
1782
+ }
1783
+
1784
+ const initExtensions = async ({ onBlur, onChange, onFocus, onEnter, onEscape, readonly = true, vimMode = false, multiCursorText, provider, theme = "light", dark, light, languages, keyMaps, defaultKeyMaps, imageSrcGetter, }) => {
1785
+ const multiCursorMode = Boolean(multiCursorText && provider);
1786
+ const asyncPlugins = await Promise.all([
1787
+ InitSettings({ readonly, vimMode }),
1788
+ initKeyMaps({
1789
+ onEnter,
1790
+ onEscape,
1791
+ multiCursorMode,
1792
+ keyMaps,
1793
+ vimMode,
1794
+ theme,
1795
+ dark,
1796
+ defaultKeyMaps,
1797
+ light,
1372
1798
  }),
1373
- markdownDecorationPlugin,
1799
+ new Promise((resolve) => {
1800
+ void Promise.resolve().then(() => require('./index-CDtGUxs-.js')).then(({ initMarkdown }) => {
1801
+ resolve(initMarkdown({ languages, imageSrcGetter }));
1802
+ });
1803
+ }),
1804
+ ]);
1805
+ const extensions = [
1806
+ ...asyncPlugins,
1807
+ initTheme({ theme, dark, light }),
1808
+ initListeners({ onBlur, onChange, onFocus }),
1374
1809
  ];
1810
+ if (multiCursorText && provider) {
1811
+ const multiCursorModules = await Promise.all([import('yjs'), import('y-codemirror.next')]);
1812
+ const [{ UndoManager }, { yCollab }] = multiCursorModules;
1813
+ const undoManager = new UndoManager(multiCursorText);
1814
+ extensions.push(yCollab(multiCursorText, provider.awareness, { undoManager }));
1815
+ }
1816
+ return extensions;
1375
1817
  };
1376
1818
 
1377
- exports.initMarkdown = initMarkdown;
1378
- //# sourceMappingURL=index-DPZa3gzd.js.map
1819
+ async function initEditorProvider({ roomId, url, userName = "Anonymous", userColor, initialText, onStartProvider, }) {
1820
+ const { Doc } = await import('yjs');
1821
+ const { WebsocketProvider } = await import('y-websocket');
1822
+ const multiCursorDocument = new Doc();
1823
+ const multiCursorText = multiCursorDocument.getText(roomId);
1824
+ if (!userColor || !userColor.startsWith("#")) {
1825
+ // eslint-disable-next-line no-console
1826
+ console.warn("user color must be hex!");
1827
+ userColor = "#30bced";
1828
+ }
1829
+ const userColorLight = `${userColor.substring(0, 7)}33`;
1830
+ const provider = new WebsocketProvider(url, roomId, multiCursorDocument);
1831
+ provider.awareness.setLocalStateField("user", {
1832
+ name: userName,
1833
+ color: userColor,
1834
+ colorLight: userColorLight,
1835
+ });
1836
+ if (onStartProvider)
1837
+ provider.on("status", (event) => {
1838
+ onStartProvider(event?.status);
1839
+ });
1840
+ if (provider && multiCursorText)
1841
+ provider.on("sync", (isSynced) => {
1842
+ if (isSynced && !multiCursorText.length && initialText) {
1843
+ multiCursorText.insert(0, initialText);
1844
+ }
1845
+ });
1846
+ return { provider, multiCursorText };
1847
+ }
1848
+
1849
+ async function initEditorState({ text, ...rest }) {
1850
+ const extensions = await initExtensions(rest);
1851
+ return state.EditorState.create({
1852
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
1853
+ doc: rest.multiCursorText ? rest.multiCursorText.toString() : text,
1854
+ extensions,
1855
+ });
1856
+ }
1857
+
1858
+ async function initEditor({ multiCursor, root, initialText, ...rest }) {
1859
+ let provider;
1860
+ let multiCursorText;
1861
+ if (multiCursor) {
1862
+ const editorProvider = await initEditorProvider({ ...multiCursor, initialText });
1863
+ provider = editorProvider.provider;
1864
+ multiCursorText = editorProvider.multiCursorText;
1865
+ }
1866
+ const state = await initEditorState({
1867
+ ...rest,
1868
+ text: initialText || "",
1869
+ provider,
1870
+ multiCursorText,
1871
+ });
1872
+ const view$1 = new view.EditorView({
1873
+ state,
1874
+ parent: root,
1875
+ });
1876
+ return { view: view$1, provider };
1877
+ }
1878
+
1879
+ class Editor {
1880
+ view;
1881
+ provider;
1882
+ arguments;
1883
+ constructor(options) {
1884
+ void initEditor(options).then((editor) => {
1885
+ this.view = editor.view;
1886
+ this.provider = editor.provider;
1887
+ });
1888
+ this.arguments = options;
1889
+ }
1890
+ focus = () => {
1891
+ if (!this.view)
1892
+ return;
1893
+ this.view.focus();
1894
+ };
1895
+ getContent = () => {
1896
+ if (!this.view)
1897
+ return;
1898
+ return this.view.state.doc.toString();
1899
+ };
1900
+ setContent = (content, position) => {
1901
+ if (!this.view)
1902
+ return;
1903
+ if (position == undefined) {
1904
+ const cursor = this.view.state.selection.main.head;
1905
+ position = cursor;
1906
+ }
1907
+ const transaction = this.view.state.update({
1908
+ changes: {
1909
+ from: position,
1910
+ insert: content,
1911
+ },
1912
+ });
1913
+ saveDispatch(() => {
1914
+ if (!this.view)
1915
+ return;
1916
+ this.view.dispatch(transaction);
1917
+ });
1918
+ };
1919
+ setReadonly = (readonly) => {
1920
+ saveDispatch(() => {
1921
+ if (!this.view)
1922
+ return;
1923
+ this.view.dispatch({
1924
+ effects: ReadonlyCompartment.reconfigure(view.EditorView.editable.of(!readonly)),
1925
+ });
1926
+ });
1927
+ };
1928
+ setTheme = (theme) => {
1929
+ saveDispatch(() => {
1930
+ if (!this.view)
1931
+ return;
1932
+ this.view.dispatch({
1933
+ effects: ThemeCompartment.reconfigure(theme === "dark"
1934
+ ? getDarkTheme({
1935
+ dark: this.arguments.dark,
1936
+ light: this.arguments.light,
1937
+ theme,
1938
+ })
1939
+ : getLightTheme({
1940
+ dark: this.arguments.dark,
1941
+ light: this.arguments.light,
1942
+ theme,
1943
+ })),
1944
+ });
1945
+ });
1946
+ };
1947
+ setVimMode = async (mode) => {
1948
+ if (!this.view)
1949
+ return;
1950
+ const { vim } = await import('@replit/codemirror-vim');
1951
+ saveDispatch(() => {
1952
+ if (!this.view)
1953
+ return;
1954
+ this.view.dispatch({
1955
+ effects: VimModeCompartment.reconfigure(mode ? [vim({ status: true }), view.drawSelection()] : []),
1956
+ });
1957
+ });
1958
+ };
1959
+ setUserProvider = (name = "Anonymous", color = "#000000") => {
1960
+ if (!this.provider)
1961
+ return;
1962
+ this.provider.awareness.setLocalStateField("user", { name, color });
1963
+ };
1964
+ destroy = () => {
1965
+ saveDispatch(() => {
1966
+ if (!this.view)
1967
+ return;
1968
+ this.view.destroy();
1969
+ });
1970
+ saveDispatch(() => {
1971
+ if (this.provider)
1972
+ this.provider.destroy();
1973
+ });
1974
+ };
1975
+ }
1976
+
1977
+ exports.CODE_OF_SPACE = CODE_OF_SPACE;
1978
+ exports.CODE_OF_START_MENTION = CODE_OF_START_MENTION;
1979
+ exports.Editor = Editor;
1980
+ exports.NAME_OF_MENTION = NAME_OF_MENTION;
1981
+ exports.markdownDecorationPlugin = markdownDecorationPlugin;
1982
+ exports.markdownState = markdownState;
1983
+ //# sourceMappingURL=index-CiorogHq.js.map