@dxos/react-ui-editor 0.4.8-main.c67d72c → 0.4.8-main.cd60bd2

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 (71) hide show
  1. package/dist/lib/browser/index.mjs +1926 -1743
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -2
  5. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +2 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.d.ts +12 -11
  9. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  10. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +4 -2
  11. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -0
  13. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  14. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  15. package/dist/types/src/extensions/dnd.d.ts +7 -1
  16. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  17. package/dist/types/src/extensions/factories.d.ts +5 -1
  18. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  19. package/dist/types/src/extensions/index.d.ts +1 -1
  20. package/dist/types/src/extensions/index.d.ts.map +1 -1
  21. package/dist/types/src/extensions/markdown/action.d.ts +9 -0
  22. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  23. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  24. package/dist/types/src/extensions/markdown/formatting.d.ts +7 -4
  25. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  26. package/dist/types/src/extensions/markdown/image.d.ts +6 -0
  27. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  29. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  30. package/dist/types/src/extensions/modes.d.ts +2 -1
  31. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  32. package/dist/types/src/extensions/state.d.ts +20 -0
  33. package/dist/types/src/extensions/state.d.ts.map +1 -0
  34. package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
  35. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  36. package/dist/types/src/hooks/useDocAccessor.d.ts +4 -2
  37. package/dist/types/src/hooks/useDocAccessor.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  39. package/dist/types/src/hooks/useTextEditor.stories.d.ts +1 -0
  40. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
  41. package/dist/types/src/themes/default.d.ts.map +1 -1
  42. package/dist/types/src/translations.d.ts +1 -0
  43. package/dist/types/src/translations.d.ts.map +1 -1
  44. package/package.json +26 -24
  45. package/src/components/TextEditor/TextEditor.stories.tsx +30 -6
  46. package/src/components/TextEditor/TextEditor.tsx +13 -12
  47. package/src/components/Toolbar/Toolbar.stories.tsx +23 -24
  48. package/src/components/Toolbar/Toolbar.tsx +102 -60
  49. package/src/extensions/automerge/automerge.ts +4 -4
  50. package/src/extensions/command/state.ts +2 -2
  51. package/src/extensions/comments.ts +2 -2
  52. package/src/extensions/dnd.ts +31 -9
  53. package/src/extensions/factories.ts +41 -15
  54. package/src/extensions/index.ts +1 -1
  55. package/src/extensions/markdown/action.ts +112 -0
  56. package/src/extensions/markdown/bundle.ts +8 -11
  57. package/src/extensions/markdown/formatting.test.ts +13 -13
  58. package/src/extensions/markdown/formatting.ts +95 -87
  59. package/src/extensions/markdown/image.ts +6 -0
  60. package/src/extensions/markdown/index.ts +1 -0
  61. package/src/extensions/modes.ts +11 -2
  62. package/src/extensions/state.ts +90 -0
  63. package/src/hooks/useActionHandler.ts +4 -85
  64. package/src/hooks/useDocAccessor.ts +8 -2
  65. package/src/hooks/useTextEditor.stories.tsx +3 -3
  66. package/src/hooks/useTextEditor.ts +3 -0
  67. package/src/themes/default.ts +24 -8
  68. package/src/translations.ts +1 -0
  69. package/dist/types/src/extensions/cursor-line-margin.d.ts +0 -7
  70. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +0 -1
  71. package/src/extensions/cursor-line-margin.ts +0 -94
@@ -15,6 +15,7 @@ var translations_default = [
15
15
  "blockquote label": "Block quote",
16
16
  "codeblock label": "Code block",
17
17
  "comment label": "Create comment",
18
+ "image label": "Insert image",
18
19
  "heading label": "Heading level",
19
20
  "table label": "Create table"
20
21
  }
@@ -23,7 +24,7 @@ var translations_default = [
23
24
  ];
24
25
 
25
26
  // packages/ui/react-ui-editor/src/index.ts
26
- import { keymap as keymap9 } from "@codemirror/view";
27
+ import { keymap as keymap11 } from "@codemirror/view";
27
28
  import { tags as tags2 } from "@lezer/highlight";
28
29
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
29
30
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
@@ -33,8 +34,8 @@ import { EditorState as EditorState2 } from "@codemirror/state";
33
34
  import { EditorView as EditorView16 } from "@codemirror/view";
34
35
  import { useFocusableGroup } from "@fluentui/react-tabster";
35
36
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
36
- import { log as log9 } from "@dxos/log";
37
- import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
37
+ import { log as log7 } from "@dxos/log";
38
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
38
39
 
39
40
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
40
41
  import { StateField } from "@codemirror/state";
@@ -56,8 +57,8 @@ var Cursor = class _Cursor {
56
57
  });
57
58
  }
