@dxos/react-ui-editor 0.4.8-main.6d7437e → 0.4.8-main.6dbc1f1

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 (72) hide show
  1. package/dist/lib/browser/index.mjs +1927 -1747
  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 -27
  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 -18
  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/decorate.ts +1 -1
  58. package/src/extensions/markdown/formatting.test.ts +13 -13
  59. package/src/extensions/markdown/formatting.ts +95 -87
  60. package/src/extensions/markdown/image.ts +6 -0
  61. package/src/extensions/markdown/index.ts +1 -0
  62. package/src/extensions/modes.ts +11 -2
  63. package/src/extensions/state.ts +90 -0
  64. package/src/hooks/useActionHandler.ts +4 -85
  65. package/src/hooks/useDocAccessor.ts +8 -2
  66. package/src/hooks/useTextEditor.stories.tsx +3 -3
  67. package/src/hooks/useTextEditor.ts +3 -0
  68. package/src/themes/default.ts +24 -8
  69. package/src/translations.ts +1 -0
  70. package/dist/types/src/extensions/cursor-line-margin.d.ts +0 -7
  71. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +0 -1
  72. 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,40 +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
- },
2237
- content: {
2238
- className: "!p-2"
2239
2231
  }
2240
2232
  };
2241
2233
  var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2242
- const slots = defaultsDeep2({}, _slots, defaultSlots);
2234
+ const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2243
2235
  return [
2244
- EditorView10.baseTheme(defaultTheme),
2245
- EditorView10.darkTheme.of(themeMode === "dark"),
2246
- theme && EditorView10.theme(theme),
2247
- 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({
2248
2240
  class: slots.editor.className
2249
2241
  }),
2250
- slots.content?.className && EditorView10.contentAttributes.of({
2242
+ slots.content?.className && EditorView9.contentAttributes.of({
2251
2243
  class: slots.content.className
2252
2244
  })
2253
2245
  ].filter(isNotFalsy2);
@@ -2275,1621 +2267,1671 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2275
2267
  };
2276
2268
 
2277
2269
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2278
- import { EditorView as EditorView11 } from "@codemirror/view";
2270
+ import { EditorView as EditorView10 } from "@codemirror/view";
2279
2271
  var listener = ({ onFocus, onChange }) => {
2280
2272
  const extensions = [];
2281
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2273
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
2282
2274
  onFocus(focusing);
2283
2275
  return null;
2284
2276
  }));
2285
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2277
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
2286
2278
  onChange(update2.state.doc.toString());
2287
2279
  }));
2288
2280
  return extensions;
2289
2281
  };
2290
2282
 
2291
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2292
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2293
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2294
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2295
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2296
- import { languages } from "@codemirror/language-data";
2297
- import { searchKeymap } from "@codemirror/search";
2298
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2299
- import { keymap as keymap5 } from "@codemirror/view";
2300
-
2301
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2302
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
2303
- import { HighlightStyle } from "@codemirror/language";
2304
- import { tags, styleTags, Tag } from "@lezer/highlight";
2305
- import { Table } from "@lezer/markdown";
2306
- var markdownTags = {
2307
- Blockquote: Tag.define(),
2308
- CodeMark: Tag.define(),
2309
- CodeText: Tag.define(),
2310
- EmphasisMark: Tag.define(),
2311
- HeaderMark: Tag.define(),
2312
- InlineCode: Tag.define(),
2313
- LinkLabel: Tag.define(),
2314
- LinkReference: Tag.define(),
2315
- ListMark: Tag.define(),
2316
- QuoteMark: Tag.define(),
2317
- URL: Tag.define(),
2318
- // Custom.
2319
- TableCell: Tag.define()
2320
- };
2321
- Table.defineNodes?.forEach((node) => {
2322
- switch (node?.name) {
2323
- case "TableCell": {
2324
- node.style = markdownTags.TableCell;
2325
- 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
+ });
2326
2368
  }
2327
2369
  }
2328
- });
2329
- var markdownTagsExtensions = [
2330
- Table,
2331
- {
2332
- props: [
2333
- styleTags(markdownTags)
2334
- ]
2370
+ if (!changes.length) {
2371
+ return false;
2335
2372
  }
2336
- ];
2337
- var markdownHighlightStyle = (readonly) => {
2338
- return HighlightStyle.define([
2339
- {
2340
- tag: [
2341
- tags.keyword,
2342
- tags.name,
2343
- tags.deleted,
2344
- tags.character,
2345
- tags.propertyName,
2346
- tags.macroName,
2347
- tags.color,
2348
- tags.constant(tags.name),
2349
- tags.standard(tags.name),
2350
- tags.definition(tags.name),
2351
- tags.separator,
2352
- tags.typeName,
2353
- tags.className,
2354
- tags.number,
2355
- tags.changed,
2356
- tags.annotation,
2357
- tags.modifier,
2358
- tags.self,
2359
- tags.namespace,
2360
- tags.operator,
2361
- tags.operatorKeyword,
2362
- tags.escape,
2363
- tags.regexp,
2364
- tags.special(tags.string),
2365
- tags.meta,
2366
- tags.comment,
2367
- tags.atom,
2368
- tags.bool,
2369
- tags.special(tags.variableName),
2370
- tags.processingInstruction,
2371
- tags.string,
2372
- tags.inserted,
2373
- tags.invalid
2374
- ],
2375
- color: "inherit !important"
2376
- },
2377
- // Markdown marks.
2378
- {
2379
- tag: [
2380
- tags.meta,
2381
- tags.processingInstruction,
2382
- markdownTags.LinkLabel,
2383
- markdownTags.LinkReference,
2384
- markdownTags.ListMark
2385
- ],
2386
- class: mark
2387
- },
2388
- // Markdown marks.
2389
- {
2390
- tag: [
2391
- markdownTags.CodeMark,
2392
- markdownTags.HeaderMark,
2393
- markdownTags.QuoteMark,
2394
- markdownTags.EmphasisMark
2395
- ],
2396
- class: mark
2397
- },
2398
- // E.g., code block language (after ```).
2399
- {
2400
- tag: [
2401
- tags.function(tags.variableName),
2402
- tags.labelName
2403
- ],
2404
- class: codeMark
2405
- },
2406
- {
2407
- tag: [
2408
- tags.monospace
2409
- ],
2410
- class: "font-mono"
2411
- },
2412
- // Headings.
2413
- {
2414
- tag: tags.heading1,
2415
- class: heading(1)
2416
- },
2417
- {
2418
- tag: tags.heading2,
2419
- class: heading(2)
2420
- },
2421
- {
2422
- tag: tags.heading3,
2423
- class: heading(3)
2424
- },
2425
- {
2426
- tag: tags.heading4,
2427
- class: heading(4)
2428
- },
2429
- {
2430
- tag: tags.heading5,
2431
- class: heading(5)
2432
- },
2433
- {
2434
- tag: tags.heading6,
2435
- class: heading(6)
2436
- },
2437
- // Emphasis.
2438
- {
2439
- tag: tags.emphasis,
2440
- class: italic
2441
- },
2442
- {
2443
- tag: tags.strong,
2444
- class: bold
2445
- },
2446
- {
2447
- tag: tags.strikethrough,
2448
- class: strikethrough
2449
- },
2450
- // Naked URLs.
2451
- {
2452
- tag: [
2453
- markdownTags.URL
2454
- ],
2455
- class: inlineUrl
2456
- },
2457
- // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
2458
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
2459
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
2460
- // the code, but all other CSS properties will be inherited.
2461
- // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
2462
- {
2463
- tag: [
2464
- markdownTags.CodeText,
2465
- markdownTags.InlineCode
2466
- ],
2467
- class: code
2468
- },
2469
- {
2470
- tag: [
2471
- markdownTags.QuoteMark
2472
- ],
2473
- class: blockquote
2474
- },
2475
- {
2476
- tag: [
2477
- markdownTags.TableCell
2478
- ],
2479
- 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
+ }
2480
2408
  }
2481
- ], {
2482
- scope: markdownLanguage2,
2483
- all: {
2484
- 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
+ };
2485
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
+ };
2486
2540
  });
2541
+ dispatch(state2.update(changes, {
2542
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2543
+ scrollIntoView: true
2544
+ }));
2545
+ return true;
2487
2546
  };
2488
-
2489
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2490
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2491
- return [
2492
- // Main extension.
2493
- // https://github.com/codemirror/lang-markdown
2494
- // https://codemirror.net/5/mode/markdown/index.html (demo).
2495
- markdown({
2496
- // GRM by default (vs strict CommonMark):
2497
- // Table, TaskList, Strikethrough, and Autolink.
2498
- // NOTE: This extends the parser; it doesn't affect rendering.
2499
- // https://github.github.com/gfm
2500
- // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
2501
- base: markdownLanguage3,
2502
- // Languages for syntax highlighting fenced code blocks.
2503
- codeLanguages: languages,
2504
- // Parser extensions.
2505
- extensions: [
2506
- // GFM provided by default.
2507
- markdownTagsExtensions
2508
- ]
2509
- }),
2510
- // https://github.com/codemirror/theme-one-dark
2511
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2512
- // Custom styles.
2513
- syntaxHighlighting(markdownHighlightStyle()),
2514
- keymap5.of([
2515
- // https://codemirror.net/docs/ref/#commands.indentWithTab
2516
- indentWithTab,
2517
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2518
- ...closeBracketsKeymap,
2519
- // https://codemirror.net/docs/ref/#commands.historyKeymap
2520
- ...historyKeymap,
2521
- // https://codemirror.net/docs/ref/#search.searchKeymap
2522
- ...searchKeymap,
2523
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2524
- ...standardKeymap
2525
- ])
2526
- ];
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);
2527
2552
  };
2528
-
2529
- // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
2530
- import { syntaxTree } from "@codemirror/language";
2531
- import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2532
- import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2533
- import { mx as mx2 } from "@dxos/react-ui-theme";
2534
- var HorizontalRuleWidget = class extends WidgetType3 {
2535
- toDOM() {
2536
- const el = document.createElement("span");
2537
- el.className = "cm-hr";
2538
- 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);
2539
2562
  }
2563
+ return node.from;
2540
2564
  };
