@dxos/react-ui-editor 0.4.8-main.8cb190e → 0.4.8-main.8ec301e

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 (57) hide show
  1. package/dist/lib/browser/index.mjs +527 -305
  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 +11 -5
  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/factories.d.ts +3 -0
  16. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  17. package/dist/types/src/extensions/index.d.ts +1 -0
  18. package/dist/types/src/extensions/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  20. package/dist/types/src/extensions/markdown/formatting.d.ts +7 -4
  21. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  22. package/dist/types/src/extensions/markdown/image.d.ts +6 -0
  23. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  24. package/dist/types/src/extensions/modes.d.ts +2 -1
  25. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  26. package/dist/types/src/extensions/state.d.ts +20 -0
  27. package/dist/types/src/extensions/state.d.ts.map +1 -0
  28. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  29. package/dist/types/src/hooks/useDocAccessor.d.ts +4 -2
  30. package/dist/types/src/hooks/useDocAccessor.d.ts.map +1 -1
  31. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  32. package/dist/types/src/hooks/useTextEditor.stories.d.ts +1 -0
  33. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
  34. package/dist/types/src/themes/default.d.ts.map +1 -1
  35. package/dist/types/src/translations.d.ts +1 -0
  36. package/dist/types/src/translations.d.ts.map +1 -1
  37. package/package.json +26 -24
  38. package/src/components/TextEditor/TextEditor.stories.tsx +16 -4
  39. package/src/components/TextEditor/TextEditor.tsx +13 -12
  40. package/src/components/Toolbar/Toolbar.stories.tsx +23 -24
  41. package/src/components/Toolbar/Toolbar.tsx +101 -35
  42. package/src/extensions/automerge/automerge.ts +4 -4
  43. package/src/extensions/comments.ts +2 -2
  44. package/src/extensions/factories.ts +30 -7
  45. package/src/extensions/index.ts +1 -0
  46. package/src/extensions/markdown/bundle.ts +8 -11
  47. package/src/extensions/markdown/formatting.test.ts +13 -13
  48. package/src/extensions/markdown/formatting.ts +95 -87
  49. package/src/extensions/markdown/image.ts +6 -0
  50. package/src/extensions/modes.ts +11 -2
  51. package/src/extensions/state.ts +90 -0
  52. package/src/hooks/useActionHandler.ts +5 -1
  53. package/src/hooks/useDocAccessor.ts +8 -2
  54. package/src/hooks/useTextEditor.stories.tsx +3 -3
  55. package/src/hooks/useTextEditor.ts +3 -0
  56. package/src/themes/default.ts +24 -8
  57. package/src/translations.ts +1 -0
@@ -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,18 +24,18 @@ 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";
30
31
 
31
32
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
32
33
  import { EditorState as EditorState2 } from "@codemirror/state";
33
- import { EditorView as EditorView16 } from "@codemirror/view";
34
+ import { EditorView as EditorView17 } 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
37
  import { log as log9 } from "@dxos/log";