58
59
  static {
59
- this.getCursorFromRange = (state, range) => {
60
- const cursorConverter2 = state.facet(_Cursor.converter);
60
+ this.getCursorFromRange = (state2, range) => {
61
+ const cursorConverter2 = state2.facet(_Cursor.converter);
61
62
  const from = cursorConverter2.toCursor(range.from);
62
63
  const to = cursorConverter2.toCursor(range.to, -1);
63
64
  return [
@@ -67,8 +68,8 @@ var Cursor = class _Cursor {
67
68
  };
68
69
  }
69
70
  static {
70
- this.getRangeFromCursor = (state, cursor) => {
71
- const cursorConverter2 = state.facet(_Cursor.converter);
71
+ this.getRangeFromCursor = (state2, cursor) => {
72
+ const cursorConverter2 = state2.facet(_Cursor.converter);
72
73
  const parts = cursor.split(":");
73
74
  const from = cursorConverter2.fromCursor(parts[0]);
74
75
  const to = cursorConverter2.fromCursor(parts[1]);
@@ -85,15 +86,15 @@ var annotationMark = Decoration.mark({
85
86
  class: "cm-annotation"
86
87
  });
87
88
  var annotations = (options = {}) => {
88
- const match = (state) => {
89
+ const match = (state2) => {
89
90
  const annotations2 = [];
90
- const text = state.doc.toString();
91
+ const text = state2.doc.toString();
91
92
  if (options.match) {
92
93
  const matches = text.matchAll(options.match);
93
94
  for (const match2 of matches) {
94
95
  const from = match2.index;
95
96
  const to = from + match2[0].length;
96
- const cursor = Cursor.getCursorFromRange(state, {
97
+ const cursor = Cursor.getCursorFromRange(state2, {
97
98
  from,
98
99
  to
99
100
  });
@@ -105,8 +106,8 @@ var annotations = (options = {}) => {
105
106
  return annotations2;
106
107
  };
107
108
  const annotationsState = StateField.define({
108
- create: (state) => {
109
- return match(state);
109
+ create: (state2) => {
110
+ return match(state2);
110
111
  },
111
112
  update: (value, tr) => {
112
113
  if (!tr.changes.empty) {
@@ -119,10 +120,10 @@ var annotations = (options = {}) => {
119
120
  annotationsState,
120
121
  EditorView.decorations.compute([
121
122
  annotationsState
122
- ], (state) => {
123
- const annotations2 = state.field(annotationsState);
124
- const decorations = annotations2.map((annotation2) => {
125
- const range = Cursor.getRangeFromCursor(state, annotation2.cursor);
123
+ ], (state2) => {
124
+ const annotations2 = state2.field(annotationsState);
125
+ const decorations = annotations2.map((annotation) => {
126
+ const range = Cursor.getRangeFromCursor(state2, annotation.cursor);
126
127
  return range && annotationMark.range(range.from, range.to);
127
128
  }).filter(isNotFalsy);
128
129
  return Decoration.set(decorations);
@@ -239,8 +240,8 @@ var cursorConverter = ({ handle, path }) => ({
239
240
 
240
241
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
241
242
  import { Annotation, StateEffect } from "@codemirror/state";
242
- var getPath = (state, field) => state.field(field).path;
243
- var getLastHeads = (state, field) => state.field(field).lastHeads;
243
+ var getPath = (state2, field) => state2.field(field).path;
244
+ var getLastHeads = (state2, field) => state2.field(field).lastHeads;
244
245
  var updateHeadsEffect = StateEffect.define({});
245
246
  var updateHeads = (newHeads) => updateHeadsEffect.of({
246
247
  newHeads
@@ -255,8 +256,8 @@ import { next as A3 } from "@dxos/automerge/automerge";
255
256
 
256
257
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
257
258
  import { next as A2 } from "@dxos/automerge/automerge";
258
- var updateAutomerge = (field, handle, transactions, state) => {
259
- const { lastHeads, path } = state.field(field);
259
+ var updateAutomerge = (field, handle, transactions, state2) => {
260
+ const { lastHeads, path } = state2.field(field);
260
261
  let hasChanges = false;
261
262
  for (const tr of transactions) {
262
263
  tr.changes.iterChanges(() => {
@@ -303,7 +304,7 @@ var updateCodeMirror = (view, selection, target, patches) => {
303
304
  annotations: reconcileAnnotation.of(false)
304
305
  });
305
306
  };
306
- var handlePatch = (patch, target, state) => {
307
+ var handlePatch = (patch, target, state2) => {
307
308
  if (patch.action === "insert") {
308
309
  return handleInsert(target, patch);
309
310
  } else if (patch.action === "splice") {
@@ -311,7 +312,7 @@ var handlePatch = (patch, target, state) => {
311
312
  } else if (patch.action === "del") {
312
313
  return handleDel(target, patch);
313
314
  } else if (patch.action === "put") {
314
- return handlePut(target, patch, state);
315
+ return handlePut(target, patch, state2);
315
316
  } else {
316
317
  return null;
317
318
  }
@@ -355,7 +356,7 @@ var handleDel = (target, patch) => {
355
356
  }
356
357
  ];
357
358
  };
358
- var handlePut = (target, patch, state) => {
359
+ var handlePut = (target, patch, state2) => {
359
360
  const index = charPath(target, [
360
361
  ...patch.path,
361
362
  0
@@ -363,7 +364,7 @@ var handlePut = (target, patch, state) => {
363
364
  if (index == null) {
364
365
  return [];
365
366
  }
366
- const length = state.doc.length;
367
+ const length = state2.doc.length;
367
368
  if (typeof patch.value !== "string") {
368
369
  return [];
369
370
  }
@@ -474,13 +475,13 @@ var automerge = (accessor) => {
474
475
  ViewPlugin.fromClass(class {
475
476
  constructor(_view) {
476
477
  this._view = _view;
477
- accessor.handle.addListener("change", this._handleChange.bind(this));
478
+ this._handleChange = () => {
479
+ syncer.reconcile(this._view, false);
480
+ };
481
+ accessor.handle.addListener("change", this._handleChange);
478
482
  }
479
483
  destroy() {
480
- accessor.handle.removeListener("change", this._handleChange.bind(this));
481
- }
482
- _handleChange() {
483
- syncer.reconcile(this._view, false);
484
+ accessor.handle.removeListener("change", this._handleChange);
484
485
  }
485
486
  }),
486
487
  // Reconcile local updates.
@@ -561,9 +562,9 @@ var RemoteSelectionsDecorator = class {
561
562
  _updateRemoteSelections(update2) {
562
563
  const decorations = [];
563
564
  const awarenessStates = this._provider.getRemoteStates();
564
- for (const state of awarenessStates) {
565
- const anchor = state.position?.anchor ? this._cursorConverter.fromCursor(state.position.anchor) : null;
566
- const head = state.position?.head ? this._cursorConverter.fromCursor(state.position.head) : null;
565
+ for (const state2 of awarenessStates) {
566
+ const anchor = state2.position?.anchor ? this._cursorConverter.fromCursor(state2.position.anchor) : null;
567
+ const head = state2.position?.head ? this._cursorConverter.fromCursor(state2.position.head) : null;
567
568
  if (anchor == null || head == null) {
568
569
  continue;
569
570
  }
@@ -571,8 +572,8 @@ var RemoteSelectionsDecorator = class {
571
572
  const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
572
573
  const startLine = update2.view.state.doc.lineAt(start);
573
574
  const endLine = update2.view.state.doc.lineAt(end);
574
- const color = state.info.color ?? "#30bced";
575
- const lightColor = state.info.lightColor ?? color + "33";
575
+ const color = state2.info.color ?? "#30bced";
576
+ const lightColor = state2.info.lightColor ?? color + "33";
576
577
  if (startLine.number === endLine.number) {
577
578
  decorations.push({
578
579
  from: start,
@@ -625,7 +626,7 @@ var RemoteSelectionsDecorator = class {
625
626
  value: Decoration2.widget({
626
627
  side: head - anchor > 0 ? -1 : 1,
627
628
  block: false,
628
- widget: new RemoteCaretWidget(state.info.displayName ?? "Anonymous", color)
629
+ widget: new RemoteCaretWidget(state2.info.displayName ?? "Anonymous", color)
629
630
  })
630
631
  });
631
632
  }
@@ -1156,7 +1157,7 @@ var commandConfig = Facet3.define({
1156
1157
  });
1157
1158
  var commandState = StateField3.define({
1158
1159
  create: () => ({}),
1159
- update: (state, tr) => {
1160
+ update: (state2, tr) => {
1160
1161
  for (const effect of tr.effects) {
1161
1162
  if (effect.is(closeEffect)) {
1162
1163
  return {};
@@ -1195,7 +1196,7 @@ var commandState = StateField3.define({
1195
1196
  }
1196
1197
  });
1197
1198
  }
1198
- setTimeout(() => view2.focus());
1199
+ requestAnimationFrame(() => view2.focus());
1199
1200
  });
1200
1201
  }
1201
1202
  };
@@ -1207,7 +1208,7 @@ var commandState = StateField3.define({
1207
1208
  };
1208
1209
  }
1209
1210
  }
1210
- return state;
1211
+ return state2;
1211
1212
  },
1212
1213
  provide: (field) => [
1213
1214
  showTooltip.from(field, (value) => value.tooltip ?? null)
@@ -1453,8 +1454,8 @@ var setComments = StateEffect3.define();
1453
1454
  var setSelection = StateEffect3.define();
1454
1455
  var setCommentState = StateEffect3.define();
1455
1456
  var commentsState = StateField4.define({
1456
- create: (state) => ({
1457
- id: state.facet(documentId),
1457
+ create: (state2) => ({
1458
+ id: state2.facet(documentId),
1458
1459
  comments: [],
1459
1460
  selection: {}
1460
1461
  }),
@@ -1526,8 +1527,8 @@ var commentCurrentMark = Decoration4.mark({
1526
1527
  });
1527
1528
  var commentsDecorations = EditorView7.decorations.compute([
1528
1529
  commentsState
1529
- ], (state) => {
1530
- const { selection: { current }, comments: comments2 } = state.field(commentsState);
1530
+ ], (state2) => {
1531
+ const { selection: { current }, comments: comments2 } = state2.field(commentsState);
1531
1532
  const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1532
1533
  const range = comment.range;
1533
1534
  if (!range || range.from === range.to) {
@@ -1691,7 +1692,7 @@ var optionsFacet = Facet4.define({
1691
1692
  });
1692
1693
  var comments = (options = {}) => {
1693
1694
  const { key: shortcut = "meta-'" } = options;
1694
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1695
+ const handleSelect = debounce((state2) => options.onSelect?.(state2), 200);
1695
1696
  return [
1696
1697
  optionsFacet.of(options),
1697
1698
  documentId.of(options.id),
@@ -1740,9 +1741,9 @@ var comments = (options = {}) => {
1740
1741
  //
1741
1742
  // Track deleted ranges and update ranges for decorations.
1742
1743
  //
1743
- EditorView7.updateListener.of(({ view, state, changes }) => {
1744
+ EditorView7.updateListener.of(({ view, state: state2, changes }) => {
1744
1745
  let mod = false;
1745
- const { comments: comments2, ...value } = state.field(commentsState);
1746
+ const { comments: comments2, ...value } = state2.field(commentsState);
1746
1747
  changes.iterChanges((from, to, from2, to2) => {
1747
1748
  comments2.forEach(({ comment, range }) => {
1748
1749
  if (from2 === to2) {
@@ -1772,10 +1773,10 @@ var comments = (options = {}) => {
1772
1773
  //
1773
1774
  // Track selection/proximity.
1774
1775
  //
1775
- EditorView7.updateListener.of(({ view, state }) => {
1776
+ EditorView7.updateListener.of(({ view, state: state2 }) => {
1776
1777
  let min = Infinity;
1777
- const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1778
- const { head } = state.selection.main;
1778
+ const { selection: { current, closest }, comments: comments2 } = state2.field(commentsState);
1779
+ const { head } = state2.selection.main;
1779
1780
  const selection = {};
1780
1781
  comments2.forEach(({ comment, range }) => {
1781
1782
  if (head >= range.from && head <= range.to) {
@@ -1796,7 +1797,7 @@ var comments = (options = {}) => {
1796
1797
  });
1797
1798
  handleSelect({
1798
1799
  selection,
1799
- id: state.facet(documentId),
1800
+ id: state2.facet(documentId),
1800
1801
  comments: comments2.map(({ comment, range }) => ({
1801
1802
  comment,
1802
1803
  range,
@@ -1831,14 +1832,14 @@ var focusComment = (view, id, center = true) => {
1831
1832
  });
1832
1833
  }
1833
1834
  };
1834
- var useComments = (view, id, comments2 = []) => {
1835
+ var useComments = (view, id, comments2) => {
1835
1836
  useEffect(() => {
1836
1837
  if (view) {
1837
1838
  if (id === view.state.facet(documentId)) {
1838
1839
  view.dispatch({
1839
1840
  effects: setComments.of({
1840
1841
  id,
1841
- comments: comments2
1842
+ comments: comments2 ?? []
1842
1843
  })
1843
1844
  });
1844
1845
  }
@@ -1850,76 +1851,14 @@ var useComments = (view, id, comments2 = []) => {
1850
1851
  ]);
1851
1852
  };
1852
1853
 
1853
- // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1854
- import { Transaction } from "@codemirror/state";
1855
- import { EditorView as EditorView8 } from "@codemirror/view";
1856
- import { log as log5 } from "@dxos/log";
1857
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts";
1858
- var annotation = "cursorLineMargin";
1859
- var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1860
- var cursorLineMargin = (options = {
1861
- lines: 3
1862
- }) => {
1863
- const { lines } = options;
1864
- if (!lines || lines <= 0) {
1865
- return [];
1866
- }
1867
- return EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1868
- if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1869
- return;
1870
- }
1871
- const s = state;
1872
- const { main } = s.selection;
1873
- const rect = view.dom.getBoundingClientRect();
1874
- const viewportTop = rect.top - view.documentTop;
1875
- const viewportBottom = rect.bottom - view.documentTop;
1876
- const mainLine = lineAtPos(s, main.head);
1877
- const _visTopLine = lineAtPos(s, view.lineBlockAtHeight(viewportTop).from);
1878
- const botLineBlk = view.lineBlockAtHeight(viewportBottom);
1879
- const visBotLineDoc = lineAtPos(s, Math.min(botLineBlk.to, s.doc.length));
1880
- const visBotLine = botLineBlk.bottom >= viewportBottom ? visBotLineDoc : visBotLineDoc + Math.floor((viewportBottom - botLineBlk.bottom) / view.defaultLineHeight);
1881
- const needsScrollTop = false;
1882
- const needsScrollBot = mainLine >= visBotLine - lines;
1883
- if (needsScrollTop && needsScrollBot) {
1884
- log5.warn("is cursorScrollMargin too large for the editor size?", void 0, {
1885
- F: __dxlog_file6,
1886
- L: 65,
1887
- S: void 0,
1888
- C: (f, a) => f(...a)
1889
- });
1890
- return;
1891
- }
1892
- if (!needsScrollTop && !needsScrollBot) {
1893
- return;
1894
- }
1895
- if (selectionSet) {
1896
- const { head } = startState.selection.main;
1897
- const oldMainLine = lineAtPos(startState, head);
1898
- if (needsScrollTop && oldMainLine < mainLine) {
1899
- return;
1900
- }
1901
- if (needsScrollBot && oldMainLine > mainLine) {
1902
- return;
1903
- }
1904
- }
1905
- view.dispatch({
1906
- annotations: Transaction.userEvent.of(annotation),
1907
- effects: EditorView8.scrollIntoView(s.selection.main.head, {
1908
- y: needsScrollTop ? "start" : "end",
1909
- yMargin: view.defaultLineHeight * lines
1910
- })
1911
- });
1912
- });
1913
- };
1914
-
1915
1854
  // packages/ui/react-ui-editor/src/extensions/doc.ts
1916
1855
  import { Facet as Facet5 } from "@codemirror/state";
1917
1856
  import { invariant as invariant3 } from "@dxos/invariant";
1918
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/doc.ts";
1857
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/doc.ts";
1919
1858
  var documentId2 = Facet5.define({
1920
1859
  combine: (providers) => {
1921
1860
  invariant3(providers.length <= 1, void 0, {
1922
- F: __dxlog_file7,
1861
+ F: __dxlog_file6,
1923
1862
  L: 11,
1924
1863
  S: void 0,
1925
1864
  A: [
@@ -1932,33 +1871,51 @@ var documentId2 = Facet5.define({
1932
1871
  });
1933
1872
 
1934
1873
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
1935
- import { EditorView as EditorView9 } from "@codemirror/view";
1936
- import { log as log6 } from "@dxos/log";
1937
- var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/dnd.ts";
1938
- var dnd = () => {
1939
- return EditorView9.domEventHandlers({
1940
- drop: (event, view) => {
1941
- log6.info("domEventHandlersDrop", {
1942
- event
1943
- }, {
1944
- F: __dxlog_file8,
1945
- L: 14,
1946
- S: void 0,
1947
- C: (f, a) => f(...a)
1948
- });
1949
- }
1950
- });
1874
+ import { dropCursor, EditorView as EditorView8 } from "@codemirror/view";
1875
+ var styles4 = EditorView8.baseTheme({
1876
+ ".cm-dropCursor": {
1877
+ borderLeft: `2px solid ${getToken("extend.colors.primary.500")}`,
1878
+ color: getToken("extend.colors.primary.500"),
1879
+ padding: "0 4px"
1880
+ },
1881
+ ".cm-dropCursor:after": {
1882
+ content: '"\u2190"'
1883
+ }
1884
+ });
1885
+ var dropFile = (options = {}) => {
1886
+ return [
1887
+ styles4,
1888
+ dropCursor(),
1889
+ EditorView8.domEventHandlers({
1890
+ drop: (event, view) => {
1891
+ event.preventDefault();
1892
+ const files = event.dataTransfer?.files;
1893
+ const pos = view.posAtCoords(event);
1894
+ if (files?.length && pos !== null) {
1895
+ view.dispatch({
1896
+ selection: {
1897
+ anchor: pos
1898
+ }
1899
+ });
1900
+ options.onDrop?.(view, {
1901
+ files
1902
+ });
1903
+ }
1904
+ }
1905
+ })
1906
+ ];
1951
1907
  };
1952
1908
 
1953
1909
  // packages/ui/react-ui-editor/src/extensions/factories.ts
1954
- import { closeBrackets } from "@codemirror/autocomplete";
1955
- import { history } from "@codemirror/commands";
1910
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1911
+ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1956
1912
  import { bracketMatching } from "@codemirror/language";
1913
+ import { searchKeymap } from "@codemirror/search";
1957
1914
  import { EditorState } from "@codemirror/state";
1958
- import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1915
+ import { EditorView as EditorView9, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap5, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1959
1916
  import defaultsDeep2 from "lodash.defaultsdeep";
1960
1917
  import { generateName } from "@dxos/display-name";
1961
- import { log as log7 } from "@dxos/log";
1918
+ import { log as log5 } from "@dxos/log";
1962
1919
  import { hueTokens } from "@dxos/react-ui-theme";
1963
1920
  import { hexToHue, isNotFalsy as isNotFalsy2 } from "@dxos/util";
1964
1921
 
@@ -2163,50 +2120,75 @@ var defaultTheme = {
2163
2120
  * </div>
2164
2121
  * </div
2165
2122
  */
2166
- ".cm-panels": {
2167
- border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
2168
- },
2123
+ ".cm-panels": {},
2169
2124
  ".cm-panel": {
2170
- background: get3(tokens, "extend.colors.neutral.50"),
2171
2125
  fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2172
2126
  },
2127
+ ".cm-panel input[type=checkbox]": {
2128
+ marginRight: "0.4rem !important"
2129
+ },
2130
+ "&light .cm-panel": {
2131
+ background: get3(tokens, "extend.colors.neutral.50")
2132
+ },
2133
+ "&dark .cm-panel": {
2134
+ background: get3(tokens, "extend.colors.neutral.850")
2135
+ },
2173
2136
  ".cm-button": {
2174
2137
  margin: "4px",
2175
2138
  fontFamily: get3(tokens, "fontFamily.body", []).join(","),
2176
- background: get3(tokens, "extend.colors.neutral.100"),
2177
2139
  backgroundImage: "none",
2178
2140
  border: "none",
2141
+ "&:active": {
2142
+ backgroundImage: "none"
2143
+ }
2144
+ },
2145
+ "&light .cm-button": {
2146
+ background: get3(tokens, "extend.colors.neutral.100"),
2179
2147
  "&:hover": {
2180
2148
  background: get3(tokens, "extend.colors.neutral.200")
2181
2149
  },
2182
2150
  "&:active": {
2183
- background: get3(tokens, "extend.colors.neutral.300"),
2184
- backgroundImage: "none"
2151
+ background: get3(tokens, "extend.colors.neutral.300")
2185
2152
  }
2186
2153
  },
2187
- ".cm-panel input[type=checkbox]": {
2188
- marginRight: "0.4rem !important"
2154
+ "&dark .cm-button": {
2155
+ background: get3(tokens, "extend.colors.neutral.800"),
2156
+ "&:hover": {
2157
+ background: get3(tokens, "extend.colors.neutral.700")
2158
+ },
2159
+ "&:active": {
2160
+ background: get3(tokens, "extend.colors.neutral.600")
2161
+ }
2189
2162
  }
2190
2163
  };
2191
2164
 
2192
2165
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2193
- var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2194
- var defaults = {
2166
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2167
+ var defaultBasicOptions = {
2168
+ allowMultipleSelections: true,
2195
2169
  bracketMatching: true,
2196
2170
  closeBrackets: true,
2197
2171
  drawSelection: true,
2198
2172
  editable: true,
2199
2173
  history: true,
2200
- lineWrapping: true
2174
+ keymap: "standard",
2175
+ lineWrapping: true,
2176
+ search: true
2177
+ };
2178
+ var keymaps = {
2179
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
2180
+ standard: standardKeymap,
2181
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
2182
+ default: defaultKeymap
2201
2183
  };
2202
2184
  var createBasicExtensions = (_props) => {
2203
- const props = defaultsDeep2({}, _props, defaults);
2185
+ const props = defaultsDeep2({}, _props, defaultBasicOptions);
2204
2186
  return [
2205
- // TODO(burdon): Doesn't catch errors in keymap functions.
2206
- EditorView10.exceptionSink.of((err) => {
2207
- log7.catch(err, void 0, {
2208
- F: __dxlog_file9,
2209
- L: 78,
2187
+ // NOTE: Doesn't catch errors in keymap functions.
2188
+ EditorView9.exceptionSink.of((err) => {
2189
+ log5.catch(err, void 0, {
2190
+ F: __dxlog_file7,
2191
+ L: 91,
2210
2192
  S: void 0,
2211
2193
  C: (f, a) => f(...a)
2212
2194
  });
@@ -2214,37 +2196,50 @@ var createBasicExtensions = (_props) => {
2214
2196
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2215
2197
  props.bracketMatching && bracketMatching(),
2216
2198
  props.closeBrackets && closeBrackets(),
2217
- props.crosshairCursor && crosshairCursor(),
2218
- props.dropCursor && dropCursor(),
2199
+ props.dropCursor && dropCursor2(),
2219
2200
  props.drawSelection && drawSelection(),
2220
2201
  props.highlightActiveLine && highlightActiveLine(),
2221
2202
  props.history && history(),
2222
2203
  props.lineNumbers && lineNumbers(),
2223
- props.lineWrapping && EditorView10.lineWrapping,
2204
+ props.lineWrapping && EditorView9.lineWrapping,
2224
2205
  props.placeholder && placeholder(props.placeholder),
2225
2206
  props.readonly && [
2226
2207
  EditorState.readOnly.of(true),
2227
- EditorView10.editable.of(false)
2208
+ EditorView9.editable.of(false)
2228
2209
  ],
2229
2210
  props.scrollPastEnd && scrollPastEnd(),
2230
- props.tabSize && EditorState.tabSize.of(props.tabSize)
2211
+ props.tabSize && EditorState.tabSize.of(props.tabSize),
2212
+ // https://codemirror.net/docs/ref/#view.KeyBinding
2213
+ keymap5.of([
2214
+ ...(props.keymap && keymaps[props.keymap]) ?? [],
2215
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
2216
+ ...props.indentWithTab ? [
2217
+ indentWithTab
2218
+ ] : [],
2219
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2220
+ ...props.closeBrackets ? closeBracketsKeymap : [],
2221
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
2222
+ ...props.history ? historyKeymap : [],
2223
+ // https://codemirror.net/docs/ref/#search.searchKeymap
2224
+ ...props.search ? searchKeymap : []
2225
+ ].filter(isNotFalsy2))
2231
2226
  ].filter(isNotFalsy2);
2232
2227
  };
2233
- var defaultSlots = {
2228
+ var defaultThemeSlots = {
2234
2229
  editor: {
2235
2230
  className: "w-full bs-full"
2236
2231
  }
2237
2232
  };
2238
2233
  var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2239
- const slots = defaultsDeep2({}, _slots, defaultSlots);
2234
+ const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2240
2235
  return [
2241
- EditorView10.baseTheme(defaultTheme),
2242
- EditorView10.darkTheme.of(themeMode === "dark"),
2243
- theme && EditorView10.theme(theme),
2244
- slots.editor?.className && EditorView10.editorAttributes.of({
2236
+ EditorView9.baseTheme(defaultTheme),
2237
+ EditorView9.darkTheme.of(themeMode === "dark"),
2238
+ theme && EditorView9.theme(theme),
2239
+ slots.editor?.className && EditorView9.editorAttributes.of({
2245
2240
  class: slots.editor.className
2246
2241
  }),
2247
- slots.content?.className && EditorView10.contentAttributes.of({
2242
+ slots.content?.className && EditorView9.contentAttributes.of({
2248
2243
  class: slots.content.className
2249
2244
  })
2250
2245
  ].filter(isNotFalsy2);
@@ -2272,1621 +2267,1671 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2272
2267
  };
2273
2268
 
2274
2269
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2275
- import { EditorView as EditorView11 } from "@codemirror/view";
2270
+ import { EditorView as EditorView10 } from "@codemirror/view";
2276
2271
  var listener = ({ onFocus, onChange }) => {
2277
2272
  const extensions = [];
2278
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2273
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
2279
2274
  onFocus(focusing);
2280
2275
  return null;
2281
2276
  }));
2282
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2277
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
2283
2278
  onChange(update2.state.doc.toString());
2284
2279
  }));
2285
2280
  return extensions;
2286
2281
  };
2287
2282
 
2288
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2289
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2290
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2291
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2292
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2293
- import { languages } from "@codemirror/language-data";
2294
- import { searchKeymap } from "@codemirror/search";
2295
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2296
- import { keymap as keymap5 } from "@codemirror/view";
2297
-
2298
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2299
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
2300
- import { HighlightStyle } from "@codemirror/language";
2301
- import { tags, styleTags, Tag } from "@lezer/highlight";
2302
- import { Table } from "@lezer/markdown";
2303
- var markdownTags = {
2304
- Blockquote: Tag.define(),
2305
- CodeMark: Tag.define(),
2306
- CodeText: Tag.define(),
2307
- EmphasisMark: Tag.define(),
2308
- HeaderMark: Tag.define(),
2309
- InlineCode: Tag.define(),
2310
- LinkLabel: Tag.define(),
2311
- LinkReference: Tag.define(),
2312
- ListMark: Tag.define(),
2313
- QuoteMark: Tag.define(),
2314
- URL: Tag.define(),
2315
- // Custom.
2316
- TableCell: Tag.define()
2317
- };
2318
- Table.defineNodes?.forEach((node) => {
2319
- switch (node?.name) {
2320
- case "TableCell": {
2321
- node.style = markdownTags.TableCell;
2322
- break;
2283
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2284
+ import { snippet } from "@codemirror/autocomplete";
2285
+ import { syntaxTree } from "@codemirror/language";
2286
+ import { EditorSelection } from "@codemirror/state";
2287
+ import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
2288
+ import { useMemo, useState } from "react";
2289
+ var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
2290
+ var Inline;
2291
+ (function(Inline2) {
2292
+ Inline2[Inline2["Strong"] = 0] = "Strong";
2293
+ Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2294
+ Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2295
+ Inline2[Inline2["Code"] = 3] = "Code";
2296
+ })(Inline || (Inline = {}));
2297
+ var List;
2298
+ (function(List2) {
2299
+ List2[List2["Ordered"] = 0] = "Ordered";
2300
+ List2[List2["Bullet"] = 1] = "Bullet";
2301
+ List2[List2["Task"] = 2] = "Task";
2302
+ })(List || (List = {}));
2303
+ var setHeading = (level) => ({ state: state2, dispatch }) => {
2304
+ const { selection: { ranges }, doc } = state2;
2305
+ const changes = [];
2306
+ let prevBlock = -1;
2307
+ for (const range of ranges) {
2308
+ let sawBlock = false;
2309
+ syntaxTree(state2).iterate({
2310
+ from: range.from,
2311
+ to: range.to,
2312
+ enter: (node) => {
2313
+ if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2314
+ return;
2315
+ }
2316
+ sawBlock = true;
2317
+ prevBlock = node.from;
2318
+ const blockType = Textblocks[node.name];
2319
+ const isHeading = /heading(\d)/.exec(blockType);
2320
+ const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2321
+ if (curLevel < 0 || curLevel === level) {
2322
+ return;
2323
+ }
2324
+ if (curLevel === 0) {
2325
+ changes.push({
2326
+ from: node.from,
2327
+ insert: "#".repeat(level) + " "
2328
+ });
2329
+ } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2330
+ const nextLine = doc.lineAt(node.to);
2331
+ if (level) {
2332
+ changes.push({
2333
+ from: node.from,
2334
+ insert: "#".repeat(level) + " "
2335
+ });
2336
+ }
2337
+ changes.push({
2338
+ from: nextLine.from - 1,
2339
+ to: nextLine.to
2340
+ });
2341
+ } else {
2342
+ if (level === 0) {
2343
+ changes.push({
2344
+ from: node.from,
2345
+ to: Math.min(node.to, node.from + curLevel + 1)
2346
+ });
2347
+ } else if (level < curLevel) {
2348
+ changes.push({
2349
+ from: node.from,
2350
+ to: node.from + (curLevel - level)
2351
+ });
2352
+ } else {
2353
+ changes.push({
2354
+ from: node.from,
2355
+ insert: "#".repeat(level - curLevel)
2356
+ });
2357
+ }
2358
+ }
2359
+ }
2360
+ });
2361
+ let line;
2362
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2363
+ changes.push({
2364
+ from: line.from,
2365
+ to: line.to,
2366
+ insert: "#".repeat(level) + " "
2367
+ });
2323
2368
  }
2324
2369
  }
2325
- });
2326
- var markdownTagsExtensions = [
2327
- Table,
2328
- {
2329
- props: [
2330
- styleTags(markdownTags)
2331
- ]
2370
+ if (!changes.length) {
2371
+ return false;
2332
2372
  }
2333
- ];
2334
- var markdownHighlightStyle = (readonly) => {
2335
- return HighlightStyle.define([
2336
- {
2337
- tag: [
2338
- tags.keyword,
2339
- tags.name,
2340
- tags.deleted,
2341
- tags.character,
2342
- tags.propertyName,
2343
- tags.macroName,
2344
- tags.color,
2345
- tags.constant(tags.name),
2346
- tags.standard(tags.name),
2347
- tags.definition(tags.name),
2348
- tags.separator,
2349
- tags.typeName,
2350
- tags.className,
2351
- tags.number,
2352
- tags.changed,
2353
- tags.annotation,
2354
- tags.modifier,
2355
- tags.self,
2356
- tags.namespace,
2357
- tags.operator,
2358
- tags.operatorKeyword,
2359
- tags.escape,
2360
- tags.regexp,
2361
- tags.special(tags.string),
2362
- tags.meta,
2363
- tags.comment,
2364
- tags.atom,
2365
- tags.bool,
2366
- tags.special(tags.variableName),
2367
- tags.processingInstruction,
2368
- tags.string,
2369
- tags.inserted,
2370
- tags.invalid
2371
- ],
2372
- color: "inherit !important"
2373
- },
2374
- // Markdown marks.
2375
- {
2376
- tag: [
2377
- tags.meta,
2378
- tags.processingInstruction,
2379
- markdownTags.LinkLabel,
2380
- markdownTags.LinkReference,
2381
- markdownTags.ListMark
2382
- ],
2383
- class: mark
2384
- },
2385
- // Markdown marks.
2386
- {
2387
- tag: [
2388
- markdownTags.CodeMark,
2389
- markdownTags.HeaderMark,
2390
- markdownTags.QuoteMark,
2391
- markdownTags.EmphasisMark
2392
- ],
2393
- class: mark
2394
- },
2395
- // E.g., code block language (after ```).
2396
- {
2397
- tag: [
2398
- tags.function(tags.variableName),
2399
- tags.labelName
2400
- ],
2401
- class: codeMark
2402
- },
2403
- {
2404
- tag: [
2405
- tags.monospace
2406
- ],
2407
- class: "font-mono"
2408
- },
2409
- // Headings.
2410
- {
2411
- tag: tags.heading1,
2412
- class: heading(1)
2413
- },
2414
- {
2415
- tag: tags.heading2,
2416
- class: heading(2)
2417
- },
2418
- {
2419
- tag: tags.heading3,
2420
- class: heading(3)
2421
- },
2422
- {
2423
- tag: tags.heading4,
2424
- class: heading(4)
2425
- },
2426
- {
2427
- tag: tags.heading5,
2428
- class: heading(5)
2429
- },
2430
- {
2431
- tag: tags.heading6,
2432
- class: heading(6)
2433
- },
2434
- // Emphasis.
2435
- {
2436
- tag: tags.emphasis,
2437
- class: italic
2438
- },
2439
- {
2440
- tag: tags.strong,
2441
- class: bold
2442
- },
2443
- {
2444
- tag: tags.strikethrough,
2445
- class: strikethrough
2446
- },
2447
- // Naked URLs.
2448
- {
2449
- tag: [
2450
- markdownTags.URL
2451
- ],
2452
- class: inlineUrl
2453
- },
2454
- // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
2455
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
2456
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
2457
- // the code, but all other CSS properties will be inherited.
2458
- // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
2459
- {
2460
- tag: [
2461
- markdownTags.CodeText,
2462
- markdownTags.InlineCode
2463
- ],
2464
- class: code
2465
- },
2466
- {
2467
- tag: [
2468
- markdownTags.QuoteMark
2469
- ],
2470
- class: blockquote
2471
- },
2472
- {
2473
- tag: [
2474
- markdownTags.TableCell
2475
- ],
2476
- class: "font-mono"
2373
+ const changeSet = state2.changes(changes);
2374
+ dispatch(state2.update({
2375
+ changes: changeSet,
2376
+ selection: state2.selection.map(changeSet, 1),
2377
+ userEvent: "format.setHeading",
2378
+ scrollIntoView: true
2379
+ }));
2380
+ return true;
2381
+ };
2382
+ var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2383
+ const marker = inlineMarkerText(type);
2384
+ const changes = state2.changeByRange((range) => {
2385
+ if (!enable && range.empty) {
2386
+ const after = state2.doc.sliceString(range.head, range.head + 6);
2387
+ const found = after.indexOf(marker);
2388
+ if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2389
+ const before = state2.doc.sliceString(range.head - 6, range.head);
2390
+ if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2391
+ ...before.slice(before.length - found)
2392
+ ].reverse().join("") === after.slice(0, found)) {
2393
+ return {
2394
+ changes: [
2395
+ {
2396
+ from: range.head - marker.length - found,
2397
+ to: range.head - found
2398
+ },
2399
+ {
2400
+ from: range.head + found,
2401
+ to: range.head + found + marker.length
2402
+ }
2403
+ ],
2404
+ range: EditorSelection.cursor(range.from - marker.length)
2405
+ };
2406
+ }
2407
+ }
2477
2408
  }
2478
- ], {
2479
- scope: markdownLanguage2,
2480
- all: {
2481
- fontFamily: getToken("fontFamily.body", []).join(",")
2409
+ const changes2 = [];
2410
+ const changesAtEnd = [];
2411
+ let blockStart = -1;
2412
+ let blockEnd = -1;
2413
+ let startCovered = false;
2414
+ let endCovered = false;
2415
+ let { from, to } = range;
2416
+ syntaxTree(state2).iterate({
2417
+ from,
2418
+ to,
2419
+ enter: (node) => {
2420
+ const { name } = node;
2421
+ if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2422
+ blockStart = blockContentStart(node);
2423
+ blockEnd = blockContentEnd(node, state2.doc);
2424
+ startCovered = endCovered = false;
2425
+ } else if (name === "Link" || name === "Image" && enable) {
2426
+ if (from < node.from && to > node.from && to <= node.to) {
2427
+ to = node.to;
2428
+ } else if (to > node.to && from >= node.from && from < node.to) {
2429
+ from = node.from;
2430
+ }
2431
+ } else if (IgnoreInline.has(name) && enable) {
2432
+ if (node.from < from && node.to > from) {
2433
+ if (to === from) {
2434
+ to = node.to;
2435
+ }
2436
+ from = node.to;
2437
+ }
2438
+ if (node.from < to && node.to > to) {
2439
+ to = node.from;
2440
+ }
2441
+ } else if (Object.hasOwn(InlineMarker, name)) {
2442
+ const markType = InlineMarker[name];
2443
+ const size = inlineMarkerText(markType).length;
2444
+ const openEnd = node.from + size;
2445
+ const closeStart = node.to - size;
2446
+ if (markType === type) {
2447
+ if (openEnd <= from && closeStart >= from) {
2448
+ startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2449
+ from: node.from,
2450
+ to: openEnd
2451
+ } : true;
2452
+ }
2453
+ if (openEnd <= to && closeStart >= to) {
2454
+ endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2455
+ from: closeStart,
2456
+ to: node.to
2457
+ } : true;
2458
+ }
2459
+ }
2460
+ if (markType === type || type === 3 && enable) {
2461
+ if (node.from >= from && openEnd <= to) {
2462
+ changes2.push({
2463
+ from: node.from,
2464
+ to: openEnd
2465
+ });
2466
+ if (markType !== type && closeStart >= to) {
2467
+ changesAtEnd.push({
2468
+ from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
2469
+ insert: inlineMarkerText(markType)
2470
+ });
2471
+ }
2472
+ }
2473
+ if (closeStart >= from && node.to <= to) {
2474
+ changes2.push({
2475
+ from: closeStart,
2476
+ to: node.to
2477
+ });
2478
+ if (markType !== type && openEnd <= from) {
2479
+ changes2.push({
2480
+ from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
2481
+ insert: inlineMarkerText(markType)
2482
+ });
2483
+ }
2484
+ }
2485
+ }
2486
+ }
2487
+ },
2488
+ leave: (node) => {
2489
+ if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
2490
+ const rangeStart = Math.max(from, blockStart);
2491
+ const rangeEnd = Math.min(to, blockEnd);
2492
+ if (enable) {
2493
+ if (!startCovered) {
2494
+ changes2.push({
2495
+ from: rangeStart,
2496
+ insert: marker
2497
+ });
2498
+ }
2499
+ if (!endCovered) {
2500
+ changes2.push({
2501
+ from: rangeEnd,
2502
+ insert: marker
2503
+ });
2504
+ }
2505
+ } else {
2506
+ if (typeof startCovered === "object") {
2507
+ changes2.push(startCovered);
2508
+ } else if (startCovered) {
2509
+ changes2.push({
2510
+ from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
2511
+ insert: marker
2512
+ });
2513
+ }
2514
+ if (typeof endCovered === "object") {
2515
+ changes2.push(endCovered);
2516
+ } else if (endCovered) {
2517
+ changes2.push({
2518
+ from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
2519
+ insert: marker
2520
+ });
2521
+ }
2522
+ }
2523
+ }
2524
+ }
2525
+ });
2526
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
2527
+ return {
2528
+ changes: {
2529
+ from: range.head,
2530
+ insert: marker + marker
2531
+ },
2532
+ range: EditorSelection.cursor(range.head + marker.length)
2533
+ };
2482
2534
  }
2535
+ const changeSet = state2.changes(changes2.concat(changesAtEnd));
2536
+ return {
2537
+ changes: changeSet,
2538
+ range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2539
+ };
2483
2540
  });
2541
+ dispatch(state2.update(changes, {
2542
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2543
+ scrollIntoView: true
2544
+ }));
2545
+ return true;
2484
2546
  };
2485
-
2486
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2487
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2488
- return [
2489
- // Main extension.
2490
- // https://github.com/codemirror/lang-markdown
2491
- // https://codemirror.net/5/mode/markdown/index.html (demo).
2492
- markdown({
2493
- // GRM by default (vs strict CommonMark):
2494
- // Table, TaskList, Strikethrough, and Autolink.
2495
- // NOTE: This extends the parser; it doesn't affect rendering.
2496
- // https://github.github.com/gfm
2497
- // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
2498
- base: markdownLanguage3,
2499
- // Languages for syntax highlighting fenced code blocks.
2500
- codeLanguages: languages,
2501
- // Parser extensions.
2502
- extensions: [
2503
- // GFM provided by default.
2504
- markdownTagsExtensions
2505
- ]
2506
- }),
2507
- // https://github.com/codemirror/theme-one-dark
2508
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2509
- // Custom styles.
2510
- syntaxHighlighting(markdownHighlightStyle()),
2511
- keymap5.of([
2512
- // https://codemirror.net/docs/ref/#commands.indentWithTab
2513
- indentWithTab,
2514
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2515
- ...closeBracketsKeymap,
2516
- // https://codemirror.net/docs/ref/#commands.historyKeymap
2517
- ...historyKeymap,
2518
- // https://codemirror.net/docs/ref/#search.searchKeymap
2519
- ...searchKeymap,
2520
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2521
- ...standardKeymap
2522
- ])
2523
- ];
2547
+ var addStyle = (style) => setStyle(style, true);
2548
+ var removeStyle = (style) => setStyle(style, false);
2549
+ var toggleStyle = (style) => (arg) => {
2550
+ const form = getFormatting(arg.state);
2551
+ return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
2524
2552
  };
2525
-
2526
- // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
2527
- import { syntaxTree } from "@codemirror/language";
2528
- import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2529
- import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2530
- import { mx as mx2 } from "@dxos/react-ui-theme";
2531
- var HorizontalRuleWidget = class extends WidgetType3 {
2532
- toDOM() {
2533
- const el = document.createElement("span");
2534
- el.className = "cm-hr";
2535
- return el;
2553
+ var toggleStrong = toggleStyle(0);
2554
+ var toggleEmphasis = toggleStyle(1);
2555
+ var toggleStrikethrough = toggleStyle(2);
2556
+ var toggleInlineCode = toggleStyle(3);
2557
+ var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
2558
+ var blockContentStart = (node) => {
2559
+ const atx = /^ATXHeading(\d)/.exec(node.name);
2560
+ if (atx) {
2561
+ return Math.min(node.to, node.from + +atx[1] + 1);
2536
2562
  }
2563
+ return node.from;
2537
2564
  };
2538
- var LinkButton = class extends WidgetType3 {
2539
- constructor(url, render) {
2540
- super();
2541
- this.url = url;
2542
- this.render = render;
2543
- }
2544
- eq(other) {
2545
- return this.url === other.url;
2546
- }
2547
- toDOM(view) {
2548
- const el = document.createElement("span");
2549
- this.render(el, this.url);
2550
- return el;
2565
+ var blockContentEnd = (node, doc) => {
2566
+ const setext = /^SetextHeading(\d)/.exec(node.name);
2567
+ const lastLine = doc.lineAt(node.to);
2568
+ if (setext || /^[\s>]*$/.exec(lastLine.text)) {
2569
+ return lastLine.from - 1;
2551
2570
  }
2571
+ return node.to;
2552
2572
  };
2553
- var CheckboxWidget = class extends WidgetType3 {
2554
- constructor(_checked) {
2555
- super();
2556
- this._checked = _checked;
2573
+ var skipSpaces = (pos, doc, dir, limit) => {
2574
+ const line = doc.lineAt(pos);
2575
+ while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
2576
+ pos += dir;
2557
2577
  }
2558
- eq(other) {
2559
- return this._checked === other._checked;
2578
+ return pos;
2579
+ };
2580
+ var skipMarkers = (pos, tree, dir, limit) => {
2581
+ for (; ; ) {
2582
+ const next = tree.resolve(pos, dir);
2583
+ if (!IgnoreInline.has(next.name)) {
2584
+ return pos;
2585
+ }
2586
+ const moveTo = dir < 0 ? next.from : next.to;
2587
+ if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
2588
+ return pos;
2589
+ }
2590
+ pos = moveTo;
2560
2591
  }
2561
- toDOM(view) {
2562
- const input = document.createElement("input");
2563
- input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
2564
- input.type = "checkbox";
2565
- input.checked = this._checked;
2566
- if (view.state.readOnly) {
2567
- input.setAttribute("disabled", "true");
2568
- } else {
2569
- input.onmousedown = (event) => {
2570
- const pos = view.posAtDOM(input);
2571
- const text = view.state.sliceDoc(pos, pos + 3);
2572
- if (text === (this._checked ? "[x]" : "[ ]")) {
2573
- view.dispatch({
2574
- changes: {
2575
- from: pos + 1,
2576
- to: pos + 2,
2577
- insert: this._checked ? " " : "x"
2578
- }
2579
- });
2580
- event.preventDefault();
2581
- }
2582
- };
2592
+ };
2593
+ var snippets = {
2594
+ codeblock: snippet([
2595
+ //
2596
+ "```#{}",
2597
+ "",
2598
+ "```"
2599
+ ].join("\n")),
2600
+ table: snippet([
2601
+ //
2602
+ "| #{col1} | #{col2} |",
2603
+ "| ---- | ---- |",
2604
+ "| #{val1} | #{val2} |",
2605
+ "| #{val3} | #{val4} |",
2606
+ ""
2607
+ ].join("\n"))
2608
+ };
2609
+ var insertTable = (view) => {
2610
+ const { selection: { main }, doc } = view.state;
2611
+ const { number } = doc.lineAt(main.anchor);
2612
+ const { from } = doc.line(number);
2613
+ snippets.table(view, null, from, from);
2614
+ };
2615
+ var removeLinkInner = (from, to, changes, state2) => {
2616
+ syntaxTree(state2).iterate({
2617
+ from,
2618
+ to,
2619
+ enter: (node) => {
2620
+ if (node.name === "Link" && node.from < to && node.to > from) {
2621
+ node.node.cursor().iterate((node2) => {
2622
+ const { name } = node2;
2623
+ if (name === "LinkMark" || name === "LinkLabel") {
2624
+ changes.push({
2625
+ from: node2.from,
2626
+ to: node2.to
2627
+ });
2628
+ } else if (name === "LinkTitle" || name === "URL") {
2629
+ changes.push({
2630
+ from: skipSpaces(node2.from, state2.doc, -1),
2631
+ to: skipSpaces(node2.to, state2.doc, 1)
2632
+ });
2633
+ }
2634
+ });
2635
+ return false;
2636
+ }
2583
2637
  }
2584
- return input;
2638
+ });
2639
+ };
2640
+ var removeLink = ({ state: state2, dispatch }) => {
2641
+ const changes = [];
2642
+ for (const { from, to } of state2.selection.ranges) {
2643
+ removeLinkInner(from, to, changes, state2);
2585
2644
  }
2586
- ignoreEvent() {
2645
+ if (!changes) {
2587
2646
  return false;
2588
2647
  }
2648
+ dispatch(state2.update({
2649
+ changes,
2650
+ userEvent: "format.link.remove",
2651
+ scrollIntoView: true
2652
+ }));
2653
+ return true;
2589
2654
  };
2590
- var hide = Decoration5.replace({});
2591
- var fencedCodeLine = Decoration5.line({
2592
- class: mx2("cm-code cm-codeblock-line")
2593
- });
2594
- var fencedCodeLineFirst = Decoration5.line({
2595
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
2596
- });
2597
- var fencedCodeLineLast = Decoration5.line({
2598
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
2599
- });
2600
- var horizontalRule = Decoration5.replace({
2601
- widget: new HorizontalRuleWidget()
2602
- });
2603
- var checkedTask = Decoration5.replace({
2604
- widget: new CheckboxWidget(true)
2605
- });
2606
- var uncheckedTask = Decoration5.replace({
2607
- widget: new CheckboxWidget(false)
2608
- });
2609
- var editingRange = (state, range, focus) => {
2610
- const { readOnly, selection: { main: { head } } } = state;
2611
- return focus && !readOnly && head >= range.from && head <= range.to;
2612
- };
2613
- var MarksByParent = /* @__PURE__ */ new Set([
2614
- "CodeMark",
2615
- "EmphasisMark",
2616
- "StrikethroughMark",
2617
- "SubscriptMark",
2618
- "SuperscriptMark"
2619
- ]);
2620
- var buildDecorations = (view, options, focus) => {
2621
- const deco = new RangeSetBuilder2();
2622
- const atomicDeco = new RangeSetBuilder2();
2623
- const { state } = view;
2624
- for (const { from, to } of view.visibleRanges) {
2625
- syntaxTree(state).iterate({
2655
+ var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
2656
+ const changes = state2.changeByRange((range) => {
2657
+ let { from, to } = range;
2658
+ const cutStyles = [];
2659
+ let okay = null;
2660
+ syntaxTree(state2).iterate({
2626
2661
  from,
2627
2662
  to,
2628
2663
  enter: (node) => {
2629
- if (node.name === "FencedCode") {
2630
- const editing = editingRange(state, node, focus);
2631
- for (const block of view.viewportLineBlocks) {
2632
- if (block.to < node.from) {
2633
- continue;
2634
- }
2635
- if (block.from > node.to) {
2636
- break;
2637
- }
2638
- const first = block.from <= node.from;
2639
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2640
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2641
- if (!editing && (first || last)) {
2642
- atomicDeco.add(block.from, block.to, hide);
2643
- }
2644
- }
2645
- return false;
2646
- } else if (node.name === "Link") {
2647
- const marks = node.node.getChildren("LinkMark");
2648
- const urlNode = node.node.getChild("URL");
2649
- const editing = editingRange(state, node, focus);
2650
- if (urlNode && marks.length >= 2) {
2651
- const url = state.sliceDoc(urlNode.from, urlNode.to);
2652
- if (!editing) {
2653
- atomicDeco.add(node.from, marks[0].to, hide);
2664
+ if (Object.hasOwn(Textblocks, node.name)) {
2665
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
2666
+ } else if (Object.hasOwn(InlineMarker, node.name)) {
2667
+ const sNode = node.node;
2668
+ if (node.from < from && node.to <= to) {
2669
+ if (sNode.firstChild.to === from) {
2670
+ from = node.from;
2671
+ } else {
2672
+ cutStyles.push(sNode);
2654
2673
  }
2655
- deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2656
- tagName: "a",
2657
- attributes: {
2658
- class: "cm-link",
2659
- href: url,
2660
- rel: "noreferrer",
2661
- target: "_blank"
2662
- }
2663
- }));
2664
- if (!editing) {
2665
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2666
- widget: new LinkButton(url, options.renderLinkButton)
2667
- }) : hide);
2674
+ } else if (node.from >= from && node.to > to) {
2675
+ if (sNode.lastChild.from === to) {
2676
+ to = node.to;
2677
+ } else {
2678
+ cutStyles.push(sNode);
2668
2679
  }
2669
2680
  }
2670
- } else if (node.name === "HeaderMark") {
2671
- const parent = node.node.parent;
2672
- if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
2673
- const next = state.doc.sliceString(node.to, node.to + 1);
2674
- atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2675
- }
2676
- } else if (node.name === "HorizontalRule") {
2677
- if (!editingRange(state, node, focus)) {
2678
- deco.add(node.from, node.to, horizontalRule);
2679
- }
2680
- } else if (node.name === "TaskMarker") {
2681
- if (!editingRange(state, node, focus)) {
2682
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2683
- atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2684
- class: "cm-task"
2685
- }));
2686
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2687
- }
2688
- } else if (MarksByParent.has(node.name)) {
2689
- if (!editingRange(state, node.node.parent, focus)) {
2690
- atomicDeco.add(node.from, node.to, hide);
2691
- }
2692
2681
  }
2693
2682
  }
2694
2683
  });
2695
- }
2696
- return {
2697
- deco: deco.finish(),
2698
- atomicDeco: atomicDeco.finish()
2699
- };
2700
- };
2701
- var forceUpdate = StateEffect4.define();
2702
- var decorateMarkdown = (options = {}) => {
2703
- return [
2704
- ViewPlugin4.fromClass(class {
2705
- constructor(view) {
2706
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2707
- }
2708
- update(update2) {
2709
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2710
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2711
- this.clearUpdate();
2712
- } else if (update2.selectionSet) {
2713
- this.scheduleUpdate(update2.view);
2714
- }
2715
- }
2716
- // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2717
- // the cursor will float in space or be in the wrong position after the decoration is applied.
2718
- scheduleUpdate(view) {
2719
- this.clearUpdate();
2720
- this.pendingUpdate = setTimeout(() => {
2721
- view.dispatch({
2722
- effects: forceUpdate.of(null)
2723
- });
2724
- }, options.selectionChangeDelay);
2725
- }
2726
- clearUpdate() {
2727
- if (this.pendingUpdate) {
2728
- clearTimeout(this.pendingUpdate);
2729
- this.pendingUpdate = void 0;
2684
+ if (okay === null) {
2685
+ const line = state2.doc.lineAt(from);
2686
+ okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
2687
+ }
2688
+ if (!okay) {
2689
+ return {
2690
+ range
2691
+ };
2692
+ }
2693
+ const changes2 = [];
2694
+ const changesAfter = [];
2695
+ removeLinkInner(from, to, changesAfter, state2);
2696
+ let cursorOffset = 1;
2697
+ for (const style of cutStyles) {
2698
+ const type = InlineMarker[style.name];
2699
+ const mark2 = inlineMarkerText(type);
2700
+ if (style.from < from) {
2701
+ changes2.push({
2702
+ from: skipSpaces(from, state2.doc, -1),
2703
+ insert: mark2
2704
+ });
2705
+ changesAfter.push({
2706
+ from: skipSpaces(from, state2.doc, 1, to),
2707
+ insert: mark2
2708
+ });
2709
+ } else {
2710
+ changes2.push({
2711
+ from: skipSpaces(to, state2.doc, -1, from),
2712
+ insert: mark2
2713
+ });
2714
+ const after = skipSpaces(to, state2.doc, 1);
2715
+ if (after === to) {
2716
+ cursorOffset += mark2.length;
2730
2717
  }
2718
+ changesAfter.push({
2719
+ from: after,
2720
+ insert: mark2
2721
+ });
2731
2722
  }
2732
- destroy() {
2733
- this.clearUpdate();
2734
- }
2735
- }, {
2736
- provide: (plugin) => [
2737
- EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2738
- EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2739
- EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2740
- ]
2741
- }),
2742
- formattingStyles
2743
- ];
2744
- };
2745
- var formattingStyles = EditorView12.baseTheme({
2746
- "& .cm-code": {
2747
- fontFamily: getToken("fontFamily.mono", []).join(",")
2748
- },
2749
- "& .cm-codeblock-line": {
2750
- paddingInline: "1rem !important"
2751
- },
2752
- "& .cm-codeblock-end": {
2753
- display: "inline-block",
2754
- width: "100%",
2755
- position: "relative",
2756
- "&::after": {
2757
- position: "absolute",
2758
- inset: 0,
2759
- content: '""'
2760
2723
  }
2761
- },
2762
- "& .cm-codeblock-first": {
2763
- borderTopLeftRadius: ".5rem",
2764
- borderTopRightRadius: ".5rem"
2765
- },
2766
- "& .cm-codeblock-last": {
2767
- borderBottomLeftRadius: ".5rem",
2768
- borderBottomRightRadius: ".5rem"
2769
- },
2770
- "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
2771
- background: getToken("extend.semanticColors.input.light"),
2772
- mixBlendMode: "darken"
2773
- },
2774
- "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
2775
- background: getToken("extend.semanticColors.input.dark"),
2776
- mixBlendMode: "lighten"
2777
- },
2778
- "& .cm-hr": {
2779
- display: "inline-block",
2780
- width: "100%",
2781
- height: "0",
2782
- verticalAlign: "middle",
2783
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2784
- },
2785
- "& .cm-task": {
2786
- color: getToken("extend.colors.blue.500")
2787
- },
2788
- "& .cm-task-checkbox": {
2789
- marginLeft: "4px",
2790
- marginRight: "4px"
2724
+ changes2.push({
2725
+ from,
2726
+ insert: image2 ? "![" : "["
2727
+ }, {
2728
+ from: to,
2729
+ insert: `](${url ?? ""})`
2730
+ });
2731
+ const changeSet = state2.changes(changes2.concat(changesAfter));
2732
+ return {
2733
+ changes: changeSet,
2734
+ range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
2735
+ };
2736
+ });
2737
+ if (changes.changes.empty) {
2738
+ return false;
2791
2739
  }
2792
- });
2793
-
2794
- // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2795
- import { snippet } from "@codemirror/autocomplete";
2796
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2797
- import { EditorSelection } from "@codemirror/state";
2798
- import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2799
- import { useState, useMemo } from "react";
2800
- var compareFormatting = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
2801
- var Inline;
2802
- (function(Inline2) {
2803
- Inline2[Inline2["Strong"] = 0] = "Strong";
2804
- Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2805
- Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2806
- Inline2[Inline2["Code"] = 3] = "Code";
2807
- })(Inline || (Inline = {}));
2808
- var List;
2809
- (function(List2) {
2810
- List2[List2["Ordered"] = 0] = "Ordered";
2811
- List2[List2["Bullet"] = 1] = "Bullet";
2812
- List2[List2["Task"] = 2] = "Task";
2813
- })(List || (List = {}));
2814
- var setHeading = (level) => ({ state, dispatch }) => {
2815
- const { selection: { ranges }, doc } = state;
2816
- const changes = [];
2817
- let prevBlock = -1;
2818
- for (const range of ranges) {
2819
- let sawBlock = false;
2820
- syntaxTree2(state).iterate({
2821
- from: range.from,
2822
- to: range.to,
2740
+ dispatch(state2.update(changes, {
2741
+ userEvent: "format.link.add",
2742
+ scrollIntoView: true
2743
+ }));
2744
+ return true;
2745
+ };
2746
+ var addList = (type) => ({ state: state2, dispatch }) => {
2747
+ let lastBlock = -1;
2748
+ let counter = 1;
2749
+ let first = true;
2750
+ let parentColumn = null;
2751
+ const blocks = [];
2752
+ for (const { from, to } of state2.selection.ranges) {
2753
+ syntaxTree(state2).iterate({
2754
+ from,
2755
+ to,
2823
2756
  enter: (node) => {
2824
- if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2825
- return;
2826
- }
2827
- sawBlock = true;
2828
- prevBlock = node.from;
2829
- const blockType = Textblocks[node.name];
2830
- const isHeading = /heading(\d)/.exec(blockType);
2831
- const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2832
- if (curLevel < 0 || curLevel === level) {
2833
- return;
2834
- }
2835
- if (curLevel === 0) {
2836
- changes.push({
2837
- from: node.from,
2838
- insert: "#".repeat(level) + " "
2839
- });
2840
- } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2841
- const nextLine = doc.lineAt(node.to);
2842
- if (level) {
2843
- changes.push({
2844
- from: node.from,
2845
- insert: "#".repeat(level) + " "
2846
- });
2847
- }
2848
- changes.push({
2849
- from: nextLine.from - 1,
2850
- to: nextLine.to
2851
- });
2852
- } else {
2853
- if (level === 0) {
2854
- changes.push({
2855
- from: node.from,
2856
- to: Math.min(node.to, node.from + curLevel + 1)
2857
- });
2858
- } else if (level < curLevel) {
2859
- changes.push({
2860
- from: node.from,
2861
- to: node.from + (curLevel - level)
2862
- });
2863
- } else {
2864
- changes.push({
2865
- from: node.from,
2866
- insert: "#".repeat(level - curLevel)
2867
- });
2757
+ if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
2758
+ if (first) {
2759
+ let before = node.node.prevSibling;
2760
+ while (before && /Mark$/.test(before.name)) {
2761
+ before = before.prevSibling;
2762
+ }
2763
+ if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
2764
+ const item = before.lastChild;
2765
+ const itemLine = state2.doc.lineAt(item.from);
2766
+ const itemText = itemLine.text.slice(item.from - itemLine.from);
2767
+ parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2768
+ if (type === 0) {
2769
+ const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2770
+ if (mark2) {
2771
+ parentColumn += mark2[1].length;
2772
+ counter = +mark2[1] + 1;
2773
+ }
2774
+ }
2775
+ }
2776
+ first = false;
2868
2777
  }
2778
+ if (node.from === lastBlock) {
2779
+ return;
2780
+ }
2781
+ lastBlock = node.from;
2782
+ blocks.push({
2783
+ node: node.node,
2784
+ counter,
2785
+ parentColumn
2786
+ });
2787
+ counter++;
2788
+ return false;
2789
+ }
2790
+ },
2791
+ leave: (node) => {
2792
+ if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
2793
+ counter = 1;
2794
+ parentColumn = null;
2869
2795
  }
2870
2796
  }
2871
2797
  });
2872
- let line;
2873
- if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state.doc.lineAt(range.from)).text)) {
2798
+ }
2799
+ if (!blocks.length) {
2800
+ const { from, to } = state2.doc.lineAt(state2.selection.main.anchor);
2801
+ if (from === to) {
2802
+ const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
2803
+ dispatch(state2.update({
2804
+ changes: [
2805
+ {
2806
+ from,
2807
+ insert
2808
+ }
2809
+ ],
2810
+ selection: {
2811
+ anchor: from + insert.length
2812
+ },
2813
+ userEvent: "format.list.add",
2814
+ scrollIntoView: true
2815
+ }));
2816
+ return true;
2817
+ }
2818
+ return false;
2819
+ }
2820
+ const changes = [];
2821
+ for (let i = 0; i < blocks.length; i++) {
2822
+ const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
2823
+ const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
2824
+ let padding = nodeFrom > 0 && !/\s/.test(state2.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
2825
+ if (type === 0) {
2826
+ padding += String(counter2).length;
2827
+ }
2828
+ let line = state2.doc.lineAt(nodeFrom);
2829
+ const column = nodeFrom - line.from;
2830
+ if (parentColumn2 !== null && parentColumn2 > column) {
2831
+ padding = Math.max(padding, parentColumn2 - column);
2832
+ }
2833
+ let mark2;
2834
+ if (type === 0) {
2835
+ let max = counter2;
2836
+ for (let j = i + 1; j < blocks.length; j++) {
2837
+ if (blocks[j].counter !== max + 1) {
2838
+ break;
2839
+ }
2840
+ max++;
2841
+ }
2842
+ const num = String(counter2);
2843
+ padding = Math.max(String(max).length, padding);
2844
+ mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
2845
+ } else {
2846
+ mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
2847
+ }
2848
+ changes.push({
2849
+ from: nodeFrom,
2850
+ insert: mark2
2851
+ });
2852
+ while (line.to < node.to) {
2853
+ line = state2.doc.lineAt(line.to + 1);
2854
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2874
2855
  changes.push({
2875
- from: line.from,
2876
- to: line.to,
2877
- insert: "#".repeat(level) + " "
2856
+ from: line.from + Math.min(open, column),
2857
+ insert: " ".repeat(mark2.length)
2878
2858
  });
2879
2859
  }
2880
2860
  }
2881
- if (!changes.length) {
2882
- return false;
2861
+ if (type === 0) {
2862
+ const last = blocks[blocks.length - 1];
2863
+ let next = last.node.nextSibling;
2864
+ while (next && /Mark$/.test(next.name)) {
2865
+ next = next.nextSibling;
2866
+ }
2867
+ if (next?.name === "OrderedList") {
2868
+ renumberListItems(next.firstChild, last.counter + 1, changes, state2.doc);
2869
+ }
2883
2870
  }
2884
- const changeSet = state.changes(changes);
2885
- dispatch(state.update({
2871
+ const changeSet = state2.changes(changes);
2872
+ dispatch(state2.update({
2886
2873
  changes: changeSet,
2887
- selection: state.selection.map(changeSet, 1),
2888
- userEvent: "format.setHeading",
2874
+ selection: state2.selection.map(changeSet, 1),
2875
+ userEvent: "format.list.add",
2889
2876
  scrollIntoView: true
2890
2877
  }));
2891
2878
  return true;
2892
2879
  };
2893
- var setStyle = (type, enable) => ({ state, dispatch }) => {
2894
- const marker = inlineMarkerText(type);
2895
- const changes = state.changeByRange((range) => {
2896
- if (!enable && range.empty) {
2897
- const after = state.doc.sliceString(range.head, range.head + 6);
2898
- const found = after.indexOf(marker);
2899
- if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2900
- const before = state.doc.sliceString(range.head - 6, range.head);
2901
- if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2902
- ...before.slice(before.length - found)
2903
- ].reverse().join("") === after.slice(0, found)) {
2904
- return {
2905
- changes: [
2906
- {
2907
- from: range.head - marker.length - found,
2908
- to: range.head - found
2909
- },
2910
- {
2911
- from: range.head + found,
2912
- to: range.head + found + marker.length
2913
- }
2914
- ],
2915
- range: EditorSelection.cursor(range.from - marker.length)
2916
- };
2917
- }
2918
- }
2919
- }
2920
- const changes2 = [];
2921
- const changesAtEnd = [];
2922
- let blockStart = -1;
2923
- let blockEnd = -1;
2924
- let startCovered = false;
2925
- let endCovered = false;
2926
- let { from, to } = range;
2927
- syntaxTree2(state).iterate({
2880
+ var removeList = (type) => ({ state: state2, dispatch }) => {
2881
+ let lastBlock = -1;
2882
+ const changes = [];
2883
+ const stack = [];
2884
+ const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
2885
+ for (const { from, to } of state2.selection.ranges) {
2886
+ syntaxTree(state2).iterate({
2928
2887
  from,
2929
2888
  to,
2930
2889
  enter: (node) => {
2931
2890
  const { name } = node;
2932
- if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2933
- blockStart = blockContentStart(node);
2934
- blockEnd = blockContentEnd(node, state.doc);
2935
- startCovered = endCovered = false;
2936
- } else if (name === "Link" || name === "Image" && enable) {
2937
- if (from < node.from && to > node.from && to <= node.to) {
2938
- to = node.to;
2939
- } else if (to > node.to && from >= node.from && from < node.to) {
2940
- from = node.from;
2941
- }
2942
- } else if (IgnoreInline.has(name) && enable) {
2943
- if (node.from < from && node.to > from) {
2944
- if (to === from) {
2945
- to = node.to;
2946
- }
2947
- from = node.to;
2948
- }
2949
- if (node.from < to && node.to > to) {
2950
- to = node.from;
2951
- }
2952
- } else if (Object.hasOwn(InlineMarker, name)) {
2953
- const markType = InlineMarker[name];
2954
- const size = inlineMarkerText(markType).length;
2955
- const openEnd = node.from + size;
2956
- const closeStart = node.to - size;
2957
- if (markType === type) {
2958
- if (openEnd <= from && closeStart >= from) {
2959
- startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2960
- from: node.from,
2961
- to: openEnd
2962
- } : true;
2963
- }
2964
- if (openEnd <= to && closeStart >= to) {
2965
- endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2966
- from: closeStart,
2967
- to: node.to
2968
- } : true;
2969
- }
2970
- }
2971
- if (markType === type || type === 3 && enable) {
2972
- if (node.from >= from && openEnd <= to) {
2973
- changes2.push({
2974
- from: node.from,
2975
- to: openEnd
2976
- });
2977
- if (markType !== type && closeStart >= to) {
2978
- changesAtEnd.push({
2979
- from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
2980
- insert: inlineMarkerText(markType)
2981
- });
2982
- }
2983
- }
2984
- if (closeStart >= from && node.to <= to) {
2985
- changes2.push({
2986
- from: closeStart,
2987
- to: node.to
2988
- });
2989
- if (markType !== type && openEnd <= from) {
2990
- changes2.push({
2991
- from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
2992
- insert: inlineMarkerText(markType)
2993
- });
2994
- }
2995
- }
2996
- }
2891
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2892
+ stack.push(name);
2893
+ } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2894
+ stack[stack.length - 1] = "TaskList";
2997
2895
  }
2998
2896
  },
2999
2897
  leave: (node) => {
3000
- if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
3001
- const rangeStart = Math.max(from, blockStart);
3002
- const rangeEnd = Math.min(to, blockEnd);
3003
- if (enable) {
3004
- if (!startCovered) {
3005
- changes2.push({
3006
- from: rangeStart,
3007
- insert: marker
3008
- });
3009
- }
3010
- if (!endCovered) {
3011
- changes2.push({
3012
- from: rangeEnd,
3013
- insert: marker
3014
- });
3015
- }
3016
- } else {
3017
- if (typeof startCovered === "object") {
3018
- changes2.push(startCovered);
3019
- } else if (startCovered) {
3020
- changes2.push({
3021
- from: skipSpaces(rangeStart, state.doc, -1, blockStart),
3022
- insert: marker
3023
- });
3024
- }
3025
- if (typeof endCovered === "object") {
3026
- changes2.push(endCovered);
3027
- } else if (endCovered) {
3028
- changes2.push({
3029
- from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
3030
- insert: marker
2898
+ const { name } = node;
2899
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2900
+ stack.pop();
2901
+ } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
2902
+ lastBlock = node.from;
2903
+ let line = state2.doc.lineAt(node.from);
2904
+ const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
2905
+ if (!mark2) {
2906
+ return false;
2907
+ }
2908
+ const column = node.from - line.from;
2909
+ changes.push({
2910
+ from: node.from,
2911
+ to: node.from + mark2[0].length
2912
+ });
2913
+ while (line.to < node.to) {
2914
+ line = state2.doc.lineAt(line.to + 1);
2915
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2916
+ if (open > column) {
2917
+ changes.push({
2918
+ from: line.from + column,
2919
+ to: line.from + Math.min(column + mark2[0].length, open)
3031
2920
  });
3032
2921
  }
3033
2922
  }
2923
+ if (node.to >= to) {
2924
+ renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
2925
+ }
2926
+ return false;
3034
2927
  }
3035
2928
  }
3036
2929
  });
3037
- if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
3038
- return {
3039
- changes: {
3040
- from: range.head,
3041
- insert: marker + marker
3042
- },
3043
- range: EditorSelection.cursor(range.head + marker.length)
3044
- };
3045
- }
3046
- const changeSet = state.changes(changes2.concat(changesAtEnd));
3047
- return {
3048
- changes: changeSet,
3049
- range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
3050
- };
3051
- });
3052
- dispatch(state.update(changes, {
3053
- userEvent: enable ? "format.style.add" : "format.style.remove",
2930
+ }
2931
+ if (!changes.length) {
2932
+ return false;
2933
+ }
2934
+ dispatch(state2.update({
2935
+ changes,
2936
+ userEvent: "format.list.remove",
3054
2937
  scrollIntoView: true
3055
2938
  }));
3056
2939
  return true;
3057
2940
  };
3058
- var addStyle = (style) => setStyle(style, true);
3059
- var removeStyle = (style) => setStyle(style, false);
3060
- var toggleStyle = (style) => (arg) => {
3061
- const form = getFormatting(arg.state);
3062
- return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
3063
- };
3064
- var toggleStrong = toggleStyle(0);
3065
- var toggleEmphasis = toggleStyle(1);
3066
- var toggleStrikethrough = toggleStyle(2);
3067
- var toggleInlineCode = toggleStyle(3);
3068
- var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
3069
- var blockContentStart = (node) => {
3070
- const atx = /^ATXHeading(\d)/.exec(node.name);
3071
- if (atx) {
3072
- return Math.min(node.to, node.from + +atx[1] + 1);
3073
- }
3074
- return node.from;
3075
- };
3076
- var blockContentEnd = (node, doc) => {
3077
- const setext = /^SetextHeading(\d)/.exec(node.name);
3078
- const lastLine = doc.lineAt(node.to);
3079
- if (setext || /^[\s>]*$/.exec(lastLine.text)) {
3080
- return lastLine.from - 1;
3081
- }
3082
- return node.to;
3083
- };
3084
- var skipSpaces = (pos, doc, dir, limit) => {
3085
- const line = doc.lineAt(pos);
3086
- while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
3087
- pos += dir;
3088
- }
3089
- return pos;
2941
+ var toggleList = (type) => (target) => {
2942
+ const formatting = getFormatting(target.state);
2943
+ const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
2944
+ return (active ? removeList(type) : addList(type))(target);
3090
2945
  };
3091
- var skipMarkers = (pos, tree, dir, limit) => {
3092
- for (; ; ) {
3093
- const next = tree.resolve(pos, dir);
3094
- if (!IgnoreInline.has(next.name)) {
3095
- return pos;
3096
- }
3097
- const moveTo = dir < 0 ? next.from : next.to;
3098
- if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
3099
- return pos;
2946
+ var renumberListItems = (item, counter, changes, doc) => {
2947
+ for (; item; item = item.nextSibling) {
2948
+ if (item.name === "ListItem") {
2949
+ const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
2950
+ if (!number || +number[2] === counter) {
2951
+ break;
2952
+ }
2953
+ const size = number[1].length + number[2].length;
2954
+ const newNum = String(counter);
2955
+ changes.push({
2956
+ from: item.from + Math.max(0, size - newNum.length),
2957
+ to: item.from + size,
2958
+ insert: newNum
2959
+ });
2960
+ counter++;
3100
2961
  }
3101
- pos = moveTo;
3102
2962
  }
3103
2963
  };
3104
- var snippets = {
3105
- codeblock: snippet([
3106
- //
3107
- "```#{}",
3108
- "",
3109
- "```"
3110
- ].join("\n")),
3111
- table: snippet([
3112
- //
3113
- "| #{col1} | #{col2} |",
3114
- "| ---- | ---- |",
3115
- "| #{val1} | #{val2} |",
3116
- "| #{val3} | #{val4} |",
3117
- ""
3118
- ].join("\n"))
3119
- };
3120
- var insertTable = (view) => {
3121
- const { selection: { main }, doc } = view.state;
3122
- const { number } = doc.lineAt(main.anchor);
3123
- const { from } = doc.line(number);
3124
- snippets.table(view, null, from, from);
3125
- };
3126
- var removeLinkInner = (from, to, changes, state) => {
3127
- syntaxTree2(state).iterate({
3128
- from,
3129
- to,
3130
- enter: (node) => {
3131
- if (node.name === "Link" && node.from < to && node.to > from) {
3132
- node.node.cursor().iterate((node2) => {
3133
- const { name } = node2;
3134
- if (name === "LinkMark" || name === "LinkLabel") {
3135
- changes.push({
3136
- from: node2.from,
3137
- to: node2.to
3138
- });
3139
- } else if (name === "LinkTitle" || name === "URL") {
3140
- changes.push({
3141
- from: skipSpaces(node2.from, state.doc, -1),
3142
- to: skipSpaces(node2.to, state.doc, 1)
3143
- });
2964
+ var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
2965
+ const lines = [];
2966
+ let lastBlock = -1;
2967
+ for (const { from, to } of state2.selection.ranges) {
2968
+ const sawBlock = false;
2969
+ syntaxTree(state2).iterate({
2970
+ from,
2971
+ to,
2972
+ enter: (node) => {
2973
+ if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
2974
+ if (node.from === lastBlock) {
2975
+ return false;
3144
2976
  }
3145
- });
3146
- return false;
2977
+ lastBlock = node.from;
2978
+ let line2 = state2.doc.lineAt(node.from);
2979
+ if (line2.number > 1) {
2980
+ const prevLine = state2.doc.line(line2.number - 1);
2981
+ if (/^[>\s]*$/.test(prevLine.text)) {
2982
+ if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2983
+ lines.push(prevLine);
2984
+ }
2985
+ }
2986
+ }
2987
+ for (; ; ) {
2988
+ lines.push(line2);
2989
+ if (line2.to >= node.to) {
2990
+ break;
2991
+ }
2992
+ line2 = state2.doc.line(line2.number + 1);
2993
+ }
2994
+ if (!enable && line2.number < state2.doc.lines) {
2995
+ const nextLine = state2.doc.line(line2.number + 1);
2996
+ if (/^[>\s]*$/.test(nextLine.text)) {
2997
+ lines.push(nextLine);
2998
+ }
2999
+ }
3000
+ return false;
3001
+ }
3147
3002
  }
3003
+ });
3004
+ let line;
3005
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
3006
+ lines.push(line);
3148
3007
  }
3149
- });
3150
- };
3151
- var removeLink = ({ state, dispatch }) => {
3008
+ }
3152
3009
  const changes = [];
3153
- for (const { from, to } of state.selection.ranges) {
3154
- removeLinkInner(from, to, changes, state);
3010
+ for (const line of lines) {
3011
+ if (enable) {
3012
+ changes.push({
3013
+ from: line.from,
3014
+ insert: /\S/.test(line.text) ? "> " : ">"
3015
+ });
3016
+ } else {
3017
+ const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
3018
+ if (quote) {
3019
+ changes.push({
3020
+ from: line.from + quote[1].length,
3021
+ to: line.from + quote[0].length
3022
+ });
3023
+ }
3024
+ }
3155
3025
  }
3156
- if (!changes) {
3026
+ if (!changes.length) {
3157
3027
  return false;
3158
3028
  }
3159
- dispatch(state.update({
3160
- changes,
3161
- userEvent: "format.link.remove",
3029
+ const changeSet = state2.changes(changes);
3030
+ dispatch(state2.update({
3031
+ changes: changeSet,
3032
+ selection: state2.selection.map(changeSet, 1),
3033
+ userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3162
3034
  scrollIntoView: true
3163
3035
  }));
3164
3036
  return true;
3165
3037
  };
3166
- var addLink = ({ state, dispatch }) => {
3167
- const changes = state.changeByRange((range) => {
3168
- let { from, to } = range;
3169
- const cutStyles = [];
3170
- let okay = null;
3171
- syntaxTree2(state).iterate({
3038
+ var addBlockquote = setBlockquote(true);
3039
+ var removeBlockquote = setBlockquote(false);
3040
+ var toggleBlockquote = (target) => {
3041
+ return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
3042
+ };
3043
+ var addCodeblock = (target) => {
3044
+ const { state: state2, dispatch } = target;
3045
+ const { selection } = state2;
3046
+ if (selection.ranges.length === 1 && selection.main.empty) {
3047
+ const { head } = selection.main;
3048
+ const line = state2.doc.lineAt(head);
3049
+ if (!/\S/.test(line.text) && head === line.from) {
3050
+ snippets.codeblock(target, null, line.from, line.to);
3051
+ return true;
3052
+ }
3053
+ }
3054
+ const ranges = [];
3055
+ for (const { from, to } of selection.ranges) {
3056
+ let blockFrom = from;
3057
+ let blockTo = to;
3058
+ syntaxTree(state2).iterate({
3172
3059
  from,
3173
3060
  to,
3174
3061
  enter: (node) => {
3175
3062
  if (Object.hasOwn(Textblocks, node.name)) {
3176
- okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
3177
- } else if (Object.hasOwn(InlineMarker, node.name)) {
3178
- const sNode = node.node;
3179
- if (node.from < from && node.to <= to) {
3180
- if (sNode.firstChild.to === from) {
3181
- from = node.from;
3182
- } else {
3183
- cutStyles.push(sNode);
3184
- }
3185
- } else if (node.from >= from && node.to > to) {
3186
- if (sNode.lastChild.from === to) {
3187
- to = node.to;
3188
- } else {
3189
- cutStyles.push(sNode);
3190
- }
3063
+ if (from >= node.from && to <= node.to) {
3064
+ blockFrom = node.from;
3065
+ blockTo = node.to;
3066
+ } else {
3067
+ blockFrom = Math.min(blockFrom, state2.doc.lineAt(node.from).from);
3068
+ blockTo = Math.max(blockTo, state2.doc.lineAt(node.to).to);
3191
3069
  }
3192
3070
  }
3193
3071
  }
3194
3072
  });
3195
- if (okay === null) {
3196
- const line = state.doc.lineAt(from);
3197
- okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
3198
- }
3199
- if (!okay) {
3200
- return {
3201
- range
3202
- };
3203
- }
3204
- const changes2 = [];
3205
- const changesAfter = [];
3206
- removeLinkInner(from, to, changesAfter, state);
3207
- let cursorOffset = 1;
3208
- for (const style of cutStyles) {
3209
- const type = InlineMarker[style.name];
3210
- const mark2 = inlineMarkerText(type);
3211
- if (style.from < from) {
3212
- changes2.push({
3213
- from: skipSpaces(from, state.doc, -1),
3214
- insert: mark2
3215
- });
3216
- changesAfter.push({
3217
- from: skipSpaces(from, state.doc, 1, to),
3218
- insert: mark2
3219
- });
3220
- } else {
3221
- changes2.push({
3222
- from: skipSpaces(to, state.doc, -1, from),
3223
- insert: mark2
3224
- });
3225
- const after = skipSpaces(to, state.doc, 1);
3226
- if (after === to) {
3227
- cursorOffset += mark2.length;
3228
- }
3229
- changesAfter.push({
3230
- from: after,
3231
- insert: mark2
3232
- });
3233
- }
3073
+ if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
3074
+ ranges[ranges.length - 1].to = blockTo;
3075
+ } else {
3076
+ ranges.push({
3077
+ from: blockFrom,
3078
+ to: blockTo
3079
+ });
3234
3080
  }
3235
- changes2.push({
3236
- from,
3237
- insert: "["
3238
- }, {
3239
- from: to,
3240
- insert: "]()"
3241
- });
3242
- const changeSet = state.changes(changes2.concat(changesAfter));
3243
- return {
3244
- changes: changeSet,
3245
- range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
3246
- };
3081
+ }
3082
+ if (!ranges.length) {
3083
+ return false;
3084
+ }
3085
+ const changes = ranges.map(({ from, to }) => {
3086
+ const column = from - state2.doc.lineAt(from).from;
3087
+ return [
3088
+ {
3089
+ from,
3090
+ insert: "```\n" + " ".repeat(column)
3091
+ },
3092
+ {
3093
+ from: to,
3094
+ insert: "\n" + " ".repeat(column) + "```"
3095
+ }
3096
+ ];
3247
3097
  });
3248
- if (changes.changes.empty) {
3249
- return false;
3250
- }
3251
- dispatch(state.update(changes, {
3252
- userEvent: "format.link.add",
3098
+ dispatch(state2.update({
3099
+ changes,
3100
+ userEvent: "format.codeblock.add",
3253
3101
  scrollIntoView: true
3254
3102
  }));
3255
3103
  return true;
3256
3104
  };
3257
- var addList = (type) => ({ state, dispatch }) => {
3105
+ var removeCodeblock = ({ state: state2, dispatch }) => {
3106
+ const changes = [];
3258
3107
  let lastBlock = -1;
3259
- let counter = 1;
3260
- let first = true;
3261
- let parentColumn = null;
3262
- const blocks = [];
3263
- for (const { from, to } of state.selection.ranges) {
3264
- syntaxTree2(state).iterate({
3108
+ for (const { from, to } of state2.selection.ranges) {
3109
+ syntaxTree(state2).iterate({
3265
3110
  from,
3266
3111
  to,
3267
3112
  enter: (node) => {
3268
- if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
3269
- if (first) {
3270
- let before = node.node.prevSibling;
3271
- while (before && /Mark$/.test(before.name)) {
3272
- before = before.prevSibling;
3113
+ if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3114
+ lastBlock = node.from;
3115
+ const firstLine = state2.doc.lineAt(node.from);
3116
+ if (node.name === "FencedCode") {
3117
+ changes.push({
3118
+ from: node.from,
3119
+ to: firstLine.to + 1 + node.from - firstLine.from
3120
+ });
3121
+ const lastLine = state2.doc.lineAt(node.to);
3122
+ if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3123
+ changes.push({
3124
+ from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3125
+ to: lastLine.to
3126
+ });
3273
3127
  }
3274
- if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
3275
- const item = before.lastChild;
3276
- const itemLine = state.doc.lineAt(item.from);
3277
- const itemText = itemLine.text.slice(item.from - itemLine.from);
3278
- parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
3279
- if (type === 0) {
3280
- const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
3281
- if (mark2) {
3282
- parentColumn += mark2[1].length;
3283
- counter = +mark2[1] + 1;
3284
- }
3128
+ } else {
3129
+ const column = node.from - firstLine.from;
3130
+ for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3131
+ changes.push({
3132
+ from: line.from + column - 4,
3133
+ to: line.from + column
3134
+ });
3135
+ if (line.to >= node.to) {
3136
+ break;
3285
3137
  }
3286
3138
  }
3287
- first = false;
3288
- }
3289
- if (node.from === lastBlock) {
3290
- return;
3291
3139
  }
3292
- lastBlock = node.from;
3293
- blocks.push({
3294
- node: node.node,
3295
- counter,
3296
- parentColumn
3297
- });
3298
- counter++;
3299
- return false;
3300
- }
3301
- },
3302
- leave: (node) => {
3303
- if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
3304
- counter = 1;
3305
- parentColumn = null;
3306
3140
  }
3307
3141
  }
3308
3142
  });
3309
3143
  }
3310
- if (!blocks.length) {
3311
- const { from, to } = state.doc.lineAt(state.selection.main.anchor);
3312
- if (from === to) {
3313
- const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
3314
- dispatch(state.update({
3315
- changes: [
3316
- {
3317
- from,
3318
- insert
3319
- }
3320
- ],
3321
- selection: {
3322
- anchor: from + insert.length
3323
- },
3324
- userEvent: "format.list.add",
3325
- scrollIntoView: true
3326
- }));
3327
- return true;
3328
- }
3144
+ if (!changes.length) {
3329
3145
  return false;
3330
3146
  }
3331
- const changes = [];
3332
- for (let i = 0; i < blocks.length; i++) {
3333
- const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
3334
- const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
3335
- let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
3336
- if (type === 0) {
3337
- padding += String(counter2).length;
3147
+ dispatch(state2.update({
3148
+ changes,
3149
+ userEvent: "format.codeblock.remove",
3150
+ scrollIntoView: true
3151
+ }));
3152
+ return true;
3153
+ };
3154
+ var toggleCodeblock = (target) => {
3155
+ return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3156
+ };
3157
+ var formattingKeymap = (options = {}) => {
3158
+ return [
3159
+ keymap6.of([
3160
+ {
3161
+ key: "meta-b",
3162
+ run: toggleStrong
3163
+ }
3164
+ ])
3165
+ ];
3166
+ };
3167
+ var InlineMarker = {
3168
+ Emphasis: 1,
3169
+ StrongEmphasis: 0,
3170
+ InlineCode: 3,
3171
+ Strikethrough: 2
3172
+ };
3173
+ var IgnoreInline = /* @__PURE__ */ new Set([
3174
+ "Autolink",
3175
+ "CodeMark",
3176
+ "CodeText",
3177
+ "Comment",
3178
+ "EmphasisMark",
3179
+ "Hardbreak",
3180
+ "HeaderMark",
3181
+ "HTMLTag",
3182
+ "LinkMark",
3183
+ "ListMark",
3184
+ "ProcessingInstruction",
3185
+ "QuoteMark",
3186
+ "StrikethroughMark",
3187
+ "SubscriptMark",
3188
+ "SuperscriptMark",
3189
+ "TaskMarker"
3190
+ ]);
3191
+ var Textblocks = {
3192
+ ATXHeading1: "heading1",
3193
+ ATXHeading2: "heading2",
3194
+ ATXHeading3: "heading3",
3195
+ ATXHeading4: "heading4",
3196
+ ATXHeading5: "heading5",
3197
+ ATXHeading6: "heading6",
3198
+ CodeBlock: "codeblock",
3199
+ FencedCode: "codeblock",
3200
+ Paragraph: "paragraph",
3201
+ SetextHeading1: "heading1",
3202
+ SetextHeading2: "heading2",
3203
+ TableCell: "tablecell",
3204
+ Task: "paragraph"
3205
+ };
3206
+ var getFormatting = (state2) => {
3207
+ let blockType = null;
3208
+ const inline = [
3209
+ null,
3210
+ null,
3211
+ null,
3212
+ null
3213
+ ];
3214
+ let link = false;
3215
+ let blockQuote = null;
3216
+ let listStyle = null;
3217
+ const stack = [];
3218
+ let currentBlock = null;
3219
+ const advanceInline = (upto) => {
3220
+ if (!currentBlock) {
3221
+ return;
3338
3222
  }
3339
- let line = state.doc.lineAt(nodeFrom);
3340
- const column = nodeFrom - line.from;
3341
- if (parentColumn2 !== null && parentColumn2 > column) {
3342
- padding = Math.max(padding, parentColumn2 - column);
3223
+ upto = Math.min(upto, currentBlock.end);
3224
+ if (upto <= currentBlock.pos) {
3225
+ return;
3343
3226
  }
3344
- let mark2;
3345
- if (type === 0) {
3346
- let max = counter2;
3347
- for (let j = i + 1; j < blocks.length; j++) {
3348
- if (blocks[j].counter !== max + 1) {
3349
- break;
3350
- }
3351
- max++;
3227
+ for (let i = 0; i < currentBlock.active.length; i++) {
3228
+ if (inline[i] === false) {
3229
+ continue;
3230
+ } else if (currentBlock.active[i]) {
3231
+ inline[i] = true;
3232
+ } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3233
+ inline[i] = false;
3352
3234
  }
3353
- const num = String(counter2);
3354
- padding = Math.max(String(max).length, padding);
3355
- mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3356
- } else {
3357
- mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3358
- }
3359
- changes.push({
3360
- from: nodeFrom,
3361
- insert: mark2
3362
- });
3363
- while (line.to < node.to) {
3364
- line = state.doc.lineAt(line.to + 1);
3365
- const open = /^[\s>]*/.exec(line.text)[0].length;
3366
- changes.push({
3367
- from: line.from + Math.min(open, column),
3368
- insert: " ".repeat(mark2.length)
3369
- });
3370
3235
  }
3371
- }
3372
- if (type === 0) {
3373
- const last = blocks[blocks.length - 1];
3374
- let next = last.node.nextSibling;
3375
- while (next && /Mark$/.test(next.name)) {
3376
- next = next.nextSibling;
3236
+ currentBlock.pos = upto;
3237
+ };
3238
+ const skipInline = (upto) => {
3239
+ if (currentBlock && upto > currentBlock.pos) {
3240
+ currentBlock.pos = Math.min(upto, currentBlock.end);
3377
3241
  }
3378
- if (next?.name === "OrderedList") {
3379
- renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
3242
+ };
3243
+ const { selection } = state2;
3244
+ for (const range of selection.ranges) {
3245
+ if (range.empty && inline.some((v) => v === null)) {
3246
+ const contextSize = Math.min(range.head, 6);
3247
+ const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3248
+ let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3249
+ for (let i = 0; i < contextSize; i++) {
3250
+ const ch = contextAfter[i];
3251
+ if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3252
+ contextAfter = contextAfter.slice(0, i);
3253
+ break;
3254
+ }
3255
+ }
3256
+ for (let i = 0; i < inline.length; i++) {
3257
+ const mark2 = inlineMarkerText(i);
3258
+ const found = contextAfter.indexOf(mark2);
3259
+ if (found > -1) {
3260
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3261
+ if (inline[i] === null) {
3262
+ inline[i] = true;
3263
+ }
3264
+ }
3265
+ }
3380
3266
  }
3381
- }
3382
- const changeSet = state.changes(changes);
3383
- dispatch(state.update({
3384
- changes: changeSet,
3385
- selection: state.selection.map(changeSet, 1),
3386
- userEvent: "format.list.add",
3387
- scrollIntoView: true
3388
- }));
3389
- return true;
3390
- };
3391
- var removeList = (type) => ({ state, dispatch }) => {
3392
- let lastBlock = -1;
3393
- const changes = [];
3394
- const stack = [];
3395
- const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3396
- for (const { from, to } of state.selection.ranges) {
3397
- syntaxTree2(state).iterate({
3398
- from,
3399
- to,
3267
+ syntaxTree(state2).iterate({
3268
+ from: range.from,
3269
+ to: range.to,
3400
3270
  enter: (node) => {
3271
+ advanceInline(node.from);
3401
3272
  const { name } = node;
3402
3273
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3403
3274
  stack.push(name);
3404
- } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3405
- stack[stack.length - 1] = "TaskList";
3275
+ } else if (name === "Link") {
3276
+ link = true;
3277
+ } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3278
+ if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3279
+ stack[stack.length - 1] = "TaskList";
3280
+ }
3281
+ const blockCode = Textblocks[name];
3282
+ if (blockType === null) {
3283
+ blockType = blockCode;
3284
+ } else if (blockType !== blockCode) {
3285
+ blockType = false;
3286
+ }
3287
+ if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3288
+ currentBlock = {
3289
+ pos: Math.max(range.from, node.from),
3290
+ end: Math.min(range.to, node.to),
3291
+ active: [
3292
+ false,
3293
+ false,
3294
+ false,
3295
+ false
3296
+ ]
3297
+ };
3298
+ }
3299
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3300
+ const index = InlineMarker[name];
3301
+ if (range.empty && inline[index] === null) {
3302
+ inline[index] = true;
3303
+ }
3304
+ currentBlock.active[index] = true;
3305
+ } else if (IgnoreInline.has(name)) {
3306
+ skipInline(node.to);
3406
3307
  }
3407
3308
  },
3408
3309
  leave: (node) => {
3310
+ advanceInline(node.to);
3409
3311
  const { name } = node;
3410
3312
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3411
3313
  stack.pop();
3412
- } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3413
- lastBlock = node.from;
3414
- let line = state.doc.lineAt(node.from);
3415
- const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3416
- if (!mark2) {
3417
- return false;
3418
- }
3419
- const column = node.from - line.from;
3420
- changes.push({
3421
- from: node.from,
3422
- to: node.from + mark2[0].length
3423
- });
3424
- while (line.to < node.to) {
3425
- line = state.doc.lineAt(line.to + 1);
3426
- const open = /^[\s>]*/.exec(line.text)[0].length;
3427
- if (open > column) {
3428
- changes.push({
3429
- from: line.from + column,
3430
- to: line.from + Math.min(column + mark2[0].length, open)
3431
- });
3314
+ } else if (Object.hasOwn(Textblocks, name)) {
3315
+ let hasList = false;
3316
+ let hasQuote = false;
3317
+ for (let i = stack.length - 1; i >= 0; i--) {
3318
+ if (stack[i] === "Blockquote") {
3319
+ hasQuote = true;
3320
+ } else if (!hasList) {
3321
+ hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3432
3322
  }
3433
3323
  }
3434
- if (node.to >= to) {
3435
- renumberListItems(node.node.nextSibling, 1, changes, state.doc);
3324
+ if (blockQuote === null) {
3325
+ blockQuote = hasQuote;
3326
+ } else if (!hasQuote && blockQuote) {
3327
+ blockQuote = false;
3436
3328
  }
3437
- return false;
3329
+ if (listStyle === null) {
3330
+ listStyle = hasList;
3331
+ } else if (listStyle !== hasList) {
3332
+ listStyle = false;
3333
+ }
3334
+ currentBlock = null;
3335
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3336
+ currentBlock.active[InlineMarker[name]] = false;
3438
3337
  }
3439
3338
  }
3440
3339
  });
3441
3340
  }
3442
- if (!changes.length) {
3443
- return false;
3444
- }
3445
- dispatch(state.update({
3446
- changes,
3447
- userEvent: "format.list.remove",
3448
- scrollIntoView: true
3449
- }));
3450
- return true;
3451
- };
3452
- var toggleList = (type) => (target) => {
3453
- const formatting = getFormatting(target.state);
3454
- const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
3455
- return (active ? removeList(type) : addList(type))(target);
3341
+ const { from, to } = state2.doc.lineAt(selection.main.anchor);
3342
+ const blankLine = from === to;
3343
+ return {
3344
+ blankLine,
3345
+ blockType: blockType || null,
3346
+ blockQuote: blockQuote ?? false,
3347
+ code: inline[3] ?? false,
3348
+ emphasis: inline[1] ?? false,
3349
+ strong: inline[0] ?? false,
3350
+ strikethrough: inline[2] ?? false,
3351
+ link,
3352
+ listStyle: listStyle || null
3353
+ };
3456
3354
  };
3457
- var renumberListItems = (item, counter, changes, doc) => {
3458
- for (; item; item = item.nextSibling) {
3459
- if (item.name === "ListItem") {
3460
- const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
3461
- if (!number || +number[2] === counter) {
3462
- break;
3463
- }
3464
- const size = number[1].length + number[2].length;
3465
- const newNum = String(counter);
3466
- changes.push({
3467
- from: item.from + Math.max(0, size - newNum.length),
3468
- to: item.from + size,
3469
- insert: newNum
3355
+ var useFormattingState = () => {
3356
+ const [state2, setState] = useState();
3357
+ const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3358
+ if (update2.docChanged || update2.selectionSet) {
3359
+ setState((prevState) => {
3360
+ const newState = getFormatting(update2.state);
3361
+ if (!prevState || !formattingEquals(prevState, newState)) {
3362
+ return newState;
3363
+ }
3364
+ return prevState;
3470
3365
  });
3471
- counter++;
3472
3366
  }
3367
+ }), [
3368
+ setState
3369
+ ]);
3370
+ return [
3371
+ state2,
3372
+ observer
3373
+ ];
3374
+ };
3375
+
3376
+ // packages/ui/react-ui-editor/src/extensions/markdown/action.ts
3377
+ var processAction = (view, action) => {
3378
+ let inlineType, listType;
3379
+ switch (action.type) {
3380
+ case "heading":
3381
+ setHeading(parseInt(action.data))(view);
3382
+ break;
3383
+ case "strong":
3384
+ case "emphasis":
3385
+ case "strikethrough":
3386
+ case "code":
3387
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
3388
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
3389
+ break;
3390
+ case "list-ordered":
3391
+ case "list-bullet":
3392
+ case "list-task":
3393
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
3394
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
3395
+ break;
3396
+ case "blockquote":
3397
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3398
+ break;
3399
+ case "codeblock":
3400
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3401
+ break;
3402
+ case "table":
3403
+ insertTable(view);
3404
+ break;
3405
+ case "link":
3406
+ (action.data === false ? removeLink : addLink())(view);
3407
+ break;
3408
+ case "image":
3409
+ addLink({
3410
+ url: action.data,
3411
+ image: true
3412
+ })(view);
3413
+ break;
3414
+ case "comment":
3415
+ createComment(view);
3416
+ break;
3473
3417
  }
3418
+ requestAnimationFrame(() => {
3419
+ if (!view.hasFocus) {
3420
+ view.focus();
3421
+ }
3422
+ });
3474
3423
  };
3475
- var setBlockquote = (enable) => ({ state, dispatch }) => {
3476
- const lines = [];
3477
- let lastBlock = -1;
3478
- for (const { from, to } of state.selection.ranges) {
3479
- const sawBlock = false;
3480
- syntaxTree2(state).iterate({
3481
- from,
3482
- to,
3483
- enter: (node) => {
3484
- if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
3485
- if (node.from === lastBlock) {
3486
- return false;
3487
- }
3488
- lastBlock = node.from;
3489
- let line2 = state.doc.lineAt(node.from);
3490
- if (line2.number > 1) {
3491
- const prevLine = state.doc.line(line2.number - 1);
3492
- if (/^[>\s]*$/.test(prevLine.text)) {
3493
- if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
3494
- lines.push(prevLine);
3495
- }
3496
- }
3497
- }
3498
- for (; ; ) {
3499
- lines.push(line2);
3500
- if (line2.to >= node.to) {
3501
- break;
3502
- }
3503
- line2 = state.doc.line(line2.number + 1);
3504
- }
3505
- if (!enable && line2.number < state.doc.lines) {
3506
- const nextLine = state.doc.line(line2.number + 1);
3507
- if (/^[>\s]*$/.test(nextLine.text)) {
3508
- lines.push(nextLine);
3509
- }
3510
- }
3511
- return false;
3512
- }
3513
- }
3514
- });
3515
- let line;
3516
- if (!sawBlock && enable && from === to && !/\S/.test((line = state.doc.lineAt(from)).text)) {
3517
- lines.push(line);
3424
+
3425
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3426
+ import { completionKeymap as completionKeymap2 } from "@codemirror/autocomplete";
3427
+ import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
3428
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
3429
+ import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
3430
+ import { languages } from "@codemirror/language-data";
3431
+ import { lintKeymap } from "@codemirror/lint";
3432
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
3433
+ import { keymap as keymap7 } from "@codemirror/view";
3434
+
3435
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
3436
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
3437
+ import { HighlightStyle } from "@codemirror/language";
3438
+ import { tags, styleTags, Tag } from "@lezer/highlight";
3439
+ import { Table } from "@lezer/markdown";
3440
+ var markdownTags = {
3441
+ Blockquote: Tag.define(),
3442
+ CodeMark: Tag.define(),
3443
+ CodeText: Tag.define(),
3444
+ EmphasisMark: Tag.define(),
3445
+ HeaderMark: Tag.define(),
3446
+ InlineCode: Tag.define(),
3447
+ LinkLabel: Tag.define(),
3448
+ LinkReference: Tag.define(),
3449
+ ListMark: Tag.define(),
3450
+ QuoteMark: Tag.define(),
3451
+ URL: Tag.define(),
3452
+ // Custom.
3453
+ TableCell: Tag.define()
3454
+ };
3455
+ Table.defineNodes?.forEach((node) => {
3456
+ switch (node?.name) {
3457
+ case "TableCell": {
3458
+ node.style = markdownTags.TableCell;
3459
+ break;
3460
+ }
3461
+ }
3462
+ });
3463
+ var markdownTagsExtensions = [
3464
+ Table,
3465
+ {
3466
+ props: [
3467
+ styleTags(markdownTags)
3468
+ ]
3469
+ }
3470
+ ];
3471
+ var markdownHighlightStyle = (readonly) => {
3472
+ return HighlightStyle.define([
3473
+ {
3474
+ tag: [
3475
+ tags.keyword,
3476
+ tags.name,
3477
+ tags.deleted,
3478
+ tags.character,
3479
+ tags.propertyName,
3480
+ tags.macroName,
3481
+ tags.color,
3482
+ tags.constant(tags.name),
3483
+ tags.standard(tags.name),
3484
+ tags.definition(tags.name),
3485
+ tags.separator,
3486
+ tags.typeName,
3487
+ tags.className,
3488
+ tags.number,
3489
+ tags.changed,
3490
+ tags.annotation,
3491
+ tags.modifier,
3492
+ tags.self,
3493
+ tags.namespace,
3494
+ tags.operator,
3495
+ tags.operatorKeyword,
3496
+ tags.escape,
3497
+ tags.regexp,
3498
+ tags.special(tags.string),
3499
+ tags.meta,
3500
+ tags.comment,
3501
+ tags.atom,
3502
+ tags.bool,
3503
+ tags.special(tags.variableName),
3504
+ tags.processingInstruction,
3505
+ tags.string,
3506
+ tags.inserted,
3507
+ tags.invalid
3508
+ ],
3509
+ color: "inherit !important"
3510
+ },
3511
+ // Markdown marks.
3512
+ {
3513
+ tag: [
3514
+ tags.meta,
3515
+ tags.processingInstruction,
3516
+ markdownTags.LinkLabel,
3517
+ markdownTags.LinkReference,
3518
+ markdownTags.ListMark
3519
+ ],
3520
+ class: mark
3521
+ },
3522
+ // Markdown marks.
3523
+ {
3524
+ tag: [
3525
+ markdownTags.CodeMark,
3526
+ markdownTags.HeaderMark,
3527
+ markdownTags.QuoteMark,
3528
+ markdownTags.EmphasisMark
3529
+ ],
3530
+ class: mark
3531
+ },
3532
+ // E.g., code block language (after ```).
3533
+ {
3534
+ tag: [
3535
+ tags.function(tags.variableName),
3536
+ tags.labelName
3537
+ ],
3538
+ class: codeMark
3539
+ },
3540
+ {
3541
+ tag: [
3542
+ tags.monospace
3543
+ ],
3544
+ class: "font-mono"
3545
+ },
3546
+ // Headings.
3547
+ {
3548
+ tag: tags.heading1,
3549
+ class: heading(1)
3550
+ },
3551
+ {
3552
+ tag: tags.heading2,
3553
+ class: heading(2)
3554
+ },
3555
+ {
3556
+ tag: tags.heading3,
3557
+ class: heading(3)
3558
+ },
3559
+ {
3560
+ tag: tags.heading4,
3561
+ class: heading(4)
3562
+ },
3563
+ {
3564
+ tag: tags.heading5,
3565
+ class: heading(5)
3566
+ },
3567
+ {
3568
+ tag: tags.heading6,
3569
+ class: heading(6)
3570
+ },
3571
+ // Emphasis.
3572
+ {
3573
+ tag: tags.emphasis,
3574
+ class: italic
3575
+ },
3576
+ {
3577
+ tag: tags.strong,
3578
+ class: bold
3579
+ },
3580
+ {
3581
+ tag: tags.strikethrough,
3582
+ class: strikethrough
3583
+ },
3584
+ // Naked URLs.
3585
+ {
3586
+ tag: [
3587
+ markdownTags.URL
3588
+ ],
3589
+ class: inlineUrl
3590
+ },
3591
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
3592
+ // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
3593
+ // when a language is specified. In this case, the syntax highlighting extensions will colorize
3594
+ // the code, but all other CSS properties will be inherited.
3595
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
3596
+ {
3597
+ tag: [
3598
+ markdownTags.CodeText,
3599
+ markdownTags.InlineCode
3600
+ ],
3601
+ class: code
3602
+ },
3603
+ {
3604
+ tag: [
3605
+ markdownTags.QuoteMark
3606
+ ],
3607
+ class: blockquote
3608
+ },
3609
+ {
3610
+ tag: [
3611
+ markdownTags.TableCell
3612
+ ],
3613
+ class: "font-mono"
3518
3614
  }
3519
- }
3520
- const changes = [];
3521
- for (const line of lines) {
3522
- if (enable) {
3523
- changes.push({
3524
- from: line.from,
3525
- insert: /\S/.test(line.text) ? "> " : ">"
3526
- });
3527
- } else {
3528
- const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
3529
- if (quote) {
3530
- changes.push({
3531
- from: line.from + quote[1].length,
3532
- to: line.from + quote[0].length
3533
- });
3534
- }
3615
+ ], {
3616
+ scope: markdownLanguage2,
3617
+ all: {
3618
+ fontFamily: getToken("fontFamily.body", []).join(",")
3535
3619
  }
3536
- }
3537
- if (!changes.length) {
3538
- return false;
3539
- }
3540
- const changeSet = state.changes(changes);
3541
- dispatch(state.update({
3542
- changes: changeSet,
3543
- selection: state.selection.map(changeSet, 1),
3544
- userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3545
- scrollIntoView: true
3546
- }));
3547
- return true;
3620
+ });
3548
3621
  };
3549
- var addBlockquote = setBlockquote(true);
3550
- var removeBlockquote = setBlockquote(false);
3551
- var toggleBlockquote = (target) => {
3552
- return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
3622
+
3623
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3624
+ var createMarkdownExtensions = ({ themeMode } = {}) => {
3625
+ return [
3626
+ // Main extension.
3627
+ // https://github.com/codemirror/lang-markdown
3628
+ // https://codemirror.net/5/mode/markdown/index.html (demo).
3629
+ markdown({
3630
+ // GRM by default (vs strict CommonMark):
3631
+ // Table, TaskList, Strikethrough, and Autolink.
3632
+ // NOTE: This extends the parser; it doesn't affect rendering.
3633
+ // https://github.github.com/gfm
3634
+ // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
3635
+ base: markdownLanguage3,
3636
+ // Languages for syntax highlighting fenced code blocks.
3637
+ codeLanguages: languages,
3638
+ // Parser extensions.
3639
+ extensions: [
3640
+ // GFM provided by default.
3641
+ markdownTagsExtensions
3642
+ ]
3643
+ }),
3644
+ // https://github.com/codemirror/theme-one-dark
3645
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
3646
+ // Custom styles.
3647
+ syntaxHighlighting(markdownHighlightStyle()),
3648
+ keymap7.of([
3649
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
3650
+ indentWithTab2,
3651
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
3652
+ ...defaultKeymap2,
3653
+ ...completionKeymap2,
3654
+ ...lintKeymap
3655
+ ])
3656
+ ];
3553
3657
  };
3554
- var addCodeblock = (target) => {
3555
- const { state, dispatch } = target;
3556
- const { selection } = state;
3557
- if (selection.ranges.length === 1 && selection.main.empty) {
3558
- const { head } = selection.main;
3559
- const line = state.doc.lineAt(head);
3560
- if (!/\S/.test(line.text) && head === line.from) {
3561
- snippets.codeblock(target, null, line.from, line.to);
3562
- return true;
3563
- }
3658
+
3659
+ // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
3660
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3661
+ import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
3662
+ import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
3663
+ import { mx as mx2 } from "@dxos/react-ui-theme";
3664
+ var HorizontalRuleWidget = class extends WidgetType3 {
3665
+ toDOM() {
3666
+ const el = document.createElement("span");
3667
+ el.className = "cm-hr";
3668
+ return el;
3564
3669
  }
3565
- const ranges = [];
3566
- for (const { from, to } of selection.ranges) {
3567
- let blockFrom = from;
3568
- let blockTo = to;
3569
- syntaxTree2(state).iterate({
3570
- from,
3571
- to,
3572
- enter: (node) => {
3573
- if (Object.hasOwn(Textblocks, node.name)) {
3574
- if (from >= node.from && to <= node.to) {
3575
- blockFrom = node.from;
3576
- blockTo = node.to;
3577
- } else {
3578
- blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
3579
- blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
3580
- }
3581
- }
3582
- }
3583
- });
3584
- if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
3585
- ranges[ranges.length - 1].to = blockTo;
3586
- } else {
3587
- ranges.push({
3588
- from: blockFrom,
3589
- to: blockTo
3590
- });
3591
- }
3670
+ };
3671
+ var LinkButton = class extends WidgetType3 {
3672
+ constructor(url, render) {
3673
+ super();
3674
+ this.url = url;
3675
+ this.render = render;
3592
3676
  }
3593
- if (!ranges.length) {
3594
- return false;
3677
+ eq(other) {
3678
+ return this.url === other.url;
3595
3679
  }
3596
- const changes = ranges.map(({ from, to }) => {
3597
- const column = from - state.doc.lineAt(from).from;
3598
- return [
3599
- {
3600
- from,
3601
- insert: "```\n" + " ".repeat(column)
3602
- },
3603
- {
3604
- from: to,
3605
- insert: "\n" + " ".repeat(column) + "```"
3606
- }
3607
- ];
3608
- });
3609
- dispatch(state.update({
3610
- changes,
3611
- userEvent: "format.codeblock.add",
3612
- scrollIntoView: true
3613
- }));
3614
- return true;
3615
- };
3616
- var removeCodeblock = ({ state, dispatch }) => {
3617
- const changes = [];
3618
- let lastBlock = -1;
3619
- for (const { from, to } of state.selection.ranges) {
3620
- syntaxTree2(state).iterate({
3621
- from,
3622
- to,
3623
- enter: (node) => {
3624
- if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3625
- lastBlock = node.from;
3626
- const firstLine = state.doc.lineAt(node.from);
3627
- if (node.name === "FencedCode") {
3628
- changes.push({
3629
- from: node.from,
3630
- to: firstLine.to + 1 + node.from - firstLine.from
3631
- });
3632
- const lastLine = state.doc.lineAt(node.to);
3633
- if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3634
- changes.push({
3635
- from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3636
- to: lastLine.to
3637
- });
3638
- }
3639
- } else {
3640
- const column = node.from - firstLine.from;
3641
- for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
3642
- changes.push({
3643
- from: line.from + column - 4,
3644
- to: line.from + column
3645
- });
3646
- if (line.to >= node.to) {
3647
- break;
3648
- }
3680
+ toDOM(view) {
3681
+ const el = document.createElement("span");
3682
+ this.render(el, this.url);
3683
+ return el;
3684
+ }
3685
+ };
3686
+ var CheckboxWidget = class extends WidgetType3 {
3687
+ constructor(_checked) {
3688
+ super();
3689
+ this._checked = _checked;
3690
+ }
3691
+ eq(other) {
3692
+ return this._checked === other._checked;
3693
+ }
3694
+ toDOM(view) {
3695
+ const input = document.createElement("input");
3696
+ input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
3697
+ input.type = "checkbox";
3698
+ input.checked = this._checked;
3699
+ if (view.state.readOnly) {
3700
+ input.setAttribute("disabled", "true");
3701
+ } else {
3702
+ input.onmousedown = (event) => {
3703
+ const pos = view.posAtDOM(input);
3704
+ const text = view.state.sliceDoc(pos, pos + 3);
3705
+ if (text === (this._checked ? "[x]" : "[ ]")) {
3706
+ view.dispatch({
3707
+ changes: {
3708
+ from: pos + 1,
3709
+ to: pos + 2,
3710
+ insert: this._checked ? " " : "x"
3649
3711
  }
3650
- }
3712
+ });
3713
+ event.preventDefault();
3651
3714
  }
3652
- }
3653
- });
3715
+ };
3716
+ }
3717
+ return input;
3654
3718
  }
3655
- if (!changes.length) {
3719
+ ignoreEvent() {
3656
3720
  return false;
3657
3721
  }
3658
- dispatch(state.update({
3659
- changes,
3660
- userEvent: "format.codeblock.remove",
3661
- scrollIntoView: true
3662
- }));
3663
- return true;
3664
- };
3665
- var toggleCodeblock = (target) => {
3666
- return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3667
- };
3668
- var formattingKeymap = (options = {}) => {
3669
- return [
3670
- keymap6.of([
3671
- {
3672
- key: "meta-b",
3673
- run: toggleStrong
3674
- }
3675
- ])
3676
- ];
3677
3722
  };
3678
- var InlineMarker = {
3679
- Emphasis: 1,
3680
- StrongEmphasis: 0,
3681
- InlineCode: 3,
3682
- Strikethrough: 2
3723
+ var hide = Decoration5.replace({});
3724
+ var fencedCodeLine = Decoration5.line({
3725
+ class: mx2("cm-code cm-codeblock-line")
3726
+ });
3727
+ var fencedCodeLineFirst = Decoration5.line({
3728
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
3729
+ });
3730
+ var fencedCodeLineLast = Decoration5.line({
3731
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
3732
+ });
3733
+ var horizontalRule = Decoration5.replace({
3734
+ widget: new HorizontalRuleWidget()
3735
+ });
3736
+ var checkedTask = Decoration5.replace({
3737
+ widget: new CheckboxWidget(true)
3738
+ });
3739
+ var uncheckedTask = Decoration5.replace({
3740
+ widget: new CheckboxWidget(false)
3741
+ });
3742
+ var editingRange = (state2, range, focus) => {
3743
+ const { readOnly, selection: { main: { head } } } = state2;
3744
+ return focus && !readOnly && head >= range.from && head <= range.to;
3683
3745
  };
3684
- var IgnoreInline = /* @__PURE__ */ new Set([
3685
- "Autolink",
3746
+ var MarksByParent = /* @__PURE__ */ new Set([
3686
3747
  "CodeMark",
3687
- "CodeText",
3688
- "Comment",
3689
3748
  "EmphasisMark",
3690
- "Hardbreak",
3691
- "HeaderMark",
3692
- "HTMLTag",
3693
- "LinkMark",
3694
- "ListMark",
3695
- "ProcessingInstruction",
3696
- "QuoteMark",
3697
3749
  "StrikethroughMark",
3698
3750
  "SubscriptMark",
3699
- "SuperscriptMark",
3700
- "TaskMarker"
3751
+ "SuperscriptMark"
3701
3752
  ]);
3702
- var Textblocks = {
3703
- ATXHeading1: "heading1",
3704
- ATXHeading2: "heading2",
3705
- ATXHeading3: "heading3",
3706
- ATXHeading4: "heading4",
3707
- ATXHeading5: "heading5",
3708
- ATXHeading6: "heading6",
3709
- CodeBlock: "codeblock",
3710
- FencedCode: "codeblock",
3711
- Paragraph: "paragraph",
3712
- SetextHeading1: "heading1",
3713
- SetextHeading2: "heading2",
3714
- TableCell: "tablecell",
3715
- Task: "paragraph"
3716
- };
3717
- var getFormatting = (state) => {
3718
- let blockType = null;
3719
- const inline = [
3720
- null,
3721
- null,
3722
- null,
3723
- null
3724
- ];
3725
- let link = false;
3726
- let blockQuote = null;
3727
- let listStyle = null;
3728
- const stack = [];
3729
- let currentBlock = null;
3730
- const advanceInline = (upto) => {
3731
- if (!currentBlock) {
3732
- return;
3733
- }
3734
- upto = Math.min(upto, currentBlock.end);
3735
- if (upto <= currentBlock.pos) {
3736
- return;
3737
- }
3738
- for (let i = 0; i < currentBlock.active.length; i++) {
3739
- if (inline[i] === false) {
3740
- continue;
3741
- } else if (currentBlock.active[i]) {
3742
- inline[i] = true;
3743
- } else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
3744
- inline[i] = false;
3745
- }
3746
- }
3747
- currentBlock.pos = upto;
3748
- };
3749
- const skipInline = (upto) => {
3750
- if (currentBlock && upto > currentBlock.pos) {
3751
- currentBlock.pos = Math.min(upto, currentBlock.end);
3752
- }
3753
- };
3754
- const { selection } = state;
3755
- for (const range of selection.ranges) {
3756
- if (range.empty && inline.some((v) => v === null)) {
3757
- const contextSize = Math.min(range.head, 6);
3758
- const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
3759
- let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
3760
- for (let i = 0; i < contextSize; i++) {
3761
- const ch = contextAfter[i];
3762
- if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3763
- contextAfter = contextAfter.slice(0, i);
3764
- break;
3765
- }
3766
- }
3767
- for (let i = 0; i < inline.length; i++) {
3768
- const mark2 = inlineMarkerText(i);
3769
- const found = contextAfter.indexOf(mark2);
3770
- if (found > -1) {
3771
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3772
- if (inline[i] === null) {
3773
- inline[i] = true;
3774
- }
3775
- }
3776
- }
3777
- }
3778
- syntaxTree2(state).iterate({
3779
- from: range.from,
3780
- to: range.to,
3753
+ var buildDecorations = (view, options, focus) => {
3754
+ const deco = new RangeSetBuilder2();
3755
+ const atomicDeco = new RangeSetBuilder2();
3756
+ const { state: state2 } = view;
3757
+ for (const { from, to } of view.visibleRanges) {
3758
+ syntaxTree2(state2).iterate({
3759
+ from,
3760
+ to,
3781
3761
  enter: (node) => {
3782
- advanceInline(node.from);
3783
- const { name } = node;
3784
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3785
- stack.push(name);
3786
- } else if (name === "Link") {
3787
- link = true;
3788
- } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3789
- if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3790
- stack[stack.length - 1] = "TaskList";
3791
- }
3792
- const blockCode = Textblocks[name];
3793
- if (blockType === null) {
3794
- blockType = blockCode;
3795
- } else if (blockType !== blockCode) {
3796
- blockType = false;
3797
- }
3798
- if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3799
- currentBlock = {
3800
- pos: Math.max(range.from, node.from),
3801
- end: Math.min(range.to, node.to),
3802
- active: [
3803
- false,
3804
- false,
3805
- false,
3806
- false
3807
- ]
3808
- };
3809
- }
3810
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3811
- const index = InlineMarker[name];
3812
- if (range.empty && inline[index] === null) {
3813
- inline[index] = true;
3762
+ if (node.name === "FencedCode") {
3763
+ const editing = editingRange(state2, node, focus);
3764
+ for (const block of view.viewportLineBlocks) {
3765
+ if (block.to < node.from) {
3766
+ continue;
3767
+ }
3768
+ if (block.from > node.to) {
3769
+ break;
3770
+ }
3771
+ const first = block.from <= node.from;
3772
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
3773
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
3774
+ if (!editing && (first || last)) {
3775
+ atomicDeco.add(block.from, block.to, hide);
3776
+ }
3814
3777
  }
3815
- currentBlock.active[index] = true;
3816
- } else if (IgnoreInline.has(name)) {
3817
- skipInline(node.to);
3818
- }
3819
- },
3820
- leave: (node) => {
3821
- advanceInline(node.to);
3822
- const { name } = node;
3823
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3824
- stack.pop();
3825
- } else if (Object.hasOwn(Textblocks, name)) {
3826
- let hasList = false;
3827
- let hasQuote = false;
3828
- for (let i = stack.length - 1; i >= 0; i--) {
3829
- if (stack[i] === "Blockquote") {
3830
- hasQuote = true;
3831
- } else if (!hasList) {
3832
- hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3778
+ return false;
3779
+ } else if (node.name === "Link") {
3780
+ const marks = node.node.getChildren("LinkMark");
3781
+ const urlNode = node.node.getChild("URL");
3782
+ const editing = editingRange(state2, node, focus);
3783
+ if (urlNode && marks.length >= 2) {
3784
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
3785
+ if (!editing) {
3786
+ atomicDeco.add(node.from, marks[0].to, hide);
3787
+ }
3788
+ deco.add(marks[0].to, marks[1].from, Decoration5.mark({
3789
+ tagName: "a",
3790
+ attributes: {
3791
+ class: "cm-link",
3792
+ href: url,
3793
+ rel: "noreferrer",
3794
+ target: "_blank"
3795
+ }
3796
+ }));
3797
+ if (!editing) {
3798
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
3799
+ widget: new LinkButton(url, options.renderLinkButton)
3800
+ }) : hide);
3833
3801
  }
3834
3802
  }
3835
- if (blockQuote === null) {
3836
- blockQuote = hasQuote;
3837
- } else if (!hasQuote && blockQuote) {
3838
- blockQuote = false;
3803
+ } else if (node.name === "HeaderMark") {
3804
+ const parent = node.node.parent;
3805
+ if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
3806
+ const next = state2.doc.sliceString(node.to, node.to + 1);
3807
+ atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
3839
3808
  }
3840
- if (listStyle === null) {
3841
- listStyle = hasList;
3842
- } else if (listStyle !== hasList) {
3843
- listStyle = false;
3809
+ } else if (node.name === "HorizontalRule") {
3810
+ if (!editingRange(state2, node, focus)) {
3811
+ deco.add(node.from, node.to, horizontalRule);
3812
+ }
3813
+ } else if (node.name === "TaskMarker") {
3814
+ if (!editingRange(state2, node, focus)) {
3815
+ const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
3816
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
3817
+ class: "cm-task"
3818
+ }));
3819
+ atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
3820
+ }
3821
+ } else if (MarksByParent.has(node.name)) {
3822
+ if (!editingRange(state2, node.node.parent, focus)) {
3823
+ atomicDeco.add(node.from, node.to, hide);
3844
3824
  }
3845
- currentBlock = null;
3846
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3847
- currentBlock.active[InlineMarker[name]] = false;
3848
3825
  }
3849
3826
  }
3850
3827
  });
3851
3828
  }
3852
- const { from, to } = state.doc.lineAt(selection.main.anchor);
3853
- const blankLine = from === to;
3854
3829
  return {
3855
- blankLine,
3856
- blockType: blockType || null,
3857
- blockQuote: blockQuote ?? false,
3858
- code: inline[3] ?? false,
3859
- emphasis: inline[1] ?? false,
3860
- strong: inline[0] ?? false,
3861
- strikethrough: inline[2] ?? false,
3862
- link,
3863
- listStyle: listStyle || null
3830
+ deco: deco.finish(),
3831
+ atomicDeco: atomicDeco.finish()
3864
3832
  };
3865
3833
  };
3866
- var useFormattingState = () => {
3867
- const [state, setState] = useState(null);
3868
- const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3869
- if (update2.docChanged || update2.selectionSet) {
3870
- const newState = getFormatting(update2.state);
3871
- if (!state || !compareFormatting(state, newState)) {
3872
- setState(newState);
3873
- }
3874
- }
3875
- }), []);
3834
+ var forceUpdate = StateEffect4.define();
3835
+ var decorateMarkdown = (options = {}) => {
3876
3836
  return [
3877
- state,
3878
- observer
3837
+ ViewPlugin4.fromClass(class {
3838
+ constructor(view) {
3839
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
3840
+ }
3841
+ update(update2) {
3842
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
3843
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
3844
+ this.clearUpdate();
3845
+ } else if (update2.selectionSet) {
3846
+ this.scheduleUpdate(update2.view);
3847
+ }
3848
+ }
3849
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
3850
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
3851
+ scheduleUpdate(view) {
3852
+ this.clearUpdate();
3853
+ this.pendingUpdate = setTimeout(() => {
3854
+ view.dispatch({
3855
+ effects: forceUpdate.of(null)
3856
+ });
3857
+ }, options.selectionChangeDelay);
3858
+ }
3859
+ clearUpdate() {
3860
+ if (this.pendingUpdate) {
3861
+ clearTimeout(this.pendingUpdate);
3862
+ this.pendingUpdate = void 0;
3863
+ }
3864
+ }
3865
+ destroy() {
3866
+ this.clearUpdate();
3867
+ }
3868
+ }, {
3869
+ provide: (plugin) => [
3870
+ EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3871
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3872
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
3873
+ ]
3874
+ }),
3875
+ formattingStyles
3879
3876
  ];
3880
3877
  };
3878
+ var formattingStyles = EditorView12.baseTheme({
3879
+ "& .cm-code": {
3880
+ fontFamily: getToken("fontFamily.mono", []).join(",")
3881
+ },
3882
+ "& .cm-codeblock-line": {
3883
+ paddingInline: "1rem !important"
3884
+ },
3885
+ "& .cm-codeblock-end": {
3886
+ display: "inline-block",
3887
+ width: "100%",
3888
+ position: "relative",
3889
+ "&::after": {
3890
+ position: "absolute",
3891
+ inset: 0,
3892
+ content: '""'
3893
+ }
3894
+ },
3895
+ "& .cm-codeblock-first": {
3896
+ borderTopLeftRadius: ".5rem",
3897
+ borderTopRightRadius: ".5rem"
3898
+ },
3899
+ "& .cm-codeblock-last": {
3900
+ borderBottomLeftRadius: ".5rem",
3901
+ borderBottomRightRadius: ".5rem"
3902
+ },
3903
+ "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
3904
+ background: getToken("extend.semanticColors.input.light"),
3905
+ mixBlendMode: "darken"
3906
+ },
3907
+ "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
3908
+ background: getToken("extend.semanticColors.input.dark"),
3909
+ mixBlendMode: "lighten"
3910
+ },
3911
+ "& .cm-hr": {
3912
+ display: "inline-block",
3913
+ width: "100%",
3914
+ height: "0",
3915
+ verticalAlign: "middle",
3916
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
3917
+ },
3918
+ "& .cm-task": {
3919
+ color: getToken("extend.colors.blue.500")
3920
+ },
3921
+ "& .cm-task-checkbox": {
3922
+ marginLeft: "4px",
3923
+ marginRight: "4px"
3924
+ }
3925
+ });
3881
3926
 
3882
3927
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3883
3928
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3884
3929
  import { StateField as StateField5 } from "@codemirror/state";
3885
- import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3930
+ import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3886
3931
  var image = (options = {}) => {
3887
3932
  return StateField5.define({
3888
- create: (state) => {
3889
- return Decoration6.set(buildDecorations2(0, state.doc.length, state));
3933
+ create: (state2) => {
3934
+ return Decoration6.set(buildDecorations2(0, state2.doc.length, state2));
3890
3935
  },
3891
3936
  update: (value, tr) => {
3892
3937
  if (!tr.docChanged && !tr.selection) {
@@ -3909,7 +3954,7 @@ var image = (options = {}) => {
3909
3954
  add: buildDecorations2(from, to, tr.state)
3910
3955
  });
3911
3956
  },
3912
- provide: (field) => EditorView14.decorations.from(field)
3957
+ provide: (field) => EditorView13.decorations.from(field)
3913
3958
  });
3914
3959
  };
3915
3960
  var preloaded = /* @__PURE__ */ new Set();
@@ -3920,16 +3965,16 @@ var preloadImage = (url) => {
3920
3965
  preloaded.add(url);
3921
3966
  }
3922
3967
  };
3923
- var buildDecorations2 = (from, to, state) => {
3968
+ var buildDecorations2 = (from, to, state2) => {
3924
3969
  const decorations = [];
3925
- const cursor = state.selection.main.head;
3926
- syntaxTree3(state).iterate({
3970
+ const cursor = state2.selection.main.head;
3971
+ syntaxTree3(state2).iterate({
3927
3972
  enter: (node) => {
3928
3973
  if (node.name === "Image") {
3929
3974
  const urlNode = node.node.getChild("URL");
3930
3975
  if (urlNode) {
3931
- const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3932
- const url = state.sliceDoc(urlNode.from, urlNode.to);
3976
+ const hide2 = state2.readOnly || cursor < node.from || cursor > node.to;
3977
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
3933
3978
  preloadImage(url);
3934
3979
  decorations.push(Decoration6.replace({
3935
3980
  block: true,
@@ -3959,6 +4004,8 @@ var ImageWidget = class extends WidgetType4 {
3959
4004
  return img;
3960
4005
  }
3961
4006
  };
4007
+ var imageUpload = (options = {}) => {
4008
+ };
3962
4009
 
3963
4010
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3964
4011
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -3997,24 +4044,24 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
3997
4044
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3998
4045
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3999
4046
  import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
4000
- import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
4047
+ import { Decoration as Decoration7, EditorView as EditorView14, WidgetType as WidgetType5 } from "@codemirror/view";
4001
4048
  var table = (options = {}) => {
4002
4049
  return StateField6.define({
4003
- create: (state) => update(state, options),
4050
+ create: (state2) => update(state2, options),
4004
4051
  update: (_, tr) => update(tr.state, options),
4005
- provide: (field) => EditorView15.decorations.from(field)
4052
+ provide: (field) => EditorView14.decorations.from(field)
4006
4053
  });
4007
4054
  };
4008
- var update = (state, options) => {
4055
+ var update = (state2, options) => {
4009
4056
  const builder = new RangeSetBuilder3();
4010
- const cursor = state.selection.main.head;
4057
+ const cursor = state2.selection.main.head;
4011
4058
  const tables = [];
4012
4059
  const getTable = () => tables[tables.length - 1];
4013
4060
  const getRow = () => {
4014
4061
  const table2 = getTable();
4015
4062
  return table2.rows?.[table2.rows.length - 1];
4016
4063
  };
4017
- syntaxTree5(state).iterate({
4064
+ syntaxTree5(state2).iterate({
4018
4065
  enter: (node) => {
4019
4066
  switch (node.name) {
4020
4067
  case "Table": {
@@ -4035,9 +4082,9 @@ var update = (state, options) => {
4035
4082
  case "TableCell": {
4036
4083
  const row = getRow();
4037
4084
  if (row) {
4038
- row.push(state.sliceDoc(node.from, node.to));
4085
+ row.push(state2.sliceDoc(node.from, node.to));
4039
4086
  } else {
4040
- getTable().header?.push(state.sliceDoc(node.from, node.to));
4087
+ getTable().header?.push(state2.sliceDoc(node.from, node.to));
4041
4088
  }
4042
4089
  break;
4043
4090
  }
@@ -4045,7 +4092,7 @@ var update = (state, options) => {
4045
4092
  }
4046
4093
  });
4047
4094
  tables.forEach((table2) => {
4048
- const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
4095
+ const hide2 = state2.readOnly || cursor < table2.from || cursor > table2.to;
4049
4096
  if (hide2) {
4050
4097
  builder.add(table2.from, table2.to, Decoration7.replace({
4051
4098
  widget: new TableWidget(table2)
@@ -4096,8 +4143,8 @@ var TableWidget = class extends WidgetType5 {
4096
4143
 
4097
4144
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4098
4145
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4099
- import { log as log8 } from "@dxos/log";
4100
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4146
+ import { log as log6 } from "@dxos/log";
4147
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4101
4148
  var mention = ({ onSearch }) => {
4102
4149
  return autocompletion2({
4103
4150
  // TODO(burdon): Not working.
@@ -4109,10 +4156,10 @@ var mention = ({ onSearch }) => {
4109
4156
  icons: false,
4110
4157
  override: [
4111
4158
  (context) => {
4112
- log8.info("completion context", {
4159
+ log6.info("completion context", {
4113
4160
  context
4114
4161
  }, {
4115
- F: __dxlog_file10,
4162
+ F: __dxlog_file8,
4116
4163
  L: 26,
4117
4164
  S: void 0,
4118
4165
  C: (f, a) => f(...a)
@@ -4134,25 +4181,35 @@ var mention = ({ onSearch }) => {
4134
4181
 
4135
4182
  // packages/ui/react-ui-editor/src/extensions/modes.ts
4136
4183
  import { Facet as Facet6 } from "@codemirror/state";
4137
- import { keymap as keymap7 } from "@codemirror/view";
4184
+ import { keymap as keymap8 } from "@codemirror/view";
4138
4185
  import { vim } from "@replit/codemirror-vim";
4186
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
4187
+ var focusEvent = "focus.container";
4139
4188
  var editorMode = Facet6.define({
4140
4189
  combine: (modes) => modes[0] ?? {}
4141
4190
  });
4142
4191
  var EditorModes = {
4143
4192
  default: [],
4193
+ vscode: [
4194
+ // https://github.com/replit/codemirror-vscode-keymap
4195
+ editorMode.of({
4196
+ type: "vscode"
4197
+ }),
4198
+ keymap8.of(vscodeKeymap)
4199
+ ],
4144
4200
  vim: [
4201
+ // https://github.com/replit/codemirror-vim
4145
4202
  vim(),
4146
4203
  editorMode.of({
4147
4204
  type: "vim",
4148
4205
  noTabster: true
4149
4206
  }),
4150
- keymap7.of([
4207
+ keymap8.of([
4151
4208
  {
4152
4209
  key: "Alt-Escape",
4153
4210
  run: (view) => {
4154
4211
  view.dispatch({
4155
- userEvent: "focus.container"
4212
+ userEvent: focusEvent
4156
4213
  });
4157
4214
  return true;
4158
4215
  }
@@ -4161,8 +4218,95 @@ var EditorModes = {
4161
4218
  ]
4162
4219
  };
4163
4220
 
4221
+ // packages/ui/react-ui-editor/src/extensions/state.ts
4222
+ import { Transaction } from "@codemirror/state";
4223
+ import { EditorView as EditorView15, keymap as keymap9 } from "@codemirror/view";
4224
+ import { debounce as debounce2 } from "@dxos/async";
4225
+ import { invariant as invariant4 } from "@dxos/invariant";
4226
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4227
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4228
+ var scrollAnnotation = "dxos.org/cm/scrolling";
4229
+ var keyPrefix = "dxos.org/react-ui-editor/state";
4230
+ var localStorageStateStoreAdapter = {
4231
+ setState: (id, state2) => {
4232
+ invariant4(id, void 0, {
4233
+ F: __dxlog_file9,
4234
+ L: 35,
4235
+ S: void 0,
4236
+ A: [
4237
+ "id",
4238
+ ""
4239
+ ]
4240
+ });
4241
+ localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4242
+ },
4243
+ getState: (id) => {
4244
+ invariant4(id, void 0, {
4245
+ F: __dxlog_file9,
4246
+ L: 39,
4247
+ S: void 0,
4248
+ A: [
4249
+ "id",
4250
+ ""
4251
+ ]
4252
+ });
4253
+ const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4254
+ return state2 ? JSON.parse(state2) : void 0;
4255
+ }
4256
+ };
4257
+ var state = ({ getState, setState } = {}) => {
4258
+ const setStateDebounced = debounce2(setState, 1e3);
4259
+ return [
4260
+ // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4261
+ EditorView15.updateListener.of(({ view, changes, transactions }) => {
4262
+ const id = view.state.facet(documentId2);
4263
+ if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4264
+ return;
4265
+ }
4266
+ if (setState) {
4267
+ const { top } = view.dom.getBoundingClientRect();
4268
+ const pos = view.posAtCoords({
4269
+ x: 0,
4270
+ y: top
4271
+ });
4272
+ if (pos !== null) {
4273
+ const { anchor, head } = view.state.selection.main;
4274
+ setStateDebounced(id, {
4275
+ scrollTo: {
4276
+ from: pos,
4277
+ yMargin: 0
4278
+ },
4279
+ selection: {
4280
+ anchor,
4281
+ head
4282
+ }
4283
+ });
4284
+ }
4285
+ }
4286
+ }),
4287
+ getState && keymap9.of([
4288
+ {
4289
+ key: "ctrl-r",
4290
+ run: (view) => {
4291
+ const state2 = getState(view.state.facet(documentId2));
4292
+ if (state2) {
4293
+ view.dispatch({
4294
+ effects: EditorView15.scrollIntoView(state2.scrollTo.from, {
4295
+ yMargin: 0
4296
+ }),
4297
+ selection: state2.selection,
4298
+ annotations: Transaction.userEvent.of(scrollAnnotation)
4299
+ });
4300
+ }
4301
+ return true;
4302
+ }
4303
+ }
4304
+ ])
4305
+ ].filter(isNotFalsy3);
4306
+ };
4307
+
4164
4308
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4165
- import { keymap as keymap8 } from "@codemirror/view";
4309
+ import { keymap as keymap10 } from "@codemirror/view";
4166
4310
  var defaultItems = [
4167
4311
  "hello world!",
4168
4312
  "this is a test.",
@@ -4172,7 +4316,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4172
4316
  let t;
4173
4317
  let idx = 0;
4174
4318
  return [
4175
- keymap8.of([
4319
+ keymap10.of([
4176
4320
  {
4177
4321
  // Reset.
4178
4322
  key: "alt-meta-'",
@@ -4219,9 +4363,11 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4219
4363
  };
4220
4364
 
4221
4365
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4222
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4366
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4223
4367
  var instanceCount = 0;
4224
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4368
+ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0, {
4369
+ yMargin: 0
4370
+ }), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4225
4371
  const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4226
4372
  const tabsterDOMAttribute = useFocusableGroup({
4227
4373
  tabBehavior: "limited"
@@ -4240,24 +4386,24 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4240
4386
  autoFocus
4241
4387
  ]);
4242
4388
  useEffect2(() => {
4243
- log9("updating", {
4389
+ log7("create", {
4244
4390
  id,
4245
4391
  instanceId
4246
4392
  }, {
4247
- F: __dxlog_file11,
4393
+ F: __dxlog_file10,
4248
4394
  L: 88,
4249
4395
  S: void 0,
4250
4396
  C: (f, a) => f(...a)
4251
4397
  });
4252
- const state = EditorState2.create({
4398
+ const state2 = EditorState2.create({
4253
4399
  doc,
4254
4400
  selection,
4255
4401
  extensions: [
4256
4402
  id && documentId2.of(id),
4257
4403
  // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4258
4404
  EditorView16.exceptionSink.of((err) => {
4259
- log9.catch(err, void 0, {
4260
- F: __dxlog_file11,
4405
+ log7.catch(err, void 0, {
4406
+ F: __dxlog_file10,
4261
4407
  L: 101,
4262
4408
  S: void 0,
4263
4409
  C: (f, a) => f(...a)
@@ -4266,16 +4412,16 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4266
4412
  // Focus.
4267
4413
  EditorView16.updateListener.of((update2) => {
4268
4414
  update2.transactions.forEach((transaction) => {
4269
- if (transaction.isUserEvent("focus.container")) {
4415
+ if (transaction.isUserEvent(focusEvent)) {
4270
4416
  rootRef.current?.focus();
4271
4417
  }
4272
4418
  });
4273
4419
  }),
4274
4420
  extensions
4275
- ].filter(isNotFalsy3)
4421
+ ].filter(isNotFalsy4)
4276
4422
  });
4277
4423
  const view2 = new EditorView16({
4278
- state,
4424
+ state: state2,
4279
4425
  parent: rootRef.current,
4280
4426
  scrollTo,
4281
4427
  // NOTE: Uncomment to debug/monitor all transactions.
@@ -4287,21 +4433,29 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4287
4433
  view3.update(trs);
4288
4434
  }
4289
4435
  });
4290
- setView(view2);
4291
- if (moveToEndOfLine) {
4436
+ if (moveToEndOfLine && !(scrollTo || selection)) {
4292
4437
  const { to } = view2.state.doc.lineAt(0);
4293
- view2?.dispatch({
4438
+ view2.dispatch({
4294
4439
  selection: {
4295
4440
  anchor: to
4296
4441
  }
4297
4442
  });
4298
4443
  }
4299
- if (state.facet(editorMode).noTabster) {
4444
+ if (state2.facet(editorMode).noTabster) {
4300
4445
  rootRef.current?.removeAttribute("data-tabster");
4301
4446
  }
4447
+ setView(view2);
4302
4448
  return () => {
4449
+ log7("destroy", {
4450
+ id,
4451
+ instanceId
4452
+ }, {
4453
+ F: __dxlog_file10,
4454
+ L: 150,
4455
+ S: void 0,
4456
+ C: (f, a) => f(...a)
4457
+ });
4303
4458
  view2?.destroy();
4304
- setView(null);
4305
4459
  };
4306
4460
  }, [
4307
4461
  doc,
@@ -4331,9 +4485,10 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4331
4485
  });
4332
4486
 
4333
4487
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4334
- import { ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, Quotes } from "@phosphor-icons/react";
4488
+ import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
4335
4489
  import { createContext } from "@radix-ui/react-context";
4336
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4490
+ import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
4491
+ import { useDropzone } from "react-dropzone";
4337
4492
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4338
4493
  import { getSize } from "@dxos/react-ui-theme";
4339
4494
  var tooltipProps = {
@@ -4350,10 +4505,10 @@ var HeadingIcons = {
4350
4505
  "6": TextHSix
4351
4506
  };
4352
4507
  var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
4353
- var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4508
+ var ToolbarRoot = ({ children, onAction, classNames, state: state2 }) => {
4354
4509
  return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
4355
4510
  onAction,
4356
- state
4511
+ state: state2
4357
4512
  }, /* @__PURE__ */ React2.createElement(DensityProvider, {
4358
4513
  density: "fine"
4359
4514
  }, /* @__PURE__ */ React2.createElement(ElevationProvider, {
@@ -4367,7 +4522,7 @@ var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4367
4522
  };
4368
4523
  var buttonStyles = "min-bs-0 p-2";
4369
4524
  var iconStyles = getSize(5);
4370
- var ToolbarButton = ({ Icon, children, ...props }) => {
4525
+ var ToolbarToggleButton = ({ Icon, children, ...props }) => {
4371
4526
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4372
4527
  asChild: true
4373
4528
  }, /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroupItem, {
@@ -4380,14 +4535,27 @@ var ToolbarButton = ({ Icon, children, ...props }) => {
4380
4535
  className: "sr-only"
4381
4536
  }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4382
4537
  };
4538
+ var ToolbarButton = ({ Icon, children, ...props }) => {
4539
+ return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4540
+ asChild: true
4541
+ }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4542
+ variant: "ghost",
4543
+ ...props,
4544
+ classNames: buttonStyles
4545
+ }, /* @__PURE__ */ React2.createElement(Icon, {
4546
+ className: iconStyles
4547
+ }), /* @__PURE__ */ React2.createElement("span", {
4548
+ className: "sr-only"
4549
+ }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4550
+ };
4383
4551
  var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
4384
4552
  role: "separator",
4385
4553
  className: "grow"
4386
4554
  });
4387
4555
  var MarkdownHeading = () => {
4388
4556
  const { t } = useTranslation(translationKey);
4389
- const { onAction, state } = useToolbarContext("MarkdownFormatting");
4390
- const blockType = state ? state.blockType : "paragraph";
4557
+ const { onAction, state: state2 } = useToolbarContext("MarkdownFormatting");
4558
+ const blockType = state2 ? state2.blockType : "paragraph";
4391
4559
  const header = blockType && /heading(\d)/.exec(blockType);
4392
4560
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4393
4561
  const HeadingIcon = HeadingIcons[value ?? "0"];
@@ -4461,43 +4629,43 @@ var markdownStyles = [
4461
4629
  {
4462
4630
  type: "strong",
4463
4631
  Icon: TextB,
4464
- getState: (state) => state.strong
4632
+ getState: (state2) => state2.strong
4465
4633
  },
4466
4634
  {
4467
4635
  type: "emphasis",
4468
4636
  Icon: TextItalic,
4469
- getState: (state) => state.emphasis
4637
+ getState: (state2) => state2.emphasis
4470
4638
  },
4471
4639
  {
4472
4640
  type: "strikethrough",
4473
4641
  Icon: TextStrikethrough,
4474
- getState: (state) => state.strikethrough
4642
+ getState: (state2) => state2.strikethrough
4475
4643
  },
4476
4644
  {
4477
4645
  type: "code",
4478
4646
  Icon: Code,
4479
- getState: (state) => state.code
4647
+ getState: (state2) => state2.code
4480
4648
  },
4481
4649
  {
4482
4650
  type: "link",
4483
4651
  Icon: Link,
4484
- getState: (state) => state.link
4652
+ getState: (state2) => state2.link
4485
4653
  }
4486
4654
  ];
4487
4655
  var MarkdownStyles = () => {
4488
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4656
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4489
4657
  const { t } = useTranslation(translationKey);
4490
4658
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4491
4659
  type: "multiple",
4492
- value: markdownStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)
4493
- }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4660
+ value: markdownStyles.filter(({ getState }) => state2 && getState(state2)).map(({ type }) => type)
4661
+ }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4494
4662
  key: type,
4495
4663
  value: type,
4496
4664
  Icon,
4497
- disabled: state?.blockType === "codeblock",
4498
- onClick: state ? () => onAction?.({
4665
+ disabled: state2?.blockType === "codeblock",
4666
+ onClick: state2 ? () => onAction?.({
4499
4667
  type,
4500
- data: !getState(state)
4668
+ data: !getState(state2)
4501
4669
  }) : void 0
4502
4670
  }, t(`${type} label`))));
4503
4671
  };
@@ -4505,32 +4673,32 @@ var markdownLists = [
4505
4673
  {
4506
4674
  type: "list-bullet",
4507
4675
  Icon: ListBullets,
4508
- getState: (state) => state.listStyle === "bullet"
4676
+ getState: (state2) => state2.listStyle === "bullet"
4509
4677
  },
4510
4678
  {
4511
4679
  type: "list-ordered",
4512
4680
  Icon: ListNumbers,
4513
- getState: (state) => state.listStyle === "ordered"
4681
+ getState: (state2) => state2.listStyle === "ordered"
4514
4682
  },
4515
4683
  {
4516
4684
  type: "list-task",
4517
4685
  Icon: ListChecks,
4518
- getState: (state) => state.listStyle === "task"
4686
+ getState: (state2) => state2.listStyle === "task"
4519
4687
  }
4520
4688
  ];
4521
4689
  var MarkdownLists = () => {
4522
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4690
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4523
4691
  const { t } = useTranslation(translationKey);
4524
4692
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4525
4693
  type: "single",
4526
- value: state?.listStyle ? `list-${state.listStyle}` : ""
4527
- }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4694
+ value: state2?.listStyle ? `list-${state2.listStyle}` : ""
4695
+ }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4528
4696
  key: type,
4529
4697
  value: type,
4530
4698
  Icon,
4531
- onClick: state ? () => onAction?.({
4699
+ onClick: state2 ? () => onAction?.({
4532
4700
  type,
4533
- data: !getState(state)
4701
+ data: !getState(state2)
4534
4702
  }) : void 0
4535
4703
  }, t(`${type} label`))));
4536
4704
  };
@@ -4538,111 +4706,106 @@ var markdownBlocks = [
4538
4706
  {
4539
4707
  type: "blockquote",
4540
4708
  Icon: Quotes,
4541
- getState: (state) => state.blockQuote
4709
+ getState: (state2) => state2.blockQuote
4542
4710
  },
4543
4711
  {
4544
4712
  type: "codeblock",
4545
4713
  Icon: CodeBlock,
4546
- getState: (state) => state.blockType === "codeblock"
4714
+ getState: (state2) => state2.blockType === "codeblock"
4547
4715
  },
4548
4716
  {
4549
4717
  type: "table",
4550
4718
  Icon: Table2,
4551
- getState: (state) => state.blockType === "tablecell",
4552
- disabled: (state) => !state.blankLine
4719
+ getState: (state2) => state2.blockType === "tablecell",
4720
+ disabled: (state2) => !state2.blankLine
4553
4721
  }
4554
4722
  ];
4555
4723
  var MarkdownBlocks = () => {
4556
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4724
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4557
4725
  const { t } = useTranslation(translationKey);
4558
- const value = markdownBlocks.find(({ getState }) => state && getState(state));
4726
+ const value = markdownBlocks.find(({ getState }) => state2 && getState(state2));
4559
4727
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4560
4728
  type: "single",
4561
4729
  value: value?.type ?? ""
4562
- }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4730
+ }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4563
4731
  key: type,
4564
4732
  value: type,
4565
4733
  Icon,
4566
- disabled: !state || disabled?.(state),
4567
- onClick: state ? () => onAction?.({
4734
+ disabled: !state2 || disabled?.(state2),
4735
+ onClick: state2 ? () => onAction?.({
4568
4736
  type,
4569
- data: !getState(state)
4737
+ data: !getState(state2)
4570
4738
  }) : void 0
4571
4739
  }, t(`${type} label`))));
4572
4740
  };
4573
4741
  var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null));
4574
- var MarkdownExtended = () => {
4742
+ var MarkdownCustom = ({ onUpload } = {}) => {
4575
4743
  const { onAction } = useToolbarContext("MarkdownStyles");
4576
4744
  const { t } = useTranslation(translationKey);
4577
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4578
- asChild: true
4579
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4580
- variant: "ghost",
4745
+ const { acceptedFiles, getInputProps, open } = useDropzone({
4746
+ multiple: false,
4747
+ noDrag: true,
4748
+ accept: {
4749
+ "image/*": [
4750
+ ".jpg",
4751
+ ".jpeg",
4752
+ ".png",
4753
+ ".gif"
4754
+ ]
4755
+ }
4756
+ });
4757
+ useEffect3(() => {
4758
+ if (onUpload && acceptedFiles.length) {
4759
+ requestAnimationFrame(async () => {
4760
+ const f = acceptedFiles[0];
4761
+ const file = new File([
4762
+ f
4763
+ ], f.name, {
4764
+ type: f.type,
4765
+ lastModified: f.lastModified
4766
+ });
4767
+ const { url } = await onUpload(file);
4768
+ if (url) {
4769
+ onAction?.({
4770
+ type: "image",
4771
+ data: url
4772
+ });
4773
+ }
4774
+ });
4775
+ }
4776
+ }, [
4777
+ acceptedFiles
4778
+ ]);
4779
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("input", getInputProps()), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4780
+ value: "image",
4781
+ Icon: Image,
4782
+ onClick: () => open()
4783
+ }, t("image label")));
4784
+ };
4785
+ var MarkdownActions = () => {
4786
+ const { onAction } = useToolbarContext("MarkdownStyles");
4787
+ const { t } = useTranslation(translationKey);
4788
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4789
+ value: "comment",
4790
+ Icon: ChatText,
4581
4791
  "data-testid": "editor.toolbar.comment",
4582
4792
  onClick: () => onAction?.({
4583
4793
  type: "comment"
4584
- }),
4585
- classNames: buttonStyles
4586
- }, /* @__PURE__ */ React2.createElement(ChatText, {
4587
- className: iconStyles
4588
- }), /* @__PURE__ */ React2.createElement("span", {
4589
- className: "sr-only"
4590
- }, t("comment label")))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("comment label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4794
+ })
4795
+ }, t("comment label")));
4591
4796
  };
4592
4797
  var Toolbar = {
4593
4798
  Root: ToolbarRoot,
4594
- Button: ToolbarButton,
4799
+ Button: ToolbarToggleButton,
4595
4800
  Separator: ToolbarSeparator,
4596
4801
  Markdown: MarkdownStandard,
4597
- Extended: MarkdownExtended
4802
+ Custom: MarkdownCustom,
4803
+ Actions: MarkdownActions
4598
4804
  };
4599
4805
 
4600
4806
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4601
4807
  var useActionHandler = (view) => {
4602
- return (action) => {
4603
- if (!view) {
4604
- return;
4605
- }
4606
- let inlineType, listType;
4607
- switch (action.type) {
4608
- case "heading":
4609
- setHeading(parseInt(action.data))(view);
4610
- break;
4611
- case "strong":
4612
- case "emphasis":
4613
- case "strikethrough":
4614
- case "code":
4615
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4616
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4617
- break;
4618
- case "list-ordered":
4619
- case "list-bullet":
4620
- case "list-task":
4621
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4622
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4623
- break;
4624
- case "blockquote":
4625
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4626
- break;
4627
- case "codeblock":
4628
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4629
- break;
4630
- case "table":
4631
- insertTable(view);
4632
- break;
4633
- case "link":
4634
- (action.data === false ? removeLink : addLink)(view);
4635
- break;
4636
- case "comment":
4637
- createComment(view);
4638
- break;
4639
- }
4640
- setTimeout(() => {
4641
- if (!view.hasFocus) {
4642
- view.focus();
4643
- }
4644
- });
4645
- };
4808
+ return (action) => view && processAction(view, action);
4646
4809
  };
4647
4810
 
4648
4811
  // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
@@ -4661,28 +4824,36 @@ var useDocAccessor = (text) => {
4661
4824
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4662
4825
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4663
4826
  import { EditorView as EditorView17 } from "@codemirror/view";
4664
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4665
- import { log as log10 } from "@dxos/log";
4666
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4667
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4827
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4828
+ import { log as log8 } from "@dxos/log";
4829
+ import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4830
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4668
4831
  var useTextEditor = (cb = () => ({}), deps = []) => {
4669
4832
  let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4670
4833
  const onUpdate = useRef3();
4671
4834
  const [view, setView] = useState4();
4672
4835
  const parentRef = useRef3(null);
4673
- useEffect3(() => {
4836
+ useEffect4(() => {
4674
4837
  let view2;
4675
4838
  if (parentRef.current) {
4676
- const state = EditorState3.create({
4839
+ log8("create", {
4840
+ id
4841
+ }, {
4842
+ F: __dxlog_file11,
4843
+ L: 36,
4844
+ S: void 0,
4845
+ C: (f, a) => f(...a)
4846
+ });
4847
+ const state2 = EditorState3.create({
4677
4848
  doc,
4678
4849
  selection,
4679
4850
  extensions: [
4680
4851
  id && documentId2.of(id),
4681
4852
  // TODO(burdon): Doesn't catch errors in keymap functions.
4682
4853
  EditorView17.exceptionSink.of((err) => {
4683
- log10.catch(err, void 0, {
4684
- F: __dxlog_file12,
4685
- L: 44,
4854
+ log8.catch(err, void 0, {
4855
+ F: __dxlog_file11,
4856
+ L: 46,
4686
4857
  S: void 0,
4687
4858
  C: (f, a) => f(...a)
4688
4859
  });
@@ -4691,12 +4862,12 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4691
4862
  EditorView17.updateListener.of(() => {
4692
4863
  onUpdate.current?.();
4693
4864
  })
4694
- ].filter(isNotFalsy4)
4865
+ ].filter(isNotFalsy5)
4695
4866
  });
4696
4867
  view2 = new EditorView17({
4697
4868
  parent: parentRef.current,
4698
4869
  scrollTo,
4699
- state,
4870
+ state: state2,
4700
4871
  // NOTE: Uncomment to debug/monitor all transactions.
4701
4872
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4702
4873
  dispatchTransactions: (trs, view3) => {
@@ -4709,10 +4880,18 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4709
4880
  setView(view2);
4710
4881
  }
4711
4882
  return () => {
4883
+ log8("destroy", {
4884
+ id
4885
+ }, {
4886
+ F: __dxlog_file11,
4887
+ L: 74,
4888
+ S: void 0,
4889
+ C: (f, a) => f(...a)
4890
+ });
4712
4891
  view2?.destroy();
4713
4892
  };
4714
4893
  }, deps);
4715
- useEffect3(() => {
4894
+ useEffect4(() => {
4716
4895
  if (view) {
4717
4896
  if (!selection && !view.state.selection.main.anchor) {
4718
4897
  selection = EditorSelection2.single(view.state.doc.line(1).to);
@@ -4771,36 +4950,39 @@ export {
4771
4950
  callbackWrapper,
4772
4951
  command,
4773
4952
  comments,
4774
- compareFormatting,
4775
4953
  createBasicExtensions,
4776
4954
  createComment,
4777
4955
  createDataExtensions,
4778
4956
  createMarkdownExtensions,
4779
4957
  createThemeExtensions,
4780
- cursorLineMargin,
4781
4958
  decorateMarkdown,
4782
4959
  defaultOptions,
4783
4960
  defaultTheme,
4784
- dnd,
4785
4961
  documentId2 as documentId,
4962
+ dropFile,
4786
4963
  editorFillLayoutEditor,
4787
4964
  editorFillLayoutRoot,
4788
4965
  editorMode,
4789
4966
  editorWithToolbarLayout,
4790
4967
  focusComment,
4968
+ focusEvent,
4969
+ formattingEquals,
4791
4970
  formattingKeymap,
4792
4971
  getFormatting,
4793
4972
  getToken,
4794
4973
  image,
4974
+ imageUpload,
4795
4975
  insertTable,
4796
- keymap9 as keymap,
4976
+ keymap11 as keymap,
4797
4977
  linkTooltip,
4798
4978
  listener,
4979
+ localStorageStateStoreAdapter,
4799
4980
  logChanges,
4800
4981
  markdownHighlightStyle,
4801
4982
  markdownTags,
4802
4983
  markdownTagsExtensions,
4803
4984
  mention,
4985
+ processAction,
4804
4986
  removeBlockquote,
4805
4987
  removeCodeblock,
4806
4988
  removeLink,
@@ -4811,6 +4993,7 @@ export {
4811
4993
  setHeading,
4812
4994
  setSelection,
4813
4995
  setStyle,
4996
+ state,
4814
4997
  table,
4815
4998
  tags2 as tags,
4816
4999
  toggleBlockquote,