2541
- var LinkButton = class extends WidgetType3 {
2542
- constructor(url, render) {
2543
- super();
2544
- this.url = url;
2545
- this.render = render;
2546
- }
2547
- eq(other) {
2548
- return this.url === other.url;
2549
- }
2550
- toDOM(view) {
2551
- const el = document.createElement("span");
2552
- this.render(el, this.url);
2553
- 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;
2554
2570
  }
2571
+ return node.to;
2555
2572
  };
2556
- var CheckboxWidget = class extends WidgetType3 {
2557
- constructor(_checked) {
2558
- super();
2559
- 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;
2560
2577
  }
2561
- eq(other) {
2562
- 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;
2563
2591
  }
2564
- toDOM(view) {
2565
- const input = document.createElement("input");
2566
- input.className = "cm-task-checkbox";
2567
- input.type = "checkbox";
2568
- input.checked = this._checked;
2569
- if (view.state.readOnly) {
2570
- input.setAttribute("disabled", "true");
2571
- } else {
2572
- input.onmousedown = (event) => {
2573
- const pos = view.posAtDOM(input);
2574
- const text = view.state.sliceDoc(pos, pos + 3);
2575
- if (text === (this._checked ? "[x]" : "[ ]")) {
2576
- view.dispatch({
2577
- changes: {
2578
- from: pos + 1,
2579
- to: pos + 2,
2580
- insert: this._checked ? " " : "x"
2581
- }
2582
- });
2583
- event.preventDefault();
2584
- }
2585
- };
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
+ }
2586
2637
  }
2587
- 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);
2588
2644
  }
2589
- ignoreEvent() {
2645
+ if (!changes) {
2590
2646
  return false;
2591
2647
  }
2648
+ dispatch(state2.update({
2649
+ changes,
2650
+ userEvent: "format.link.remove",
2651
+ scrollIntoView: true
2652
+ }));
2653
+ return true;
2592
2654
  };
2593
- var hide = Decoration5.replace({});
2594
- var fencedCodeLine = Decoration5.line({
2595
- class: mx2("cm-code cm-codeblock-line")
2596
- });
2597
- var fencedCodeLineFirst = Decoration5.line({
2598
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
2599
- });
2600
- var fencedCodeLineLast = Decoration5.line({
2601
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
2602
- });
2603
- var horizontalRule = Decoration5.replace({
2604
- widget: new HorizontalRuleWidget()
2605
- });
2606
- var checkedTask = Decoration5.replace({
2607
- widget: new CheckboxWidget(true)
2608
- });
2609
- var uncheckedTask = Decoration5.replace({
2610
- widget: new CheckboxWidget(false)
2611
- });
2612
- var editingRange = (state, range, focus) => {
2613
- const { readOnly, selection: { main: { head } } } = state;
2614
- return focus && !readOnly && head >= range.from && head <= range.to;
2615
- };
2616
- var MarksByParent = /* @__PURE__ */ new Set([
2617
- "CodeMark",
2618
- "EmphasisMark",
2619
- "StrikethroughMark",
2620
- "SubscriptMark",
2621
- "SuperscriptMark"
2622
- ]);
2623
- var buildDecorations = (view, options, focus) => {
2624
- const deco = new RangeSetBuilder2();
2625
- const atomicDeco = new RangeSetBuilder2();
2626
- const { state } = view;
2627
- for (const { from, to } of view.visibleRanges) {
2628
- 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({
2629
2661
  from,
2630
2662
  to,
2631
2663
  enter: (node) => {
2632
- if (node.name === "FencedCode") {
2633
- const editing = editingRange(state, node, focus);
2634
- for (const block of view.viewportLineBlocks) {
2635
- if (block.to < node.from) {
2636
- continue;
2637
- }
2638
- if (block.from > node.to) {
2639
- break;
2640
- }
2641
- const first = block.from <= node.from;
2642
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2643
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2644
- if (!editing && (first || last)) {
2645
- atomicDeco.add(block.from, block.to, hide);
2646
- }
2647
- }
2648
- return false;
2649
- } else if (node.name === "Link") {
2650
- const marks = node.node.getChildren("LinkMark");
2651
- const urlNode = node.node.getChild("URL");
2652
- const editing = editingRange(state, node, focus);
2653
- if (urlNode && marks.length >= 2) {
2654
- const url = state.sliceDoc(urlNode.from, urlNode.to);
2655
- if (!editing) {
2656
- atomicDeco.add(node.from, marks[0].to, hide);
2657
- }
2658
- deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2659
- tagName: "a",
2660
- attributes: {
2661
- class: "cm-link",
2662
- href: url,
2663
- rel: "noreferrer",
2664
- target: "_blank"
2665
- }
2666
- }));
2667
- if (!editing) {
2668
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2669
- widget: new LinkButton(url, options.renderLinkButton)
2670
- }) : 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);
2673
+ }
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);
2671
2679
  }
2672
- }
2673
- } else if (node.name === "HeaderMark") {
2674
- const parent = node.node.parent;
2675
- if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
2676
- const next = state.doc.sliceString(node.to, node.to + 1);
2677
- atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2678
- }
2679
- } else if (node.name === "HorizontalRule") {
2680
- if (!editingRange(state, node, focus)) {
2681
- deco.add(node.from, node.to, horizontalRule);
2682
- }
2683
- } else if (node.name === "TaskMarker") {
2684
- if (!editingRange(state, node, focus)) {
2685
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2686
- atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2687
- class: "cm-task"
2688
- }));
2689
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2690
- }
2691
- } else if (MarksByParent.has(node.name)) {
2692
- if (!editingRange(state, node.node.parent, focus)) {
2693
- atomicDeco.add(node.from, node.to, hide);
2694
2680
  }
2695
2681
  }
2696
2682
  }
2697
2683
  });
2698
- }
2699
- return {
2700
- deco: deco.finish(),
2701
- atomicDeco: atomicDeco.finish()
2702
- };
2703
- };
2704
- var forceUpdate = StateEffect4.define();
2705
- var decorateMarkdown = (options = {}) => {
2706
- return [
2707
- ViewPlugin4.fromClass(class {
2708
- constructor(view) {
2709
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2710
- }
2711
- update(update2) {
2712
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2713
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2714
- this.clearUpdate();
2715
- } else if (update2.selectionSet) {
2716
- this.scheduleUpdate(update2.view);
2717
- }
2718
- }
2719
- // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2720
- // the cursor will float in space or be in the wrong position after the decoration is applied.
2721
- scheduleUpdate(view) {
2722
- this.clearUpdate();
2723
- this.pendingUpdate = setTimeout(() => {
2724
- view.dispatch({
2725
- effects: forceUpdate.of(null)
2726
- });
2727
- }, options.selectionChangeDelay);
2728
- }
2729
- clearUpdate() {
2730
- if (this.pendingUpdate) {
2731
- clearTimeout(this.pendingUpdate);
2732
- 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;
2733
2717
  }
2718
+ changesAfter.push({
2719
+ from: after,
2720
+ insert: mark2
2721
+ });
2734
2722
  }
2735
- destroy() {
2736
- this.clearUpdate();
2737
- }
2738
- }, {
2739
- provide: (plugin) => [
2740
- EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2741
- EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2742
- EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2743
- ]
2744
- }),
2745
- formattingStyles
2746
- ];
2747
- };
2748
- var formattingStyles = EditorView12.baseTheme({
2749
- "& .cm-code": {
2750
- fontFamily: getToken("fontFamily.mono", []).join(",")
2751
- },
2752
- "& .cm-codeblock-line": {
2753
- paddingInline: "1rem !important"
2754
- },
2755
- "& .cm-codeblock-end": {
2756
- display: "inline-block",
2757
- width: "100%",
2758
- position: "relative",
2759
- "&::after": {
2760
- position: "absolute",
2761
- inset: 0,
2762
- content: '""'
2763
2723
  }
2764
- },
2765
- "& .cm-codeblock-first": {
2766
- borderTopLeftRadius: ".5rem",
2767
- borderTopRightRadius: ".5rem"
2768
- },
2769
- "& .cm-codeblock-last": {
2770
- borderBottomLeftRadius: ".5rem",
2771
- borderBottomRightRadius: ".5rem"
2772
- },
2773
- "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
2774
- background: getToken("extend.semanticColors.input.light"),
2775
- mixBlendMode: "darken"
2776
- },
2777
- "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
2778
- background: getToken("extend.semanticColors.input.dark"),
2779
- mixBlendMode: "lighten"
2780
- },
2781
- "& .cm-hr": {
2782
- display: "inline-block",
2783
- width: "100%",
2784
- height: "0",
2785
- verticalAlign: "middle",
2786
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2787
- },
2788
- "& .cm-task": {
2789
- color: getToken("extend.colors.blue.500")
2790
- },
2791
- "& .cm-task-checkbox": {
2792
- marginLeft: "4px",
2793
- 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;
2794
2739
  }
