@dxos/react-ui-editor 0.3.11-main.cc2fe2c → 0.3.11-main.ccc0ca3

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 (82) hide show
  1. package/dist/lib/browser/index.mjs +782 -241
  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 +1 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  8. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
  9. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  16. package/dist/types/src/components/index.d.ts +1 -0
  17. package/dist/types/src/components/index.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  20. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  21. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  22. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  23. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  24. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  25. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  27. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  28. package/dist/types/src/extensions/awareness.d.ts +1 -1
  29. package/dist/types/src/extensions/basic.d.ts +1 -1
  30. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  31. package/dist/types/src/extensions/comments.d.ts +6 -2
  32. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  33. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  34. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  35. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  37. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  38. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  39. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  40. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  41. package/dist/types/src/hooks/index.d.ts +2 -0
  42. package/dist/types/src/hooks/index.d.ts.map +1 -1
  43. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  44. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  45. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  46. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  47. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  48. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  49. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  50. package/dist/types/src/themes/default.d.ts.map +1 -1
  51. package/package.json +39 -26
  52. package/src/components/TextEditor/MarkdownEditor.stories.tsx +10 -16
  53. package/src/components/TextEditor/TextEditor.tsx +24 -12
  54. package/src/components/TextEditor/comments.stories.tsx +3 -3
  55. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  56. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  57. package/src/components/Toolbar/Toolbar.tsx +166 -0
  58. package/src/components/Toolbar/index.ts +5 -0
  59. package/src/components/index.ts +1 -0
  60. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  61. package/src/extensions/automerge/automerge.test.ts +13 -0
  62. package/src/extensions/automerge/automerge.ts +26 -11
  63. package/src/extensions/automerge/cursor.ts +3 -3
  64. package/src/extensions/automerge/defs.ts +9 -9
  65. package/src/extensions/automerge/semaphore.ts +17 -19
  66. package/src/extensions/automerge/update-automerge.ts +12 -6
  67. package/src/extensions/automerge/update-codemirror.ts +5 -5
  68. package/src/extensions/awareness.ts +3 -3
  69. package/src/extensions/basic.ts +15 -13
  70. package/src/extensions/comments.ts +113 -65
  71. package/src/extensions/markdown/bundle.ts +10 -5
  72. package/src/extensions/markdown/code.ts +24 -9
  73. package/src/extensions/markdown/formatting.ts +188 -0
  74. package/src/extensions/markdown/heading.ts +1 -1
  75. package/src/extensions/markdown/index.ts +1 -0
  76. package/src/extensions/markdown/link.ts +4 -0
  77. package/src/hooks/index.ts +2 -0
  78. package/src/hooks/useActionHandler.ts +62 -0
  79. package/src/hooks/useEditorView.ts +29 -0
  80. package/src/hooks/useTextEditor.ts +143 -19
  81. package/src/hooks/useTextModel.ts +11 -8
  82. package/src/themes/default.ts +4 -2
@@ -8,13 +8,13 @@ import { tags as tags2 } from "@lezer/highlight";
8
8
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
9
  import { EditorState as EditorState2 } from "@codemirror/state";
10
10
  import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
12
11
  import { vim } from "@replit/codemirror-vim";
13
12
  import defaultsDeep2 from "lodash.defaultsdeep";
14
13
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
15
14
  import { log as log2 } from "@dxos/log";
16
15
  import { useThemeContext } from "@dxos/react-ui";
17
16
  import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
18
18
 
19
19
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
20
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -53,14 +53,14 @@ var autocomplete = ({ onSearch }) => {
53
53
  };
54
54
 
55
55
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
56
+ import { invertedEffects } from "@codemirror/commands";
56
57
  import { StateField } from "@codemirror/state";
57
58
  import { EditorView, ViewPlugin } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
- import { invariant } from "@dxos/invariant";
59
+ import { next as A4 } from "@dxos/automerge/automerge";
60
60
 
61
61
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
62
62
  import get from "lodash.get";
63
- import { next as automerge } from "@dxos/automerge/automerge";
63
+ import { next as A } from "@dxos/automerge/automerge";
64
64
  var cursorConverter = (handle, path) => ({
65
65
  // TODO(burdon): Handle assoc to associate with a previous character.
66
66
  toCursor: (pos) => {
@@ -72,7 +72,7 @@ var cursorConverter = (handle, path) => ({
72
72
  if (typeof value === "string" && value.length <= pos) {
73
73
  return "end";
74
74
  }
75
- return automerge.getCursor(doc, path.slice(), pos);
75
+ return A.getCursor(doc, path.slice(), pos);
76
76
  },
77
77
  fromCursor: (cursor) => {
78
78
  if (cursor === "") {
@@ -90,26 +90,28 @@ var cursorConverter = (handle, path) => ({
90
90
  return 0;
91
91
  }
92
92
  }
93
- return automerge.getCursorPosition(doc, path.slice(), cursor);
93
+ return A.getCursorPosition(doc, path.slice(), cursor);
94
94
  }
95
95
  });
96
96
 
97
97
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
98
98
  import { Annotation, StateEffect } from "@codemirror/state";
99
- var effectType = StateEffect.define({});
100
- var reconcileAnnotationType = Annotation.define();
101
99
  var getPath = (state, field) => state.field(field).path;
102
100
  var getLastHeads = (state, field) => state.field(field).lastHeads;
103
- var updateHeads = (newHeads) => effectType.of({
101
+ var updateHeadsEffect = StateEffect.define({});
102
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
104
103
  newHeads
105
104
  });
106
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
105
+ var reconcileAnnotation = Annotation.define();
106
+ var isReconcile = (tr) => {
107
+ return !!tr.annotation(reconcileAnnotation);
108
+ };
107
109
 
108
110
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
109
- import { next as automerge2 } from "@dxos/automerge/automerge";
111
+ import { next as A3 } from "@dxos/automerge/automerge";
110
112
 
111
113
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
112
- import { next as am } from "@dxos/automerge/automerge";
114
+ import { next as A2 } from "@dxos/automerge/automerge";
113
115
  var updateAutomerge = (field, handle, transactions, state) => {
114
116
  const { lastHeads, path } = state.field(field);
115
117
  let hasChanges = false;
@@ -122,11 +124,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
122
124
  return void 0;
123
125
  }
124
126
  const newHeads = handle.changeAt(lastHeads, (doc) => {
127
+ const invertedTransactions = [];
125
128
  for (const tr of transactions) {
126
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
127
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
129
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
130
+ invertedTransactions.push({
131
+ from: fromA,
132
+ del: toA - fromA,
133
+ insert
134
+ });
128
135
  });
129
136
  }
137
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
138
+ A2.splice(doc, path.slice(), from, del, insert.toString());
139
+ });
130
140
  });
131
141
  return newHeads ?? void 0;
132
142
  };
@@ -141,13 +151,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
141
151
  selection = selection.map(changeSet, 1);
142
152
  view.dispatch({
143
153
  changes: changeSet,
144
- annotations: reconcileAnnotationType.of({})
154
+ annotations: reconcileAnnotation.of(false)
145
155
  });
