@krainovsd/markdown-editor 0.1.2 → 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-JTLnXX8Q.js → index-CiorogHq.js} +979 -372
  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 +82 -74
  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-JTLnXX8Q.js.map +0 -1
  64. package/lib/cjs/index-v4GhIAcy.js +0 -528
  65. package/lib/cjs/index-v4GhIAcy.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-v4GhIAcy.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,29 +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];
866
+ if (!image)
867
+ return false;
546
868
  delete IMAGE_NODES[this.key];
547
869
  EXISTING_WIDGETS.delete(this.key);
548
- if (image.src !== widget.link)
549
- image.src = widget.link;
870
+ if (image.src !== widget.src)
871
+ image.src = widget.src;
550
872
  if (image.alt !== widget.text)
551
873
  image.alt = widget.text;
552
874
  this.link = widget.link;
553
875
  this.text = widget.text;
554
876
  this.from = widget.from;
555
877
  this.to = widget.to;
878
+ this.registerListeners(image);
556
879
  IMAGE_NODES[this.key] = image;
557
880
  EXISTING_WIDGETS.add(this.key);
558
881
  return true;
@@ -560,24 +883,23 @@ class ImageWidget extends view.WidgetType {
560
883
  updateDOM() {
561
884
  return true;
562
885
  }
563
- toDOM(view) {
886
+ toDOM() {
564
887
  EXISTING_WIDGETS.add(this.key);
565
- if (IMAGE_NODES[this.key]) {
566
- const image = IMAGE_NODES[this.key];
567
- if (image.src !== this.link) {
568
- image.src = this.link;
888
+ let image = IMAGE_NODES[this.key];
889
+ if (image) {
890
+ if (image.src !== this.src) {
891
+ image.src = this.src;
569
892
  }
570
893
  if (image.alt !== this.text)
571
894
  image.alt = this.text;
572
895
  return image;
573
896
  }
574
- this.view = view;
575
- const image = document.createElement("img");
897
+ image = document.createElement("img");
576
898
  image.classList.add(styles.image);
577
899
  image.alt = this.text;
578
- image.src = this.link;
579
- image.addEventListener("mousedown", handleClick);
580
- image.addEventListener("click", handleClick);
900
+ image.src = this.src;
901
+ image.style.maxWidth = "100%";
902
+ this.registerListeners(image);
581
903
  IMAGE_NODES[this.key] = image;
582
904
  if (!interval)
583
905
  interval = setInterval(garbageCollectorInterval, INTERVAL_DELAY);
@@ -586,15 +908,25 @@ class ImageWidget extends view.WidgetType {
586
908
  destroy() {
587
909
  EXISTING_WIDGETS.delete(this.key);
588
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
+ }
589
923
  }
590
924
  function garbageCollectorInterval() {
591
925
  for (const [key, node] of Object.entries(IMAGE_NODES)) {
592
- if (EXISTING_WIDGETS.has(key))
926
+ if (EXISTING_WIDGETS.has(key) || !node)
593
927
  continue;
594
928
  delete IMAGE_NODES[key];
595
- node.removeEventListener("mousedown", handleClick);
596
- node.removeEventListener("click", handleClick);
597
- node.remove();
929
+ node.destroy?.();
598
930
  }
599
931
  if (Object.keys(IMAGE_NODES).length === 0 && interval) {
600
932
  clearInterval(interval);
@@ -602,44 +934,45 @@ function garbageCollectorInterval() {
602
934
  }
603
935
  }
604
936
  /** recursively find the link text node in line */
605
- 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) {
606
951
  if (!line)
607
952
  return null;
608
- const link = imageNode.src;
609
- let textNode = null;
610
953
  for (const node of Array.from(line.childNodes)) {
611
- if (node.nodeType !== 3) {
612
- const innerNode = getTextNode(imageNode, node);
613
- if (innerNode) {
614
- textNode = innerNode;
615
- break;
616
- }
617
- continue;
954
+ if (node instanceof HTMLElement && node.getAttribute("data-id") === key) {
955
+ return node;
618
956
  }
619
- const textContent = node.textContent;
620
- if (textContent && textContent.includes(link)) {
621
- textNode = node;
622
- break;
957
+ if (node.nodeType !== 3) {
958
+ const inner = getTextNodeContainer$1(text, link, key, node);
959
+ if (inner)
960
+ return inner;
623
961
  }
624
962
  }
625
- return textNode;
963
+ return null;
626
964
  }
627
- function getMinLength(imageNode) {
628
- const text = imageNode.alt || "";
629
- const link = imageNode.src;
630
- const startPosition = 4 + text.length;
631
- const endPosition = startPosition + link.length + 1;
632
- return endPosition;
633
- }
634
- function isCorrectNode(imageNode, node) {
965
+ function isCorrectNode$1(text, link, node) {
635
966
  if (!node)
636
967
  return false;
637
968
  const textContent = node?.textContent;
638
- const minLength = getMinLength(imageNode);
639
- 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));
640
974
  }
641
- function selectLink({ imageNode, node, selection, start }) {
642
- const link = imageNode.src;
975
+ function selectLink$1({ link, node, selection, start }) {
643
976
  const startPosition = start ?? (node.textContent?.indexOf?.(link) || 0);
644
977
  const endPosition = startPosition + link.length;
645
978
  const range = document.createRange();
@@ -648,7 +981,7 @@ function selectLink({ imageNode, node, selection, start }) {
648
981
  selection.removeAllRanges();
649
982
  selection.addRange(range);
650
983
  }
651
- function handleClick(event) {
984
+ function handleClick$1(view, text, link, key, event) {
652
985
  const selection = window.getSelection();
653
986
  if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
654
987
  return;
@@ -665,74 +998,66 @@ function handleClick(event) {
665
998
  const editor = Array.from(document.querySelectorAll(".cm-editor")).find((element) => element.contains(target));
666
999
  if (!selection || !editor || !parent)
667
1000
  return;
668
- const prevLine = parent.previousSibling;
669
- let textNode = getTextNode(target, prevLine);
670
- if (!textNode)
671
- textNode = getTextNode(target, parent);
1001
+ const textNode = getTextNode$1(text, link, key, line);
672
1002
  if (textNode) {
673
- if (isCorrectNode(target, textNode))
674
- selectLink({ selection, imageNode: target, node: textNode });
675
- return;
1003
+ return void selectLink$1({ selection, link, node: textNode });
676
1004
  }
677
- const range = document.createRange();
678
- range.selectNode(target);
679
- range.collapse(true);
680
- selection.removeAllRanges();
681
- selection.addRange(range);
682
- /** wait for the widget to disappear and link will be visible */
683
- void tick({
684
- delay: 0,
685
- maxDeep: 5,
686
- delayGetter: (deep) => {
687
- return deep > 1 ? 10 : 0;
688
- },
689
- recursiveCondition: () => {
690
- const textNode = getTextNode(target, line);
691
- return isCorrectNode(target, textNode);
692
- },
693
- })
694
- .then(() => {
695
- const textNode = getTextNode(target, line);
696
- if (isCorrectNode(target, textNode))
697
- 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
+ });
698
1019
  });
699
1020
  return false;
700
1021
  }
701
1022
 
702
- function getImageSelectionDecorations({ decorations, node, view, isReadonly, }) {
1023
+ function getImageDecorations({ decorations, node, view }) {
703
1024
  if (node.name !== NAME_OF_IMAGE) {
704
1025
  return;
705
1026
  }
706
1027
  const { text, url } = parseInfo(view, node);
707
- const line = view.lineBlockAt(node.from);
708
- if (line.from === node.from && line.to === node.to) {
709
- if (isReadonly ||
710
- !view.hasFocus ||
711
- !isInRange(view.state.selection.ranges, [line.from, line.to])) {
712
- decorations.push(getReplaceDecoration({
713
- range: [line.from, line.to],
714
- widget: new ImageWidget(text, url, node.from, node.to),
715
- }));
716
- }
717
- else {
718
- decorations.push(getWidgetDecorationOptions({
719
- range: [node.to + 1],
720
- widget: new ImageWidget(text, url, node.from, node.to),
721
- }));
722
- }
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;
723
1037
  }
724
- else if (isReadonly ||
725
- !view.hasFocus ||
726
- !isInRange(view.state.selection.ranges, [line.from, line.to])) {
727
- 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({
728
1044
  range: [node.from, node.to],
729
- widget: new ImageWidget(text, url, node.from, node.to),
1045
+ attributes: {
1046
+ "data-id": key,
1047
+ },
730
1048
  }));
731
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
+ }
732
1055
  else {
733
- decorations.push(getWidgetDecorationOptions({
734
- range: [node.to],
735
- 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
+ },
736
1061
  }));
737
1062
  }
738
1063
  }
@@ -765,6 +1090,7 @@ function parseInfo(view, node) {
765
1090
  }
766
1091
  const imageDecorationPlugin = {
767
1092
  selectionDecorations: [getImageSelectionDecorations],
1093
+ decorations: [getImageDecorations],
768
1094
  };
769
1095
 
770
1096
  const NAME_OF_ITALIC = "Emphasis";
@@ -779,12 +1105,12 @@ function getItalicDecorations({ decorations, node }) {
779
1105
  range: [node.from, node.to],
780
1106
  }));
781
1107
  }
782
- function getItalicSelectionDecorations({ decorations, node, view, isReadonly, }) {
1108
+ function getItalicSelectionDecorations({ decorations, node, view, forceActive, }) {
783
1109
  if (node.name !== NAME_OF_ITALIC) {
784
1110
  return;
785
1111
  }
786
- if (checkIsSeveralEmphasis({ decorations, node, view, isReadonly })) {
787
- return void splitEmphasis({ decorations, node, view, isReadonly });
1112
+ if (checkIsSeveralEmphasis({ decorations, node, view, forceActive })) {
1113
+ return void splitEmphasis({ decorations, node, view, forceActive });
788
1114
  }
789
1115
  let step = 1;
790
1116
  const startText = view.state.doc.sliceString(node.from, node.from + 3);
@@ -792,7 +1118,7 @@ function getItalicSelectionDecorations({ decorations, node, view, isReadonly, })
792
1118
  LIST_OF_ITALIC_MARKS.has(startText.charCodeAt(1)) &&
793
1119
  LIST_OF_ITALIC_MARKS.has(startText.charCodeAt(2)))
794
1120
  step = 3;
795
- if (isReadonly ||
1121
+ if (forceActive ||
796
1122
  !view.hasFocus ||
797
1123
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
798
1124
  decorations.push(getHideDecoration({ range: [node.from, node.from + step] }));
@@ -813,7 +1139,7 @@ function checkIsSeveralEmphasis({ node, view }) {
813
1139
  return true;
814
1140
  return false;
815
1141
  }
816
- function splitEmphasis({ decorations, node, view, isReadonly }) {
1142
+ function splitEmphasis({ decorations, node, view, forceActive }) {
817
1143
  const text = view.state.doc.sliceString(node.from, node.to);
818
1144
  let marks = 0;
819
1145
  let pos = 0;
@@ -826,13 +1152,13 @@ function splitEmphasis({ decorations, node, view, isReadonly }) {
826
1152
  decorations,
827
1153
  view,
828
1154
  node: { ...node, name: node.name, from: node.from, to: node.from + pos },
829
- isReadonly,
1155
+ forceActive,
830
1156
  });
831
1157
  getItalicSelectionDecorations({
832
1158
  decorations,
833
1159
  view,
834
1160
  node: { ...node, name: node.name, from: node.from + pos, to: node.to },
835
- isReadonly,
1161
+ forceActive,
836
1162
  });
837
1163
  }
838
1164
  const italicDecorationPlugin = {
@@ -848,10 +1174,11 @@ const CODE_OF_END_LINK_URL = 41; // )
848
1174
  const NAME_OF_AUTO_LINK = "Autolink";
849
1175
  const CODE_OF_LINK_LABEL_END = 58; // :
850
1176
 
851
- function getLinkLabelSelectionDecoration({ decorations, node, view, isReadonly, }) {
1177
+ // [label]:
1178
+ function getLinkLabelSelectionDecoration({ decorations, node, view, forceActive, }) {
852
1179
  if (view.state.doc.sliceString(node.to, node.to + 1).charCodeAt(0) !== CODE_OF_LINK_LABEL_END)
853
1180
  return;
854
- if (isReadonly ||
1181
+ if (forceActive ||
855
1182
  !view.hasFocus ||
856
1183
  !isInRange(view.state.selection.ranges, [node.from, node.to + 1])) {
857
1184
  decorations.push(getMarkDecoration({
@@ -863,141 +1190,165 @@ function getLinkLabelSelectionDecoration({ decorations, node, view, isReadonly,
863
1190
  }
864
1191
  }
865
1192
 
1193
+ const LINK_NODES = {};
866
1194
  class LinkWidget extends view.WidgetType {
867
1195
  text;
868
1196
  link;
869
- type;
1197
+ from;
1198
+ to;
870
1199
  view;
871
- constructor(text, link, type) {
1200
+ constructor(text, link, from, to, view) {
872
1201
  super();
873
1202
  this.text = text;
874
1203
  this.link = link;
875
- this.type = type;
1204
+ this.from = from;
1205
+ this.to = to;
1206
+ this.view = view;
876
1207
  }
877
- /** recursively find the link text node in line */
878
- getTextNode(line) {
879
- if (!line)
880
- return null;
881
- let textNode = null;
882
- for (const node of Array.from(line.childNodes)) {
883
- if (node.nodeType !== 3) {
884
- const innerNode = this.getTextNode(node);
885
- if (innerNode) {
886
- textNode = innerNode;
887
- break;
888
- }
889
- continue;
890
- }
891
- const textContent = node.textContent;
892
- if (textContent && textContent.includes(this.link)) {
893
- textNode = node;
894
- break;
895
- }
896
- }
897
- return textNode;
898
- }
899
- getMinLength() {
900
- let startPosition = 0;
901
- if (this.type === "link")
902
- startPosition = 3 + this.text.length;
903
- if (this.type === "auto")
904
- startPosition = 1;
905
- const endPosition = startPosition + this.link.length;
906
- return endPosition;
907
- }
908
- /** check that the text node is correct node */
909
- isCorrectNode(node) {
910
- 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)
911
1214
  return false;
912
- const textContent = node.textContent;
913
- const minLength = this.getMinLength();
914
- return Boolean(textContent && node.nodeType === 3 && textContent.length >= minLength);
915
- }
916
- selectLink(node, selection) {
917
- const startPosition = node.textContent?.indexOf?.(this.link) || 0;
918
- const endPosition = startPosition + this.link.length;
919
- const range = document.createRange();
920
- range.setStart(node, startPosition);
921
- range.setEnd(node, endPosition);
922
- selection.removeAllRanges();
923
- selection.addRange(range);
924
- }
925
- handleClick(event) {
926
- if (!this.view)
927
- return;
928
- /** open the link if has special key or the view is readonly */
929
- const contentEditable = this.view.contentDOM.getAttribute("contenteditable");
930
- const isReadonly = !contentEditable || contentEditable === "false";
931
- if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey || isReadonly) {
932
- if (event.type === "mousedown") {
933
- const target = event.target;
934
- window.open(target.href, "_blank");
935
- }
936
- return;
937
- }
938
- event.stopPropagation();
939
- event.preventDefault();
940
- const target = event.target;
941
- const parent = target.parentNode;
942
- let line = parent;
943
- /** recursively find line that contains link */
944
- while (line && !line.classList.contains("cm-line")) {
945
- line = line.parentNode;
946
- }
947
- const editor = this.view.dom.querySelector(".cm-content");
948
- const selection = window.getSelection();
949
- if (!selection || !editor || !parent)
950
- return;
951
- const range = document.createRange();
952
- range.selectNode(target);
953
- range.collapse(true);
954
- selection.removeAllRanges();
955
- selection.addRange(range);
956
- /** trick for correct select the link by click when the view is not focused */
957
- void tick({ delay: 0 }).then(() => {
958
- if (selection && selection.anchorNode?.nodeType !== 3) {
959
- selection.removeAllRanges();
960
- selection.addRange(range);
961
- }
962
- });
963
- /** wait for the widget to disappear and link will be visible */
964
- void tick({
965
- delay: 0,
966
- maxDeep: 5,
967
- delayGetter: (deep) => {
968
- return deep > 1 ? 10 : 5;
969
- },
970
- recursiveCondition: () => {
971
- const textNode = this.getTextNode(line);
972
- return this.isCorrectNode(textNode);
973
- },
974
- })
975
- .then(() => {
976
- const textNode = this.getTextNode(line);
977
- if (this.isCorrectNode(textNode))
978
- this.selectLink(textNode, selection);
979
- });
980
- 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;
981
1227
  }
982
- toDOM(view) {
983
- this.view = view;
1228
+ toDOM() {
984
1229
  const anchor = document.createElement("a");
985
1230
  anchor.classList.add(styles.link);
986
- anchor.classList.add(index.CLASSES.link);
1231
+ anchor.classList.add(CLASSES.link);
987
1232
  anchor.target = "_blank";
988
1233
  anchor.textContent = this.text;
989
1234
  anchor.href = this.link;
990
- anchor.addEventListener("mousedown", this.handleClick.bind(this));
991
- anchor.addEventListener("click", this.handleClick.bind(this));
1235
+ this.registerListeners(anchor);
1236
+ LINK_NODES[this.key] = anchor;
992
1237
  return anchor;
993
1238
  }
994
1239
  destroy(dom) {
995
- dom.removeEventListener("mousedown", this.handleClick.bind(this));
996
- 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;
997
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;
998
1349
  }
999
1350
 
1000
- function getLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1351
+ function getLinkSelectionDecorations({ decorations, node, view, forceActive, }) {
1001
1352
  if (node.name !== NAME_OF_LINK) {
1002
1353
  return;
1003
1354
  }
@@ -1024,15 +1375,34 @@ function getLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1024
1375
  urlCoordinates.to = pos;
1025
1376
  }
1026
1377
  if (urlCoordinates.from === -1 || urlCoordinates.to === -1)
1027
- return void getLinkLabelSelectionDecoration({ decorations, isReadonly, node, view });
1378
+ return void getLinkLabelSelectionDecoration({ decorations, forceActive, node, view });
1028
1379
  const text = content.substring(textCoordinates.from, textCoordinates.to);
1029
1380
  const url = content.substring(urlCoordinates.from, urlCoordinates.to);
1030
- 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 ||
1031
1393
  !view.hasFocus ||
1032
1394
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1033
1395
  decorations.push(getReplaceDecoration({
1034
1396
  range: [node.from, node.to],
1035
- 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
+ },
1036
1406
  }));
1037
1407
  }
1038
1408
  }
@@ -1040,16 +1410,35 @@ const linkDecorationPlugin = {
1040
1410
  selectionDecorations: [getLinkSelectionDecorations],
1041
1411
  };
1042
1412
 
1043
- function getAutoLinkSelectionDecorations({ decorations, node, view, isReadonly, }) {
1413
+ function getAutoLinkSelectionDecorations({ decorations, node, view, forceActive, }) {
1044
1414
  if (node.name !== NAME_OF_AUTO_LINK)
1045
1415
  return;
1046
1416
  const url = view.state.doc.sliceString(node.from + 1, node.to - 1);
1047
- 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 ||
1048
1429
  !view.hasFocus ||
1049
1430
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1050
1431
  decorations.push(getReplaceDecoration({
1051
1432
  range: [node.from, node.to],
1052
- 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
+ },
1053
1442
  }));
1054
1443
  }
1055
1444
  }
@@ -1076,7 +1465,7 @@ class ListPointWidget extends view.WidgetType {
1076
1465
  span.textContent = this.mark;
1077
1466
  if (LIST_OF_LIST_MARKS.has(this.lastCodePoint)) {
1078
1467
  span.classList.add(styles.common);
1079
- span.classList.add(index.CLASSES.listCommon);
1468
+ span.classList.add(CLASSES.listCommon);
1080
1469
  }
1081
1470
  if (CODE_OF_ORDERED_LIST_MARK === this.lastCodePoint) {
1082
1471
  span.classList.add(styles.ordered);
@@ -1085,7 +1474,7 @@ class ListPointWidget extends view.WidgetType {
1085
1474
  }
1086
1475
  }
1087
1476
 
1088
- function getListSelectionDecorations({ decorations, node, view, isReadonly, }) {
1477
+ function getListSelectionDecorations({ decorations, node, view, forceActive, }) {
1089
1478
  if (node.name !== NAME_OF_LIST) {
1090
1479
  return;
1091
1480
  }
@@ -1096,14 +1485,14 @@ function getListSelectionDecorations({ decorations, node, view, isReadonly, }) {
1096
1485
  }
1097
1486
  const nextSibling = node.node.nextSibling;
1098
1487
  if (nextSibling && nextSibling.name === NAME_OF_TODO$1) {
1099
- if (isReadonly ||
1488
+ if (forceActive ||
1100
1489
  !view.hasFocus ||
1101
1490
  !isInRange(view.state.selection.ranges, [node.from, nextSibling.from + 3])) {
1102
1491
  decorations.push(getHideDecoration({ range: [node.from, nextSibling.from] }));
1103
1492
  }
1104
1493
  return;
1105
1494
  }
1106
- if (isReadonly ||
1495
+ if (forceActive ||
1107
1496
  !view.hasFocus ||
1108
1497
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1109
1498
  decorations.push(getReplaceDecoration({
@@ -1125,15 +1514,15 @@ function getMentionDecorations({ decorations, node }) {
1125
1514
  return;
1126
1515
  }
1127
1516
  decorations.push(getMarkDecoration({
1128
- style: clsx(styles.mention, index.CLASSES.mention),
1517
+ style: clsx(styles.mention, CLASSES.mention),
1129
1518
  range: [node.from, node.to],
1130
1519
  }));
1131
1520
  }
1132
- function getMentionSelectionDecorations({ decorations, node, view, isReadonly, }) {
1521
+ function getMentionSelectionDecorations({ decorations, node, view, forceActive, }) {
1133
1522
  if (node.name !== NAME_OF_MENTION) {
1134
1523
  return;
1135
1524
  }
1136
- if (isReadonly ||
1525
+ if (forceActive ||
1137
1526
  !view.hasFocus ||
1138
1527
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1139
1528
  decorations.push(getHideDecoration({ range: [node.from, node.from + 1] }));
@@ -1155,11 +1544,11 @@ function getStrikeThroughDecorations({ decorations, node }) {
1155
1544
  range: [node.from, node.to],
1156
1545
  }));
1157
1546
  }
1158
- function getStrikeThroughSelectionDecorations({ decorations, node, view, isReadonly, }) {
1547
+ function getStrikeThroughSelectionDecorations({ decorations, node, view, forceActive, }) {
1159
1548
  if (node.name !== NAME_OF_STRIKE_THROUGH) {
1160
1549
  return;
1161
1550
  }
1162
- if (isReadonly ||
1551
+ if (forceActive ||
1163
1552
  !view.hasFocus ||
1164
1553
  !isInRange(view.state.selection.ranges, [node.from, node.to])) {
1165
1554
  decorations.push(getHideDecoration({ range: [node.from, node.from + 2] }));
@@ -1193,7 +1582,7 @@ class TodoWidget extends view.WidgetType {
1193
1582
  to: this.position + 1,
1194
1583
  insert: this.checked ? " " : "x",
1195
1584
  };
1196
- index.saveDispatch(() => {
1585
+ saveDispatch(() => {
1197
1586
  if (!this.view)
1198
1587
  return;
1199
1588
  this.view.dispatch(this.view.state.update({ changes: change }));
@@ -1223,14 +1612,14 @@ class TodoWidget extends view.WidgetType {
1223
1612
  }
1224
1613
  }
1225
1614
 
1226
- function getTodoSelectionDecoration({ decorations, node, view, isReadonly, }) {
1615
+ function getTodoSelectionDecoration({ decorations, node, view, forceActive, }) {
1227
1616
  if (node.name !== NAME_OF_TODO)
1228
1617
  return;
1229
1618
  const prevSibling = node.node.prevSibling;
1230
1619
  if (!prevSibling || prevSibling.name !== NAME_OF_LIST_MARK)
1231
1620
  return;
1232
1621
  const isChecked = LIST_OF_TODO_MARKS.has(view.state.doc.sliceString(node.from + 1, node.from + 2).codePointAt(0) || 0);
1233
- if (isReadonly ||
1622
+ if (forceActive ||
1234
1623
  !view.hasFocus ||
1235
1624
  !isInRange(view.state.selection.ranges, [prevSibling.from, node.from + 3])) {
1236
1625
  decorations.push(getReplaceDecoration({
@@ -1240,7 +1629,7 @@ function getTodoSelectionDecoration({ decorations, node, view, isReadonly, }) {
1240
1629
  }
1241
1630
  if (isChecked) {
1242
1631
  const line = view.lineBlockAt(node.from);
1243
- if (isReadonly ||
1632
+ if (forceActive ||
1244
1633
  !view.hasFocus ||
1245
1634
  !isInRange(view.state.selection.ranges, [line.from, line.to]))
1246
1635
  decorations.push(getMarkDecoration({ style: styles.todo__checked, range: [node.from + 4, node.to] }));
@@ -1290,87 +1679,305 @@ const SKIP_MARKS = new Set([
1290
1679
  "HeaderMark",
1291
1680
  "TaskMarker",
1292
1681
  ]);
1293
- let markdownDecorations = [];
1294
- function getDecorations(view$1, isChanged) {
1295
- const decorations = isChanged ? [] : markdownDecorations;
1296
- const selectionDecorations = [];
1297
- const contentEditable = view$1.contentDOM.getAttribute("contenteditable");
1298
- const isReadonly = !contentEditable || contentEditable === "false";
1299
- for (const { from: fromVisible, to: toVisible } of view$1.visibleRanges) {
1300
- language.syntaxTree(view$1.state).iterate({
1301
- from: fromVisible,
1302
- to: toVisible,
1303
- enter: (node) => {
1304
- if (SKIP_MARKS.has(node.name))
1305
- return;
1306
- /** Decoration by change content */
1307
- if (isChanged) {
1308
- decorationFunctions.forEach((f) => f({ decorations, node, view: view$1 }));
1309
- }
1310
- /** Decoration by selection content */
1311
- selectionDecorationFunctions.forEach((f) => f({
1312
- decorations: selectionDecorations,
1313
- node,
1314
- view: view$1,
1315
- isReadonly,
1316
- }));
1317
- },
1318
- });
1319
- }
1320
- if (isChanged) {
1321
- 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);
1322
1722
  }
1323
- return view.Decoration.set([...decorations, ...selectionDecorations], true);
1723
+ return getDecorations;
1324
1724
  }
1325
- const markdownDecorationPlugin = view.ViewPlugin.fromClass(class DecorationMarkdown {
1326
- decorations;
1327
- constructor(view) {
1328
- this.decorations = getDecorations(view, true);
1329
- }
1330
- update(update) {
1331
- const isDocumentChanged = update.docChanged ||
1332
- update.viewportChanged ||
1333
- language.syntaxTree(update.startState) != language.syntaxTree(update.state);
1334
- this.decorations = getDecorations(update.view, isDocumentChanged);
1335
- }
1336
- }, {
1337
- decorations: (plugin) => plugin.decorations,
1338
- });
1339
-
1340
- const mentionParser = {
1341
- defineNodes: [{ name: NAME_OF_MENTION }],
1342
- parseInline: [
1343
- {
1344
- name: NAME_OF_MENTION,
1345
- parse(cx, next, pos) {
1346
- if (next != CODE_OF_START_MENTION)
1347
- return -1;
1348
- let end = pos + 1;
1349
- for (let i = pos + 1; i < cx.end; i++) {
1350
- const next = cx.char(i);
1351
- if (next === CODE_OF_SPACE)
1352
- break;
1353
- end++;
1354
- }
1355
- return cx.addElement(cx.elt(NAME_OF_MENTION, pos, end));
1356
- },
1357
- },
1358
- ],
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
+ });
1359
1769
  };
1360
1770
 
1361
- const markdownParserPlugin = [mentionParser];
1362
-
1363
- 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
+ }
1364
1777
  return [
1365
- langMarkdown.markdown({
1366
- base: langMarkdown.markdownLanguage,
1367
- codeLanguages: languages,
1368
- addKeymap: true,
1369
- 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,
1370
1798
  }),
1371
- 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 }),
1372
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;
1373
1817
  };
1374
1818
 
1375
- exports.initMarkdown = initMarkdown;
1376
- //# sourceMappingURL=index-JTLnXX8Q.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