2795
- });
2796
-
2797
- // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2798
- import { snippet } from "@codemirror/autocomplete";
2799
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2800
- import { EditorSelection } from "@codemirror/state";
2801
- import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2802
- import { useState, useMemo } from "react";
2803
- 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;
2804
- var Inline;
2805
- (function(Inline2) {
2806
- Inline2[Inline2["Strong"] = 0] = "Strong";
2807
- Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2808
- Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2809
- Inline2[Inline2["Code"] = 3] = "Code";
2810
- })(Inline || (Inline = {}));
2811
- var List;
2812
- (function(List2) {
2813
- List2[List2["Ordered"] = 0] = "Ordered";
2814
- List2[List2["Bullet"] = 1] = "Bullet";
2815
- List2[List2["Task"] = 2] = "Task";
2816
- })(List || (List = {}));
2817
- var setHeading = (level) => ({ state, dispatch }) => {
2818
- const { selection: { ranges }, doc } = state;
2819
- const changes = [];
2820
- let prevBlock = -1;
2821
- for (const range of ranges) {
2822
- let sawBlock = false;
2823
- syntaxTree2(state).iterate({
2824
- from: range.from,
2825
- 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,
2826
2756
  enter: (node) => {
2827
- if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2828
- return;
2829
- }
2830
- sawBlock = true;
2831
- prevBlock = node.from;
2832
- const blockType = Textblocks[node.name];
2833
- const isHeading = /heading(\d)/.exec(blockType);
2834
- const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2835
- if (curLevel < 0 || curLevel === level) {
2836
- return;
2837
- }
2838
- if (curLevel === 0) {
2839
- changes.push({
2840
- from: node.from,
2841
- insert: "#".repeat(level) + " "
2842
- });
2843
- } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2844
- const nextLine = doc.lineAt(node.to);
2845
- if (level) {
2846
- changes.push({
2847
- from: node.from,
2848
- insert: "#".repeat(level) + " "
2849
- });
2850
- }
2851
- changes.push({
2852
- from: nextLine.from - 1,
2853
- to: nextLine.to
2854
- });
2855
- } else {
2856
- if (level === 0) {
2857
- changes.push({
2858
- from: node.from,
2859
- to: Math.min(node.to, node.from + curLevel + 1)
2860
- });
2861
- } else if (level < curLevel) {
2862
- changes.push({
2863
- from: node.from,
2864
- to: node.from + (curLevel - level)
2865
- });
2866
- } else {
2867
- changes.push({
2868
- from: node.from,
2869
- insert: "#".repeat(level - curLevel)
2870
- });
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;
2777
+ }
2778
+ if (node.from === lastBlock) {
2779
+ return;
2871
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;
2872
2795
  }
2873
2796
  }
2874
2797
  });
2875
- let line;
2876
- 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;
2877
2855
  changes.push({
2878
- from: line.from,
2879
- to: line.to,
2880
- insert: "#".repeat(level) + " "
2856
+ from: line.from + Math.min(open, column),
2857
+ insert: " ".repeat(mark2.length)
2881
2858
  });
2882
2859
  }
2883
2860
  }
2884
- if (!changes.length) {
2885
- 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
+ }
2886
2870
  }
2887
- const changeSet = state.changes(changes);
2888
- dispatch(state.update({
2871
+ const changeSet = state2.changes(changes);
2872
+ dispatch(state2.update({
2889
2873
  changes: changeSet,
2890
- selection: state.selection.map(changeSet, 1),
2891
- userEvent: "format.setHeading",
2874
+ selection: state2.selection.map(changeSet, 1),
2875
+ userEvent: "format.list.add",
2892
2876
  scrollIntoView: true
2893
2877
  }));
2894
2878
  return true;
2895
2879
  };
2896
- var setStyle = (type, enable) => ({ state, dispatch }) => {
2897
- const marker = inlineMarkerText(type);
2898
- const changes = state.changeByRange((range) => {
2899
- if (!enable && range.empty) {
2900
- const after = state.doc.sliceString(range.head, range.head + 6);
2901
- const found = after.indexOf(marker);
2902
- if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2903
- const before = state.doc.sliceString(range.head - 6, range.head);
2904
- if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2905
- ...before.slice(before.length - found)
2906
- ].reverse().join("") === after.slice(0, found)) {
2907
- return {
2908
- changes: [
2909
- {
2910
- from: range.head - marker.length - found,
2911
- to: range.head - found
2912
- },
2913
- {
2914
- from: range.head + found,
2915
- to: range.head + found + marker.length
2916
- }
2917
- ],
2918
- range: EditorSelection.cursor(range.from - marker.length)
2919
- };
2920
- }
2921
- }
2922
- }
2923
- const changes2 = [];
2924
- const changesAtEnd = [];
2925
- let blockStart = -1;
2926
- let blockEnd = -1;
2927
- let startCovered = false;
2928
- let endCovered = false;
2929
- let { from, to } = range;
2930
- 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({
2931
2887
  from,
2932
2888
  to,
2933
2889
  enter: (node) => {
2934
2890
  const { name } = node;
2935
- if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2936
- blockStart = blockContentStart(node);
2937
- blockEnd = blockContentEnd(node, state.doc);
2938
- startCovered = endCovered = false;
2939
- } else if (name === "Link" || name === "Image" && enable) {
2940
- if (from < node.from && to > node.from && to <= node.to) {
2941
- to = node.to;
2942
- } else if (to > node.to && from >= node.from && from < node.to) {
2943
- from = node.from;
2944
- }
2945
- } else if (IgnoreInline.has(name) && enable) {
2946
- if (node.from < from && node.to > from) {
2947
- if (to === from) {
2948
- to = node.to;
2949
- }
2950
- from = node.to;
2951
- }
2952
- if (node.from < to && node.to > to) {
2953
- to = node.from;
2954
- }
2955
- } else if (Object.hasOwn(InlineMarker, name)) {
2956
- const markType = InlineMarker[name];
2957
- const size = inlineMarkerText(markType).length;
2958
- const openEnd = node.from + size;
2959
- const closeStart = node.to - size;
2960
- if (markType === type) {
2961
- if (openEnd <= from && closeStart >= from) {
2962
- startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2963
- from: node.from,
2964
- to: openEnd
2965
- } : true;
2966
- }
2967
- if (openEnd <= to && closeStart >= to) {
2968
- endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2969
- from: closeStart,
2970
- to: node.to
2971
- } : true;
2972
- }
2973
- }
2974
- if (markType === type || type === 3 && enable) {
2975
- if (node.from >= from && openEnd <= to) {
2976
- changes2.push({
2977
- from: node.from,
2978
- to: openEnd
2979
- });
2980
- if (markType !== type && closeStart >= to) {
2981
- changesAtEnd.push({
2982
- from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
2983
- insert: inlineMarkerText(markType)
2984
- });
2985
- }
2986
- }
2987
- if (closeStart >= from && node.to <= to) {
2988
- changes2.push({
2989
- from: closeStart,
2990
- to: node.to
2991
- });
2992
- if (markType !== type && openEnd <= from) {
2993
- changes2.push({
2994
- from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
2995
- insert: inlineMarkerText(markType)
2996
- });
2997
- }
2998
- }
2999
- }
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";
3000
2895
  }
3001
2896
  },
3002
2897
  leave: (node) => {
3003
- if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
3004
- const rangeStart = Math.max(from, blockStart);
3005
- const rangeEnd = Math.min(to, blockEnd);
3006
- if (enable) {
3007
- if (!startCovered) {
3008
- changes2.push({
3009
- from: rangeStart,
3010
- insert: marker
3011
- });
3012
- }
3013
- if (!endCovered) {
3014
- changes2.push({
3015
- from: rangeEnd,
3016
- insert: marker
3017
- });
3018
- }
3019
- } else {
3020
- if (typeof startCovered === "object") {
3021
- changes2.push(startCovered);
3022
- } else if (startCovered) {
3023
- changes2.push({
3024
- from: skipSpaces(rangeStart, state.doc, -1, blockStart),
3025
- insert: marker
3026
- });
3027
- }
3028
- if (typeof endCovered === "object") {
3029
- changes2.push(endCovered);
3030
- } else if (endCovered) {
3031
- changes2.push({
3032
- from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
3033
- 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)
3034
2920
  });
3035
2921
  }
3036
2922
  }
2923
+ if (node.to >= to) {
2924
+ renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
2925
+ }
2926
+ return false;
3037
2927
  }
3038
2928
  }
3039
2929
  });
3040
- if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
3041
- return {
3042
- changes: {
3043
- from: range.head,
3044
- insert: marker + marker
3045
- },
3046
- range: EditorSelection.cursor(range.head + marker.length)
3047
- };
3048
- }
3049
- const changeSet = state.changes(changes2.concat(changesAtEnd));
3050
- return {
3051
- changes: changeSet,
3052
- range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
3053
- };
3054
- });
3055
- dispatch(state.update(changes, {
3056
- 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",
3057
2937
  scrollIntoView: true
3058
2938
  }));
3059
2939
  return true;
3060
2940
  };
3061
- var addStyle = (style) => setStyle(style, true);
3062
- var removeStyle = (style) => setStyle(style, false);
3063
- var toggleStyle = (style) => (arg) => {
3064
- const form = getFormatting(arg.state);
3065
- return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
3066
- };
3067
- var toggleStrong = toggleStyle(0);
3068
- var toggleEmphasis = toggleStyle(1);
3069
- var toggleStrikethrough = toggleStyle(2);
3070
- var toggleInlineCode = toggleStyle(3);
3071
- var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
3072
- var blockContentStart = (node) => {
3073
- const atx = /^ATXHeading(\d)/.exec(node.name);
3074
- if (atx) {
3075
- return Math.min(node.to, node.from + +atx[1] + 1);
3076
- }
3077
- return node.from;
3078
- };
3079
- var blockContentEnd = (node, doc) => {
3080
- const setext = /^SetextHeading(\d)/.exec(node.name);
3081
- const lastLine = doc.lineAt(node.to);
3082
- if (setext || /^[\s>]*$/.exec(lastLine.text)) {
3083
- return lastLine.from - 1;
3084
- }
3085
- return node.to;
3086
- };
3087
- var skipSpaces = (pos, doc, dir, limit) => {
3088
- const line = doc.lineAt(pos);
3089
- while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
3090
- pos += dir;
3091
- }
3092
- 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);
3093
2945
  };
3094
- var skipMarkers = (pos, tree, dir, limit) => {
3095
- for (; ; ) {
3096
- const next = tree.resolve(pos, dir);
3097
- if (!IgnoreInline.has(next.name)) {
3098
- return pos;
3099
- }
3100
- const moveTo = dir < 0 ? next.from : next.to;
3101
- if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
3102
- 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++;
3103
2961
  }
3104
- pos = moveTo;
3105
2962
  }
3106
2963
  };
3107
- var snippets = {
3108
- codeblock: snippet([
3109
- //
3110
- "```#{}",
3111
- "",
3112
- "```"
3113
- ].join("\n")),
3114
- table: snippet([
3115
- //
3116
- "| #{col1} | #{col2} |",
3117
- "| ---- | ---- |",
3118
- "| #{val1} | #{val2} |",
3119
- "| #{val3} | #{val4} |",
3120
- ""
3121
- ].join("\n"))
3122
- };
3123
- var insertTable = (view) => {
3124
- const { selection: { main }, doc } = view.state;
3125
- const { number } = doc.lineAt(main.anchor);
3126
- const { from } = doc.line(number);
3127
- snippets.table(view, null, from, from);
3128
- };
3129
- var removeLinkInner = (from, to, changes, state) => {
3130
- syntaxTree2(state).iterate({
3131
- from,
3132
- to,
3133
- enter: (node) => {
3134
- if (node.name === "Link" && node.from < to && node.to > from) {
3135
- node.node.cursor().iterate((node2) => {
3136
- const { name } = node2;
3137
- if (name === "LinkMark" || name === "LinkLabel") {
3138
- changes.push({
3139
- from: node2.from,
3140
- to: node2.to
3141
- });
3142
- } else if (name === "LinkTitle" || name === "URL") {
3143
- changes.push({
3144
- from: skipSpaces(node2.from, state.doc, -1),
3145
- to: skipSpaces(node2.to, state.doc, 1)
3146
- });
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;
3147
2976
  }
3148
- });
3149
- 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
+ }
3150
3002
  }
3003
+ });
3004
+ let line;
3005
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
3006
+ lines.push(line);
3151
3007
  }