146
156
  }
147
157
  }
148
158
  view.dispatch({
149
159
  selection,
150
- annotations: reconcileAnnotationType.of({})
160
+ annotations: reconcileAnnotation.of(false)
151
161
  });
152
162
  };
153
163
  var handlePatch = (patch, target, state) => {
@@ -168,7 +178,7 @@ var handleInsert = (target, patch) => {
168
178
  if (index == null) {
169
179
  return [];
170
180
  }
171
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
181
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
172
182
  return [
173
183
  {
174
184
  from: index,
@@ -246,36 +256,37 @@ var PatchSemaphore = class {
246
256
  this._state = _state;
247
257
  this._inReconcile = false;
248
258
  }
249
- // NOTE: Cannot destruct view.state.
250
259
  reconcile(view) {
251
- if (this._inReconcile) {
252
- return;
260
+ if (!this._inReconcile) {
261
+ this._inReconcile = true;
262
+ this.doReconcile(view);
263
+ this._inReconcile = false;
253
264
  }
254
- this._inReconcile = true;
265
+ }
266
+ doReconcile(view) {
255
267
  const path = getPath(view.state, this._state);
256
268
  const oldHeads = getLastHeads(view.state, this._state);
257
269
  let selection = view.state.selection;
258
- const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
270
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
259
271
  const toInvert = transactions.slice().reverse();
260
- for (const tx of toInvert) {
261
- const inverted = tx.changes.invert(tx.startState.doc);
272
+ for (const tr of toInvert) {
273
+ const inverted = tr.changes.invert(tr.startState.doc);
262
274
  selection = selection.map(inverted);
263
275
  view.dispatch({
264
276
  changes: inverted,
265
- annotations: reconcileAnnotationType.of(true)
277
+ annotations: reconcileAnnotation.of(true)
266
278
  });
267
279
  }
268
280
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
281
  if (newHeads === null || newHeads === void 0) {
270
- newHeads = automerge2.getHeads(this._handle.docSync());
282
+ newHeads = A3.getHeads(this._handle.docSync());
271
283
  }
272
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
284
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
273
285
  updateCodeMirror(view, selection, path, diff);
274
286
  view.dispatch({
275
287
  effects: updateHeads(newHeads),
276
- annotations: reconcileAnnotationType.of({})
288
+ annotations: reconcileAnnotation.of(false)
277
289
  });
278
- this._inReconcile = false;
279
290
  }
280
291
  };
281
292
 
@@ -317,12 +328,11 @@ var Cursor = class _Cursor {
317
328
  };
318
329
 
319
330
  // 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({
331
+ var automerge = ({ handle, path }) => {
332
+ const syncState = StateField.define({
323
333
  create: () => ({
324
334
  path: path.slice(),
325
- lastHeads: A.getHeads(handle.docSync()),
335
+ lastHeads: A4.getHeads(handle.docSync()),
326
336
  unreconciledTransactions: []
327
337
  }),
328
338
  update: (value, tr) => {
@@ -333,7 +343,7 @@ var automerge3 = ({ handle, path }) => {
333
343
  };
334
344
  let clearUnreconciled = false;
335
345
  for (const effect of tr.effects) {
336
- if (effect.is(effectType)) {
346
+ if (effect.is(updateHeadsEffect)) {
337
347
  result.lastHeads = effect.value.newHeads;
338
348
  clearUnreconciled = true;
339
349
  }
@@ -341,29 +351,21 @@ var automerge3 = ({ handle, path }) => {
341
351
  if (clearUnreconciled) {
342
352
  result.unreconciledTransactions = [];
343
353
  } else {
344
- if (!isReconcileTx(tr)) {
354
+ if (!isReconcile(tr)) {
345
355
  result.unreconciledTransactions.push(tr);
346
356
  }
347
357
  }
348
358
  return result;
349
359
  }
350
360
  });
351
- const semaphore = new PatchSemaphore(handle, state);
361
+ const semaphore = new PatchSemaphore(handle, syncState);
352
362
  return [
353
363
  Cursor.converter.of(cursorConverter(handle, path)),
364
+ syncState,
354
365
  // Reconcile external updates.
355
366
  ViewPlugin.fromClass(class {
356
367
  constructor(_view) {
357
368
  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
369
  handle.addListener("change", this._handleChange.bind(this));
368
370
  }
369
371
  destroy() {
@@ -379,7 +381,17 @@ var automerge3 = ({ handle, path }) => {
379
381
  semaphore.reconcile(view);
380
382
  }
381
383
  }),
382
- state
384
+ // TODO(burdon): Record undo transactions.
385
+ // See https://github.com/yjs/y-codemirror.next/tree/main
386
+ // https://codemirror.net/examples/inverted-effect
387
+ invertedEffects.of((tr) => {
388
+ const effects = [];
389
+ if (!tr.changes.empty) {
390
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
391
+ });
392
+ }
393
+ return effects;
394
+ })
383
395
  ];
384
396
  };
385
397
 
@@ -398,13 +410,13 @@ var dummyProvider = {
398
410
  update: () => {
399
411
  }
400
412
  };
401
- var AwarenessProvider = Facet2.define({
413
+ var awarenessProvider = Facet2.define({
402
414
  combine: (providers) => providers[0] ?? dummyProvider
403
415
  });
404
416
  var RemoteSelectionChangedAnnotation = Annotation2.define();
405
417
  var awareness = (provider = dummyProvider) => {
406
418
  return [
407
- AwarenessProvider.of(provider),
419
+ awarenessProvider.of(provider),
408
420
  ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
409
421
  decorations: (value) => value.decorations
410
422
  }),
@@ -418,7 +430,7 @@ var RemoteSelectionsDecorator = class {
418
430
  this._lastAnchor = void 0;
419
431
  this._lastHead = void 0;
420
432
  this._cursorConverter = view.state.facet(Cursor.converter);
421
- this._provider = view.state.facet(AwarenessProvider);
433
+ this._provider = view.state.facet(awarenessProvider);
422
434
  this._provider.open();
423
435
  this._provider.remoteStateChange.on(this._ctx, () => {
424
436
  view.dispatch({
@@ -624,24 +636,25 @@ import { history } from "@codemirror/commands";
624
636
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
625
637
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
626
638
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
627
- var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
628
- lineWrapping ? EditorView3.lineWrapping : [],
639
+ import { isNotFalsy } from "@dxos/util";
640
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
641
+ lineWrapping && EditorView3.lineWrapping,
629
642
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
630
643
  bracketMatching(),
631
644
  closeBrackets(),
632
645
  drawSelection(),
633
646
  highlightActiveLine(),
634
647
  history(),
635
- _placeholder ? placeholder(_placeholder) : [],
648
+ _placeholder && placeholder(_placeholder),
636
649
  // https://github.com/codemirror/theme-one-dark
637
650
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
638
- ];
651
+ ].filter(isNotFalsy);
639
652
 
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,15 +1080,13 @@ 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
- };
1079
- var setFocus = (view, id, center = true) => {
1083
+ var commentMark = Decoration2.mark({
1084
+ class: "cm-comment"
1085
+ });
1086
+ var commentCurrentMark = Decoration2.mark({
1087
+ class: "cm-comment-current"
1088
+ });
1089
+ var focusComment = (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) {
1082
1092
  return;
@@ -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: to
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
  //
@@ -1403,6 +1453,7 @@ import { searchKeymap } from "@codemirror/search";
1403
1453
  import { EditorState } from "@codemirror/state";
1404
1454
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1405
1455
  import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1456
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1406
1457
 
1407
1458
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1408
1459
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -1593,11 +1644,11 @@ var markdownHighlightStyle = (readonly) => {
1593
1644
  };
1594
1645
 
1595
1646
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1596
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1647
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1597
1648
  return [
1649
+ lineWrapping && EditorView7.lineWrapping,
1598
1650
  EditorState.allowMultipleSelections.of(true),
1599
1651
  EditorState.tabSize.of(2),
1600
- EditorView7.lineWrapping,
1601
1652
  // https://github.com/codemirror/basic-setup
1602
1653
  bracketMatching2(),
1603
1654
  closeBrackets2(),
@@ -1607,7 +1658,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1607
1658
  highlightActiveLine2(),
1608
1659
  history2(),
1609
1660
  indentOnInput(),
1610
- _placeholder ? placeholder2(_placeholder) : [],
1661
+ _placeholder && placeholder2(_placeholder),
1611
1662
  // Main extension.
1612
1663
  // https://github.com/codemirror/lang-markdown
1613
1664
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1642,7 +1693,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1642
1693
  // https://codemirror.net/docs/ref/#commands.standardKeymap
1643
1694
  ...standardKeymap
1644
1695
  ])
1645
- ];
1696
+ ].filter(isNotFalsy2);
1646
1697
  };
1647
1698
 
1648
1699
  // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
@@ -1657,21 +1708,37 @@ var styles3 = EditorView8.baseTheme({
1657
1708
  },
1658
1709
  "& .cm-codeblock": {
1659
1710
  display: "inline-block",
1660
- width: "100%"
1711
+ width: "100%",
1712
+ position: "relative",
1713
+ "&::after": {
1714
+ content: '""',
1715
+ position: "absolute",
1716
+ inset: 0
1717
+ }
1661
1718
  },
1662
- "&light .cm-codeblock, &light .cm-codeblock.cm-activeLine": {
1663
- background: getToken("extend.colors.neutral.25")
1719
+ "&light .cm-codeblock": {
1720
+ "&::after": {
1721
+ background: getToken("extend.semanticColors.input.light"),
1722
+ mixBlendMode: "darken"
1723
+ }
1664
1724
  },
1665
- "&dark .cm-codeblock, &dark .cm-codeblock.cm-activeLine": {
1666
- background: getToken("extend.colors.neutral.850")
1725
+ "&dark .cm-codeblock": {
1726
+ "&::after": {
1727
+ background: getToken("extend.semanticColors.input.dark"),
1728
+ mixBlendMode: "lighten"
1729
+ }
1667
1730
  },
1668
1731
  "& .cm-codeblock-first": {
1669
- borderTopLeftRadius: ".5rem",
1670
- borderTopRightRadius: ".5rem"
1732
+ "&::after": {
1733
+ borderTopLeftRadius: ".5rem",
1734
+ borderTopRightRadius: ".5rem"
1735
+ }
1671
1736
  },
1672
1737
  "& .cm-codeblock-last": {
1673
- borderBottomLeftRadius: ".5rem",
1674
- borderBottomRightRadius: ".5rem"
1738
+ "&::after": {
1739
+ borderBottomLeftRadius: ".5rem",
1740
+ borderBottomRightRadius: ".5rem"
1741
+ }
1675
1742
  }
1676
1743
  });
1677
1744
  var getLineRange = (lines, from, to) => {
@@ -1743,16 +1810,156 @@ var code2 = () => {
1743
1810
  ];
1744
1811
  };
1745
1812
 
1746
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1813
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1814
+ import { snippet } from "@codemirror/autocomplete";
1747
1815
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
1816
  import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
- import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1817
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1818
+ var setHeading = (level) => (view) => {
1819
+ const { selection: { ranges }, doc } = view.state;
1820
+ const changes = [];
1821
+ for (const range of ranges) {
1822
+ const { number } = doc.lineAt(range.anchor);
1823
+ const { from, to } = doc.line(number);
1824
+ const line = doc.sliceString(from, to);
1825
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1826
+ const current = marks?.length ?? 0;
1827
+ if (level !== current) {
1828
+ changes.push({
1829
+ from,
1830
+ to: from + current + (spaces?.length ?? 0),
1831
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1832
+ });
1833
+ }
1834
+ }
1835
+ if (changes.length) {
1836
+ view.dispatch({
1837
+ changes
1838
+ });
1839
+ }
1840
+ return true;
1841
+ };
1842
+ var toggleStyle = (mark2) => (view) => {
1843
+ const { ranges } = view.state.selection;
1844
+ for (const range of ranges) {
1845
+ if (range.from === range.to) {
1846
+ return false;
1847
+ }
1848
+ view.dispatch({
1849
+ changes: [
1850
+ {
1851
+ from: range.from,
1852
+ insert: mark2
1853
+ },
1854
+ {
1855
+ from: range.to,
1856
+ insert: mark2
1857
+ }
1858
+ ]
1859
+ });
1860
+ }
1861
+ return true;
1862
+ };
1863
+ var snippets = {
1864
+ codeblock: snippet([
1865
+ "```#{lang}",
1866
+ " #{}",
1867
+ "```"
1868
+ ].join("\n")),
1869
+ table: snippet([
1870
+ "| #{col1} | #{col2} |",
1871
+ "| ---- | ---- |",
1872
+ "| #{val1} | #{val2} |",
1873
+ "| #{val3} | #{val4} |"
1874
+ ].join("\n"))
1875
+ };
1876
+ var insertCodeblock = (view) => {
1877
+ const { selection: { main }, doc } = view.state;
1878
+ const { number } = doc.lineAt(main.anchor);
1879
+ const { from } = doc.line(number);
1880
+ snippets.codeblock(view, null, from, from);
1881
+ };
1882
+ var insertTable = (view) => {
1883
+ const { selection: { main }, doc } = view.state;
1884
+ const { number } = doc.lineAt(main.anchor);
1885
+ const { from } = doc.line(number);
1886
+ snippets.table(view, null, from, from);
1887
+ };
1888
+ var toggleBold = toggleStyle("**");
1889
+ var toggleItalic = toggleStyle("_");
1890
+ var toggleStrikethrough = toggleStyle("~~");
1891
+ var toggleList = (view) => {
1892
+ };
1893
+ var formatting = (options = {}) => {
1894
+ return [
1895
+ keymap5.of([
1896
+ {
1897
+ key: "meta-b",
1898
+ run: toggleBold
1899
+ }
1900
+ ]),
1901
+ styling()
1902
+ ];
1903
+ };
1904
+ var styling = () => {
1905
+ const buildDecorations7 = (view) => {
1906
+ const builder = new RangeSetBuilder2();
1907
+ const { state } = view;
1908
+ const cursor = state.selection.main.head;
1909
+ const replace = (node, marks) => {
1910
+ if (cursor <= node.from || cursor >= node.to) {
1911
+ for (const mark2 of marks) {
1912
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1913
+ }
1914
+ }
1915
+ };
1916
+ for (const { from, to } of view.visibleRanges) {
1917
+ syntaxTree2(state).iterate({
1918
+ enter: (node) => {
1919
+ switch (node.name) {
1920
+ case "Emphasis":
1921
+ case "StrongEmphasis": {
1922
+ const marks = node.node.getChildren("EmphasisMark");
1923
+ replace(node, marks);
1924
+ break;
1925
+ }
1926
+ case "Strikethrough": {
1927
+ const marks = node.node.getChildren("StrikethroughMark");
1928
+ replace(node, marks);
1929
+ break;
1930
+ }
1931
+ }
1932
+ },
1933
+ from,
1934
+ to
1935
+ });
1936
+ }
1937
+ return builder.finish();
1938
+ };
1939
+ return [
1940
+ ViewPlugin4.fromClass(class {
1941
+ constructor(view) {
1942
+ this.decorations = buildDecorations7(view);
1943
+ }
1944
+ update(update2) {
1945
+ this.decorations = buildDecorations7(update2.view);
1946
+ }
1947
+ }, {
1948
+ decorations: (value) => value.decorations
1949
+ })
1950
+ ];
1951
+ };
1952
+
1953
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1954
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1955
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1956
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1750
1957
  var buildDecorations2 = (view) => {
1751
- const builder = new RangeSetBuilder2();
1958
+ const builder = new RangeSetBuilder3();
1752
1959
  const { state } = view;
1753
1960
  const cursor = state.selection.main.head;
1754
1961
  for (const { from, to } of view.visibleRanges) {
1755
- syntaxTree2(state).iterate({
1962
+ syntaxTree3(state).iterate({
1756
1963
  enter: (node) => {
1757
1964
  switch (node.name) {
1758
1965
  case "ATXHeading1":
@@ -1763,8 +1970,8 @@ var buildDecorations2 = (view) => {
1763
1970
  case "ATXHeading6": {
1764
1971
  const mark2 = node.node.getChild("HeaderMark");
1765
1972
  if (mark2) {
1766
- if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
- builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1973
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1974
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1768
1975
  }
1769
1976
  }
1770
1977
  }
@@ -1778,7 +1985,7 @@ var buildDecorations2 = (view) => {
1778
1985
  };
1779
1986
  var heading2 = () => {
1780
1987
  return [
1781
- ViewPlugin4.fromClass(class {
1988
+ ViewPlugin5.fromClass(class {
1782
1989
  constructor(view) {
1783
1990
  this.decorations = buildDecorations2(view);
1784
1991
  }
@@ -1792,9 +1999,9 @@ var heading2 = () => {
1792
1999
  };
1793
2000
 
1794
2001
  // 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";
2002
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2003
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2004
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1798
2005
  var styles4 = EditorView9.baseTheme({
1799
2006
  "& .cm-hr": {
1800
2007
  // Note that block-level decorations should not have vertical margins,
@@ -1808,15 +2015,15 @@ var HorizontalRuleWidget = class extends WidgetType3 {
1808
2015
  return el;
1809
2016
  }
1810
2017
  };
1811
- var decoration = Decoration5.replace({
2018
+ var decoration = Decoration6.replace({
1812
2019
  widget: new HorizontalRuleWidget()
1813
2020
  });
1814
2021
  var buildDecorations3 = (view) => {
1815
- const builder = new RangeSetBuilder3();
2022
+ const builder = new RangeSetBuilder4();
1816
2023
  const { state } = view;
1817
2024
  const cursor = state.selection.main.head;
1818
2025
  for (const { from, to } of view.visibleRanges) {
1819
- syntaxTree3(state).iterate({
2026
+ syntaxTree4(state).iterate({
1820
2027
  enter: (node) => {
1821
2028
  if (node.name === "HorizontalRule") {
1822
2029
  if (cursor <= node.from || cursor >= node.to) {
@@ -1832,7 +2039,7 @@ var buildDecorations3 = (view) => {
1832
2039
  };
1833
2040
  var hr = () => {
1834
2041
  return [
1835
- ViewPlugin5.fromClass(class {
2042
+ ViewPlugin6.fromClass(class {
1836
2043
  constructor(view) {
1837
2044
  this.decorations = buildDecorations3(view);
1838
2045
  }
@@ -1847,13 +2054,13 @@ var hr = () => {
1847
2054
  };
1848
2055
 
1849
2056
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2057
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1851
2058
  import { StateField as StateField3 } from "@codemirror/state";
1852
- import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2059
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
1853
2060
  var image = (options = {}) => {
1854
2061
  return StateField3.define({
1855
2062
  create: (state) => {
1856
- return Decoration6.set(buildDecorations4(0, state.doc.length, state));
2063
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
1857
2064
  },
1858
2065
  update: (value, tr) => {
1859
2066
  if (!tr.docChanged && !tr.selection) {
@@ -1882,14 +2089,14 @@ var image = (options = {}) => {
1882
2089
  var buildDecorations4 = (from, to, state) => {
1883
2090
  const decorations = [];
1884
2091
  const cursor = state.selection.main.head;
1885
- syntaxTree4(state).iterate({
2092
+ syntaxTree5(state).iterate({
1886
2093
  enter: (node) => {
1887
2094
  if (node.name === "Image") {
1888
2095
  const urlNode = node.node.getChild("URL");
1889
2096
  if (urlNode) {
1890
2097
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
2098
  const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
- decorations.push(Decoration6.replace({
2099
+ decorations.push(Decoration7.replace({
1893
2100
  block: true,
1894
2101
  widget: new ImageWidget(url)
1895
2102
  }).range(hide ? node.from : node.to, node.to));
@@ -1919,13 +2126,13 @@ var ImageWidget = class extends WidgetType4 {
1919
2126
  };
1920
2127
 
1921
2128
  // 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";
2129
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2130
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2131
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
1925
2132
  import { tooltipContent } from "@dxos/react-ui-theme";
1926
2133
  var link = (options = {}) => {
1927
2134
  const extensions = [
1928
- ViewPlugin6.fromClass(class {
2135
+ ViewPlugin7.fromClass(class {
1929
2136
  constructor(view) {
1930
2137
  this.decorations = buildDecorations5(view, options);
1931
2138
  }
@@ -1944,7 +2151,7 @@ var link = (options = {}) => {
1944
2151
  // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
2152
  // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
2153
  hoverTooltip2((view, pos, side) => {
1947
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
2154
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
1948
2155
  let link2 = null;
1949
2156
  for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
2157
  link2 = node.name === "Link" ? node : null;
@@ -2021,27 +2228,27 @@ var LinkText = class extends WidgetType5 {
2021
2228
  }
2022
2229
  };
2023
2230
  var buildDecorations5 = (view, options) => {
2024
- const builder = new RangeSetBuilder4();
2231
+ const builder = new RangeSetBuilder5();
2025
2232
  const { state } = view;
2026
2233
  const cursor = state.selection.main.head;
2027
2234
  for (const { from, to } of view.visibleRanges) {
2028
- syntaxTree5(state).iterate({
2235
+ syntaxTree6(state).iterate({
2029
2236
  enter: (node) => {
2030
2237
  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) : "";
2238
+ const marks = node.node.getChildren("LinkMark");
2239
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2033
2240
  const urlNode = node.node.getChild("URL");
2034
2241
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
2242
  if (!url) {
2036
2243
  return false;
2037
2244
  }
2038
2245
  if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
- builder.add(node.from, node.to, Decoration7.replace({
2246
+ builder.add(node.from, node.to, Decoration8.replace({
2040
2247
  widget: new LinkText(text, options.link ? url : void 0)
2041
2248
  }));
2042
2249
  }
2043
2250
  if (options.onRender) {
2044
- builder.add(node.to, node.to, Decoration7.replace({
2251
+ builder.add(node.to, node.to, Decoration8.replace({
2045
2252
  widget: new LinkButton(url, options.onRender)
2046
2253
  }));
2047
2254
  }
@@ -2056,9 +2263,9 @@ var buildDecorations5 = (view, options) => {
2056
2263
  };
2057
2264
 
2058
2265
  // 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";
2266
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2267
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2268
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2062
2269
  var table = (options = {}) => {
2063
2270
  return StateField4.define({
2064
2271
  create: (state) => update(state, options),
@@ -2067,7 +2274,7 @@ var table = (options = {}) => {
2067
2274
  });
2068
2275
  };
2069
2276
  var update = (state, options) => {
2070
- const builder = new RangeSetBuilder5();
2277
+ const builder = new RangeSetBuilder6();
2071
2278
  const cursor = state.selection.main.head;
2072
2279
  const tables = [];
2073
2280
  const getTable = () => tables[tables.length - 1];
@@ -2075,7 +2282,7 @@ var update = (state, options) => {
2075
2282
  const table2 = getTable();
2076
2283
  return table2.rows?.[table2.rows.length - 1];
2077
2284
  };
2078
- syntaxTree6(state).iterate({
2285
+ syntaxTree7(state).iterate({
2079
2286
  enter: (node) => {
2080
2287
  switch (node.name) {
2081
2288
  case "Table": {
@@ -2107,10 +2314,10 @@ var update = (state, options) => {
2107
2314
  });
2108
2315
  tables.forEach((table2) => {
2109
2316
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2110
- hide && builder.add(table2.from, table2.to, Decoration8.replace({
2317
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2111
2318
  widget: new TableWidget(table2)
2112
2319
  }));
2113
- builder.add(table2.from, table2.to, Decoration8.mark({
2320
+ builder.add(table2.from, table2.to, Decoration9.mark({
2114
2321
  class: "cm-table"
2115
2322
  }));
2116
2323
  });
@@ -2154,9 +2361,9 @@ var TableWidget = class extends WidgetType6 {
2154
2361
  };
2155
2362
 
2156
2363
  // 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";
2364
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2365
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2366
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2160
2367
  var styles5 = EditorView12.baseTheme({
2161
2368
  "& .cm-task": {
2162
2369
  color: getToken("extend.colors.blue.500")
@@ -2203,23 +2410,23 @@ var CheckboxWidget = class extends WidgetType7 {
2203
2410
  return false;
2204
2411
  }
2205
2412
  };
2206
- var checkedDecoration = Decoration9.replace({
2413
+ var checkedDecoration = Decoration10.replace({
2207
2414
  widget: new CheckboxWidget(true)
2208
2415
  });
2209
- var uncheckedDecoration = Decoration9.replace({
2416
+ var uncheckedDecoration = Decoration10.replace({
2210
2417
  widget: new CheckboxWidget(false)
2211
2418
  });
2212
2419
  var buildDecorations6 = (view) => {
2213
- const builder = new RangeSetBuilder6();
2420
+ const builder = new RangeSetBuilder7();
2214
2421
  const { state } = view;
2215
2422
  const cursor = state.selection.main.head;
2216
2423
  for (const { from, to } of view.visibleRanges) {
2217
- syntaxTree7(state).iterate({
2424
+ syntaxTree8(state).iterate({
2218
2425
  enter: (node) => {
2219
2426
  if (node.name === "TaskMarker") {
2220
2427
  if (cursor < node.from || cursor > node.to) {
2221
2428
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
- builder.add(node.from - 2, node.from - 1, Decoration9.mark({
2429
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2223
2430
  class: "cm-task"
2224
2431
  }));
2225
2432
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2234,7 +2441,7 @@ var buildDecorations6 = (view) => {
2234
2441
  };
2235
2442
  var tasklist = (options = {}) => {
2236
2443
  return [
2237
- ViewPlugin7.fromClass(class {
2444
+ ViewPlugin8.fromClass(class {
2238
2445
  constructor(view) {
2239
2446
  this.decorations = buildDecorations6(view);
2240
2447
  }
@@ -2285,7 +2492,7 @@ var outliner = (options = {}) => {
2285
2492
  };
2286
2493
 
2287
2494
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2288
- import { keymap as keymap5 } from "@codemirror/view";
2495
+ import { keymap as keymap6 } from "@codemirror/view";
2289
2496
  var defaultItems = [
2290
2497
  "hello world!",
2291
2498
  "this is a test.",
@@ -2295,7 +2502,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2295
2502
  let t;
2296
2503
  let idx = 0;
2297
2504
  return [
2298
- keymap5.of([
2505
+ keymap6.of([
2299
2506
  {
2300
2507
  // Reset.
2301
2508
  key: "alt-meta-'",
@@ -2372,8 +2579,9 @@ var defaultTheme = {
2372
2579
  outline: "none"
2373
2580
  },
2374
2581
  ".cm-scroller": {
2375
- overflow: "visible",
2376
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
2582
+ // overflow: 'visible',
2583
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
2584
+ lineHeight: 1.4
2377
2585
  },
2378
2586
  ".cm-content": {
2379
2587
  padding: 0,
@@ -2407,7 +2615,7 @@ var defaultTheme = {
2407
2615
  paddingInline: 0
2408
2616
  },
2409
2617
  ".cm-activeLine": {
2410
- background: "inherit"
2618
+ background: "transparent"
2411
2619
  },
2412
2620
  //
2413
2621
  // Selection
@@ -2578,15 +2786,12 @@ var codeTheme = {
2578
2786
  };
2579
2787
 
2580
2788
  // 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";
2789
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2582
2790
  var EditorModes = [
2583
2791
  "default",
2584
2792
  "vim"
2585
2793
  ];
2586
2794
  var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2587
- const tabsterDOMAttribute = useFocusableGroup({
2588
- tabBehavior: "limited"
2589
- });
2590
2795
  const { themeMode } = useThemeContext();
2591
2796
  const rootRef = useRef(null);
2592
2797
  const [view, setView] = useState(null);
@@ -2594,7 +2799,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2594
2799
  view
2595
2800
  ]);
2596
2801
  useEffect2(() => {
2597
- if (autoFocus) {
2802
+ if (autoFocus && !view?.hasFocus) {
2803
+ if (view?.state.selection.main?.from === 0) {
2804
+ const { to } = view.state.doc.lineAt(0);
2805
+ view?.dispatch({
2806
+ selection: {
2807
+ anchor: to
2808
+ }
2809
+ });
2810
+ }
2598
2811
  view?.focus();
2599
2812
  }
2600
2813
  }, [
@@ -2612,8 +2825,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2612
2825
  // TODO(burdon): Doesn't catch errors in keymap functions.
2613
2826
  EditorView13.exceptionSink.of((err) => {
2614
2827
  log2.catch(err, void 0, {
2615
- F: __dxlog_file5,
2616
- L: 123,
2828
+ F: __dxlog_file3,
2829
+ L: 134,
2617
2830
  S: void 0,
2618
2831
  C: (f, a) => f(...a)
2619
2832
  });
@@ -2630,15 +2843,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2630
2843
  class: slots.content?.className ?? ""
2631
2844
  }),
2632
2845
  // State.
2846
+ EditorView13.editable.of(!readonly),
2633
2847
  EditorState2.readOnly.of(!!readonly),
2634
2848
  // Storage and replication.
2635
2849
  // NOTE: This must come before user extensions.
2636
2850
  model.extension,
2637
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
2851
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2638
2852
  editorMode === "vim" && vim(),
2639
2853
  // Custom.
2640
2854
  ...extensions
2641
- ].filter(Boolean)
2855
+ ].filter(isNotFalsy3)
2642
2856
  });
2643
2857
  const newView = new EditorView13({
2644
2858
  state,
@@ -2675,7 +2889,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2675
2889
  }
2676
2890
  case "Escape": {
2677
2891
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2678
- rootRef.current?.focus();
2892
+ view?.focus();
2679
2893
  }
2680
2894
  break;
2681
2895
  }
@@ -2688,9 +2902,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2688
2902
  key: model.id,
2689
2903
  role: "none",
2690
2904
  tabIndex: 0,
2691
- onKeyUp: handleKeyUp,
2692
2905
  ...slots.root,
2693
- ...editorMode !== "vim" && tabsterDOMAttribute,
2906
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2907
+ onKeyUp: handleKeyUp,
2694
2908
  ref: rootRef
2695
2909
  });
2696
2910
  });
@@ -2701,7 +2915,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2701
2915
  ref: forwardedRef,
2702
2916
  readonly,
2703
2917
  extensions: [
2704
- basicBundle({
2918
+ createBasicBundle({
2705
2919
  themeMode,
2706
2920
  placeholder: placeholder3,
2707
2921
  lineWrapping
@@ -2720,7 +2934,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2720
2934
  ref: forwardedRef,
2721
2935
  readonly,
2722
2936
  extensions: [
2723
- markdownBundle({
2937
+ createMarkdownExtensions({
2724
2938
  themeMode,
2725
2939
  placeholder: placeholder3
2726
2940
  }),
@@ -2747,12 +2961,173 @@ var defaultMarkdownSlots = {
2747
2961
  ...defaultSlots
2748
2962
  };
2749
2963
 
2964
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2965
+ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
2966
+ import { createContext } from "@radix-ui/react-context";
2967
+ import React2 from "react";
2968
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
2969
+ import { getSize } from "@dxos/react-ui-theme";
2970
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2971
+ var ToolbarRoot = ({ children, onAction }) => {
2972
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2973
+ onAction
2974
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2975
+ density: "fine"
2976
+ }, /* @__PURE__ */ React2.createElement("div", {
2977
+ role: "toolbar",
2978
+ className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
2979
+ }, children)));
2980
+ };
2981
+ var ToolbarButton = ({ Icon, onClick, title }) => {
2982
+ const { onAction } = useToolbarContext("ToolbarButton");
2983
+ const handleClick = () => {
2984
+ const action = onClick();
2985
+ if (action) {
2986
+ onAction?.(action);
2987
+ }
2988
+ };
2989
+ return /* @__PURE__ */ React2.createElement(Button, {
2990
+ variant: "ghost",
2991
+ classNames: "p-2",
2992
+ onClick: handleClick,
2993
+ title
2994
+ }, /* @__PURE__ */ React2.createElement(Icon, {
2995
+ className: getSize(5)
2996
+ }));
2997
+ };
2998
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
2999
+ className: "grow"
3000
+ });
3001
+ var MarkdownHeading = () => {
3002
+ const { onAction } = useToolbarContext("MarkdownFormatting");
3003
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3004
+ defaultValue: "0",
3005
+ onValueChange: (value) => onAction?.({
3006
+ type: "heading",
3007
+ data: parseInt(value)
3008
+ })
3009
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3010
+ classNames: "w-[8rem]"
3011
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3012
+ value: "0"
3013
+ }, "Paragraph"), [
3014
+ 1,
3015
+ 2,
3016
+ 3,
3017
+ 4,
3018
+ 5,
3019
+ 6
3020
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3021
+ key: level,
3022
+ value: String(level)
3023
+ }, "Heading ", level))))));
3024
+ };
3025
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3026
+ role: "none"
3027
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3028
+ Icon: TextB,
3029
+ title: "Bold",
3030
+ onClick: () => ({
3031
+ type: "bold"
3032
+ })
3033
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3034
+ Icon: TextItalic,
3035
+ title: "Italic",
3036
+ onClick: () => ({
3037
+ type: "italic"
3038
+ })
3039
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3040
+ Icon: TextStrikethrough,
3041
+ title: "Strike-through",
3042
+ onClick: () => ({
3043
+ type: "strikethrough"
3044
+ })
3045
+ }));
3046
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3047
+ role: "none"
3048
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3049
+ Icon: ListBullets,
3050
+ title: "Bullet list",
3051
+ onClick: () => ({
3052
+ type: "list"
3053
+ })
3054
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3055
+ Icon: ListNumbers,
3056
+ title: "Numbered list",
3057
+ onClick: () => ({
3058
+ type: "list-ordered"
3059
+ })
3060
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3061
+ Icon: ListChecks,
3062
+ title: "Task list",
3063
+ onClick: () => ({
3064
+ type: "list-tasks"
3065
+ })
3066
+ }));
3067
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3068
+ role: "none"
3069
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3070
+ Icon: CaretRight,
3071
+ title: "Block quite",
3072
+ onClick: () => ({
3073
+ type: "blockquote"
3074
+ })
3075
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3076
+ Icon: Code,
3077
+ title: "Code block",
3078
+ onClick: () => ({
3079
+ type: "codeblock"
3080
+ })
3081
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3082
+ Icon: Table2,
3083
+ title: "Table",
3084
+ onClick: () => ({
3085
+ type: "table"
3086
+ })
3087
+ }));
3088
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3089
+ role: "none"
3090
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3091
+ Icon: Link,
3092
+ title: "Link",
3093
+ onClick: () => ({
3094
+ type: "link"
3095
+ })
3096
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3097
+ Icon: At,
3098
+ title: "Mention",
3099
+ onClick: () => ({
3100
+ type: "mention"
3101
+ })
3102
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3103
+ Icon: Image,
3104
+ title: "Image",
3105
+ onClick: () => ({
3106
+ type: "image"
3107
+ })
3108
+ }));
3109
+ 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), /* @__PURE__ */ React2.createElement(MarkdownLinks, null));
3110
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
3111
+ Icon: ChatText,
3112
+ title: "Create comment",
3113
+ onClick: () => ({
3114
+ type: "comment"
3115
+ })
3116
+ });
3117
+ var Toolbar = {
3118
+ Root: ToolbarRoot,
3119
+ Button: ToolbarButton,
3120
+ Separator: ToolbarSeparator,
3121
+ Markdown: MarkdownStandard,
3122
+ Extended: MarkdownExtended
3123
+ };
3124
+
2750
3125
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2751
3126
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2752
3127
  import { Context as Context2 } from "@dxos/context";
2753
- import { invariant as invariant4 } from "@dxos/invariant";
3128
+ import { invariant as invariant2 } from "@dxos/invariant";
2754
3129
  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";
3130
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2756
3131
  var DEBOUNCE_INTERVAL = 100;
2757
3132
  var SpaceAwarenessProvider = class {
2758
3133
  constructor(params) {
@@ -2792,7 +3167,7 @@ var SpaceAwarenessProvider = class {
2792
3167
  log3.debug("failed to query awareness", {
2793
3168
  err
2794
3169
  }, {
2795
- F: __dxlog_file6,
3170
+ F: __dxlog_file4,
2796
3171
  L: 89,
2797
3172
  S: this,
2798
3173
  C: (f, a) => f(...a)
@@ -2808,8 +3183,8 @@ var SpaceAwarenessProvider = class {
2808
3183
  return Array.from(this._remoteStates.values());
2809
3184
  }
2810
3185
  update(position) {
2811
- invariant4(this._postTask, void 0, {
2812
- F: __dxlog_file6,
3186
+ invariant2(this._postTask, void 0, {
3187
+ F: __dxlog_file4,
2813
3188
  L: 104,
2814
3189
  S: this,
2815
3190
  A: [
@@ -2825,8 +3200,8 @@ var SpaceAwarenessProvider = class {
2825
3200
  this._postTask.schedule();
2826
3201
  }
2827
3202
  _handleQueryMessage() {
2828
- invariant4(this._postTask, void 0, {
2829
- F: __dxlog_file6,
3203
+ invariant2(this._postTask, void 0, {
3204
+ F: __dxlog_file4,
2830
3205
  L: 115,
2831
3206
  S: this,
2832
3207
  A: [
@@ -2837,8 +3212,8 @@ var SpaceAwarenessProvider = class {
2837
3212
  this._postTask.schedule();
2838
3213
  }
2839
3214
  _handlePostMessage(message) {
2840
- invariant4(message.kind === "post", void 0, {
2841
- F: __dxlog_file6,
3215
+ invariant2(message.kind === "post", void 0, {
3216
+ F: __dxlog_file4,
2842
3217
  L: 120,
2843
3218
  S: this,
2844
3219
  A: [
@@ -2858,9 +3233,50 @@ var modelState = StateField5.define({
2858
3233
  update: (model) => model
2859
3234
  });
2860
3235
 
2861
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3236
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3237
+ var useActionHandler = (view) => {
3238
+ return (action) => {
3239
+ if (!view) {
3240
+ return;
3241
+ }
3242
+ switch (action.type) {
3243
+ case "heading":
3244
+ setHeading(parseInt(action.data))(view);
3245
+ break;
3246
+ case "bold":
3247
+ toggleBold(view);
3248
+ break;
3249
+ case "italic":
3250
+ toggleItalic(view);
3251
+ break;
3252
+ case "strikethrough":
3253
+ toggleStrikethrough(view);
3254
+ break;
3255
+ case "list":
3256
+ toggleList(view);
3257
+ break;
3258
+ case "codeblock":
3259
+ insertCodeblock(view);
3260
+ break;
3261
+ case "table":
3262
+ insertTable(view);
3263
+ break;
3264
+ case "comment":
3265
+ createComment(view);
3266
+ break;
3267
+ }
3268
+ setTimeout(() => {
3269
+ view.focus();
3270
+ view.dispatch({
3271
+ selection: view.state.selection
3272
+ });
3273
+ });
3274
+ };
3275
+ };
3276
+
3277
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
2862
3278
  import { useState as useState2 } from "react";
2863
- var useTextEditor = () => {
3279
+ var useEditorView = () => {
2864
3280
  const [view, setView] = useState2(null);
2865
3281
  return [
2866
3282
  (ref) => {
@@ -2870,19 +3286,125 @@ var useTextEditor = () => {
2870
3286
  ];
2871
3287
  };
2872
3288
 
3289
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3290
+ import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3291
+ import { EditorView as EditorView14 } from "@codemirror/view";
3292
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
3293
+ import { log as log4 } from "@dxos/log";
3294
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3295
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3296
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3297
+ const onUpdate = useRef2();
3298
+ const [view, setView] = useState3();
3299
+ const parentRef = useRef2(null);
3300
+ useEffect3(() => {
3301
+ if (parentRef.current) {
3302
+ const state = EditorState3.create({
3303
+ doc,
3304
+ selection,
3305
+ extensions: [
3306
+ // TODO(burdon): Doesn't catch errors in keymap functions.
3307
+ EditorView14.exceptionSink.of((err) => {
3308
+ log4.catch(err, void 0, {
3309
+ F: __dxlog_file5,
3310
+ L: 57,
3311
+ S: void 0,
3312
+ C: (f, a) => f(...a)
3313
+ });
3314
+ }),
3315
+ extensions,
3316
+ EditorView14.updateListener.of(() => {
3317
+ onUpdate.current?.();
3318
+ })
3319
+ ].filter(isNotFalsy4)
3320
+ });
3321
+ const view2 = new EditorView14({
3322
+ parent: parentRef.current,
3323
+ scrollTo,
3324
+ state,
3325
+ // NOTE: Uncomment to debug/monitor all transactions.
3326
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
3327
+ dispatchTransactions: (trs, view3) => {
3328
+ if (debug) {
3329
+ logChanges(trs);
3330
+ }
3331
+ view3.update(trs);
3332
+ }
3333
+ });
3334
+ setView(view2);
3335
+ }
3336
+ return () => {
3337
+ view?.destroy();
3338
+ };
3339
+ }, [
3340
+ parentRef
3341
+ ]);
3342
+ useEffect3(() => {
3343
+ if (view) {
3344
+ if (!selection && !view.state.selection.main.anchor) {
3345
+ selection = EditorSelection.single(view.state.doc.line(1).to);
3346
+ }
3347
+ if (selection || scrollTo) {
3348
+ onUpdate.current = () => {
3349
+ onUpdate.current = void 0;
3350
+ view.dispatch({
3351
+ selection,
3352
+ effects: scrollTo && [
3353
+ scrollTo
3354
+ ],
3355
+ scrollIntoView: !scrollTo
3356
+ });
3357
+ };
3358
+ }
3359
+ if (autoFocus) {
3360
+ view.focus();
3361
+ }
3362
+ }
3363
+ }, [
3364
+ view,
3365
+ autoFocus,
3366
+ selection,
3367
+ scrollTo
3368
+ ]);
3369
+ return {
3370
+ parentRef,
3371
+ view
3372
+ };
3373
+ };
3374
+ var createDataExtensions = ({ readonly = false } = {}) => {
3375
+ return [
3376
+ //
3377
+ EditorState3.readOnly.of(readonly),
3378
+ EditorView14.editable.of(!readonly)
3379
+ ];
3380
+ };
3381
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3382
+ return [
3383
+ //
3384
+ EditorView14.baseTheme(defaultTheme),
3385
+ theme && EditorView14.theme(theme),
3386
+ EditorView14.darkTheme.of(themeMode === "dark"),
3387
+ EditorView14.editorAttributes.of({
3388
+ class: slots?.editor?.className ?? ""
3389
+ }),
3390
+ EditorView14.contentAttributes.of({
3391
+ class: slots?.content?.className ?? ""
3392
+ })
3393
+ ].filter(isNotFalsy4);
3394
+ };
3395
+
2873
3396
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2874
3397
  import get4 from "lodash.get";
2875
- import { useEffect as useEffect3, useState as useState3 } from "react";
3398
+ import { useEffect as useEffect4, useState as useState4 } from "react";
2876
3399
  import { generateName } from "@dxos/display-name";
2877
3400
  import { getRawDoc } from "@dxos/echo-schema";
2878
- import { invariant as invariant5 } from "@dxos/invariant";
3401
+ import { invariant as invariant3 } from "@dxos/invariant";
2879
3402
  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";
3403
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2882
3404
  var useTextModel = (props) => {
2883
3405
  const { identity, space, text } = props;
2884
- const [model, setModel] = useState3();
2885
- useEffect3(() => setModel(createModel(props)), [
3406
+ const [model, setModel] = useState4();
3407
+ useEffect4(() => setModel(createModel(props)), [
2886
3408
  identity,
2887
3409
  space,
2888
3410
  text
@@ -2890,9 +3412,9 @@ var useTextModel = (props) => {
2890
3412
  return model;
2891
3413
  };
2892
3414
  var createModel = ({ space, identity, text }) => {
2893
- invariant5(isAutomergeObject(text), void 0, {
2894
- F: __dxlog_file7,
2895
- L: 35,
3415
+ invariant3(isAutomergeObject(text), void 0, {
3416
+ F: __dxlog_file6,
3417
+ L: 34,
2896
3418
  S: void 0,
2897
3419
  A: [
2898
3420
  "isAutomergeObject(text)",
@@ -2903,7 +3425,7 @@ var createModel = ({ space, identity, text }) => {
2903
3425
  const doc = getRawDoc(obj, [
2904
3426
  obj.field
2905
3427
  ]);
2906
- const awarenessProvider = space && new SpaceAwarenessProvider({
3428
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2907
3429
  space,
2908
3430
  channel: `automerge.awareness.${obj.id}`,
2909
3431
  info: {
@@ -2913,19 +3435,22 @@ var createModel = ({ space, identity, text }) => {
2913
3435
  },
2914
3436
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2915
3437
  });
3438
+ const extensions = [
3439
+ //
3440
+ modelState.init(() => model),
3441
+ automerge({
3442
+ handle: doc.handle,
3443
+ path: doc.path
3444
+ })
3445
+ ];
3446
+ if (awarenessProvider2) {
3447
+ extensions.push(awareness(awarenessProvider2));
3448
+ }
2916
3449
  const model = {
2917
3450
  id: obj.id,
2918
3451
  content: doc,
2919
3452
  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),
3453
+ extension: extensions,
2929
3454
  peer: identity ? {
2930
3455
  id: identity.identityKey.toHex(),
2931
3456
  name: identity.profile?.displayName
@@ -2934,7 +3459,6 @@ var createModel = ({ space, identity, text }) => {
2934
3459
  return model;
2935
3460
  };
2936
3461
  export {
2937
- AwarenessProvider,
2938
3462
  BaseTextEditor,
2939
3463
  Cursor,
2940
3464
  Doc,
@@ -2944,30 +3468,39 @@ export {
2944
3468
  SpaceAwarenessProvider,
2945
3469
  TextEditor,
2946
3470
  TextKind,
3471
+ Toolbar,
2947
3472
  YText,
2948
3473
  YXmlFragment,
2949
3474
  autocomplete,
2950
- automerge3 as automerge,
3475
+ automerge,
2951
3476
  awareness,
2952
- basicBundle,
3477
+ awarenessProvider,
2953
3478
  blast,
2954
3479
  callbackWrapper,
2955
3480
  code2 as code,
2956
3481
  codeTheme,
2957
3482
  comments,
3483
+ createBasicBundle,
3484
+ createComment,
3485
+ createDataExtensions,
3486
+ createMarkdownExtensions,
3487
+ createThemeExtensions,
2958
3488
  defaultMarkdownSlots,
2959
3489
  defaultOptions,
2960
3490
  defaultSlots,
2961
3491
  defaultTextSlots,
2962
3492
  defaultTheme,
3493
+ focusComment,
3494
+ formatting,
2963
3495
  getToken,
2964
3496
  heading2 as heading,
2965
3497
  hr,
2966
3498
  image,
3499
+ insertCodeblock,
3500
+ insertTable,
2967
3501
  link,
2968
3502
  listener,
2969
3503
  logChanges,
2970
- markdownBundle,
2971
3504
  markdownHighlightStyle,
2972
3505
  markdownTags,
2973
3506
  markdownTagsExtensions,
@@ -2976,15 +3509,23 @@ export {
2976
3509
  modelState,
2977
3510
  outliner,
2978
3511
  setComments,
2979
- setFocus,
3512
+ setHeading,
2980
3513
  setSelection,
2981
3514
  table,
2982
3515
  tags2 as tags,
2983
3516
  tasklist,
2984
3517
  textTheme,
3518
+ toggleBold,
3519
+ toggleItalic,
3520
+ toggleList,
3521
+ toggleStrikethrough,
3522
+ toggleStyle,
2985
3523
  typewriter,
3524
+ useActionHandler,
2986
3525
  useComments,
3526
+ useEditorView,
2987
3527
  useTextEditor,
2988
- useTextModel
3528
+ useTextModel,
3529
+ useToolbarContext
2989
3530
  };
2990
3531
  //# sourceMappingURL=index.mjs.map