@dxos/react-ui-editor 0.3.11-main.c95bc86 → 0.3.11-main.cb8120f

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 +434 -210
  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/MarkdownEditor.stories.d.ts.map +1 -1
  5. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  6. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
  7. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  8. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  9. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  10. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  11. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  12. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  13. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  14. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  15. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  16. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  18. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/awareness.d.ts +1 -1
  21. package/dist/types/src/extensions/comments.d.ts +5 -1
  22. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  23. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -1
  24. package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
  25. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  26. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  27. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  29. package/dist/types/src/hooks/index.d.ts +1 -0
  30. package/dist/types/src/hooks/index.d.ts.map +1 -1
  31. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  32. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  33. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  34. package/package.json +36 -24
  35. package/src/components/TextEditor/MarkdownEditor.stories.tsx +8 -13
  36. package/src/components/TextEditor/TextEditor.tsx +3 -1
  37. package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
  38. package/src/components/Toolbar/Toolbar.tsx +115 -0
  39. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  40. package/src/extensions/automerge/automerge.test.ts +13 -0
  41. package/src/extensions/automerge/automerge.ts +26 -11
  42. package/src/extensions/automerge/cursor.ts +3 -3
  43. package/src/extensions/automerge/defs.ts +9 -9
  44. package/src/extensions/automerge/semaphore.ts +17 -19
  45. package/src/extensions/automerge/update-automerge.ts +12 -6
  46. package/src/extensions/automerge/update-codemirror.ts +5 -5
  47. package/src/extensions/awareness.ts +3 -3
  48. package/src/extensions/comments.ts +112 -64
  49. package/src/extensions/markdown/code.ts +24 -9
  50. package/src/extensions/markdown/formatting.ts +159 -0
  51. package/src/extensions/markdown/heading.ts +1 -1
  52. package/src/extensions/markdown/index.ts +1 -0
  53. package/src/extensions/markdown/link.ts +4 -0
  54. package/src/hooks/index.ts +1 -0
  55. package/src/hooks/useActionHandler.ts +45 -0
  56. package/src/hooks/useTextModel.ts +11 -8
  57. package/src/themes/default.ts +1 -1
@@ -15,6 +15,7 @@ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeH
15
15
  import { log as log2 } from "@dxos/log";
16
16
  import { useThemeContext } from "@dxos/react-ui";
17
17
  import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
18
+ import { nonNullable as nonNullable2 } from "@dxos/util";
18
19
 
19
20
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
21
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -53,14 +54,14 @@ var autocomplete = ({ onSearch }) => {
53
54
  };
54
55
 
55
56
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
57
+ import { invertedEffects } from "@codemirror/commands";
56
58
  import { StateField } from "@codemirror/state";
57
59
  import { EditorView, ViewPlugin } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
- import { invariant } from "@dxos/invariant";
60
+ import { next as A4 } from "@dxos/automerge/automerge";
60
61
 
61
62
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
62
63
  import get from "lodash.get";
63
- import { next as automerge } from "@dxos/automerge/automerge";
64
+ import { next as A } from "@dxos/automerge/automerge";
64
65
  var cursorConverter = (handle, path) => ({
65
66
  // TODO(burdon): Handle assoc to associate with a previous character.
66
67
  toCursor: (pos) => {
@@ -72,7 +73,7 @@ var cursorConverter = (handle, path) => ({
72
73
  if (typeof value === "string" && value.length <= pos) {
73
74
  return "end";
74
75
  }
75
- return automerge.getCursor(doc, path.slice(), pos);
76
+ return A.getCursor(doc, path.slice(), pos);
76
77
  },
77
78
  fromCursor: (cursor) => {
78
79
  if (cursor === "") {
@@ -90,26 +91,28 @@ var cursorConverter = (handle, path) => ({
90
91
  return 0;
91
92
  }
92
93
  }
93
- return automerge.getCursorPosition(doc, path.slice(), cursor);
94
+ return A.getCursorPosition(doc, path.slice(), cursor);
94
95
  }
95
96
  });
96
97
 
97
98
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
98
99
  import { Annotation, StateEffect } from "@codemirror/state";
99
- var effectType = StateEffect.define({});
100
- var reconcileAnnotationType = Annotation.define();
101
100
  var getPath = (state, field) => state.field(field).path;
102
101
  var getLastHeads = (state, field) => state.field(field).lastHeads;
103
- var updateHeads = (newHeads) => effectType.of({
102
+ var updateHeadsEffect = StateEffect.define({});
103
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
104
104
  newHeads
105
105
  });
106
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
106
+ var reconcileAnnotation = Annotation.define();
107
+ var isReconcile = (tr) => {
108
+ return !!tr.annotation(reconcileAnnotation);
109
+ };
107
110
 
108
111
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
109
- import { next as automerge2 } from "@dxos/automerge/automerge";
112
+ import { next as A3 } from "@dxos/automerge/automerge";
110
113
 
111
114
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
112
- import { next as am } from "@dxos/automerge/automerge";
115
+ import { next as A2 } from "@dxos/automerge/automerge";
113
116
  var updateAutomerge = (field, handle, transactions, state) => {
114
117
  const { lastHeads, path } = state.field(field);
115
118
  let hasChanges = false;
@@ -122,11 +125,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
122
125
  return void 0;
123
126
  }
124
127
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
+ const t = [];
125
129
  for (const tr of transactions) {
126
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
127
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
130
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
131
+ t.push({
132
+ fromA,
133
+ toA,
134
+ insert
135
+ });
128
136
  });
129
137
  }
138
+ t.reverse().forEach(({ fromA, toA, insert }) => {
139
+ A2.splice(doc, path.slice(), fromA, toA - fromA, insert.toString());
140
+ });
130
141
  });
131
142
  return newHeads ?? void 0;
132
143
  };
@@ -141,13 +152,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
141
152
  selection = selection.map(changeSet, 1);
142
153
  view.dispatch({
143
154
  changes: changeSet,
144
- annotations: reconcileAnnotationType.of({})
155
+ annotations: reconcileAnnotation.of(false)
145
156
  });
146
157
  }
147
158
  }
148
159
  view.dispatch({
149
160
  selection,
150
- annotations: reconcileAnnotationType.of({})
161
+ annotations: reconcileAnnotation.of(false)
151
162
  });
152
163
  };