3152
- });
3153
- };
3154
- var removeLink = ({ state, dispatch }) => {
3008
+ }
3155
3009
  const changes = [];
3156
- for (const { from, to } of state.selection.ranges) {
3157
- 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
+ }
3158
3025
  }
3159
- if (!changes) {
3026
+ if (!changes.length) {
3160
3027
  return false;
3161
3028
  }
3162
- dispatch(state.update({
3163
- changes,
3164
- 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",
3165
3034
  scrollIntoView: true
3166
3035
  }));
3167
3036
  return true;
3168
3037
  };
3169
- var addLink = ({ state, dispatch }) => {
3170
- const changes = state.changeByRange((range) => {
3171
- let { from, to } = range;
3172
- const cutStyles = [];
3173
- let okay = null;
3174
- 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({
3175
3059
  from,
3176
3060
  to,
3177
3061
  enter: (node) => {
3178
3062
  if (Object.hasOwn(Textblocks, node.name)) {
3179
- okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
3180
- } else if (Object.hasOwn(InlineMarker, node.name)) {
3181
- const sNode = node.node;
3182
- if (node.from < from && node.to <= to) {
3183
- if (sNode.firstChild.to === from) {
3184
- from = node.from;
3185
- } else {
3186
- cutStyles.push(sNode);
3187
- }
3188
- } else if (node.from >= from && node.to > to) {
3189
- if (sNode.lastChild.from === to) {
3190
- to = node.to;
3191
- } else {
3192
- cutStyles.push(sNode);
3193
- }
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);
3194
3069
  }
3195
3070
  }
3196
3071
  }
3197
3072
  });
3198
- if (okay === null) {
3199
- const line = state.doc.lineAt(from);
3200
- okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
3201
- }
3202
- if (!okay) {
3203
- return {
3204
- range
3205
- };
3206
- }
3207
- const changes2 = [];
3208
- const changesAfter = [];
3209
- removeLinkInner(from, to, changesAfter, state);
3210
- let cursorOffset = 1;
3211
- for (const style of cutStyles) {
3212
- const type = InlineMarker[style.name];
3213
- const mark2 = inlineMarkerText(type);
3214
- if (style.from < from) {
3215
- changes2.push({
3216
- from: skipSpaces(from, state.doc, -1),
3217
- insert: mark2
3218
- });
3219
- changesAfter.push({
3220
- from: skipSpaces(from, state.doc, 1, to),
3221
- insert: mark2
3222
- });
3223
- } else {
3224
- changes2.push({
3225
- from: skipSpaces(to, state.doc, -1, from),
3226
- insert: mark2
3227
- });
3228
- const after = skipSpaces(to, state.doc, 1);
3229
- if (after === to) {
3230
- cursorOffset += mark2.length;
3231
- }
3232
- changesAfter.push({
3233
- from: after,
3234
- insert: mark2
3235
- });
3236
- }
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
+ });
3237
3080
  }
3238
- changes2.push({
3239
- from,
3240
- insert: "["
3241
- }, {
3242
- from: to,
3243
- insert: "]()"
3244
- });
3245
- const changeSet = state.changes(changes2.concat(changesAfter));
3246
- return {
3247
- changes: changeSet,
3248
- range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
3249
- };
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
+ ];
3250
3097
  });
3251
- if (changes.changes.empty) {
3252
- return false;
3253
- }
3254
- dispatch(state.update(changes, {
3255
- userEvent: "format.link.add",
3098
+ dispatch(state2.update({
3099
+ changes,
3100
+ userEvent: "format.codeblock.add",
3256
3101
  scrollIntoView: true
3257
3102
  }));
3258
3103
  return true;
3259
3104
  };
3260
- var addList = (type) => ({ state, dispatch }) => {
3105
+ var removeCodeblock = ({ state: state2, dispatch }) => {
3106
+ const changes = [];
3261
3107
  let lastBlock = -1;
3262
- let counter = 1;
3263
- let first = true;
3264
- let parentColumn = null;
3265
- const blocks = [];
3266
- for (const { from, to } of state.selection.ranges) {
3267
- syntaxTree2(state).iterate({
3108
+ for (const { from, to } of state2.selection.ranges) {
3109
+ syntaxTree(state2).iterate({
3268
3110
  from,
3269
3111
  to,
3270
3112
  enter: (node) => {
3271
- if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
3272
- if (first) {
3273
- let before = node.node.prevSibling;
3274
- while (before && /Mark$/.test(before.name)) {
3275
- 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
+ });
3276
3127
  }
3277
- if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
3278
- const item = before.lastChild;
3279
- const itemLine = state.doc.lineAt(item.from);
3280
- const itemText = itemLine.text.slice(item.from - itemLine.from);
3281
- parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
3282
- if (type === 0) {
3283
- const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
3284
- if (mark2) {
3285
- parentColumn += mark2[1].length;
3286
- counter = +mark2[1] + 1;
3287
- }
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;
3288
3137
  }
3289
3138
  }
3290
- first = false;
3291
- }
3292
- if (node.from === lastBlock) {
3293
- return;
3294
3139
  }
3295
- lastBlock = node.from;
3296
- blocks.push({
3297
- node: node.node,
3298
- counter,
3299
- parentColumn
3300
- });
3301
- counter++;
3302
- return false;
3303
- }
3304
- },
3305
- leave: (node) => {
3306
- if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
3307
- counter = 1;
3308
- parentColumn = null;
3309
3140
  }
3310
3141
  }
3311
3142
  });
3312
3143
  }
3313
- if (!blocks.length) {
3314
- const { from, to } = state.doc.lineAt(state.selection.main.anchor);
3315
- if (from === to) {
3316
- const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
3317
- dispatch(state.update({
3318
- changes: [
3319
- {
3320
- from,
3321
- insert
3322
- }
3323
- ],
3324
- selection: {
3325
- anchor: from + insert.length
3326
- },
3327
- userEvent: "format.list.add",
3328
- scrollIntoView: true
3329
- }));
3330
- return true;
3331
- }
3144
+ if (!changes.length) {
3332
3145
  return false;
3333
3146
  }
3334
- const changes = [];
3335
- for (let i = 0; i < blocks.length; i++) {
3336
- const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
3337
- const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
3338
- let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
3339
- if (type === 0) {
3340
- 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;
3341
3222
  }
3342
- let line = state.doc.lineAt(nodeFrom);
3343
- const column = nodeFrom - line.from;
3344
- if (parentColumn2 !== null && parentColumn2 > column) {
3345
- padding = Math.max(padding, parentColumn2 - column);
3223
+ upto = Math.min(upto, currentBlock.end);
3224
+ if (upto <= currentBlock.pos) {
3225
+ return;
3346
3226
  }
3347
- let mark2;
3348
- if (type === 0) {
3349
- let max = counter2;
3350
- for (let j = i + 1; j < blocks.length; j++) {
3351
- if (blocks[j].counter !== max + 1) {
3352
- break;
3353
- }
3354
- 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;
3355
3234
  }
3356
- const num = String(counter2);
3357
- padding = Math.max(String(max).length, padding);
3358
- mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3359
- } else {
3360
- mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3361
- }
3362
- changes.push({
3363
- from: nodeFrom,
3364
- insert: mark2
3365
- });
3366
- while (line.to < node.to) {
3367
- line = state.doc.lineAt(line.to + 1);
3368
- const open = /^[\s>]*/.exec(line.text)[0].length;
3369
- changes.push({
3370
- from: line.from + Math.min(open, column),
3371
- insert: " ".repeat(mark2.length)
3372
- });
3373
3235
  }
3374
- }
3375
- if (type === 0) {
3376
- const last = blocks[blocks.length - 1];
3377
- let next = last.node.nextSibling;
3378
- while (next && /Mark$/.test(next.name)) {
3379
- 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);
3380
3241
  }
3381
- if (next?.name === "OrderedList") {
3382
- 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
+ }
3383
3266
  }
3384
- }
3385
- const changeSet = state.changes(changes);
3386
- dispatch(state.update({
3387
- changes: changeSet,
3388
- selection: state.selection.map(changeSet, 1),
3389
- userEvent: "format.list.add",
3390
- scrollIntoView: true
3391
- }));
3392
- return true;
3393
- };
3394
- var removeList = (type) => ({ state, dispatch }) => {
3395
- let lastBlock = -1;
3396
- const changes = [];
3397
- const stack = [];
3398
- const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3399
- for (const { from, to } of state.selection.ranges) {
3400
- syntaxTree2(state).iterate({
3401
- from,
3402
- to,
3267
+ syntaxTree(state2).iterate({
3268
+ from: range.from,
3269
+ to: range.to,
3403
3270
  enter: (node) => {
3271
+ advanceInline(node.from);
3404
3272
  const { name } = node;
3405
3273
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3406
3274
  stack.push(name);
3407
- } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3408
- 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);
3409
3307
  }
3410
3308
  },
3411
3309
  leave: (node) => {
3310
+ advanceInline(node.to);
3412
3311
  const { name } = node;
3413
3312
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3414
3313
  stack.pop();
3415
- } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3416
- lastBlock = node.from;
3417
- let line = state.doc.lineAt(node.from);
3418
- const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3419
- if (!mark2) {
3420
- return false;
3421
- }
3422
- const column = node.from - line.from;
3423
- changes.push({
3424
- from: node.from,
3425
- to: node.from + mark2[0].length
3426
- });
3427
- while (line.to < node.to) {
3428
- line = state.doc.lineAt(line.to + 1);
3429
- const open = /^[\s>]*/.exec(line.text)[0].length;
3430
- if (open > column) {
3431
- changes.push({
3432
- from: line.from + column,
3433
- to: line.from + Math.min(column + mark2[0].length, open)
3434
- });
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";
3435
3322
  }
3436
3323
  }
3437
- if (node.to >= to) {
3438
- renumberListItems(node.node.nextSibling, 1, changes, state.doc);
3324
+ if (blockQuote === null) {
3325
+ blockQuote = hasQuote;
3326
+ } else if (!hasQuote && blockQuote) {
3327
+ blockQuote = false;
3439
3328
  }
3440
- 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;
3441
3337
  }
3442
3338
  }
3443
3339
  });
3444
3340
  }