37
- import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
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);
123
+ ], (state2) => {
124
+ const annotations2 = state2.field(annotationsState);
124
125
  const decorations = annotations2.map((annotation2) => {
125
- const range = Cursor.getRangeFromCursor(state, annotation2.cursor);
126
+ const range = Cursor.getRangeFromCursor(state2, annotation2.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 {};
@@ -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
  }
@@ -1864,11 +1865,11 @@ var cursorLineMargin = (options = {
1864
1865
  if (!lines || lines <= 0) {
1865
1866
  return [];
1866
1867
  }
1867
- return EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1868
+ return EditorView8.updateListener.of(({ transactions, state: state2, selectionSet, startState, view }) => {
1868
1869
  if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1869
1870
  return;
1870
1871
  }
1871
- const s = state;
1872
+ const s = state2;
1872
1873
  const { main } = s.selection;
1873
1874
  const rect = view.dom.getBoundingClientRect();
1874
1875
  const viewportTop = rect.top - view.documentTop;
@@ -1951,11 +1952,12 @@ var dnd = () => {
1951
1952
  };
1952
1953
 
1953
1954
  // packages/ui/react-ui-editor/src/extensions/factories.ts
1954
- import { closeBrackets } from "@codemirror/autocomplete";
1955
- import { history } from "@codemirror/commands";
1955
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1956
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1956
1957
  import { bracketMatching } from "@codemirror/language";
1958
+ import { searchKeymap } from "@codemirror/search";
1957
1959
  import { EditorState } from "@codemirror/state";
1958
- import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1960
+ import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, highlightSpecialChars, keymap as keymap5, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1959
1961
  import defaultsDeep2 from "lodash.defaultsdeep";
1960
1962
  import { generateName } from "@dxos/display-name";
1961
1963
  import { log as log7 } from "@dxos/log";
@@ -2163,41 +2165,59 @@ var defaultTheme = {
2163
2165
  * </div>
2164
2166
  * </div
2165
2167
  */
2166
- ".cm-panels": {
2167
- border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
2168
- },
2168
+ ".cm-panels": {},
2169
2169
  ".cm-panel": {
2170
- background: get3(tokens, "extend.colors.neutral.50"),
2171
2170
  fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2172
2171
  },
2172
+ ".cm-panel input[type=checkbox]": {
2173
+ marginRight: "0.4rem !important"
2174
+ },
2175
+ "&light .cm-panel": {
2176
+ background: get3(tokens, "extend.colors.neutral.50")
2177
+ },
2178
+ "&dark .cm-panel": {
2179
+ background: get3(tokens, "extend.colors.neutral.850")
2180
+ },
2173
2181
  ".cm-button": {
2174
2182
  margin: "4px",
2175
2183
  fontFamily: get3(tokens, "fontFamily.body", []).join(","),
2176
- background: get3(tokens, "extend.colors.neutral.100"),
2177
2184
  backgroundImage: "none",
2178
2185
  border: "none",
2186
+ "&:active": {
2187
+ backgroundImage: "none"
2188
+ }
2189
+ },
2190
+ "&light .cm-button": {
2191
+ background: get3(tokens, "extend.colors.neutral.100"),
2179
2192
  "&:hover": {
2180
2193
  background: get3(tokens, "extend.colors.neutral.200")
2181
2194
  },
2182
2195
  "&:active": {
2183
- background: get3(tokens, "extend.colors.neutral.300"),
2184
- backgroundImage: "none"
2196
+ background: get3(tokens, "extend.colors.neutral.300")
2185
2197
  }
2186
2198
  },
2187
- ".cm-panel input[type=checkbox]": {
2188
- marginRight: "0.4rem !important"
2199
+ "&dark .cm-button": {
2200
+ background: get3(tokens, "extend.colors.neutral.800"),
2201
+ "&:hover": {
2202
+ background: get3(tokens, "extend.colors.neutral.700")
2203
+ },
2204
+ "&:active": {
2205
+ background: get3(tokens, "extend.colors.neutral.600")
2206
+ }
2189
2207
  }
2190
2208
  };
2191
2209
 
2192
2210
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2193
2211
  var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2194
2212
  var defaults = {
2213
+ allowMultipleSelections: true,
2195
2214
  bracketMatching: true,
2196
2215
  closeBrackets: true,
2197
2216
  drawSelection: true,
2198
2217
  editable: true,
2199
2218
  history: true,
2200
- lineWrapping: true
2219
+ lineWrapping: true,
2220
+ search: true
2201
2221
  };
2202
2222
  var createBasicExtensions = (_props) => {
2203
2223
  const props = defaultsDeep2({}, _props, defaults);
@@ -2206,7 +2226,7 @@ var createBasicExtensions = (_props) => {
2206
2226
  EditorView10.exceptionSink.of((err) => {
2207
2227
  log7.catch(err, void 0, {
2208
2228
  F: __dxlog_file9,
2209
- L: 78,
2229
+ L: 83,
2210
2230
  S: void 0,
2211
2231
  C: (f, a) => f(...a)
2212
2232
  });
@@ -2218,6 +2238,7 @@ var createBasicExtensions = (_props) => {
2218
2238
  props.dropCursor && dropCursor(),
2219
2239
  props.drawSelection && drawSelection(),
2220
2240
  props.highlightActiveLine && highlightActiveLine(),
2241
+ highlightSpecialChars(),
2221
2242
  props.history && history(),
2222
2243
  props.lineNumbers && lineNumbers(),
2223
2244
  props.lineWrapping && EditorView10.lineWrapping,
@@ -2227,7 +2248,20 @@ var createBasicExtensions = (_props) => {
2227
2248
  EditorView10.editable.of(false)
2228
2249
  ],
2229
2250
  props.scrollPastEnd && scrollPastEnd(),
2230
- props.tabSize && EditorState.tabSize.of(props.tabSize)
2251
+ props.tabSize && EditorState.tabSize.of(props.tabSize),
2252
+ // https://codemirror.net/docs/ref/#view.KeyBinding
2253
+ keymap5.of([
2254
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
2255
+ ...standardKeymap,
2256
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
2257
+ props.indentWithTab && indentWithTab,
2258
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2259
+ ...props.closeBrackets ? closeBracketsKeymap : [],
2260
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
2261
+ ...props.history ? historyKeymap : [],
2262
+ // https://codemirror.net/docs/ref/#search.searchKeymap
2263
+ ...props.search ? searchKeymap : []
2264
+ ].filter(isNotFalsy2))
2231
2265
  ].filter(isNotFalsy2);
2232
2266
  };
2233
2267
  var defaultSlots = {
@@ -2286,14 +2320,14 @@ var listener = ({ onFocus, onChange }) => {
2286
2320
  };
2287
2321
 
2288
2322
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2289
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2290
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2323
+ import { completionKeymap as completionKeymap2 } from "@codemirror/autocomplete";
2324
+ import { defaultKeymap, indentWithTab as indentWithTab2 } from "@codemirror/commands";
2291
2325
  import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2292
2326
  import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2293
2327
  import { languages } from "@codemirror/language-data";
2294
- import { searchKeymap } from "@codemirror/search";
2328
+ import { lintKeymap } from "@codemirror/lint";
2295
2329
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2296
- import { keymap as keymap5 } from "@codemirror/view";
2330
+ import { keymap as keymap6 } from "@codemirror/view";
2297
2331
 
2298
2332
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2299
2333
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -2508,17 +2542,13 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
2508
2542
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2509
2543
  // Custom styles.
2510
2544
  syntaxHighlighting(markdownHighlightStyle()),
2511
- keymap5.of([
2545
+ keymap6.of([
2512
2546
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2513
- indentWithTab,
2514
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2515
- ...closeBracketsKeymap,
2516
- // https://codemirror.net/docs/ref/#commands.historyKeymap
2517
- ...historyKeymap,
2518
- // https://codemirror.net/docs/ref/#search.searchKeymap
2519
- ...searchKeymap,
2520
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2521
- ...standardKeymap
2547
+ indentWithTab2,
2548
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
2549
+ ...defaultKeymap,
2550
+ ...completionKeymap2,
2551
+ ...lintKeymap
2522
2552
  ])
2523
2553
  ];
2524
2554
  };
@@ -2606,8 +2636,8 @@ var checkedTask = Decoration5.replace({
2606
2636
  var uncheckedTask = Decoration5.replace({
2607
2637
  widget: new CheckboxWidget(false)
2608
2638
  });
2609
- var editingRange = (state, range, focus) => {
2610
- const { readOnly, selection: { main: { head } } } = state;
2639
+ var editingRange = (state2, range, focus) => {
2640
+ const { readOnly, selection: { main: { head } } } = state2;
2611
2641
  return focus && !readOnly && head >= range.from && head <= range.to;
2612
2642
  };
2613
2643
  var MarksByParent = /* @__PURE__ */ new Set([
@@ -2620,14 +2650,14 @@ var MarksByParent = /* @__PURE__ */ new Set([
2620
2650
  var buildDecorations = (view, options, focus) => {
2621
2651
  const deco = new RangeSetBuilder2();
2622
2652
  const atomicDeco = new RangeSetBuilder2();
2623
- const { state } = view;
2653
+ const { state: state2 } = view;
2624
2654
  for (const { from, to } of view.visibleRanges) {
2625
- syntaxTree(state).iterate({
2655
+ syntaxTree(state2).iterate({
2626
2656
  from,
2627
2657
  to,
2628
2658
  enter: (node) => {
2629
2659
  if (node.name === "FencedCode") {
2630
- const editing = editingRange(state, node, focus);
2660
+ const editing = editingRange(state2, node, focus);
2631
2661
  for (const block of view.viewportLineBlocks) {
2632
2662
  if (block.to < node.from) {
2633
2663
  continue;
@@ -2636,7 +2666,7 @@ var buildDecorations = (view, options, focus) => {
2636
2666
  break;
2637
2667
  }
2638
2668
  const first = block.from <= node.from;
2639
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2669
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
2640
2670
  deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2641
2671
  if (!editing && (first || last)) {
2642
2672
  atomicDeco.add(block.from, block.to, hide);
@@ -2646,9 +2676,9 @@ var buildDecorations = (view, options, focus) => {
2646
2676
  } else if (node.name === "Link") {
2647
2677
  const marks = node.node.getChildren("LinkMark");
2648
2678
  const urlNode = node.node.getChild("URL");
2649
- const editing = editingRange(state, node, focus);
2679
+ const editing = editingRange(state2, node, focus);
2650
2680
  if (urlNode && marks.length >= 2) {
2651
- const url = state.sliceDoc(urlNode.from, urlNode.to);
2681
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
2652
2682
  if (!editing) {
2653
2683
  atomicDeco.add(node.from, marks[0].to, hide);
2654
2684
  }
@@ -2669,24 +2699,24 @@ var buildDecorations = (view, options, focus) => {
2669
2699
  }
2670
2700
  } else if (node.name === "HeaderMark") {
2671
2701
  const parent = node.node.parent;
2672
- if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
2673
- const next = state.doc.sliceString(node.to, node.to + 1);
2702
+ if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
2703
+ const next = state2.doc.sliceString(node.to, node.to + 1);
2674
2704
  atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2675
2705
  }
2676
2706
  } else if (node.name === "HorizontalRule") {
2677
- if (!editingRange(state, node, focus)) {
2707
+ if (!editingRange(state2, node, focus)) {
2678
2708
  deco.add(node.from, node.to, horizontalRule);
2679
2709
  }
2680
2710
  } else if (node.name === "TaskMarker") {
2681
- if (!editingRange(state, node, focus)) {
2682
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2711
+ if (!editingRange(state2, node, focus)) {
2712
+ const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
2683
2713
  atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2684
2714
  class: "cm-task"
2685
2715
  }));
2686
2716
  atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2687
2717
  }
2688
2718
  } else if (MarksByParent.has(node.name)) {
2689
- if (!editingRange(state, node.node.parent, focus)) {
2719
+ if (!editingRange(state2, node.node.parent, focus)) {
2690
2720
  atomicDeco.add(node.from, node.to, hide);
2691
2721
  }
2692
2722
  }
@@ -2795,9 +2825,9 @@ var formattingStyles = EditorView12.baseTheme({
2795
2825
  import { snippet } from "@codemirror/autocomplete";
2796
2826
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2797
2827
  import { EditorSelection } from "@codemirror/state";
2798
- import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2799
- import { useState, useMemo } from "react";
2800
- var compareFormatting = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
2828
+ import { EditorView as EditorView13, keymap as keymap7 } from "@codemirror/view";
2829
+ import { useMemo, useState } from "react";
2830
+ 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;
2801
2831
  var Inline;
2802
2832
  (function(Inline2) {
2803
2833
  Inline2[Inline2["Strong"] = 0] = "Strong";
@@ -2811,13 +2841,13 @@ var List;
2811
2841
  List2[List2["Bullet"] = 1] = "Bullet";
2812
2842
  List2[List2["Task"] = 2] = "Task";
2813
2843
  })(List || (List = {}));
2814
- var setHeading = (level) => ({ state, dispatch }) => {
2815
- const { selection: { ranges }, doc } = state;
2844
+ var setHeading = (level) => ({ state: state2, dispatch }) => {
2845
+ const { selection: { ranges }, doc } = state2;
2816
2846
  const changes = [];
2817
2847
  let prevBlock = -1;
2818
2848
  for (const range of ranges) {
2819
2849
  let sawBlock = false;
2820
- syntaxTree2(state).iterate({
2850
+ syntaxTree2(state2).iterate({
2821
2851
  from: range.from,
2822
2852
  to: range.to,
2823
2853
  enter: (node) => {
@@ -2870,7 +2900,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
2870
2900
  }
2871
2901
  });
2872
2902
  let line;
2873
- if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state.doc.lineAt(range.from)).text)) {
2903
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2874
2904
  changes.push({
2875
2905
  from: line.from,
2876
2906
  to: line.to,
@@ -2881,23 +2911,23 @@ var setHeading = (level) => ({ state, dispatch }) => {
2881
2911
  if (!changes.length) {
2882
2912
  return false;
2883
2913
  }
2884
- const changeSet = state.changes(changes);
2885
- dispatch(state.update({
2914
+ const changeSet = state2.changes(changes);
2915
+ dispatch(state2.update({
2886
2916
  changes: changeSet,
2887
- selection: state.selection.map(changeSet, 1),
2917
+ selection: state2.selection.map(changeSet, 1),
2888
2918
  userEvent: "format.setHeading",
2889
2919
  scrollIntoView: true
2890
2920
  }));
2891
2921
  return true;
2892
2922
  };
2893
- var setStyle = (type, enable) => ({ state, dispatch }) => {
2923
+ var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2894
2924
  const marker = inlineMarkerText(type);
2895
- const changes = state.changeByRange((range) => {
2925
+ const changes = state2.changeByRange((range) => {
2896
2926
  if (!enable && range.empty) {
2897
- const after = state.doc.sliceString(range.head, range.head + 6);
2927
+ const after = state2.doc.sliceString(range.head, range.head + 6);
2898
2928
  const found = after.indexOf(marker);
2899
2929
  if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2900
- const before = state.doc.sliceString(range.head - 6, range.head);
2930
+ const before = state2.doc.sliceString(range.head - 6, range.head);
2901
2931
  if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2902
2932
  ...before.slice(before.length - found)
2903
2933
  ].reverse().join("") === after.slice(0, found)) {
@@ -2924,14 +2954,14 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2924
2954
  let startCovered = false;
2925
2955
  let endCovered = false;
2926
2956
  let { from, to } = range;
2927
- syntaxTree2(state).iterate({
2957
+ syntaxTree2(state2).iterate({
2928
2958
  from,
2929
2959
  to,
2930
2960
  enter: (node) => {
2931
2961
  const { name } = node;
2932
2962
  if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2933
2963
  blockStart = blockContentStart(node);
2934
- blockEnd = blockContentEnd(node, state.doc);
2964
+ blockEnd = blockContentEnd(node, state2.doc);
2935
2965
  startCovered = endCovered = false;
2936
2966
  } else if (name === "Link" || name === "Image" && enable) {
2937
2967
  if (from < node.from && to > node.from && to <= node.to) {
@@ -2976,7 +3006,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2976
3006
  });
2977
3007
  if (markType !== type && closeStart >= to) {
2978
3008
  changesAtEnd.push({
2979
- from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
3009
+ from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
2980
3010
  insert: inlineMarkerText(markType)
2981
3011
  });
2982
3012
  }
@@ -2988,7 +3018,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2988
3018
  });
2989
3019
  if (markType !== type && openEnd <= from) {
2990
3020
  changes2.push({
2991
- from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
3021
+ from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
2992
3022
  insert: inlineMarkerText(markType)
2993
3023
  });
2994
3024
  }
@@ -3018,7 +3048,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
3018
3048
  changes2.push(startCovered);
3019
3049
  } else if (startCovered) {
3020
3050
  changes2.push({
3021
- from: skipSpaces(rangeStart, state.doc, -1, blockStart),
3051
+ from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
3022
3052
  insert: marker
3023
3053
  });
3024
3054
  }
@@ -3026,7 +3056,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
3026
3056
  changes2.push(endCovered);
3027
3057
  } else if (endCovered) {
3028
3058
  changes2.push({
3029
- from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
3059
+ from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
3030
3060
  insert: marker
3031
3061
  });
3032
3062
  }
@@ -3034,7 +3064,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
3034
3064
  }
3035
3065
  }
3036
3066
  });
3037
- if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
3067
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
3038
3068
  return {
3039
3069
  changes: {
3040
3070
  from: range.head,
@@ -3043,13 +3073,13 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
3043
3073
  range: EditorSelection.cursor(range.head + marker.length)
3044
3074
  };
3045
3075
  }
3046
- const changeSet = state.changes(changes2.concat(changesAtEnd));
3076
+ const changeSet = state2.changes(changes2.concat(changesAtEnd));
3047
3077
  return {
3048
3078
  changes: changeSet,
3049
3079
  range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
3050
3080
  };
3051
3081
  });
3052
- dispatch(state.update(changes, {
3082
+ dispatch(state2.update(changes, {
3053
3083
  userEvent: enable ? "format.style.add" : "format.style.remove",
3054
3084
  scrollIntoView: true
3055
3085
  }));
@@ -3123,8 +3153,8 @@ var insertTable = (view) => {
3123
3153
  const { from } = doc.line(number);
3124
3154
  snippets.table(view, null, from, from);
3125
3155
  };
3126
- var removeLinkInner = (from, to, changes, state) => {
3127
- syntaxTree2(state).iterate({
3156
+ var removeLinkInner = (from, to, changes, state2) => {
3157
+ syntaxTree2(state2).iterate({
3128
3158
  from,
3129
3159
  to,
3130
3160
  enter: (node) => {
@@ -3138,8 +3168,8 @@ var removeLinkInner = (from, to, changes, state) => {
3138
3168
  });
3139
3169
  } else if (name === "LinkTitle" || name === "URL") {
3140
3170
  changes.push({
3141
- from: skipSpaces(node2.from, state.doc, -1),
3142
- to: skipSpaces(node2.to, state.doc, 1)
3171
+ from: skipSpaces(node2.from, state2.doc, -1),
3172
+ to: skipSpaces(node2.to, state2.doc, 1)
3143
3173
  });
3144
3174
  }
3145
3175
  });
@@ -3148,32 +3178,32 @@ var removeLinkInner = (from, to, changes, state) => {
3148
3178
  }
3149
3179
  });
3150
3180
  };
3151
- var removeLink = ({ state, dispatch }) => {
3181
+ var removeLink = ({ state: state2, dispatch }) => {
3152
3182
  const changes = [];
3153
- for (const { from, to } of state.selection.ranges) {
3154
- removeLinkInner(from, to, changes, state);
3183
+ for (const { from, to } of state2.selection.ranges) {
3184
+ removeLinkInner(from, to, changes, state2);
3155
3185
  }
3156
3186
  if (!changes) {
3157
3187
  return false;
3158
3188
  }
3159
- dispatch(state.update({
3189
+ dispatch(state2.update({
3160
3190
  changes,
3161
3191
  userEvent: "format.link.remove",
3162
3192
  scrollIntoView: true
3163
3193
  }));
3164
3194
  return true;
3165
3195
  };
3166
- var addLink = ({ state, dispatch }) => {
3167
- const changes = state.changeByRange((range) => {
3196
+ var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
3197
+ const changes = state2.changeByRange((range) => {
3168
3198
  let { from, to } = range;
3169
3199
  const cutStyles = [];
3170
3200
  let okay = null;
3171
- syntaxTree2(state).iterate({
3201
+ syntaxTree2(state2).iterate({
3172
3202
  from,
3173
3203
  to,
3174
3204
  enter: (node) => {
3175
3205
  if (Object.hasOwn(Textblocks, node.name)) {
3176
- okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
3206
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
3177
3207
  } else if (Object.hasOwn(InlineMarker, node.name)) {
3178
3208
  const sNode = node.node;
3179
3209
  if (node.from < from && node.to <= to) {
@@ -3193,7 +3223,7 @@ var addLink = ({ state, dispatch }) => {
3193
3223
  }
3194
3224
  });
3195
3225
  if (okay === null) {
3196
- const line = state.doc.lineAt(from);
3226
+ const line = state2.doc.lineAt(from);
3197
3227
  okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
3198
3228
  }
3199
3229
  if (!okay) {
@@ -3203,26 +3233,26 @@ var addLink = ({ state, dispatch }) => {
3203
3233
  }
3204
3234
  const changes2 = [];
3205
3235
  const changesAfter = [];
3206
- removeLinkInner(from, to, changesAfter, state);
3236
+ removeLinkInner(from, to, changesAfter, state2);
3207
3237
  let cursorOffset = 1;
3208
3238
  for (const style of cutStyles) {
3209
3239
  const type = InlineMarker[style.name];
3210
3240
  const mark2 = inlineMarkerText(type);
3211
3241
  if (style.from < from) {
3212
3242
  changes2.push({
3213
- from: skipSpaces(from, state.doc, -1),
3243
+ from: skipSpaces(from, state2.doc, -1),
3214
3244
  insert: mark2
3215
3245
  });
3216
3246
  changesAfter.push({
3217
- from: skipSpaces(from, state.doc, 1, to),
3247
+ from: skipSpaces(from, state2.doc, 1, to),
3218
3248
  insert: mark2
3219
3249
  });
3220
3250
  } else {
3221
3251
  changes2.push({
3222
- from: skipSpaces(to, state.doc, -1, from),
3252
+ from: skipSpaces(to, state2.doc, -1, from),
3223
3253
  insert: mark2
3224
3254
  });
3225
- const after = skipSpaces(to, state.doc, 1);
3255
+ const after = skipSpaces(to, state2.doc, 1);
3226
3256
  if (after === to) {
3227
3257
  cursorOffset += mark2.length;
3228
3258
  }
@@ -3234,34 +3264,34 @@ var addLink = ({ state, dispatch }) => {
3234
3264
  }
3235
3265
  changes2.push({
3236
3266
  from,
3237
- insert: "["
3267
+ insert: image2 ? "![" : "["
3238
3268
  }, {
3239
3269
  from: to,
3240
- insert: "]()"
3270
+ insert: `](${url ?? ""})`
3241
3271
  });
3242
- const changeSet = state.changes(changes2.concat(changesAfter));
3272
+ const changeSet = state2.changes(changes2.concat(changesAfter));
3243
3273
  return {
3244
3274
  changes: changeSet,
3245
- range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
3275
+ range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
3246
3276
  };
3247
3277
  });
3248
3278
  if (changes.changes.empty) {
3249
3279
  return false;
3250
3280
  }
3251
- dispatch(state.update(changes, {
3281
+ dispatch(state2.update(changes, {
3252
3282
  userEvent: "format.link.add",
3253
3283
  scrollIntoView: true
3254
3284
  }));
3255
3285
  return true;
3256
3286
  };
3257
- var addList = (type) => ({ state, dispatch }) => {
3287
+ var addList = (type) => ({ state: state2, dispatch }) => {
3258
3288
  let lastBlock = -1;
3259
3289
  let counter = 1;
3260
3290
  let first = true;
3261
3291
  let parentColumn = null;
3262
3292
  const blocks = [];
3263
- for (const { from, to } of state.selection.ranges) {
3264
- syntaxTree2(state).iterate({
3293
+ for (const { from, to } of state2.selection.ranges) {
3294
+ syntaxTree2(state2).iterate({
3265
3295
  from,
3266
3296
  to,
3267
3297
  enter: (node) => {
@@ -3273,7 +3303,7 @@ var addList = (type) => ({ state, dispatch }) => {
3273
3303
  }
3274
3304
  if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
3275
3305
  const item = before.lastChild;
3276
- const itemLine = state.doc.lineAt(item.from);
3306
+ const itemLine = state2.doc.lineAt(item.from);
3277
3307
  const itemText = itemLine.text.slice(item.from - itemLine.from);
3278
3308
  parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
3279
3309
  if (type === 0) {
@@ -3308,10 +3338,10 @@ var addList = (type) => ({ state, dispatch }) => {
3308
3338
  });
3309
3339
  }
3310
3340
  if (!blocks.length) {
3311
- const { from, to } = state.doc.lineAt(state.selection.main.anchor);
3341
+ const { from, to } = state2.doc.lineAt(state2.selection.main.anchor);
3312
3342
  if (from === to) {
3313
3343
  const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
3314
- dispatch(state.update({
3344
+ dispatch(state2.update({
3315
3345
  changes: [
3316
3346
  {
3317
3347
  from,
@@ -3332,11 +3362,11 @@ var addList = (type) => ({ state, dispatch }) => {
3332
3362
  for (let i = 0; i < blocks.length; i++) {
3333
3363
  const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
3334
3364
  const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
3335
- let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
3365
+ let padding = nodeFrom > 0 && !/\s/.test(state2.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
3336
3366
  if (type === 0) {
3337
3367
  padding += String(counter2).length;
3338
3368
  }
3339
- let line = state.doc.lineAt(nodeFrom);
3369
+ let line = state2.doc.lineAt(nodeFrom);
3340
3370
  const column = nodeFrom - line.from;
3341
3371
  if (parentColumn2 !== null && parentColumn2 > column) {
3342
3372
  padding = Math.max(padding, parentColumn2 - column);
@@ -3361,7 +3391,7 @@ var addList = (type) => ({ state, dispatch }) => {
3361
3391
  insert: mark2
3362
3392
  });
3363
3393
  while (line.to < node.to) {
3364
- line = state.doc.lineAt(line.to + 1);
3394
+ line = state2.doc.lineAt(line.to + 1);
3365
3395
  const open = /^[\s>]*/.exec(line.text)[0].length;
3366
3396
  changes.push({
3367
3397
  from: line.from + Math.min(open, column),
@@ -3376,25 +3406,25 @@ var addList = (type) => ({ state, dispatch }) => {
3376
3406
  next = next.nextSibling;
3377
3407
  }
3378
3408
  if (next?.name === "OrderedList") {
3379
- renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
3409
+ renumberListItems(next.firstChild, last.counter + 1, changes, state2.doc);
3380
3410
  }
3381
3411
  }
3382
- const changeSet = state.changes(changes);
3383
- dispatch(state.update({
3412
+ const changeSet = state2.changes(changes);
3413
+ dispatch(state2.update({
3384
3414
  changes: changeSet,
3385
- selection: state.selection.map(changeSet, 1),
3415
+ selection: state2.selection.map(changeSet, 1),
3386
3416
  userEvent: "format.list.add",
3387
3417
  scrollIntoView: true
3388
3418
  }));
3389
3419
  return true;
3390
3420
  };
3391
- var removeList = (type) => ({ state, dispatch }) => {
3421
+ var removeList = (type) => ({ state: state2, dispatch }) => {
3392
3422
  let lastBlock = -1;
3393
3423
  const changes = [];
3394
3424
  const stack = [];
3395
3425
  const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3396
- for (const { from, to } of state.selection.ranges) {
3397
- syntaxTree2(state).iterate({
3426
+ for (const { from, to } of state2.selection.ranges) {
3427
+ syntaxTree2(state2).iterate({
3398
3428
  from,
3399
3429
  to,
3400
3430
  enter: (node) => {
@@ -3411,7 +3441,7 @@ var removeList = (type) => ({ state, dispatch }) => {
3411
3441
  stack.pop();
3412
3442
  } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3413
3443
  lastBlock = node.from;
3414
- let line = state.doc.lineAt(node.from);
3444
+ let line = state2.doc.lineAt(node.from);
3415
3445
  const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3416
3446
  if (!mark2) {
3417
3447
  return false;
@@ -3422,7 +3452,7 @@ var removeList = (type) => ({ state, dispatch }) => {
3422
3452
  to: node.from + mark2[0].length
3423
3453
  });
3424
3454
  while (line.to < node.to) {
3425
- line = state.doc.lineAt(line.to + 1);
3455
+ line = state2.doc.lineAt(line.to + 1);
3426
3456
  const open = /^[\s>]*/.exec(line.text)[0].length;
3427
3457
  if (open > column) {
3428
3458
  changes.push({
@@ -3432,7 +3462,7 @@ var removeList = (type) => ({ state, dispatch }) => {
3432
3462
  }
3433
3463
  }
3434
3464
  if (node.to >= to) {
3435
- renumberListItems(node.node.nextSibling, 1, changes, state.doc);
3465
+ renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
3436
3466
  }
3437
3467
  return false;
3438
3468
  }
@@ -3442,7 +3472,7 @@ var removeList = (type) => ({ state, dispatch }) => {
3442
3472
  if (!changes.length) {
3443
3473
  return false;
3444
3474
  }
3445
- dispatch(state.update({
3475
+ dispatch(state2.update({
3446
3476
  changes,
3447
3477
  userEvent: "format.list.remove",
3448
3478
  scrollIntoView: true
@@ -3472,12 +3502,12 @@ var renumberListItems = (item, counter, changes, doc) => {
3472
3502
  }
3473
3503
  }
3474
3504
  };
3475
- var setBlockquote = (enable) => ({ state, dispatch }) => {
3505
+ var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
3476
3506
  const lines = [];
3477
3507
  let lastBlock = -1;
3478
- for (const { from, to } of state.selection.ranges) {
3508
+ for (const { from, to } of state2.selection.ranges) {
3479
3509
  const sawBlock = false;
3480
- syntaxTree2(state).iterate({
3510
+ syntaxTree2(state2).iterate({
3481
3511
  from,
3482
3512
  to,
3483
3513
  enter: (node) => {
@@ -3486,9 +3516,9 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
3486
3516
  return false;
3487
3517
  }
3488
3518
  lastBlock = node.from;
3489
- let line2 = state.doc.lineAt(node.from);
3519
+ let line2 = state2.doc.lineAt(node.from);
3490
3520
  if (line2.number > 1) {
3491
- const prevLine = state.doc.line(line2.number - 1);
3521
+ const prevLine = state2.doc.line(line2.number - 1);
3492
3522
  if (/^[>\s]*$/.test(prevLine.text)) {
3493
3523
  if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
3494
3524
  lines.push(prevLine);
@@ -3500,10 +3530,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
3500
3530
  if (line2.to >= node.to) {
3501
3531
  break;
3502
3532
  }
3503
- line2 = state.doc.line(line2.number + 1);
3533
+ line2 = state2.doc.line(line2.number + 1);
3504
3534
  }
3505
- if (!enable && line2.number < state.doc.lines) {
3506
- const nextLine = state.doc.line(line2.number + 1);
3535
+ if (!enable && line2.number < state2.doc.lines) {
3536
+ const nextLine = state2.doc.line(line2.number + 1);
3507
3537
  if (/^[>\s]*$/.test(nextLine.text)) {
3508
3538
  lines.push(nextLine);
3509
3539
  }
@@ -3513,7 +3543,7 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
3513
3543
  }
3514
3544
  });
3515
3545
  let line;
3516
- if (!sawBlock && enable && from === to && !/\S/.test((line = state.doc.lineAt(from)).text)) {
3546
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
3517
3547
  lines.push(line);
3518
3548
  }
3519
3549
  }
@@ -3537,10 +3567,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
3537
3567
  if (!changes.length) {
3538
3568
  return false;
3539
3569
  }
3540
- const changeSet = state.changes(changes);
3541
- dispatch(state.update({
3570
+ const changeSet = state2.changes(changes);
3571
+ dispatch(state2.update({
3542
3572
  changes: changeSet,
3543
- selection: state.selection.map(changeSet, 1),
3573
+ selection: state2.selection.map(changeSet, 1),
3544
3574
  userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3545
3575
  scrollIntoView: true
3546
3576
  }));
@@ -3552,11 +3582,11 @@ var toggleBlockquote = (target) => {
3552
3582
  return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
3553
3583
  };
3554
3584
  var addCodeblock = (target) => {
3555
- const { state, dispatch } = target;
3556
- const { selection } = state;
3585
+ const { state: state2, dispatch } = target;
3586
+ const { selection } = state2;
3557
3587
  if (selection.ranges.length === 1 && selection.main.empty) {
3558
3588
  const { head } = selection.main;
3559
- const line = state.doc.lineAt(head);
3589
+ const line = state2.doc.lineAt(head);
3560
3590
  if (!/\S/.test(line.text) && head === line.from) {
3561
3591
  snippets.codeblock(target, null, line.from, line.to);
3562
3592
  return true;
@@ -3566,7 +3596,7 @@ var addCodeblock = (target) => {
3566
3596
  for (const { from, to } of selection.ranges) {
3567
3597
  let blockFrom = from;
3568
3598
  let blockTo = to;
3569
- syntaxTree2(state).iterate({
3599
+ syntaxTree2(state2).iterate({
3570
3600
  from,
3571
3601
  to,
3572
3602
  enter: (node) => {
@@ -3575,8 +3605,8 @@ var addCodeblock = (target) => {
3575
3605
  blockFrom = node.from;
3576
3606
  blockTo = node.to;
3577
3607
  } else {
3578
- blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
3579
- blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
3608
+ blockFrom = Math.min(blockFrom, state2.doc.lineAt(node.from).from);
3609
+ blockTo = Math.max(blockTo, state2.doc.lineAt(node.to).to);
3580
3610
  }
3581
3611
  }
3582
3612
  }
@@ -3594,7 +3624,7 @@ var addCodeblock = (target) => {
3594
3624
  return false;
3595
3625
  }
3596
3626
  const changes = ranges.map(({ from, to }) => {
3597
- const column = from - state.doc.lineAt(from).from;
3627
+ const column = from - state2.doc.lineAt(from).from;
3598
3628
  return [
3599
3629
  {
3600
3630
  from,
@@ -3606,30 +3636,30 @@ var addCodeblock = (target) => {
3606
3636
  }
3607
3637
  ];
3608
3638
  });
3609
- dispatch(state.update({
3639
+ dispatch(state2.update({
3610
3640
  changes,
3611
3641
  userEvent: "format.codeblock.add",
3612
3642
  scrollIntoView: true
3613
3643
  }));
3614
3644
  return true;
3615
3645
  };
3616
- var removeCodeblock = ({ state, dispatch }) => {
3646
+ var removeCodeblock = ({ state: state2, dispatch }) => {
3617
3647
  const changes = [];
3618
3648
  let lastBlock = -1;
3619
- for (const { from, to } of state.selection.ranges) {
3620
- syntaxTree2(state).iterate({
3649
+ for (const { from, to } of state2.selection.ranges) {
3650
+ syntaxTree2(state2).iterate({
3621
3651
  from,
3622
3652
  to,
3623
3653
  enter: (node) => {
3624
3654
  if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3625
3655
  lastBlock = node.from;
3626
- const firstLine = state.doc.lineAt(node.from);
3656
+ const firstLine = state2.doc.lineAt(node.from);
3627
3657
  if (node.name === "FencedCode") {
3628
3658
  changes.push({
3629
3659
  from: node.from,
3630
3660
  to: firstLine.to + 1 + node.from - firstLine.from
3631
3661
  });
3632
- const lastLine = state.doc.lineAt(node.to);
3662
+ const lastLine = state2.doc.lineAt(node.to);
3633
3663
  if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3634
3664
  changes.push({
3635
3665
  from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
@@ -3638,7 +3668,7 @@ var removeCodeblock = ({ state, dispatch }) => {
3638
3668
  }
3639
3669
  } else {
3640
3670
  const column = node.from - firstLine.from;
3641
- for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
3671
+ for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3642
3672
  changes.push({
3643
3673
  from: line.from + column - 4,
3644
3674
  to: line.from + column
@@ -3655,7 +3685,7 @@ var removeCodeblock = ({ state, dispatch }) => {
3655
3685
  if (!changes.length) {
3656
3686
  return false;
3657
3687
  }
3658
- dispatch(state.update({
3688
+ dispatch(state2.update({
3659
3689
  changes,
3660
3690
  userEvent: "format.codeblock.remove",
3661
3691
  scrollIntoView: true
@@ -3667,7 +3697,7 @@ var toggleCodeblock = (target) => {
3667
3697
  };
3668
3698
  var formattingKeymap = (options = {}) => {
3669
3699
  return [
3670
- keymap6.of([
3700
+ keymap7.of([
3671
3701
  {
3672
3702
  key: "meta-b",
3673
3703
  run: toggleStrong
@@ -3714,7 +3744,7 @@ var Textblocks = {
3714
3744
  TableCell: "tablecell",
3715
3745
  Task: "paragraph"
3716
3746
  };
3717
- var getFormatting = (state) => {
3747
+ var getFormatting = (state2) => {
3718
3748
  let blockType = null;
3719
3749
  const inline = [
3720
3750
  null,
@@ -3740,7 +3770,7 @@ var getFormatting = (state) => {
3740
3770
  continue;
3741
3771
  } else if (currentBlock.active[i]) {
3742
3772
  inline[i] = true;
3743
- } else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
3773
+ } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3744
3774
  inline[i] = false;
3745
3775
  }
3746
3776
  }
@@ -3751,12 +3781,12 @@ var getFormatting = (state) => {
3751
3781
  currentBlock.pos = Math.min(upto, currentBlock.end);
3752
3782
  }
3753
3783
  };
3754
- const { selection } = state;
3784
+ const { selection } = state2;
3755
3785
  for (const range of selection.ranges) {
3756
3786
  if (range.empty && inline.some((v) => v === null)) {
3757
3787
  const contextSize = Math.min(range.head, 6);
3758
- const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
3759
- let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
3788
+ const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3789
+ let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3760
3790
  for (let i = 0; i < contextSize; i++) {
3761
3791
  const ch = contextAfter[i];
3762
3792
  if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
@@ -3775,7 +3805,7 @@ var getFormatting = (state) => {
3775
3805
  }
3776
3806
  }
3777
3807
  }
3778
- syntaxTree2(state).iterate({
3808
+ syntaxTree2(state2).iterate({
3779
3809
  from: range.from,
3780
3810
  to: range.to,
3781
3811
  enter: (node) => {
@@ -3849,7 +3879,7 @@ var getFormatting = (state) => {
3849
3879
  }
3850
3880
  });
3851
3881
  }
3852
- const { from, to } = state.doc.lineAt(selection.main.anchor);
3882
+ const { from, to } = state2.doc.lineAt(selection.main.anchor);
3853
3883
  const blankLine = from === to;
3854
3884
  return {
3855
3885
  blankLine,
@@ -3864,17 +3894,22 @@ var getFormatting = (state) => {
3864
3894
  };
3865
3895
  };
3866
3896
  var useFormattingState = () => {
3867
- const [state, setState] = useState(null);
3897
+ const [state2, setState] = useState();
3868
3898
  const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3869
3899
  if (update2.docChanged || update2.selectionSet) {
3870
- const newState = getFormatting(update2.state);
3871
- if (!state || !compareFormatting(state, newState)) {
3872
- setState(newState);
3873
- }
3900
+ setState((prevState) => {
3901
+ const newState = getFormatting(update2.state);
3902
+ if (!prevState || !formattingEquals(prevState, newState)) {
3903
+ return newState;
3904
+ }
3905
+ return prevState;
3906
+ });
3874
3907
  }
3875
- }), []);
3908
+ }), [
3909
+ setState
3910
+ ]);
3876
3911
  return [
3877
- state,
3912
+ state2,
3878
3913
  observer
3879
3914
  ];
3880
3915
  };
@@ -3885,8 +3920,8 @@ import { StateField as StateField5 } from "@codemirror/state";
3885
3920
  import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3886
3921
  var image = (options = {}) => {
3887
3922
  return StateField5.define({
3888
- create: (state) => {
3889
- return Decoration6.set(buildDecorations2(0, state.doc.length, state));
3923
+ create: (state2) => {
3924
+ return Decoration6.set(buildDecorations2(0, state2.doc.length, state2));
3890
3925
  },
3891
3926
  update: (value, tr) => {
3892
3927
  if (!tr.docChanged && !tr.selection) {
@@ -3920,16 +3955,16 @@ var preloadImage = (url) => {
3920
3955
  preloaded.add(url);
3921
3956
  }
3922
3957
  };
3923
- var buildDecorations2 = (from, to, state) => {
3958
+ var buildDecorations2 = (from, to, state2) => {
3924
3959
  const decorations = [];
3925
- const cursor = state.selection.main.head;
3926
- syntaxTree3(state).iterate({
3960
+ const cursor = state2.selection.main.head;
3961
+ syntaxTree3(state2).iterate({
3927
3962
  enter: (node) => {
3928
3963
  if (node.name === "Image") {
3929
3964
  const urlNode = node.node.getChild("URL");
3930
3965
  if (urlNode) {
3931
- const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3932
- const url = state.sliceDoc(urlNode.from, urlNode.to);
3966
+ const hide2 = state2.readOnly || cursor < node.from || cursor > node.to;
3967
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
3933
3968
  preloadImage(url);
3934
3969
  decorations.push(Decoration6.replace({
3935
3970
  block: true,
@@ -3959,6 +3994,8 @@ var ImageWidget = class extends WidgetType4 {
3959
3994
  return img;
3960
3995
  }
3961
3996
  };
3997
+ var imageUpload = (options = {}) => {
3998
+ };
3962
3999
 
3963
4000
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3964
4001
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -4000,21 +4037,21 @@ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "
4000
4037
  import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
4001
4038
  var table = (options = {}) => {
4002
4039
  return StateField6.define({
4003
- create: (state) => update(state, options),
4040
+ create: (state2) => update(state2, options),
4004
4041
  update: (_, tr) => update(tr.state, options),
4005
4042
  provide: (field) => EditorView15.decorations.from(field)
4006
4043
  });
4007
4044
  };
4008
- var update = (state, options) => {
4045
+ var update = (state2, options) => {
4009
4046
  const builder = new RangeSetBuilder3();
4010
- const cursor = state.selection.main.head;
4047
+ const cursor = state2.selection.main.head;
4011
4048
  const tables = [];
4012
4049
  const getTable = () => tables[tables.length - 1];
4013
4050
  const getRow = () => {
4014
4051
  const table2 = getTable();
4015
4052
  return table2.rows?.[table2.rows.length - 1];
4016
4053
  };
4017
- syntaxTree5(state).iterate({
4054
+ syntaxTree5(state2).iterate({
4018
4055
  enter: (node) => {
4019
4056
  switch (node.name) {
4020
4057
  case "Table": {
@@ -4035,9 +4072,9 @@ var update = (state, options) => {
4035
4072
  case "TableCell": {
4036
4073
  const row = getRow();
4037
4074
  if (row) {
4038
- row.push(state.sliceDoc(node.from, node.to));
4075
+ row.push(state2.sliceDoc(node.from, node.to));
4039
4076
  } else {
4040
- getTable().header?.push(state.sliceDoc(node.from, node.to));
4077
+ getTable().header?.push(state2.sliceDoc(node.from, node.to));
4041
4078
  }
4042
4079
  break;
4043
4080
  }
@@ -4045,7 +4082,7 @@ var update = (state, options) => {
4045
4082
  }
4046
4083
  });
4047
4084
  tables.forEach((table2) => {
4048
- const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
4085
+ const hide2 = state2.readOnly || cursor < table2.from || cursor > table2.to;
4049
4086
  if (hide2) {
4050
4087
  builder.add(table2.from, table2.to, Decoration7.replace({
4051
4088
  widget: new TableWidget(table2)
@@ -4134,25 +4171,35 @@ var mention = ({ onSearch }) => {
4134
4171
 
4135
4172
  // packages/ui/react-ui-editor/src/extensions/modes.ts
4136
4173
  import { Facet as Facet6 } from "@codemirror/state";
4137
- import { keymap as keymap7 } from "@codemirror/view";
4174
+ import { keymap as keymap8 } from "@codemirror/view";
4138
4175
  import { vim } from "@replit/codemirror-vim";
4176
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
4177
+ var focusEvent = "focus.container";
4139
4178
  var editorMode = Facet6.define({
4140
4179
  combine: (modes) => modes[0] ?? {}
4141
4180
  });
4142
4181
  var EditorModes = {
4143
4182
  default: [],
4183
+ vscode: [
4184
+ // https://github.com/replit/codemirror-vscode-keymap
4185
+ editorMode.of({
4186
+ type: "vscode"
4187
+ }),
4188
+ keymap8.of(vscodeKeymap)
4189
+ ],
4144
4190
  vim: [
4191
+ // https://github.com/replit/codemirror-vim
4145
4192
  vim(),
4146
4193
  editorMode.of({
4147
4194
  type: "vim",
4148
4195
  noTabster: true
4149
4196
  }),
4150
- keymap7.of([
4197
+ keymap8.of([
4151
4198
  {
4152
4199
  key: "Alt-Escape",
4153
4200
  run: (view) => {
4154
4201
  view.dispatch({
4155
- userEvent: "focus.container"
4202
+ userEvent: focusEvent
4156
4203
  });
4157
4204
  return true;
4158
4205
  }
@@ -4161,8 +4208,95 @@ var EditorModes = {
4161
4208
  ]
4162
4209
  };
4163
4210
 
4211
+ // packages/ui/react-ui-editor/src/extensions/state.ts
4212
+ import { Transaction as Transaction2 } from "@codemirror/state";
4213
+ import { EditorView as EditorView16, keymap as keymap9 } from "@codemirror/view";
4214
+ import { debounce as debounce2 } from "@dxos/async";
4215
+ import { invariant as invariant4 } from "@dxos/invariant";
4216
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4217
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4218
+ var scrollAnnotation = "dxos.org/cm/scrolling";
4219
+ var keyPrefix = "dxos.org/react-ui-editor/state";
4220
+ var localStorageStateStoreAdapter = {
4221
+ setState: (id, state2) => {
4222
+ invariant4(id, void 0, {
4223
+ F: __dxlog_file11,
4224
+ L: 35,
4225
+ S: void 0,
4226
+ A: [
4227
+ "id",
4228
+ ""
4229
+ ]
4230
+ });
4231
+ localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4232
+ },
4233
+ getState: (id) => {
4234
+ invariant4(id, void 0, {
4235
+ F: __dxlog_file11,
4236
+ L: 39,
4237
+ S: void 0,
4238
+ A: [
4239
+ "id",
4240
+ ""
4241
+ ]
4242
+ });
4243
+ const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4244
+ return state2 ? JSON.parse(state2) : void 0;
4245
+ }
4246
+ };
4247
+ var state = ({ getState, setState } = {}) => {
4248
+ const setStateDebounced = debounce2(setState, 1e3);
4249
+ return [
4250
+ // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4251
+ EditorView16.updateListener.of(({ view, changes, transactions }) => {
4252
+ const id = view.state.facet(documentId2);
4253
+ if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4254
+ return;
4255
+ }
4256
+ if (setState) {
4257
+ const { top } = view.dom.getBoundingClientRect();
4258
+ const pos = view.posAtCoords({
4259
+ x: 0,
4260
+ y: top
4261
+ });
4262
+ if (pos !== null) {
4263
+ const { anchor, head } = view.state.selection.main;
4264
+ setStateDebounced(id, {
4265
+ scrollTo: {
4266
+ from: pos,
4267
+ yMargin: 0
4268
+ },
4269
+ selection: {
4270
+ anchor,
4271
+ head
4272
+ }
4273
+ });
4274
+ }
4275
+ }
4276
+ }),
4277
+ getState && keymap9.of([
4278
+ {
4279
+ key: "ctrl-r",
4280
+ run: (view) => {
4281
+ const state2 = getState(view.state.facet(documentId2));
4282
+ if (state2) {
4283
+ view.dispatch({
4284
+ effects: EditorView16.scrollIntoView(state2.scrollTo.from, {
4285
+ yMargin: 0
4286
+ }),
4287
+ selection: state2.selection,
4288
+ annotations: Transaction2.userEvent.of(scrollAnnotation)
4289
+ });
4290
+ }
4291
+ return true;
4292
+ }
4293
+ }
4294
+ ])
4295
+ ].filter(isNotFalsy3);
4296
+ };
4297
+
4164
4298
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4165
- import { keymap as keymap8 } from "@codemirror/view";
4299
+ import { keymap as keymap10 } from "@codemirror/view";
4166
4300
  var defaultItems = [
4167
4301
  "hello world!",
4168
4302
  "this is a test.",
@@ -4172,7 +4306,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4172
4306
  let t;
4173
4307
  let idx = 0;
4174
4308
  return [
4175
- keymap8.of([
4309
+ keymap10.of([
4176
4310
  {
4177
4311
  // Reset.
4178
4312
  key: "alt-meta-'",
@@ -4219,9 +4353,11 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4219
4353
  };
4220
4354
 
4221
4355
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4222
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4356
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4223
4357
  var instanceCount = 0;
4224
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4358
+ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView17.scrollIntoView(0, {
4359
+ yMargin: 0
4360
+ }), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4225
4361
  const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4226
4362
  const tabsterDOMAttribute = useFocusableGroup({
4227
4363
  tabBehavior: "limited"
@@ -4240,42 +4376,42 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4240
4376
  autoFocus
4241
4377
  ]);
4242
4378
  useEffect2(() => {
4243
- log9("updating", {
4379
+ log9("create", {
4244
4380
  id,
4245
4381
  instanceId
4246
4382
  }, {
4247
- F: __dxlog_file11,
4383
+ F: __dxlog_file12,
4248
4384
  L: 88,
4249
4385
  S: void 0,
4250
4386
  C: (f, a) => f(...a)
4251
4387
  });
4252
- const state = EditorState2.create({
4388
+ const state2 = EditorState2.create({
4253
4389
  doc,
4254
4390
  selection,
4255
4391
  extensions: [
4256
4392
  id && documentId2.of(id),
4257
4393
  // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4258
- EditorView16.exceptionSink.of((err) => {
4394
+ EditorView17.exceptionSink.of((err) => {
4259
4395
  log9.catch(err, void 0, {
4260
- F: __dxlog_file11,
4396
+ F: __dxlog_file12,
4261
4397
  L: 101,
4262
4398
  S: void 0,
4263
4399
  C: (f, a) => f(...a)
4264
4400
  });
4265
4401
  }),
4266
4402
  // Focus.
4267
- EditorView16.updateListener.of((update2) => {
4403
+ EditorView17.updateListener.of((update2) => {
4268
4404
  update2.transactions.forEach((transaction) => {
4269
- if (transaction.isUserEvent("focus.container")) {
4405
+ if (transaction.isUserEvent(focusEvent)) {
4270
4406
  rootRef.current?.focus();
4271
4407
  }
4272
4408
  });
4273
4409
  }),
4274
4410
  extensions
4275
- ].filter(isNotFalsy3)
4411
+ ].filter(isNotFalsy4)
4276
4412
  });
4277
- const view2 = new EditorView16({
4278
- state,
4413
+ const view2 = new EditorView17({
4414
+ state: state2,
4279
4415
  parent: rootRef.current,
4280
4416
  scrollTo,
4281
4417
  // NOTE: Uncomment to debug/monitor all transactions.
@@ -4287,21 +4423,29 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4287
4423
  view3.update(trs);
4288
4424
  }
4289
4425
  });
4290
- setView(view2);
4291
- if (moveToEndOfLine) {
4426
+ if (moveToEndOfLine && !(scrollTo || selection)) {
4292
4427
  const { to } = view2.state.doc.lineAt(0);
4293
- view2?.dispatch({
4428
+ view2.dispatch({
4294
4429
  selection: {
4295
4430
  anchor: to
4296
4431
  }
4297
4432
  });
4298
4433
  }
4299
- if (state.facet(editorMode).noTabster) {
4434
+ if (state2.facet(editorMode).noTabster) {
4300
4435
  rootRef.current?.removeAttribute("data-tabster");
4301
4436
  }
4437
+ setView(view2);
4302
4438
  return () => {
4439
+ log9("destroy", {
4440
+ id,
4441
+ instanceId
4442
+ }, {
4443
+ F: __dxlog_file12,
4444
+ L: 150,
4445
+ S: void 0,
4446
+ C: (f, a) => f(...a)
4447
+ });
4303
4448
  view2?.destroy();
4304
- setView(null);
4305
4449
  };
4306
4450
  }, [
4307
4451
  doc,
@@ -4331,9 +4475,10 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4331
4475
  });
4332
4476
 
4333
4477
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4334
- import { ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, Quotes } from "@phosphor-icons/react";
4478
+ import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
4335
4479
  import { createContext } from "@radix-ui/react-context";
4336
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4480
+ import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
4481
+ import { useDropzone } from "react-dropzone";
4337
4482
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4338
4483
  import { getSize } from "@dxos/react-ui-theme";
4339
4484
  var tooltipProps = {
@@ -4350,10 +4495,10 @@ var HeadingIcons = {
4350
4495
  "6": TextHSix
4351
4496
  };
4352
4497
  var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
4353
- var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4498
+ var ToolbarRoot = ({ children, onAction, classNames, state: state2 }) => {
4354
4499
  return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
4355
4500
  onAction,
4356
- state
4501
+ state: state2
4357
4502
  }, /* @__PURE__ */ React2.createElement(DensityProvider, {
4358
4503
  density: "fine"
4359
4504
  }, /* @__PURE__ */ React2.createElement(ElevationProvider, {
@@ -4367,7 +4512,7 @@ var ToolbarRoot = ({ children, onAction, classNames, state }) => {
4367
4512
  };
4368
4513
  var buttonStyles = "min-bs-0 p-2";
4369
4514
  var iconStyles = getSize(5);
4370
- var ToolbarButton = ({ Icon, children, ...props }) => {
4515
+ var ToolbarToggleButton = ({ Icon, children, ...props }) => {
4371
4516
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4372
4517
  asChild: true
4373
4518
  }, /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroupItem, {
@@ -4380,14 +4525,27 @@ var ToolbarButton = ({ Icon, children, ...props }) => {
4380
4525
  className: "sr-only"
4381
4526
  }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4382
4527
  };
4528
+ var ToolbarButton = ({ Icon, children, ...props }) => {
4529
+ return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4530
+ asChild: true
4531
+ }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4532
+ variant: "ghost",
4533
+ ...props,
4534
+ classNames: buttonStyles
4535
+ }, /* @__PURE__ */ React2.createElement(Icon, {
4536
+ className: iconStyles
4537
+ }), /* @__PURE__ */ React2.createElement("span", {
4538
+ className: "sr-only"
4539
+ }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4540
+ };
4383
4541
  var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
4384
4542
  role: "separator",
4385
4543
  className: "grow"
4386
4544
  });
4387
4545
  var MarkdownHeading = () => {
4388
4546
  const { t } = useTranslation(translationKey);
4389
- const { onAction, state } = useToolbarContext("MarkdownFormatting");
4390
- const blockType = state ? state.blockType : "paragraph";
4547
+ const { onAction, state: state2 } = useToolbarContext("MarkdownFormatting");
4548
+ const blockType = state2 ? state2.blockType : "paragraph";
4391
4549
  const header = blockType && /heading(\d)/.exec(blockType);
4392
4550
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4393
4551
  const HeadingIcon = HeadingIcons[value ?? "0"];
@@ -4461,43 +4619,43 @@ var markdownStyles = [
4461
4619
  {
4462
4620
  type: "strong",
4463
4621
  Icon: TextB,
4464
- getState: (state) => state.strong
4622
+ getState: (state2) => state2.strong
4465
4623
  },
4466
4624
  {
4467
4625
  type: "emphasis",
4468
4626
  Icon: TextItalic,
4469
- getState: (state) => state.emphasis
4627
+ getState: (state2) => state2.emphasis
4470
4628
  },
4471
4629
  {
4472
4630
  type: "strikethrough",
4473
4631
  Icon: TextStrikethrough,
4474
- getState: (state) => state.strikethrough
4632
+ getState: (state2) => state2.strikethrough
4475
4633
  },
4476
4634
  {
4477
4635
  type: "code",
4478
4636
  Icon: Code,
4479
- getState: (state) => state.code
4637
+ getState: (state2) => state2.code
4480
4638
  },
4481
4639
  {
4482
4640
  type: "link",
4483
4641
  Icon: Link,
4484
- getState: (state) => state.link
4642
+ getState: (state2) => state2.link
4485
4643
  }
4486
4644
  ];
4487
4645
  var MarkdownStyles = () => {
4488
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4646
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4489
4647
  const { t } = useTranslation(translationKey);
4490
4648
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4491
4649
  type: "multiple",
4492
- value: markdownStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)
4493
- }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4650
+ value: markdownStyles.filter(({ getState }) => state2 && getState(state2)).map(({ type }) => type)
4651
+ }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4494
4652
  key: type,
4495
4653
  value: type,
4496
4654
  Icon,
4497
- disabled: state?.blockType === "codeblock",
4498
- onClick: state ? () => onAction?.({
4655
+ disabled: state2?.blockType === "codeblock",
4656
+ onClick: state2 ? () => onAction?.({
4499
4657
  type,
4500
- data: !getState(state)
4658
+ data: !getState(state2)
4501
4659
  }) : void 0
4502
4660
  }, t(`${type} label`))));
4503
4661
  };
@@ -4505,32 +4663,32 @@ var markdownLists = [
4505
4663
  {
4506
4664
  type: "list-bullet",
4507
4665
  Icon: ListBullets,
4508
- getState: (state) => state.listStyle === "bullet"
4666
+ getState: (state2) => state2.listStyle === "bullet"
4509
4667
  },
4510
4668
  {
4511
4669
  type: "list-ordered",
4512
4670
  Icon: ListNumbers,
4513
- getState: (state) => state.listStyle === "ordered"
4671
+ getState: (state2) => state2.listStyle === "ordered"
4514
4672
  },
4515
4673
  {
4516
4674
  type: "list-task",
4517
4675
  Icon: ListChecks,
4518
- getState: (state) => state.listStyle === "task"
4676
+ getState: (state2) => state2.listStyle === "task"
4519
4677
  }
4520
4678
  ];
4521
4679
  var MarkdownLists = () => {
4522
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4680
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4523
4681
  const { t } = useTranslation(translationKey);
4524
4682
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4525
4683
  type: "single",
4526
- value: state?.listStyle ? `list-${state.listStyle}` : ""
4527
- }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4684
+ value: state2?.listStyle ? `list-${state2.listStyle}` : ""
4685
+ }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4528
4686
  key: type,
4529
4687
  value: type,
4530
4688
  Icon,
4531
- onClick: state ? () => onAction?.({
4689
+ onClick: state2 ? () => onAction?.({
4532
4690
  type,
4533
- data: !getState(state)
4691
+ data: !getState(state2)
4534
4692
  }) : void 0
4535
4693
  }, t(`${type} label`))));
4536
4694
  };
@@ -4538,63 +4696,101 @@ var markdownBlocks = [
4538
4696
  {
4539
4697
  type: "blockquote",
4540
4698
  Icon: Quotes,
4541
- getState: (state) => state.blockQuote
4699
+ getState: (state2) => state2.blockQuote
4542
4700
  },
4543
4701
  {
4544
4702
  type: "codeblock",
4545
4703
  Icon: CodeBlock,
4546
- getState: (state) => state.blockType === "codeblock"
4704
+ getState: (state2) => state2.blockType === "codeblock"
4547
4705
  },
4548
4706
  {
4549
4707
  type: "table",
4550
4708
  Icon: Table2,
4551
- getState: (state) => state.blockType === "tablecell",
4552
- disabled: (state) => !state.blankLine
4709
+ getState: (state2) => state2.blockType === "tablecell",
4710
+ disabled: (state2) => !state2.blankLine
4553
4711
  }
4554
4712
  ];
4555
4713
  var MarkdownBlocks = () => {
4556
- const { onAction, state } = useToolbarContext("MarkdownStyles");
4714
+ const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4557
4715
  const { t } = useTranslation(translationKey);
4558
- const value = markdownBlocks.find(({ getState }) => state && getState(state));
4716
+ const value = markdownBlocks.find(({ getState }) => state2 && getState(state2));
4559
4717
  return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4560
4718
  type: "single",
4561
4719
  value: value?.type ?? ""
4562
- }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarButton, {
4720
+ }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4563
4721
  key: type,
4564
4722
  value: type,
4565
4723
  Icon,
4566
- disabled: !state || disabled?.(state),
4567
- onClick: state ? () => onAction?.({
4724
+ disabled: !state2 || disabled?.(state2),
4725
+ onClick: state2 ? () => onAction?.({
4568
4726
  type,
4569
- data: !getState(state)
4727
+ data: !getState(state2)
4570
4728
  }) : void 0
4571
4729
  }, t(`${type} label`))));
4572
4730
  };
4573
4731
  var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null));
4574
- var MarkdownExtended = () => {
4732
+ var MarkdownCustom = ({ onUpload } = {}) => {
4575
4733
  const { onAction } = useToolbarContext("MarkdownStyles");
4576
4734
  const { t } = useTranslation(translationKey);
4577
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4578
- asChild: true
4579
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4580
- variant: "ghost",
4735
+ const { acceptedFiles, getInputProps, open } = useDropzone({
4736
+ multiple: false,
4737
+ noDrag: true,
4738
+ accept: {
4739
+ "image/*": [
4740
+ ".jpg",
4741
+ ".jpeg",
4742
+ ".png",
4743
+ ".gif"
4744
+ ]
4745
+ }
4746
+ });
4747
+ useEffect3(() => {
4748
+ if (onUpload && acceptedFiles.length) {
4749
+ setTimeout(async () => {
4750
+ const f = acceptedFiles[0];
4751
+ const file = new File([
4752
+ f
4753
+ ], f.name, {
4754
+ type: f.type,
4755
+ lastModified: f.lastModified
4756
+ });
4757
+ const { url } = await onUpload(file);
4758
+ if (url) {
4759
+ onAction?.({
4760
+ type: "image",
4761
+ data: url
4762
+ });
4763
+ }
4764
+ });
4765
+ }
4766
+ }, [
4767
+ acceptedFiles
4768
+ ]);
4769
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("input", getInputProps()), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4770
+ value: "image",
4771
+ Icon: Image,
4772
+ onClick: () => open()
4773
+ }, t("image label")));
4774
+ };
4775
+ var MarkdownActions = () => {
4776
+ const { onAction } = useToolbarContext("MarkdownStyles");
4777
+ const { t } = useTranslation(translationKey);
4778
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4779
+ value: "comment",
4780
+ Icon: ChatText,
4581
4781
  "data-testid": "editor.toolbar.comment",
4582
4782
  onClick: () => onAction?.({
4583
4783
  type: "comment"
4584
- }),
4585
- classNames: buttonStyles
4586
- }, /* @__PURE__ */ React2.createElement(ChatText, {
4587
- className: iconStyles
4588
- }), /* @__PURE__ */ React2.createElement("span", {
4589
- className: "sr-only"
4590
- }, t("comment label")))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("comment label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4784
+ })
4785
+ }, t("comment label")));
4591
4786
  };
4592
4787
  var Toolbar = {
4593
4788
  Root: ToolbarRoot,
4594
- Button: ToolbarButton,
4789
+ Button: ToolbarToggleButton,
4595
4790
  Separator: ToolbarSeparator,
4596
4791
  Markdown: MarkdownStandard,
4597
- Extended: MarkdownExtended
4792
+ Custom: MarkdownCustom,
4793
+ Actions: MarkdownActions
4598
4794
  };
4599
4795
 
4600
4796
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
@@ -4631,7 +4827,13 @@ var useActionHandler = (view) => {
4631
4827
  insertTable(view);
4632
4828
  break;
4633
4829
  case "link":
4634
- (action.data === false ? removeLink : addLink)(view);
4830
+ (action.data === false ? removeLink : addLink())(view);
4831
+ break;
4832
+ case "image":
4833
+ addLink({
4834
+ url: action.data,
4835
+ image: true
4836
+ })(view);
4635
4837
  break;
4636
4838
  case "comment":
4637
4839
  createComment(view);
@@ -4660,43 +4862,51 @@ var useDocAccessor = (text) => {
4660
4862
 
4661
4863
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4662
4864
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4663
- import { EditorView as EditorView17 } from "@codemirror/view";
4664
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4865
+ import { EditorView as EditorView18 } from "@codemirror/view";
4866
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4665
4867
  import { log as log10 } from "@dxos/log";
4666
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4667
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4868
+ import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4869
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4668
4870
  var useTextEditor = (cb = () => ({}), deps = []) => {
4669
4871
  let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4670
4872
  const onUpdate = useRef3();
4671
4873
  const [view, setView] = useState4();
4672
4874
  const parentRef = useRef3(null);
4673
- useEffect3(() => {
4875
+ useEffect4(() => {
4674
4876
  let view2;
4675
4877
  if (parentRef.current) {
4676
- const state = EditorState3.create({
4878
+ log10("create", {
4879
+ id
4880
+ }, {
4881
+ F: __dxlog_file13,
4882
+ L: 36,
4883
+ S: void 0,
4884
+ C: (f, a) => f(...a)
4885
+ });
4886
+ const state2 = EditorState3.create({
4677
4887
  doc,
4678
4888
  selection,
4679
4889
  extensions: [
4680
4890
  id && documentId2.of(id),
4681
4891
  // TODO(burdon): Doesn't catch errors in keymap functions.
4682
- EditorView17.exceptionSink.of((err) => {
4892
+ EditorView18.exceptionSink.of((err) => {
4683
4893
  log10.catch(err, void 0, {
4684
- F: __dxlog_file12,
4685
- L: 44,
4894
+ F: __dxlog_file13,
4895
+ L: 46,
4686
4896
  S: void 0,
4687
4897
  C: (f, a) => f(...a)
4688
4898
  });
4689
4899
  }),
4690
4900
  extensions,
4691
- EditorView17.updateListener.of(() => {
4901
+ EditorView18.updateListener.of(() => {
4692
4902
  onUpdate.current?.();
4693
4903
  })
4694
- ].filter(isNotFalsy4)
4904
+ ].filter(isNotFalsy5)
4695
4905
  });
4696
- view2 = new EditorView17({
4906
+ view2 = new EditorView18({
4697
4907
  parent: parentRef.current,
4698
4908
  scrollTo,
4699
- state,
4909
+ state: state2,
4700
4910
  // NOTE: Uncomment to debug/monitor all transactions.
4701
4911
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4702
4912
  dispatchTransactions: (trs, view3) => {
@@ -4709,10 +4919,18 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4709
4919
  setView(view2);
4710
4920
  }
4711
4921
  return () => {
4922
+ log10("destroy", {
4923
+ id
4924
+ }, {
4925
+ F: __dxlog_file13,
4926
+ L: 74,
4927
+ S: void 0,
4928
+ C: (f, a) => f(...a)
4929
+ });
4712
4930
  view2?.destroy();
4713
4931
  };
4714
4932
  }, deps);
4715
- useEffect3(() => {
4933
+ useEffect4(() => {
4716
4934
  if (view) {
4717
4935
  if (!selection && !view.state.selection.main.anchor) {
4718
4936
  selection = EditorSelection2.single(view.state.doc.line(1).to);
@@ -4771,7 +4989,6 @@ export {
4771
4989
  callbackWrapper,
4772
4990
  command,
4773
4991
  comments,
4774
- compareFormatting,
4775
4992
  createBasicExtensions,
4776
4993
  createComment,
4777
4994
  createDataExtensions,
@@ -4788,14 +5005,18 @@ export {
4788
5005
  editorMode,
4789
5006
  editorWithToolbarLayout,
4790
5007
  focusComment,
5008
+ focusEvent,
5009
+ formattingEquals,
4791
5010
  formattingKeymap,
4792
5011
  getFormatting,
4793
5012
  getToken,
4794
5013
  image,
5014
+ imageUpload,
4795
5015
  insertTable,
4796
- keymap9 as keymap,
5016
+ keymap11 as keymap,
4797
5017
  linkTooltip,
4798
5018
  listener,
5019
+ localStorageStateStoreAdapter,
4799
5020
  logChanges,
4800
5021
  markdownHighlightStyle,
4801
5022
  markdownTags,
@@ -4811,6 +5032,7 @@ export {
4811
5032
  setHeading,
4812
5033
  setSelection,
4813
5034
  setStyle,
5035
+ state,
4814
5036
  table,
4815
5037
  tags2 as tags,
4816
5038
  toggleBlockquote,