153
164
  var handlePatch = (patch, target, state) => {
@@ -168,7 +179,7 @@ var handleInsert = (target, patch) => {
168
179
  if (index == null) {
169
180
  return [];
170
181
  }
171
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
182
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
172
183
  return [
173
184
  {
174
185
  from: index,
@@ -246,36 +257,37 @@ var PatchSemaphore = class {
246
257
  this._state = _state;
247
258
  this._inReconcile = false;
248
259
  }
249
- // NOTE: Cannot destruct view.state.
250
260
  reconcile(view) {
251
- if (this._inReconcile) {
252
- return;
261
+ if (!this._inReconcile) {
262
+ this._inReconcile = true;
263
+ this.doReconcile(view);
264
+ this._inReconcile = false;
253
265
  }
254
- this._inReconcile = true;
266
+ }
267
+ doReconcile(view) {
255
268
  const path = getPath(view.state, this._state);
256
269
  const oldHeads = getLastHeads(view.state, this._state);
257
270
  let selection = view.state.selection;
258
- const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
271
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
259
272
  const toInvert = transactions.slice().reverse();
260
- for (const tx of toInvert) {
261
- const inverted = tx.changes.invert(tx.startState.doc);
273
+ for (const tr of toInvert) {
274
+ const inverted = tr.changes.invert(tr.startState.doc);
262
275
  selection = selection.map(inverted);
263
276
  view.dispatch({
264
277
  changes: inverted,
265
- annotations: reconcileAnnotationType.of(true)
278
+ annotations: reconcileAnnotation.of(true)
266
279
  });
267
280
  }
268
281
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
282
  if (newHeads === null || newHeads === void 0) {
270
- newHeads = automerge2.getHeads(this._handle.docSync());
283
+ newHeads = A3.getHeads(this._handle.docSync());
271
284
  }
272
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
285
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
273
286
  updateCodeMirror(view, selection, path, diff);
274
287
  view.dispatch({
275
288
  effects: updateHeads(newHeads),
276
- annotations: reconcileAnnotationType.of({})
289
+ annotations: reconcileAnnotation.of(false)
277
290
  });
278
- this._inReconcile = false;
279
291
  }
280
292
  };
281
293
 
@@ -317,12 +329,11 @@ var Cursor = class _Cursor {
317
329
  };
318
330
 
319
331
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
320
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
321
- var automerge3 = ({ handle, path }) => {
322
- const state = StateField.define({
332
+ var automerge = ({ handle, path }) => {
333
+ const syncState = StateField.define({
323
334
  create: () => ({
324
335
  path: path.slice(),
325
- lastHeads: A.getHeads(handle.docSync()),
336
+ lastHeads: A4.getHeads(handle.docSync()),
326
337
  unreconciledTransactions: []
327
338
  }),
328
339
  update: (value, tr) => {
@@ -333,7 +344,7 @@ var automerge3 = ({ handle, path }) => {
333
344
  };
334
345
  let clearUnreconciled = false;
335
346
  for (const effect of tr.effects) {
336
- if (effect.is(effectType)) {
347
+ if (effect.is(updateHeadsEffect)) {
337
348
  result.lastHeads = effect.value.newHeads;
338
349
  clearUnreconciled = true;
339
350
  }
@@ -341,29 +352,21 @@ var automerge3 = ({ handle, path }) => {
341
352
  if (clearUnreconciled) {
342
353
  result.unreconciledTransactions = [];
343
354
  } else {
344
- if (!isReconcileTx(tr)) {
355
+ if (!isReconcile(tr)) {
345
356
  result.unreconciledTransactions.push(tr);
346
357
  }
347
358
  }
348
359
  return result;
349
360
  }
350
361
  });
351
- const semaphore = new PatchSemaphore(handle, state);
362
+ const semaphore = new PatchSemaphore(handle, syncState);
352
363
  return [
353
364
  Cursor.converter.of(cursorConverter(handle, path)),
365
+ syncState,
354
366
  // Reconcile external updates.
355
367
  ViewPlugin.fromClass(class {
356
368
  constructor(_view) {
357
369
  this._view = _view;
358
- invariant(this._view, void 0, {
359
- F: __dxlog_file,
360
- L: 66,
361
- S: this,
362
- A: [
363
- "this._view",
364
- ""
365
- ]
366
- });
367
370
  handle.addListener("change", this._handleChange.bind(this));
368
371
  }
369
372
  destroy() {
@@ -379,7 +382,17 @@ var automerge3 = ({ handle, path }) => {
379
382
  semaphore.reconcile(view);
380
383
  }
381
384
  }),
382
- state
385
+ // TODO(burdon): Record undo transactions.
386
+ // See https://github.com/yjs/y-codemirror.next/tree/main
387
+ // https://codemirror.net/examples/inverted-effect
388
+ invertedEffects.of((tr) => {
389
+ const effects = [];
390
+ if (!tr.changes.empty) {
391
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
392
+ });
393
+ }
394
+ return effects;
395
+ })
383
396
  ];
384
397
  };
385
398
 
@@ -398,13 +411,13 @@ var dummyProvider = {
398
411
  update: () => {
399
412
  }
400
413
  };
401
- var AwarenessProvider = Facet2.define({
414
+ var awarenessProvider = Facet2.define({
402
415
  combine: (providers) => providers[0] ?? dummyProvider
403
416
  });
404
417
  var RemoteSelectionChangedAnnotation = Annotation2.define();
405
418
  var awareness = (provider = dummyProvider) => {
406
419
  return [
407
- AwarenessProvider.of(provider),
420
+ awarenessProvider.of(provider),
408
421
  ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
409
422
  decorations: (value) => value.decorations
410
423
  }),
@@ -418,7 +431,7 @@ var RemoteSelectionsDecorator = class {
418
431
  this._lastAnchor = void 0;
419
432
  this._lastHead = void 0;
420
433
  this._cursorConverter = view.state.facet(Cursor.converter);
421
- this._provider = view.state.facet(AwarenessProvider);
434
+ this._provider = view.state.facet(awarenessProvider);
422
435
  this._provider.open();
423
436
  this._provider.remoteStateChange.on(this._ctx, () => {
424
437
  view.dispatch({
@@ -640,8 +653,8 @@ var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }
640
653
  // packages/ui/react-ui-editor/src/extensions/blast.ts
641
654
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
642
655
  import defaultsDeep from "lodash.defaultsdeep";
643
- import { invariant as invariant2 } from "@dxos/invariant";
644
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
656
+ import { invariant } from "@dxos/invariant";
657
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
645
658
  var defaultOptions = {
646
659
  effect: 2,
647
660
  maxParticles: 200,
@@ -767,8 +780,8 @@ var Blaster = class {
767
780
  return this._node;
768
781
  }
769
782
  initialize() {
770
- invariant2(!this._canvas && !this._ctx, void 0, {
771
- F: __dxlog_file2,
783
+ invariant(!this._canvas && !this._ctx, void 0, {
784
+ F: __dxlog_file,
772
785
  L: 138,
773
786
  S: this,
774
787
  A: [
@@ -804,8 +817,8 @@ var Blaster = class {
804
817
  }
805
818
  }
806
819
  start() {
807
- invariant2(this._canvas && this._ctx, void 0, {
808
- F: __dxlog_file2,
820
+ invariant(this._canvas && this._ctx, void 0, {
821
+ F: __dxlog_file,
809
822
  L: 177,
810
823
  S: this,
811
824
  A: [
@@ -948,12 +961,12 @@ var random = (min, max) => {
948
961
  };
949
962
 
950
963
  // packages/ui/react-ui-editor/src/extensions/comments.ts
951
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
964
+ import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
965
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
952
966
  import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
953
967
  import sortBy from "lodash.sortby";
954
968
  import { useEffect } from "react";
955
969
  import { debounce } from "@dxos/async";
956
- import { invariant as invariant3 } from "@dxos/invariant";
957
970
  import { nonNullable } from "@dxos/util";
958
971
 
959
972
  // packages/ui/react-ui-editor/src/styles/cursors.ts
@@ -1025,13 +1038,13 @@ var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue)
1025
1038
 
1026
1039
  // packages/ui/react-ui-editor/src/util.ts
1027
1040
  import { log } from "@dxos/log";
1028
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1041
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1029
1042
  var callbackWrapper = (fn) => (...args) => {
1030
1043
  try {
1031
1044
  return fn(...args);
1032
1045
  } catch (err) {
1033
1046
  log.catch(err, void 0, {
1034
- F: __dxlog_file3,
1047
+ F: __dxlog_file2,
1035
1048
  L: 18,
1036
1049
  S: void 0,
1037
1050
  C: (f, a) => f(...a)
@@ -1059,7 +1072,6 @@ var logChanges = (trs) => {
1059
1072
  };
1060
1073
 
1061
1074
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1062
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1063
1075
  var styles2 = EditorView5.baseTheme({
1064
1076
  "& .cm-comment": {
1065
1077
  backgroundColor: getToken("extend.colors.yellow.50")
@@ -1068,14 +1080,12 @@ var styles2 = EditorView5.baseTheme({
1068
1080
  backgroundColor: getToken("extend.colors.yellow.100")
1069
1081
  }
1070
1082
  });
1071
- var marks = {
1072
- highlight: Decoration2.mark({
1073
- class: "cm-comment"
1074
- }),
1075
- highlightActive: Decoration2.mark({
1076
- class: "cm-comment-current"
1077
- })
1078
- };
1083
+ var commentMark = Decoration2.mark({
1084
+ class: "cm-comment"
1085
+ });
1086
+ var commentCurrentMark = Decoration2.mark({
1087
+ class: "cm-comment-current"
1088
+ });
1079
1089
  var setFocus = (view, id, center = true) => {
1080
1090
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1081
1091
  if (!comment?.comment.cursor) {
@@ -1138,7 +1148,7 @@ var commentsState = StateField2.define({
1138
1148
  return value;
1139
1149
  }
1140
1150
  });
1141
- var highlightDecorations = EditorView5.decorations.compute([
1151
+ var commentsDecorations = EditorView5.decorations.compute([
1142
1152
  commentsState
1143
1153
  ], (state) => {
1144
1154
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -1149,9 +1159,9 @@ var highlightDecorations = EditorView5.decorations.compute([
1149
1159
  return void 0;
1150
1160
  }
1151
1161
  if (comment.comment.id === current) {
1152
- return marks.highlightActive.range(range.from, range.to);
1162
+ return commentCurrentMark.range(range.from, range.to);
1153
1163
  } else {
1154
- return marks.highlight.range(range.from, range.to);
1164
+ return commentMark.range(range.from, range.to);
1155
1165
  }
1156
1166
  }).filter(nonNullable);
1157
1167
  return Decoration2.set(decorations);
@@ -1180,84 +1190,124 @@ var trackPastedComments = (onUpdate) => {
1180
1190
  cut: handleTrack,
1181
1191
  copy: handleTrack
1182
1192
  }),
1183
- // Handle paste.
1184
- EditorView5.updateListener.of(({ view, state, transactions }) => {
1193
+ // Track deleted comments.
1194
+ invertedEffects2.of((tr) => {
1195
+ const { comments: comments2 } = tr.startState.field(commentsState);
1196
+ const effects = [];
1197
+ tr.changes.iterChangedRanges((fromA, toA) => {
1198
+ for (const { comment: { id }, range: { from, to } } of comments2) {
1199
+ if (from < to && from >= fromA && to <= toA) {
1200
+ effects.push(restoreCommentEffect.of({
1201
+ id,
1202
+ from,
1203
+ to
1204
+ }));
1205
+ }
1206
+ }
1207
+ });
1208
+ return effects;
1209
+ }),
1210
+ // Handle paste or the undo of comment deletion.
1211
+ EditorView5.updateListener.of((update2) => {
1212
+ const restore = [];
1213
+ for (let i = 0; i < update2.transactions.length; i++) {
1214
+ const tr = update2.transactions[i];
1215
+ for (let j = 0; j < restore.length; j++) {
1216
+ restore[j] = mapTrackedComment(restore[j], tr.changes);
1217
+ }
1218
+ for (const effect of tr.effects) {
1219
+ if (effect.is(restoreCommentEffect)) {
1220
+ restore.push(effect.value);
1221
+ }
1222
+ }
1223
+ }
1185
1224
  if (tracked) {
1186
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1225
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1187
1226
  if (paste) {
1188
1227
  let found = -1;
1189
1228
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1190
1229
  if (text.eq(tracked.text)) {
1191
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1192
- fromB = transactions[i].changes.mapPos(fromB);
1230
+ for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1231
+ fromB = update2.transactions[i].changes.mapPos(fromB);
1193
1232
  }
1194
1233
  found = fromB;
1195
1234
  }
1196
1235
  });
1197
1236
  if (found > -1) {
1198
1237
  for (const moved of tracked.comments) {
1199
- const range = {
1238
+ restore.push({
1239
+ id: moved.id,
1200
1240
  from: found + moved.from,
1201
1241
  to: found + moved.to
1202
- };
1203
- const cursor = Cursor.getCursorFromRange(state, range);
1204
- onUpdate(moved.id, cursor);
1242
+ });
1205
1243
  }
1206
1244
  }
1207
1245
  tracked = null;
1208
1246
  }
1209
1247
  }
1248
+ for (const comment of restore) {
1249
+ const { comments: comments2 } = update2.startState.field(commentsState);
1250
+ const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
1251
+ if (!exists) {
1252
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
1253
+ onUpdate(comment.id, cursor);
1254
+ }
1255
+ }
1210
1256
  })
1211
1257
  ];
1212
1258
  };
1213
- var comments = (options = {}) => {
1214
- const { key: shortcut = "meta-'" } = options;
1215
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1216
- const createCommentThread = (view) => {
1217
- invariant3(options.onCreate, void 0, {
1218
- F: __dxlog_file4,
1219
- L: 259,
1220
- S: void 0,
1221
- A: [
1222
- "options.onCreate",
1223
- ""
1224
- ]
1259
+ var mapTrackedComment = (comment, changes) => ({
1260
+ id: comment.id,
1261
+ from: changes.mapPos(comment.from, 1),
1262
+ to: changes.mapPos(comment.to, 1)
1263
+ });
1264
+ var restoreCommentEffect = StateEffect2.define({
1265
+ map: mapTrackedComment
1266
+ });
1267
+ var optionsFacet = Facet3.define({
1268
+ combine: (providers) => providers[0]
1269
+ });
1270
+ var createComment = (view) => {
1271
+ const options = view.state.facet(optionsFacet);
1272
+ const { from, to } = view.state.selection.main;
1273
+ if (from === to) {
1274
+ return false;
1275
+ }
1276
+ if (to === view.state.doc.length) {
1277
+ view.dispatch({
1278
+ changes: {
1279
+ from: to,
1280
+ insert: "\n"
1281
+ }
1225
1282
  });
1226
- const { from, to } = view.state.selection.main;
1227
- if (from === to) {
1228
- return false;
1229
- }
1230
- if (to === view.state.doc.length) {
1283
+ }
1284
+ const cursor = Cursor.getCursorFromRange(view.state, {
1285
+ from,
1286
+ to
1287
+ });
1288
+ if (cursor) {
1289
+ const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1290
+ if (id) {
1231
1291
  view.dispatch({
1232
- changes: {
1233
- from: to,
1234
- insert: "\n"
1292
+ effects: setSelection.of({
1293
+ current: id
1294
+ }),
1295
+ selection: {
1296
+ anchor: from
1235
1297
  }
1236
1298
  });
1299
+ return true;
1237
1300
  }
1238
- const cursor = Cursor.getCursorFromRange(view.state, {
1239
- from,
1240
- to
1241
- });
1242
- if (cursor) {
1243
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1244
- if (id) {
1245
- view.dispatch({
1246
- effects: setSelection.of({
1247
- current: id
1248
- }),
1249
- selection: {
1250
- anchor: from
1251
- }
1252
- });
1253
- return true;
1254
- }
1255
- }
1256
- return false;
1257
- };
1301
+ }
1302
+ return false;
1303
+ };
1304
+ var comments = (options = {}) => {
1305
+ const { key: shortcut = "meta-'" } = options;
1306
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1258
1307
  return [
1308
+ optionsFacet.of(options),
1259
1309
  commentsState,
1260
- highlightDecorations,
1310
+ commentsDecorations,
1261
1311
  styles2,
1262
1312
  //
1263
1313
  // Keymap.
@@ -1265,7 +1315,7 @@ var comments = (options = {}) => {
1265
1315
  options.onCreate ? keymap3.of([
1266
1316
  {
1267
1317
  key: shortcut,
1268
- run: callbackWrapper(createCommentThread)
1318
+ run: callbackWrapper(createComment)
1269
1319
  }
1270
1320
  ]) : [],
1271
1321
  //
@@ -1657,21 +1707,37 @@ var styles3 = EditorView8.baseTheme({
1657
1707
  },
1658
1708
  "& .cm-codeblock": {
1659
1709
  display: "inline-block",
1660
- width: "100%"
1710
+ width: "100%",
1711
+ position: "relative",
1712
+ "&::after": {
1713
+ content: '""',
1714
+ position: "absolute",
1715
+ inset: 0
1716
+ }
1661
1717
  },
1662
- "&light .cm-codeblock, &light .cm-codeblock.cm-activeLine": {
1663
- background: getToken("extend.colors.neutral.25")
1718
+ "&light .cm-codeblock": {
1719
+ "&::after": {
1720
+ background: getToken("extend.semanticColors.input.light"),
1721
+ mixBlendMode: "darken"
1722
+ }
1664
1723
  },
1665
- "&dark .cm-codeblock, &dark .cm-codeblock.cm-activeLine": {
1666
- background: getToken("extend.colors.neutral.850")
1724
+ "&dark .cm-codeblock": {
1725
+ "&::after": {
1726
+ background: getToken("extend.semanticColors.input.dark"),
1727
+ mixBlendMode: "lighten"
1728
+ }
1667
1729
  },
1668
1730
  "& .cm-codeblock-first": {
1669
- borderTopLeftRadius: ".5rem",
1670
- borderTopRightRadius: ".5rem"
1731
+ "&::after": {
1732
+ borderTopLeftRadius: ".5rem",
1733
+ borderTopRightRadius: ".5rem"
1734
+ }
1671
1735
  },
1672
1736
  "& .cm-codeblock-last": {
1673
- borderBottomLeftRadius: ".5rem",
1674
- borderBottomRightRadius: ".5rem"
1737
+ "&::after": {
1738
+ borderBottomLeftRadius: ".5rem",
1739
+ borderBottomRightRadius: ".5rem"
1740
+ }
1675
1741
  }
1676
1742
  });
1677
1743
  var getLineRange = (lines, from, to) => {
@@ -1743,16 +1809,130 @@ var code2 = () => {
1743
1809
  ];
1744
1810
  };
1745
1811
 
1746
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1812
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1747
1813
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
1814
  import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
- import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1815
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1816
+ var setHeading = (level) => (view) => {
1817
+ const { selection: { ranges }, doc } = view.state;
1818
+ const changes = [];
1819
+ for (const range of ranges) {
1820
+ const { number } = doc.lineAt(range.anchor);
1821
+ const { from } = doc.line(number);
1822
+ const line = doc.sliceString(from, from + 7);
1823
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1824
+ const current = marks?.length ?? 0;
1825
+ if (level !== current) {
1826
+ changes.push({
1827
+ from,
1828
+ to: from + current + (spaces?.length ?? 0),
1829
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1830
+ });
1831
+ }
1832
+ }
1833
+ if (changes.length) {
1834
+ view.dispatch({
1835
+ changes
1836
+ });
1837
+ }
1838
+ return true;
1839
+ };
1840
+ var toggleStyle = (mark2) => (view) => {
1841
+ const { ranges } = view.state.selection;
1842
+ for (const range of ranges) {
1843
+ if (range.from === range.to) {
1844
+ return false;
1845
+ }
1846
+ view.dispatch({
1847
+ changes: [
1848
+ {
1849
+ from: range.from,
1850
+ insert: mark2
1851
+ },
1852
+ {
1853
+ from: range.to,
1854
+ insert: mark2
1855
+ }
1856
+ ]
1857
+ });
1858
+ }
1859
+ return true;
1860
+ };
1861
+ var toggleBold = toggleStyle("**");
1862
+ var toggleItalic = toggleStyle("_");
1863
+ var toggleStrikethrough = toggleStyle("~~");
1864
+ var toggleList = (view) => {
1865
+ };
1866
+ var formatting = (options = {}) => {
1867
+ return [
1868
+ keymap5.of([
1869
+ {
1870
+ key: "meta-b",
1871
+ run: toggleBold
1872
+ }
1873
+ ]),
1874
+ styling()
1875
+ ];
1876
+ };
1877
+ var styling = () => {
1878
+ const buildDecorations7 = (view) => {
1879
+ const builder = new RangeSetBuilder2();
1880
+ const { state } = view;
1881
+ const cursor = state.selection.main.head;
1882
+ const replace = (node, marks) => {
1883
+ if (cursor <= node.from || cursor >= node.to) {
1884
+ for (const mark2 of marks) {
1885
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1886
+ }
1887
+ }
1888
+ };
1889
+ for (const { from, to } of view.visibleRanges) {
1890
+ syntaxTree2(state).iterate({
1891
+ enter: (node) => {
1892
+ switch (node.name) {
1893
+ case "Emphasis":
1894
+ case "StrongEmphasis": {
1895
+ const marks = node.node.getChildren("EmphasisMark");
1896
+ replace(node, marks);
1897
+ break;
1898
+ }
1899
+ case "Strikethrough": {
1900
+ const marks = node.node.getChildren("StrikethroughMark");
1901
+ replace(node, marks);
1902
+ break;
1903
+ }
1904
+ }
1905
+ },
1906
+ from,
1907
+ to
1908
+ });
1909
+ }
1910
+ return builder.finish();
1911
+ };
1912
+ return [
1913
+ ViewPlugin4.fromClass(class {
1914
+ constructor(view) {
1915
+ this.decorations = buildDecorations7(view);
1916
+ }
1917
+ update(update2) {
1918
+ this.decorations = buildDecorations7(update2.view);
1919
+ }
1920
+ }, {
1921
+ decorations: (value) => value.decorations
1922
+ })
1923
+ ];
1924
+ };
1925
+
1926
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1927
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1928
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1929
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1750
1930
  var buildDecorations2 = (view) => {
1751
- const builder = new RangeSetBuilder2();
1931
+ const builder = new RangeSetBuilder3();
1752
1932
  const { state } = view;
1753
1933
  const cursor = state.selection.main.head;
1754
1934
  for (const { from, to } of view.visibleRanges) {
1755
- syntaxTree2(state).iterate({
1935
+ syntaxTree3(state).iterate({
1756
1936
  enter: (node) => {
1757
1937
  switch (node.name) {
1758
1938
  case "ATXHeading1":
@@ -1763,8 +1943,8 @@ var buildDecorations2 = (view) => {
1763
1943
  case "ATXHeading6": {
1764
1944
  const mark2 = node.node.getChild("HeaderMark");
1765
1945
  if (mark2) {
1766
- if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
- builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1946
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1947
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1768
1948
  }
1769
1949
  }
1770
1950
  }
@@ -1778,7 +1958,7 @@ var buildDecorations2 = (view) => {
1778
1958
  };
1779
1959
  var heading2 = () => {
1780
1960
  return [
1781
- ViewPlugin4.fromClass(class {
1961
+ ViewPlugin5.fromClass(class {
1782
1962
  constructor(view) {
1783
1963
  this.decorations = buildDecorations2(view);
1784
1964
  }
@@ -1792,9 +1972,9 @@ var heading2 = () => {
1792
1972
  };
1793
1973
 
1794
1974
  // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
1795
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1796
- import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1797
- import { Decoration as Decoration5, EditorView as EditorView9, ViewPlugin as ViewPlugin5, WidgetType as WidgetType3 } from "@codemirror/view";
1975
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
1976
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
1977
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1798
1978
  var styles4 = EditorView9.baseTheme({
1799
1979
  "& .cm-hr": {
1800
1980
  // Note that block-level decorations should not have vertical margins,
@@ -1808,15 +1988,15 @@ var HorizontalRuleWidget = class extends WidgetType3 {
1808
1988
  return el;
1809
1989
  }
1810
1990
  };
1811
- var decoration = Decoration5.replace({
1991
+ var decoration = Decoration6.replace({
1812
1992
  widget: new HorizontalRuleWidget()
1813
1993
  });
1814
1994
  var buildDecorations3 = (view) => {
1815
- const builder = new RangeSetBuilder3();
1995
+ const builder = new RangeSetBuilder4();
1816
1996
  const { state } = view;
1817
1997
  const cursor = state.selection.main.head;
1818
1998
  for (const { from, to } of view.visibleRanges) {
1819
- syntaxTree3(state).iterate({
1999
+ syntaxTree4(state).iterate({
1820
2000
  enter: (node) => {
1821
2001
  if (node.name === "HorizontalRule") {
1822
2002
  if (cursor <= node.from || cursor >= node.to) {
@@ -1832,7 +2012,7 @@ var buildDecorations3 = (view) => {
1832
2012
  };
1833
2013
  var hr = () => {
1834
2014
  return [
1835
- ViewPlugin5.fromClass(class {
2015
+ ViewPlugin6.fromClass(class {
1836
2016
  constructor(view) {
1837
2017
  this.decorations = buildDecorations3(view);
1838
2018
  }
@@ -1847,13 +2027,13 @@ var hr = () => {
1847
2027
  };
1848
2028
 
1849
2029
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2030
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1851
2031
  import { StateField as StateField3 } from "@codemirror/state";
1852
- import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2032
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
1853
2033
  var image = (options = {}) => {
1854
2034
  return StateField3.define({
1855
2035
  create: (state) => {
1856
- return Decoration6.set(buildDecorations4(0, state.doc.length, state));
2036
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
1857
2037
  },
1858
2038
  update: (value, tr) => {
1859
2039
  if (!tr.docChanged && !tr.selection) {
@@ -1882,14 +2062,14 @@ var image = (options = {}) => {
1882
2062
  var buildDecorations4 = (from, to, state) => {
1883
2063
  const decorations = [];
1884
2064
  const cursor = state.selection.main.head;
1885
- syntaxTree4(state).iterate({
2065
+ syntaxTree5(state).iterate({
1886
2066
  enter: (node) => {
1887
2067
  if (node.name === "Image") {
1888
2068
  const urlNode = node.node.getChild("URL");
1889
2069
  if (urlNode) {
1890
2070
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
2071
  const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
- decorations.push(Decoration6.replace({
2072
+ decorations.push(Decoration7.replace({
1893
2073
  block: true,
1894
2074
  widget: new ImageWidget(url)
1895
2075
  }).range(hide ? node.from : node.to, node.to));
@@ -1919,13 +2099,13 @@ var ImageWidget = class extends WidgetType4 {
1919
2099
  };
1920
2100
 
1921
2101
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
1922
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1923
- import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
1924
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration7, ViewPlugin as ViewPlugin6, WidgetType as WidgetType5 } from "@codemirror/view";
2102
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2103
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2104
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
1925
2105
  import { tooltipContent } from "@dxos/react-ui-theme";
1926
2106
  var link = (options = {}) => {
1927
2107
  const extensions = [
1928
- ViewPlugin6.fromClass(class {
2108
+ ViewPlugin7.fromClass(class {
1929
2109
  constructor(view) {
1930
2110
  this.decorations = buildDecorations5(view, options);
1931
2111
  }
@@ -1944,7 +2124,7 @@ var link = (options = {}) => {
1944
2124
  // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
2125
  // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
2126
  hoverTooltip2((view, pos, side) => {
1947
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
2127
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
1948
2128
  let link2 = null;
1949
2129
  for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
2130
  link2 = node.name === "Link" ? node : null;
@@ -2021,27 +2201,27 @@ var LinkText = class extends WidgetType5 {
2021
2201
  }
2022
2202
  };
2023
2203
  var buildDecorations5 = (view, options) => {
2024
- const builder = new RangeSetBuilder4();
2204
+ const builder = new RangeSetBuilder5();
2025
2205
  const { state } = view;
2026
2206
  const cursor = state.selection.main.head;
2027
2207
  for (const { from, to } of view.visibleRanges) {
2028
- syntaxTree5(state).iterate({
2208
+ syntaxTree6(state).iterate({
2029
2209
  enter: (node) => {
2030
2210
  if (node.name === "Link") {
2031
- const marks2 = node.node.getChildren("LinkMark");
2032
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
2211
+ const marks = node.node.getChildren("LinkMark");
2212
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2033
2213
  const urlNode = node.node.getChild("URL");
2034
2214
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
2215
  if (!url) {
2036
2216
  return false;
2037
2217
  }
2038
2218
  if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
- builder.add(node.from, node.to, Decoration7.replace({
2219
+ builder.add(node.from, node.to, Decoration8.replace({
2040
2220
  widget: new LinkText(text, options.link ? url : void 0)
2041
2221
  }));
2042
2222
  }
2043
2223
  if (options.onRender) {
2044
- builder.add(node.to, node.to, Decoration7.replace({
2224
+ builder.add(node.to, node.to, Decoration8.replace({
2045
2225
  widget: new LinkButton(url, options.onRender)
2046
2226
  }));
2047
2227
  }
@@ -2056,9 +2236,9 @@ var buildDecorations5 = (view, options) => {
2056
2236
  };
2057
2237
 
2058
2238
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2059
- import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2060
- import { RangeSetBuilder as RangeSetBuilder5, StateField as StateField4 } from "@codemirror/state";
2061
- import { Decoration as Decoration8, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2239
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2240
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2241
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2062
2242
  var table = (options = {}) => {
2063
2243
  return StateField4.define({
2064
2244
  create: (state) => update(state, options),
@@ -2067,7 +2247,7 @@ var table = (options = {}) => {
2067
2247
  });
2068
2248
  };
2069
2249
  var update = (state, options) => {
2070
- const builder = new RangeSetBuilder5();
2250
+ const builder = new RangeSetBuilder6();
2071
2251
  const cursor = state.selection.main.head;
2072
2252
  const tables = [];
2073
2253
  const getTable = () => tables[tables.length - 1];
@@ -2075,7 +2255,7 @@ var update = (state, options) => {
2075
2255
  const table2 = getTable();
2076
2256
  return table2.rows?.[table2.rows.length - 1];
2077
2257
  };
2078
- syntaxTree6(state).iterate({
2258
+ syntaxTree7(state).iterate({
2079
2259
  enter: (node) => {
2080
2260
  switch (node.name) {
2081
2261
  case "Table": {
@@ -2107,10 +2287,10 @@ var update = (state, options) => {
2107
2287
  });
2108
2288
  tables.forEach((table2) => {
2109
2289
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2110
- hide && builder.add(table2.from, table2.to, Decoration8.replace({
2290
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2111
2291
  widget: new TableWidget(table2)
2112
2292
  }));
2113
- builder.add(table2.from, table2.to, Decoration8.mark({
2293
+ builder.add(table2.from, table2.to, Decoration9.mark({
2114
2294
  class: "cm-table"
2115
2295
  }));
2116
2296
  });
@@ -2154,9 +2334,9 @@ var TableWidget = class extends WidgetType6 {
2154
2334
  };
2155
2335
 
2156
2336
  // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2157
- import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2158
- import { RangeSetBuilder as RangeSetBuilder6 } from "@codemirror/state";
2159
- import { EditorView as EditorView12, Decoration as Decoration9, WidgetType as WidgetType7, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
2337
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2338
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2339
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2160
2340
  var styles5 = EditorView12.baseTheme({
2161
2341
  "& .cm-task": {
2162
2342
  color: getToken("extend.colors.blue.500")
@@ -2203,23 +2383,23 @@ var CheckboxWidget = class extends WidgetType7 {
2203
2383
  return false;
2204
2384
  }
2205
2385
  };
2206
- var checkedDecoration = Decoration9.replace({
2386
+ var checkedDecoration = Decoration10.replace({
2207
2387
  widget: new CheckboxWidget(true)
2208
2388
  });
2209
- var uncheckedDecoration = Decoration9.replace({
2389
+ var uncheckedDecoration = Decoration10.replace({
2210
2390
  widget: new CheckboxWidget(false)
2211
2391
  });
2212
2392
  var buildDecorations6 = (view) => {
2213
- const builder = new RangeSetBuilder6();
2393
+ const builder = new RangeSetBuilder7();
2214
2394
  const { state } = view;
2215
2395
  const cursor = state.selection.main.head;
2216
2396
  for (const { from, to } of view.visibleRanges) {
2217
- syntaxTree7(state).iterate({
2397
+ syntaxTree8(state).iterate({
2218
2398
  enter: (node) => {
2219
2399
  if (node.name === "TaskMarker") {
2220
2400
  if (cursor < node.from || cursor > node.to) {
2221
2401
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
- builder.add(node.from - 2, node.from - 1, Decoration9.mark({
2402
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2223
2403
  class: "cm-task"
2224
2404
  }));
2225
2405
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2234,7 +2414,7 @@ var buildDecorations6 = (view) => {
2234
2414
  };
2235
2415
  var tasklist = (options = {}) => {
2236
2416
  return [
2237
- ViewPlugin7.fromClass(class {
2417
+ ViewPlugin8.fromClass(class {
2238
2418
  constructor(view) {
2239
2419
  this.decorations = buildDecorations6(view);
2240
2420
  }
@@ -2285,7 +2465,7 @@ var outliner = (options = {}) => {
2285
2465
  };
2286
2466
 
2287
2467
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2288
- import { keymap as keymap5 } from "@codemirror/view";
2468
+ import { keymap as keymap6 } from "@codemirror/view";
2289
2469
  var defaultItems = [
2290
2470
  "hello world!",
2291
2471
  "this is a test.",
@@ -2295,7 +2475,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2295
2475
  let t;
2296
2476
  let idx = 0;
2297
2477
  return [
2298
- keymap5.of([
2478
+ keymap6.of([
2299
2479
  {
2300
2480
  // Reset.
2301
2481
  key: "alt-meta-'",
@@ -2407,7 +2587,7 @@ var defaultTheme = {
2407
2587
  paddingInline: 0
2408
2588
  },
2409
2589
  ".cm-activeLine": {
2410
- background: "inherit"
2590
+ background: "transparent"
2411
2591
  },
2412
2592
  //
2413
2593
  // Selection
@@ -2578,7 +2758,7 @@ var codeTheme = {
2578
2758
  };
2579
2759
 
2580
2760
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2581
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2761
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2582
2762
  var EditorModes = [
2583
2763
  "default",
2584
2764
  "vim"
@@ -2612,8 +2792,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2612
2792
  // TODO(burdon): Doesn't catch errors in keymap functions.
2613
2793
  EditorView13.exceptionSink.of((err) => {
2614
2794
  log2.catch(err, void 0, {
2615
- F: __dxlog_file5,
2616
- L: 123,
2795
+ F: __dxlog_file3,
2796
+ L: 124,
2617
2797
  S: void 0,
2618
2798
  C: (f, a) => f(...a)
2619
2799
  });
@@ -2630,6 +2810,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2630
2810
  class: slots.content?.className ?? ""
2631
2811
  }),
2632
2812
  // State.
2813
+ EditorView13.editable.of(!readonly),
2633
2814
  EditorState2.readOnly.of(!!readonly),
2634
2815
  // Storage and replication.
2635
2816
  // NOTE: This must come before user extensions.
@@ -2638,7 +2819,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2638
2819
  editorMode === "vim" && vim(),
2639
2820
  // Custom.
2640
2821
  ...extensions
2641
- ].filter(Boolean)
2822
+ ].filter(nonNullable2)
2642
2823
  });
2643
2824
  const newView = new EditorView13({
2644
2825
  state,
@@ -2750,9 +2931,9 @@ var defaultMarkdownSlots = {
2750
2931
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2751
2932
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2752
2933
  import { Context as Context2 } from "@dxos/context";
2753
- import { invariant as invariant4 } from "@dxos/invariant";
2934
+ import { invariant as invariant2 } from "@dxos/invariant";
2754
2935
  import { log as log3 } from "@dxos/log";
2755
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2936
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2756
2937
  var DEBOUNCE_INTERVAL = 100;
2757
2938
  var SpaceAwarenessProvider = class {
2758
2939
  constructor(params) {
@@ -2792,7 +2973,7 @@ var SpaceAwarenessProvider = class {
2792
2973
  log3.debug("failed to query awareness", {
2793
2974
  err
2794
2975
  }, {
2795
- F: __dxlog_file6,
2976
+ F: __dxlog_file4,
2796
2977
  L: 89,
2797
2978
  S: this,
2798
2979
  C: (f, a) => f(...a)
@@ -2808,8 +2989,8 @@ var SpaceAwarenessProvider = class {
2808
2989
  return Array.from(this._remoteStates.values());
2809
2990
  }
2810
2991
  update(position) {
2811
- invariant4(this._postTask, void 0, {
2812
- F: __dxlog_file6,
2992
+ invariant2(this._postTask, void 0, {
2993
+ F: __dxlog_file4,
2813
2994
  L: 104,
2814
2995
  S: this,
2815
2996
  A: [
@@ -2825,8 +3006,8 @@ var SpaceAwarenessProvider = class {
2825
3006
  this._postTask.schedule();
2826
3007
  }
2827
3008
  _handleQueryMessage() {
2828
- invariant4(this._postTask, void 0, {
2829
- F: __dxlog_file6,
3009
+ invariant2(this._postTask, void 0, {
3010
+ F: __dxlog_file4,
2830
3011
  L: 115,
2831
3012
  S: this,
2832
3013
  A: [
@@ -2837,8 +3018,8 @@ var SpaceAwarenessProvider = class {
2837
3018
  this._postTask.schedule();
2838
3019
  }
2839
3020
  _handlePostMessage(message) {
2840
- invariant4(message.kind === "post", void 0, {
2841
- F: __dxlog_file6,
3021
+ invariant2(message.kind === "post", void 0, {
3022
+ F: __dxlog_file4,
2842
3023
  L: 120,
2843
3024
  S: this,
2844
3025
  A: [
@@ -2858,6 +3039,38 @@ var modelState = StateField5.define({
2858
3039
  update: (model) => model
2859
3040
  });
2860
3041
 
3042
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3043
+ var useActionHandler = (view) => {
3044
+ return (action) => {
3045
+ if (!view) {
3046
+ return;
3047
+ }
3048
+ switch (action.type) {
3049
+ case "heading":
3050
+ setHeading(parseInt(action.data))(view);
3051
+ break;
3052
+ case "bold":
3053
+ toggleBold(view);
3054
+ break;
3055
+ case "italic":
3056
+ toggleItalic(view);
3057
+ break;
3058
+ case "strikethrough":
3059
+ toggleStrikethrough(view);
3060
+ break;
3061
+ case "list":
3062
+ toggleList(view);
3063
+ break;
3064
+ case "comment":
3065
+ createComment(view);
3066
+ break;
3067
+ }
3068
+ setTimeout(() => {
3069
+ view.focus();
3070
+ });
3071
+ };
3072
+ };
3073
+
2861
3074
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
2862
3075
  import { useState as useState2 } from "react";
2863
3076
  var useTextEditor = () => {
@@ -2875,10 +3088,9 @@ import get4 from "lodash.get";
2875
3088
  import { useEffect as useEffect3, useState as useState3 } from "react";
2876
3089
  import { generateName } from "@dxos/display-name";
2877
3090
  import { getRawDoc } from "@dxos/echo-schema";
2878
- import { invariant as invariant5 } from "@dxos/invariant";
3091
+ import { invariant as invariant3 } from "@dxos/invariant";
2879
3092
  import { isAutomergeObject } from "@dxos/react-client/echo";
2880
- import { isNotNullOrUndefined } from "@dxos/util";
2881
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3093
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2882
3094
  var useTextModel = (props) => {
2883
3095
  const { identity, space, text } = props;
2884
3096
  const [model, setModel] = useState3();
@@ -2890,9 +3102,9 @@ var useTextModel = (props) => {
2890
3102
  return model;
2891
3103
  };
2892
3104
  var createModel = ({ space, identity, text }) => {
2893
- invariant5(isAutomergeObject(text), void 0, {
2894
- F: __dxlog_file7,
2895
- L: 35,
3105
+ invariant3(isAutomergeObject(text), void 0, {
3106
+ F: __dxlog_file5,
3107
+ L: 34,
2896
3108
  S: void 0,
2897
3109
  A: [
2898
3110
  "isAutomergeObject(text)",
@@ -2903,7 +3115,7 @@ var createModel = ({ space, identity, text }) => {
2903
3115
  const doc = getRawDoc(obj, [
2904
3116
  obj.field
2905
3117
  ]);
2906
- const awarenessProvider = space && new SpaceAwarenessProvider({
3118
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2907
3119
  space,
2908
3120
  channel: `automerge.awareness.${obj.id}`,
2909
3121
  info: {
@@ -2913,19 +3125,22 @@ var createModel = ({ space, identity, text }) => {
2913
3125
  },
2914
3126
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2915
3127
  });
3128
+ const extensions = [
3129
+ //
3130
+ modelState.init(() => model),
3131
+ automerge({
3132
+ handle: doc.handle,
3133
+ path: doc.path
3134
+ })
3135
+ ];
3136
+ if (awarenessProvider2) {
3137
+ extensions.push(awareness(awarenessProvider2));
3138
+ }
2916
3139
  const model = {
2917
3140
  id: obj.id,
2918
3141
  content: doc,
2919
3142
  text: () => get4(doc.handle.docSync(), doc.path),
2920
- extension: [
2921
- modelState.init(() => model),
2922
- automerge3({
2923
- handle: doc.handle,
2924
- path: doc.path
2925
- }),
2926
- awarenessProvider && AwarenessProvider.of(awarenessProvider),
2927
- awareness()
2928
- ].filter(isNotNullOrUndefined),
3143
+ extension: extensions,
2929
3144
  peer: identity ? {
2930
3145
  id: identity.identityKey.toHex(),
2931
3146
  name: identity.profile?.displayName
@@ -2934,7 +3149,6 @@ var createModel = ({ space, identity, text }) => {
2934
3149
  return model;
2935
3150
  };
2936
3151
  export {
2937
- AwarenessProvider,
2938
3152
  BaseTextEditor,
2939
3153
  Cursor,
2940
3154
  Doc,
@@ -2947,19 +3161,22 @@ export {
2947
3161
  YText,
2948
3162
  YXmlFragment,
2949
3163
  autocomplete,
2950
- automerge3 as automerge,
3164
+ automerge,
2951
3165
  awareness,
3166
+ awarenessProvider,
2952
3167
  basicBundle,
2953
3168
  blast,
2954
3169
  callbackWrapper,
2955
3170
  code2 as code,
2956
3171
  codeTheme,
2957
3172
  comments,
3173
+ createComment,
2958
3174
  defaultMarkdownSlots,
2959
3175
  defaultOptions,
2960
3176
  defaultSlots,
2961
3177
  defaultTextSlots,
2962
3178
  defaultTheme,
3179
+ formatting,
2963
3180
  getToken,
2964
3181
  heading2 as heading,
2965
3182
  hr,
@@ -2977,12 +3194,19 @@ export {
2977
3194
  outliner,
2978
3195
  setComments,
2979
3196
  setFocus,
3197
+ setHeading,
2980
3198
  setSelection,
2981
3199
  table,
2982
3200
  tags2 as tags,
2983
3201
  tasklist,
2984
3202
  textTheme,
3203
+ toggleBold,
3204
+ toggleItalic,
3205
+ toggleList,
3206
+ toggleStrikethrough,
3207
+ toggleStyle,
2985
3208
  typewriter,
3209
+ useActionHandler,
2986
3210
  useComments,
2987
3211
  useTextEditor,
2988
3212
  useTextModel