3445
- if (!changes.length) {
3446
- return false;
3447
- }
3448
- dispatch(state.update({
3449
- changes,
3450
- userEvent: "format.list.remove",
3451
- scrollIntoView: true
3452
- }));
3453
- return true;
3454
- };
3455
- var toggleList = (type) => (target) => {
3456
- const formatting = getFormatting(target.state);
3457
- const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
3458
- 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
+ };
3459
3354
  };
3460
- var renumberListItems = (item, counter, changes, doc) => {
3461
- for (; item; item = item.nextSibling) {
3462
- if (item.name === "ListItem") {
3463
- const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
3464
- if (!number || +number[2] === counter) {
3465
- break;
3466
- }
3467
- const size = number[1].length + number[2].length;
3468
- const newNum = String(counter);
3469
- changes.push({
3470
- from: item.from + Math.max(0, size - newNum.length),
3471
- to: item.from + size,
3472
- 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;
3473
3365
  });
3474
- counter++;
3475
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;
3476
3417
  }
3418
+ requestAnimationFrame(() => {
3419
+ if (!view.hasFocus) {
3420
+ view.focus();
3421
+ }
3422
+ });
3477
3423
  };
3478
- var setBlockquote = (enable) => ({ state, dispatch }) => {
3479
- const lines = [];
3480
- let lastBlock = -1;
3481
- for (const { from, to } of state.selection.ranges) {
3482
- const sawBlock = false;
3483
- syntaxTree2(state).iterate({
3484
- from,
3485
- to,
3486
- enter: (node) => {
3487
- if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
3488
- if (node.from === lastBlock) {
3489
- return false;
3490
- }
3491
- lastBlock = node.from;
3492
- let line2 = state.doc.lineAt(node.from);
3493
- if (line2.number > 1) {
3494
- const prevLine = state.doc.line(line2.number - 1);
3495
- if (/^[>\s]*$/.test(prevLine.text)) {
3496
- if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
3497
- lines.push(prevLine);
3498
- }
3499
- }
3500
- }
3501
- for (; ; ) {
3502
- lines.push(line2);
3503
- if (line2.to >= node.to) {
3504
- break;
3505
- }
3506
- line2 = state.doc.line(line2.number + 1);
3507
- }
3508
- if (!enable && line2.number < state.doc.lines) {
3509
- const nextLine = state.doc.line(line2.number + 1);
3510
- if (/^[>\s]*$/.test(nextLine.text)) {
3511
- lines.push(nextLine);
3512
- }
3513
- }
3514
- return false;
3515
- }
3516
- }
3517
- });
3518
- let line;
3519
- if (!sawBlock && enable && from === to && !/\S/.test((line = state.doc.lineAt(from)).text)) {
3520
- 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"
3521
3614
  }
3522
- }
3523
- const changes = [];
3524
- for (const line of lines) {
3525
- if (enable) {
3526
- changes.push({
3527
- from: line.from,
3528
- insert: /\S/.test(line.text) ? "> " : ">"
3529
- });
3530
- } else {
3531
- const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
3532
- if (quote) {
3533
- changes.push({
3534
- from: line.from + quote[1].length,
3535
- to: line.from + quote[0].length
3536
- });
3537
- }
3615
+ ], {
3616
+ scope: markdownLanguage2,
3617
+ all: {
3618
+ fontFamily: getToken("fontFamily.body", []).join(",")
3538
3619
  }
3539
- }
3540
- if (!changes.length) {
3541
- return false;
3542
- }
3543
- const changeSet = state.changes(changes);
3544
- dispatch(state.update({
3545
- changes: changeSet,
3546
- selection: state.selection.map(changeSet, 1),
3547
- userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3548
- scrollIntoView: true
3549
- }));
3550
- return true;
3620
+ });
3551
3621
  };
3552
- var addBlockquote = setBlockquote(true);
3553
- var removeBlockquote = setBlockquote(false);
3554
- var toggleBlockquote = (target) => {
3555
- 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
+ ];
3556
3657
  };
3557
- var addCodeblock = (target) => {
3558
- const { state, dispatch } = target;
3559
- const { selection } = state;
3560
- if (selection.ranges.length === 1 && selection.main.empty) {
3561
- const { head } = selection.main;
3562
- const line = state.doc.lineAt(head);
3563
- if (!/\S/.test(line.text) && head === line.from) {
3564
- snippets.codeblock(target, null, line.from, line.to);
3565
- return true;
3566
- }
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;
3567
3669
  }
3568
- const ranges = [];
3569
- for (const { from, to } of selection.ranges) {
3570
- let blockFrom = from;
3571
- let blockTo = to;
3572
- syntaxTree2(state).iterate({
3573
- from,
3574
- to,
3575
- enter: (node) => {
3576
- if (Object.hasOwn(Textblocks, node.name)) {
3577
- if (from >= node.from && to <= node.to) {
3578
- blockFrom = node.from;
3579
- blockTo = node.to;
3580
- } else {
3581
- blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
3582
- blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
3583
- }
3584
- }
3585
- }
3586
- });
3587
- if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
3588
- ranges[ranges.length - 1].to = blockTo;
3589
- } else {
3590
- ranges.push({
3591
- from: blockFrom,
3592
- to: blockTo
3593
- });
3594
- }
3670
+ };
3671
+ var LinkButton = class extends WidgetType3 {
3672
+ constructor(url, render) {
3673
+ super();
3674
+ this.url = url;
3675
+ this.render = render;
3595
3676
  }
3596
- if (!ranges.length) {
3597
- return false;
3677
+ eq(other) {
3678
+ return this.url === other.url;
3598
3679
  }
3599
- const changes = ranges.map(({ from, to }) => {
3600
- const column = from - state.doc.lineAt(from).from;
3601
- return [
3602
- {
3603
- from,
3604
- insert: "```\n" + " ".repeat(column)
3605
- },
3606
- {
3607
- from: to,
3608
- insert: "\n" + " ".repeat(column) + "```"
3609
- }
3610
- ];
3611
- });
3612
- dispatch(state.update({
3613
- changes,
3614
- userEvent: "format.codeblock.add",
3615
- scrollIntoView: true
3616
- }));
3617
- return true;
3618
- };
3619
- var removeCodeblock = ({ state, dispatch }) => {
3620
- const changes = [];
3621
- let lastBlock = -1;
3622
- for (const { from, to } of state.selection.ranges) {
3623
- syntaxTree2(state).iterate({
3624
- from,
3625
- to,
3626
- enter: (node) => {
3627
- if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3628
- lastBlock = node.from;
3629
- const firstLine = state.doc.lineAt(node.from);
3630
- if (node.name === "FencedCode") {
3631
- changes.push({
3632
- from: node.from,
3633
- to: firstLine.to + 1 + node.from - firstLine.from
3634
- });
3635
- const lastLine = state.doc.lineAt(node.to);
3636
- if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3637
- changes.push({
3638
- from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3639
- to: lastLine.to
3640
- });
3641
- }
3642
- } else {
3643
- const column = node.from - firstLine.from;
3644
- for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
3645
- changes.push({
3646
- from: line.from + column - 4,
3647
- to: line.from + column
3648
- });
3649
- if (line.to >= node.to) {
3650
- break;
3651
- }
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"
3652
3711
  }
3653
- }
3712
+ });
3713
+ event.preventDefault();
3654
3714
  }
3655
- }
3656
- });
3715
+ };
3716
+ }
3717
+ return input;
3657
3718
  }
3658
- if (!changes.length) {
3719
+ ignoreEvent() {
3659
3720
  return false;
3660
3721
  }
3661
- dispatch(state.update({
3662
- changes,
3663
- userEvent: "format.codeblock.remove",
3664
- scrollIntoView: true
3665
- }));
3666
- return true;
3667
- };
3668
- var toggleCodeblock = (target) => {
3669
- return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3670
- };
3671
- var formattingKeymap = (options = {}) => {
3672
- return [
3673
- keymap6.of([
3674
- {
3675
- key: "meta-b",
3676
- run: toggleStrong
3677
- }
3678
- ])
3679
- ];
3680
3722
  };
3681
- var InlineMarker = {
3682
- Emphasis: 1,
3683
- StrongEmphasis: 0,
3684
- InlineCode: 3,
3685
- 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;
3686
3745
  };
3687
- var IgnoreInline = /* @__PURE__ */ new Set([
3688
- "Autolink",
3746
+ var MarksByParent = /* @__PURE__ */ new Set([
3689
3747
  "CodeMark",
3690
- "CodeText",
3691
- "Comment",
3692
3748
  "EmphasisMark",
3693
- "Hardbreak",
3694
- "HeaderMark",
3695
- "HTMLTag",
3696
- "LinkMark",
3697
- "ListMark",
3698
- "ProcessingInstruction",
3699
- "QuoteMark",
3700
3749
  "StrikethroughMark",
3701
3750
  "SubscriptMark",
3702
- "SuperscriptMark",
3703
- "TaskMarker"
3751
+ "SuperscriptMark"
3704
3752
  ]);
3705
- var Textblocks = {
3706
- ATXHeading1: "heading1",
3707
- ATXHeading2: "heading2",
3708
- ATXHeading3: "heading3",
3709
- ATXHeading4: "heading4",
3710
- ATXHeading5: "heading5",
3711
- ATXHeading6: "heading6",
3712
- CodeBlock: "codeblock",
3713
- FencedCode: "codeblock",
3714
- Paragraph: "paragraph",
3715
- SetextHeading1: "heading1",
3716
- SetextHeading2: "heading2",
3717
- TableCell: "tablecell",
3718
- Task: "paragraph"
3719
- };
3720
- var getFormatting = (state) => {
3721
- let blockType = null;
3722
- const inline = [
3723
- null,
3724
- null,
3725
- null,
3726
- null
3727
- ];
3728
- let link = false;
3729
- let blockQuote = null;
3730
- let listStyle = null;
3731
- const stack = [];
3732
- let currentBlock = null;
3733
- const advanceInline = (upto) => {
3734
- if (!currentBlock) {
3735
- return;
3736
- }
3737
- upto = Math.min(upto, currentBlock.end);
3738
- if (upto <= currentBlock.pos) {
3739
- return;
3740
- }
3741
- for (let i = 0; i < currentBlock.active.length; i++) {
3742
- if (inline[i] === false) {
3743
- continue;
3744
- } else if (currentBlock.active[i]) {
3745
- inline[i] = true;
3746
- } else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
3747
- inline[i] = false;
3748
- }
3749
- }
3750
- currentBlock.pos = upto;
3751
- };
3752
- const skipInline = (upto) => {
3753
- if (currentBlock && upto > currentBlock.pos) {
3754
- currentBlock.pos = Math.min(upto, currentBlock.end);
3755
- }
3756
- };
3757
- const { selection } = state;
3758
- for (const range of selection.ranges) {
3759
- if (range.empty && inline.some((v) => v === null)) {
3760
- const contextSize = Math.min(range.head, 6);
3761
- const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
3762
- let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
3763
- for (let i = 0; i < contextSize; i++) {
3764
- const ch = contextAfter[i];
3765
- if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3766
- contextAfter = contextAfter.slice(0, i);
3767
- break;
3768
- }
3769
- }
3770
- for (let i = 0; i < inline.length; i++) {
3771
- const mark2 = inlineMarkerText(i);
3772
- const found = contextAfter.indexOf(mark2);
3773
- if (found > -1) {
3774
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3775
- if (inline[i] === null) {
3776
- inline[i] = true;
3777
- }
3778
- }
3779
- }
3780
- }
3781
- syntaxTree2(state).iterate({
3782
- from: range.from,
3783
- 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,
3784
3761
  enter: (node) => {
3785
- advanceInline(node.from);
3786
- const { name } = node;
3787
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3788
- stack.push(name);
3789
- } else if (name === "Link") {
3790
- link = true;
3791
- } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3792
- if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3793
- stack[stack.length - 1] = "TaskList";
3794
- }
3795
- const blockCode = Textblocks[name];
3796
- if (blockType === null) {
3797
- blockType = blockCode;
3798
- } else if (blockType !== blockCode) {
3799
- blockType = false;
3800
- }
3801
- if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3802
- currentBlock = {
3803
- pos: Math.max(range.from, node.from),
3804
- end: Math.min(range.to, node.to),
3805
- active: [
3806
- false,
3807
- false,
3808
- false,
3809
- false
3810
- ]
3811
- };
3812
- }
3813
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3814
- const index = InlineMarker[name];
3815
- if (range.empty && inline[index] === null) {
3816
- 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
+ }
3817
3777
  }
3818
- currentBlock.active[index] = true;
3819
- } else if (IgnoreInline.has(name)) {
3820
- skipInline(node.to);
3821
- }
3822
- },
3823
- leave: (node) => {
3824
- advanceInline(node.to);
3825
- const { name } = node;
3826
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3827
- stack.pop();
3828
- } else if (Object.hasOwn(Textblocks, name)) {
3829
- let hasList = false;
3830
- let hasQuote = false;
3831
- for (let i = stack.length - 1; i >= 0; i--) {
3832
- if (stack[i] === "Blockquote") {
3833
- hasQuote = true;
3834
- } else if (!hasList) {
3835
- 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);
3836
3801
  }
3837
3802
  }
3838
- if (blockQuote === null) {
3839
- blockQuote = hasQuote;
3840
- } else if (!hasQuote && blockQuote) {
3841
- 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);
3842
3808
  }
3843
- if (listStyle === null) {
3844
- listStyle = hasList;
3845
- } else if (listStyle !== hasList) {
3846
- 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);
3847
3824
  }
3848
- currentBlock = null;
3849
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3850
- currentBlock.active[InlineMarker[name]] = false;
3851
3825
  }
3852
3826
  }
3853
3827
  });
3854
3828
  }
3855
- const { from, to } = state.doc.lineAt(selection.main.anchor);
3856
- const blankLine = from === to;
3857
3829
  return {
3858
- blankLine,
3859
- blockType: blockType || null,
3860
- blockQuote: blockQuote ?? false,
3861
- code: inline[3] ?? false,
3862
- emphasis: inline[1] ?? false,
3863
- strong: inline[0] ?? false,
3864
- strikethrough: inline[2] ?? false,
3865
- link,
3866
- listStyle: listStyle || null
3830
+ deco: deco.finish(),
3831
+ atomicDeco: atomicDeco.finish()
3867
3832
  };
3868
3833
  };
3869
- var useFormattingState = () => {
3870
- const [state, setState] = useState(null);
3871
- const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3872
- if (update2.docChanged || update2.selectionSet) {
3873
- const newState = getFormatting(update2.state);
3874
- if (!state || !compareFormatting(state, newState)) {
3875
- setState(newState);
3876
- }
3877
- }
3878
- }), []);
3834
+ var forceUpdate = StateEffect4.define();
3835
+ var decorateMarkdown = (options = {}) => {
3879
3836
  return [
3880
- state,
3881
- 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
3882
3876
  ];
3883
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
+ });
3884
3926
 
3885
3927
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3886
3928
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3887
3929
  import { StateField as StateField5 } from "@codemirror/state";
3888
- 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";
3889
3931
  var image = (options = {}) => {
3890
3932
  return StateField5.define({
3891
- create: (state) => {
3892
- return Decoration6.set(buildDecorations2(0, state.doc.length, state));
3933
+ create: (state2) => {
3934
+ return Decoration6.set(buildDecorations2(0, state2.doc.length, state2));
3893
3935
  },
3894
3936
  update: (value, tr) => {
3895
3937
  if (!tr.docChanged && !tr.selection) {
@@ -3912,7 +3954,7 @@ var image = (options = {}) => {
3912
3954
  add: buildDecorations2(from, to, tr.state)
3913
3955
  });
3914
3956
  },
3915
- provide: (field) => EditorView14.decorations.from(field)
3957
+ provide: (field) => EditorView13.decorations.from(field)
3916
3958
  });
3917
3959
  };
3918
3960
  var preloaded = /* @__PURE__ */ new Set();
@@ -3923,16 +3965,16 @@ var preloadImage = (url) => {
3923
3965
  preloaded.add(url);
3924
3966
  }
3925
3967
  };
3926
- var buildDecorations2 = (from, to, state) => {
3968
+ var buildDecorations2 = (from, to, state2) => {
3927
3969
  const decorations = [];
3928
- const cursor = state.selection.main.head;
3929
- syntaxTree3(state).iterate({
3970
+ const cursor = state2.selection.main.head;
3971
+ syntaxTree3(state2).iterate({
3930
3972
  enter: (node) => {
3931
3973
  if (node.name === "Image") {
3932
3974
  const urlNode = node.node.getChild("URL");
3933
3975
  if (urlNode) {
3934
- const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3935
- 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);
3936
3978
  preloadImage(url);
3937
3979
  decorations.push(Decoration6.replace({
3938
3980
  block: true,
@@ -3962,6 +4004,8 @@ var ImageWidget = class extends WidgetType4 {
3962
4004
  return img;
3963
4005
  }
3964
4006
  };
4007
+ var imageUpload = (options = {}) => {
4008
+ };
3965
4009
 
3966
4010
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3967
4011
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -4000,24 +4044,24 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
4000
4044
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4001
4045
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4002
4046
  import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
4003
- 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";
4004
4048
  var table = (options = {}) => {
4005
4049
  return StateField6.define({
4006
- create: (state) => update(state, options),
4050
+ create: (state2) => update(state2, options),
4007
4051
  update: (_, tr) => update(tr.state, options),
4008
- provide: (field) => EditorView15.decorations.from(field)
4052
+ provide: (field) => EditorView14.decorations.from(field)
4009
4053
  });
4010
4054
  };
4011
- var update = (state, options) => {
4055
+ var update = (state2, options) => {
4012
4056
  const builder = new RangeSetBuilder3();
4013
- const cursor = state.selection.main.head;
4057
+ const cursor = state2.selection.main.head;
4014
4058
  const tables = [];
4015
4059
  const getTable = () => tables[tables.length - 1];
4016
4060
  const getRow = () => {
4017
4061
  const table2 = getTable();
4018
4062
  return table2.rows?.[table2.rows.length - 1];
4019
4063
  };
4020
- syntaxTree5(state).iterate({
4064
+ syntaxTree5(state2).iterate({
4021
4065
  enter: (node) => {
4022
4066
  switch (node.name) {
4023
4067
  case "Table": {
@@ -4038,9 +4082,9 @@ var update = (state, options) => {
4038
4082
  case "TableCell": {
4039
4083
  const row = getRow();
4040
4084
  if (row) {
4041
- row.push(state.sliceDoc(node.from, node.to));
4085
+ row.push(state2.sliceDoc(node.from, node.to));
4042
4086
  } else {
4043
- getTable().header?.push(state.sliceDoc(node.from, node.to));
4087
+ getTable().header?.push(state2.sliceDoc(node.from, node.to));
4044
4088
  }
4045
4089
  break;
4046
4090
  }
@@ -4048,7 +4092,7 @@ var update = (state, options) => {
4048
4092
  }
4049
4093
  });
4050
4094
  tables.forEach((table2) => {
4051
- const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
4095
+ const hide2 = state2.readOnly || cursor < table2.from || cursor > table2.to;
4052
4096
  if (hide2) {
4053
4097
  builder.add(table2.from, table2.to, Decoration7.replace({
4054
4098
  widget: new TableWidget(table2)
@@ -4099,8 +4143,8 @@ var TableWidget = class extends WidgetType5 {
4099
4143
 
4100
4144
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4101
4145
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4102
- import { log as log8 } from "@dxos/log";
4103
- 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";
4104
4148
  var mention = ({ onSearch }) => {
4105
4149
  return autocompletion2({
4106
4150
  // TODO(burdon): Not working.
@@ -4112,10 +4156,10 @@ var mention = ({ onSearch }) => {
4112
4156
  icons: false,
4113
4157
  override: [
4114
4158
  (context) => {
4115
- log8.info("completion context", {
4159
+ log6.info("completion context", {
4116
4160
  context
4117
4161
  }, {
4118
- F: __dxlog_file10,
4162
+ F: __dxlog_file8,
4119
4163
  L: 26,
4120
4164
  S: void 0,
4121
4165
  C: (f, a) => f(...a)
@@ -4137,25 +4181,35 @@ var mention = ({ onSearch }) => {
4137
4181
 
4138
4182
  // packages/ui/react-ui-editor/src/extensions/modes.ts
4139
4183
  import { Facet as Facet6 } from "@codemirror/state";
4140
- import { keymap as keymap7 } from "@codemirror/view";
4184
+ import { keymap as keymap8 } from "@codemirror/view";
4141
4185
  import { vim } from "@replit/codemirror-vim";
4186
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
4187
+ var focusEvent = "focus.container";
4142
4188
  var editorMode = Facet6.define({
4143
4189
  combine: (modes) => modes[0] ?? {}
4144
4190
  });
4145
4191
  var EditorModes = {
4146
4192
  default: [],
4193
+ vscode: [
4194
+ // https://github.com/replit/codemirror-vscode-keymap
4195
+ editorMode.of({
4196
+ type: "vscode"
4197
+ }),
4198
+ keymap8.of(vscodeKeymap)
4199
+ ],
4147
4200
  vim: [
4201
+ // https://github.com/replit/codemirror-vim
4148
4202
  vim(),
4149
4203
  editorMode.of({
4150
4204
  type: "vim",
4151
4205
  noTabster: true
4152
4206
  }),
4153
- keymap7.of([
4207
+ keymap8.of([
4154
4208
  {
4155
4209
  key: "Alt-Escape",
4156
4210
  run: (view) => {
4157
4211
  view.dispatch({
4158
- userEvent: "focus.container"
4212
+ userEvent: focusEvent
4159
4213
  });
4160
4214
  return true;
4161
4215
  }
@@ -4164,8 +4218,95 @@ var EditorModes = {
4164
4218
  ]
4165
4219
  };
4166
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
+
4167
4308
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4168
- import { keymap as keymap8 } from "@codemirror/view";
4309
+ import { keymap as keymap10 } from "@codemirror/view";
4169
4310
  var defaultItems = [
4170
4311
  "hello world!",
4171
4312
  "this is a test.",
@@ -4175,7 +4316,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4175
4316
  let t;
4176
4317
  let idx = 0;
4177
4318
  return [
4178
- keymap8.of([
4319
+ keymap10.of([
4179
4320
  {
4180
4321
  // Reset.
4181
4322
  key: "alt-meta-'",
@@ -4222,9 +4363,11 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4222
4363
  };
4223
4364
 
4224
4365
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4225
- 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";
4226
4367
  var instanceCount = 0;
4227
- 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) => {
4228
4371
  const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4229
4372
  const tabsterDOMAttribute = useFocusableGroup({
4230
4373
  tabBehavior: "limited"
@@ -4243,24 +4386,24 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4243
4386
  autoFocus
4244
4387
  ]);
4245
4388
  useEffect2(() => {
4246
- log9("updating", {
4389
+ log7("create", {
4247
4390
  id,
4248
4391
  instanceId
4249
4392
  }, {
4250
- F: __dxlog_file11,
4393
+ F: __dxlog_file10,
4251
4394
  L: 88,
4252
4395
  S: void 0,
4253
4396
  C: (f, a) => f(...a)
4254
4397
  });
4255
- const state = EditorState2.create({
4398
+ const state2 = EditorState2.create({
4256
4399
  doc,
4257
4400
  selection,
4258
4401
  extensions: [
4259
4402
  id && documentId2.of(id),
4260
4403
  // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4261
4404
  EditorView16.exceptionSink.of((err) => {
4262
- log9.catch(err, void 0, {
4263
- F: __dxlog_file11,
4405
+ log7.catch(err, void 0, {
4406
+ F: __dxlog_file10,
4264
4407
  L: 101,
4265
4408
  S: void 0,
4266
4409
  C: (f, a) => f(...a)
@@ -4269,16 +4412,16 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4269
4412
  // Focus.
4270
4413
  EditorView16.updateListener.of((update2) => {
4271
4414
  update2.transactions.forEach((transaction) => {
4272
- if (transaction.isUserEvent("focus.container")) {
4415
+ if (transaction.isUserEvent(focusEvent)) {
4273
4416
  rootRef.current?.focus();
4274
4417
  }
4275
4418
  });
4276
4419
  }),
4277
4420
  extensions
4278
- ].filter(isNotFalsy3)
4421
+ ].filter(isNotFalsy4)
4279
4422
  });
4280
4423
  const view2 = new EditorView16({
4281
- state,
4424
+ state: state2,
4282
4425
  parent: rootRef.current,
4283
4426
  scrollTo,
4284
4427
  // NOTE: Uncomment to debug/monitor all transactions.
@@ -4290,21 +4433,29 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4290
4433
  view3.update(trs);
4291
4434
  }
4292
4435
  });
4293
- setView(view2);
4294
- if (moveToEndOfLine) {
4436
+ if (moveToEndOfLine && !(scrollTo || selection)) {
4295
4437
  const { to } = view2.state.doc.lineAt(0);
4296
- view2?.dispatch({
4438
+ view2.dispatch({
4297
4439
  selection: {
4298
4440
  anchor: to
4299
4441
  }
4300
4442
  });
4301
4443
  }
4302
- if (state.facet(editorMode).noTabster) {
4444
+ if (state2.facet(editorMode).noTabster) {
4303
4445
  rootRef.current?.removeAttribute("data-tabster");
4304
4446
  }
4447
+ setView(view2);
4305
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
+ });
4306
4458
  view2?.destroy();
4307
- setView(null);
4308
4459
  };
4309
4460
  }, [
4310
4461
  doc,
@@ -4334,9 +4485,10 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4334
4485
  });
4335
4486
 
4336
4487
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4337
- 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";
4338
4489
  import { createContext } from "@radix-ui/react-context";
4339
- 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";
4340
4492
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4341
4493
  import { getSize } from "@dxos/react-ui-theme";
4342
4494
  var tooltipProps = {
@@ -4353,10 +4505,10 @@ var HeadingIcons = {
4353
4505
  "6": TextHSix
4354
4506
  };
4355
4507
  var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
4356
- var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4508
+ var ToolbarRoot = ({ children, onAction, classNames, state: state2 }) => {
4357
4509
  return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
4358
4510
  onAction,
4359
- state
4511
+ state: state2
4360
4512
  }, /* @__PURE__ */ React2.createElement(DensityProvider, {
4361
4513
  density: "fine"
4362
4514
  }, /* @__PURE__ */ React2.createElement(ElevationProvider, {
@@ -4370,7 +4522,7 @@ var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4370
4522
  };
4371
4523
  var buttonStyles = "min-bs-0 p-2";
4372
4524
  var iconStyles = getSize(5);
4373
- var ToolbarButton = ({ Icon, children, ...props }) => {
4525
+ var ToolbarToggleButton = ({ Icon, children, ...props }) => {
4374
4526
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4375
4527
  asChild: true
4376
4528
  }, /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroupItem, {
@@ -4383,14 +4535,27 @@ var ToolbarButton = ({ Icon, children, ...props }) => {
4383
4535
  className: "sr-only"
4384
4536
  }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4385
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
+ };
4386
4551
  var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
4387
4552
  role: "separator",
4388
4553
  className: "grow"
4389
4554
  });
4390
4555
  var MarkdownHeading = () => {
4391
4556
  const { t } = useTranslation(translationKey);
4392
- const { onAction, state } = useToolbarContext("MarkdownFormatting");
4393
- const blockType = state ? state.blockType : "paragraph";
4557
+ const { onAction, state: state2 } = useToolbarContext("MarkdownFormatting");
4558
+ const blockType = state2 ? state2.blockType : "paragraph";
4394
4559
  const header = blockType && /heading(\d)/.exec(blockType);
4395
4560
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4396
4561
  const HeadingIcon = HeadingIcons[value ?? "0"];
@@ -4464,43 +4629,43 @@ var markdownStyles = [
4464
4629
  {
4465
4630
  type: "strong",
4466
4631
  Icon: TextB,
4467
- getState: (state) => state.strong
4632
+ getState: (state2) => state2.strong
4468
4633
  },
4469
4634
  {
4470
4635
  type: "emphasis",
4471
4636
  Icon: TextItalic,
4472
- getState: (state) => state.emphasis
4637
+ getState: (state2) => state2.emphasis
4473
4638
  },
4474
4639
  {
4475
4640
  type: "strikethrough",
4476
4641
  Icon: TextStrikethrough,
4477
- getState: (state) => state.strikethrough
4642
+ getState: (state2) => state2.strikethrough
4478
4643
  },
4479
4644
  {
4480
4645
  type: "code",
4481
4646
  Icon: Code,
4482
- getState: (state) => state.code
4647
+ getState: (state2) => state2.code
4483
4648
  },
4484
4649
  {
4485
4650
  type: "link",
4486
4651
  Icon: Link,
4487
- getState: (state) => state.link
4652
+ getState: (state2) => state2.link
4488
4653
  }
4489
4654
  ];
4490
4655
  var MarkdownStyles = () => {
4491
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4656
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4492
4657
  const { t } = useTranslation(translationKey);
4493
4658
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4494
4659
  type: "multiple",
4495
- value: markdownStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)
4496
- }, 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, {
4497
4662
  key: type,
4498
4663
  value: type,
4499
4664
  Icon,
4500
- disabled: state?.blockType === "codeblock",
4501
- onClick: state ? () => onAction?.({
4665
+ disabled: state2?.blockType === "codeblock",
4666
+ onClick: state2 ? () => onAction?.({
4502
4667
  type,
4503
- data: !getState(state)
4668
+ data: !getState(state2)
4504
4669
  }) : void 0
4505
4670
  }, t(`${type} label`))));
4506
4671
  };
@@ -4508,32 +4673,32 @@ var markdownLists = [
4508
4673
  {
4509
4674
  type: "list-bullet",
4510
4675
  Icon: ListBullets,
4511
- getState: (state) => state.listStyle === "bullet"
4676
+ getState: (state2) => state2.listStyle === "bullet"
4512
4677
  },
4513
4678
  {
4514
4679
  type: "list-ordered",
4515
4680
  Icon: ListNumbers,
4516
- getState: (state) => state.listStyle === "ordered"
4681
+ getState: (state2) => state2.listStyle === "ordered"
4517
4682
  },
4518
4683
  {
4519
4684
  type: "list-task",
4520
4685
  Icon: ListChecks,
4521
- getState: (state) => state.listStyle === "task"
4686
+ getState: (state2) => state2.listStyle === "task"
4522
4687
  }
4523
4688
  ];
4524
4689
  var MarkdownLists = () => {
4525
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4690
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4526
4691
  const { t } = useTranslation(translationKey);
4527
4692
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4528
4693
  type: "single",
4529
- value: state?.listStyle ? `list-${state.listStyle}` : ""
4530
- }, 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, {
4531
4696
  key: type,
4532
4697
  value: type,
4533
4698
  Icon,
4534
- onClick: state ? () => onAction?.({
4699
+ onClick: state2 ? () => onAction?.({
4535
4700
  type,
4536
- data: !getState(state)
4701
+ data: !getState(state2)
4537
4702
  }) : void 0
4538
4703
  }, t(`${type} label`))));
4539
4704
  };
@@ -4541,111 +4706,106 @@ var markdownBlocks = [
4541
4706
  {
4542
4707
  type: "blockquote",
4543
4708
  Icon: Quotes,
4544
- getState: (state) => state.blockQuote
4709
+ getState: (state2) => state2.blockQuote
4545
4710
  },
4546
4711
  {
4547
4712
  type: "codeblock",
4548
4713
  Icon: CodeBlock,
4549
- getState: (state) => state.blockType === "codeblock"
4714
+ getState: (state2) => state2.blockType === "codeblock"
4550
4715
  },
4551
4716
  {
4552
4717
  type: "table",
4553
4718
  Icon: Table2,
4554
- getState: (state) => state.blockType === "tablecell",
4555
- disabled: (state) => !state.blankLine
4719
+ getState: (state2) => state2.blockType === "tablecell",
4720
+ disabled: (state2) => !state2.blankLine
4556
4721
  }
4557
4722
  ];
4558
4723
  var MarkdownBlocks = () => {
4559
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4724
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4560
4725
  const { t } = useTranslation(translationKey);
4561
- const value = markdownBlocks.find(({ getState }) => state && getState(state));
4726
+ const value = markdownBlocks.find(({ getState }) => state2 && getState(state2));
4562
4727
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4563
4728
  type: "single",
4564
4729
  value: value?.type ?? ""
4565
- }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4730
+ }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4566
4731
  key: type,
4567
4732
  value: type,
4568
4733
  Icon,
4569
- disabled: !state || disabled?.(state),
4570
- onClick: state ? () => onAction?.({
4734
+ disabled: !state2 || disabled?.(state2),
4735
+ onClick: state2 ? () => onAction?.({
4571
4736
  type,
4572
- data: !getState(state)
4737
+ data: !getState(state2)
4573
4738
  }) : void 0
4574
4739
  }, t(`${type} label`))));
4575
4740
  };
4576
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));
4577
- var MarkdownExtended = () => {
4742
+ var MarkdownCustom = ({ onUpload } = {}) => {
4578
4743
  const { onAction } = useToolbarContext("MarkdownStyles");
4579
4744
  const { t } = useTranslation(translationKey);
4580
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4581
- asChild: true
4582
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4583
- 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,
4584
4791
  "data-testid": "editor.toolbar.comment",
4585
4792
  onClick: () => onAction?.({
4586
4793
  type: "comment"
4587
- }),
4588
- classNames: buttonStyles
4589
- }, /* @__PURE__ */ React2.createElement(ChatText, {
4590
- className: iconStyles
4591
- }), /* @__PURE__ */ React2.createElement("span", {
4592
- className: "sr-only"
4593
- }, 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")));
4594
4796
  };
4595
4797
  var Toolbar = {
4596
4798
  Root: ToolbarRoot,
4597
- Button: ToolbarButton,
4799
+ Button: ToolbarToggleButton,
4598
4800
  Separator: ToolbarSeparator,
4599
4801
  Markdown: MarkdownStandard,
4600
- Extended: MarkdownExtended
4802
+ Custom: MarkdownCustom,
4803
+ Actions: MarkdownActions
4601
4804
  };
4602
4805
 
4603
4806
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4604
4807
  var useActionHandler = (view) => {
4605
- return (action) => {
4606
- if (!view) {
4607
- return;
4608
- }
4609
- let inlineType, listType;
4610
- switch (action.type) {
4611
- case "heading":
4612
- setHeading(parseInt(action.data))(view);
4613
- break;
4614
- case "strong":
4615
- case "emphasis":
4616
- case "strikethrough":
4617
- case "code":
4618
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4619
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4620
- break;
4621
- case "list-ordered":
4622
- case "list-bullet":
4623
- case "list-task":
4624
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4625
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4626
- break;
4627
- case "blockquote":
4628
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4629
- break;
4630
- case "codeblock":
4631
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4632
- break;
4633
- case "table":
4634
- insertTable(view);
4635
- break;
4636
- case "link":
4637
- (action.data === false ? removeLink : addLink)(view);
4638
- break;
4639
- case "comment":
4640
- createComment(view);
4641
- break;
4642
- }
4643
- setTimeout(() => {
4644
- if (!view.hasFocus) {
4645
- view.focus();
4646
- }
4647
- });
4648
- };
4808
+ return (action) => view && processAction(view, action);
4649
4809
  };
4650
4810
 
4651
4811
  // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
@@ -4664,28 +4824,36 @@ var useDocAccessor = (text) => {
4664
4824
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4665
4825
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4666
4826
  import { EditorView as EditorView17 } from "@codemirror/view";
4667
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4668
- import { log as log10 } from "@dxos/log";
4669
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4670
- 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";
4671
4831
  var useTextEditor = (cb = () => ({}), deps = []) => {
4672
4832
  let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4673
4833
  const onUpdate = useRef3();
4674
4834
  const [view, setView] = useState4();
4675
4835
  const parentRef = useRef3(null);
4676
- useEffect3(() => {
4836
+ useEffect4(() => {
4677
4837
  let view2;
4678
4838
  if (parentRef.current) {
4679
- 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({
4680
4848
  doc,
4681
4849
  selection,
4682
4850
  extensions: [
4683
4851
  id && documentId2.of(id),
4684
4852
  // TODO(burdon): Doesn't catch errors in keymap functions.
4685
4853
  EditorView17.exceptionSink.of((err) => {
4686
- log10.catch(err, void 0, {
4687
- F: __dxlog_file12,
4688
- L: 44,
4854
+ log8.catch(err, void 0, {
4855
+ F: __dxlog_file11,
4856
+ L: 46,
4689
4857
  S: void 0,
4690
4858
  C: (f, a) => f(...a)
4691
4859
  });
@@ -4694,12 +4862,12 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4694
4862
  EditorView17.updateListener.of(() => {
4695
4863
  onUpdate.current?.();
4696
4864
  })
4697
- ].filter(isNotFalsy4)
4865
+ ].filter(isNotFalsy5)
4698
4866
  });
4699
4867
  view2 = new EditorView17({
4700
4868
  parent: parentRef.current,
4701
4869
  scrollTo,
4702
- state,
4870
+ state: state2,
4703
4871
  // NOTE: Uncomment to debug/monitor all transactions.
4704
4872
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4705
4873
  dispatchTransactions: (trs, view3) => {
@@ -4712,10 +4880,18 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4712
4880
  setView(view2);
4713
4881
  }
4714
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
+ });
4715
4891
  view2?.destroy();
4716
4892
  };
4717
4893
  }, deps);
4718
- useEffect3(() => {
4894
+ useEffect4(() => {
4719
4895
  if (view) {
4720
4896
  if (!selection && !view.state.selection.main.anchor) {
4721
4897
  selection = EditorSelection2.single(view.state.doc.line(1).to);
@@ -4774,36 +4950,39 @@ export {
4774
4950
  callbackWrapper,
4775
4951
  command,
4776
4952
  comments,
4777
- compareFormatting,
4778
4953
  createBasicExtensions,
4779
4954
  createComment,
4780
4955
  createDataExtensions,
4781
4956
  createMarkdownExtensions,
4782
4957
  createThemeExtensions,
4783
- cursorLineMargin,
4784
4958
  decorateMarkdown,
4785
4959
  defaultOptions,
4786
4960
  defaultTheme,
4787
- dnd,
4788
4961
  documentId2 as documentId,
4962
+ dropFile,
4789
4963
  editorFillLayoutEditor,
4790
4964
  editorFillLayoutRoot,
4791
4965
  editorMode,
4792
4966
  editorWithToolbarLayout,
4793
4967
  focusComment,
4968
+ focusEvent,
4969
+ formattingEquals,
4794
4970
  formattingKeymap,
4795
4971
  getFormatting,
4796
4972
  getToken,
4797
4973
  image,
4974
+ imageUpload,
4798
4975
  insertTable,
4799
- keymap9 as keymap,
4976
+ keymap11 as keymap,
4800
4977
  linkTooltip,
4801
4978
  listener,
4979
+ localStorageStateStoreAdapter,
4802
4980
  logChanges,
4803
4981
  markdownHighlightStyle,
4804
4982
  markdownTags,
4805
4983
  markdownTagsExtensions,
4806
4984
  mention,
4985
+ processAction,
4807
4986
  removeBlockquote,
4808
4987
  removeCodeblock,
4809
4988
  removeLink,
@@ -4814,6 +4993,7 @@ export {
4814
4993
  setHeading,
4815
4994
  setSelection,
4816
4995
  setStyle,
4996
+ state,
4817
4997
  table,
4818
4998
  tags2 as tags,
4819
4999
  toggleBlockquote,