@dxos/react-ui-editor 0.3.11-main.4faaf44 → 0.3.11-main.5052d1a

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 +786 -244
  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 +38 -25
  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 +36 -13
  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
@@ -1652,26 +1703,43 @@ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Dec
1652
1703
  import { mx as mx2 } from "@dxos/react-ui-theme";
1653
1704
  var styles3 = EditorView8.baseTheme({
1654
1705
  "& .cm-code": {
1655
- paddingInline: "1rem !important",
1656
1706
  fontFamily: getToken("fontFamily.mono", []).join(",")
1657
1707
  },
1658
1708
  "& .cm-codeblock": {
1659
- display: "inline-block",
1660
- width: "100%"
1709
+ width: "100%",
1710
+ paddingInline: "1rem !important",
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
+ display: "inline-block",
1732
+ "&::after": {
1733
+ borderTopLeftRadius: ".5rem",
1734
+ borderTopRightRadius: ".5rem"
1735
+ }
1671
1736
  },
1672
1737
  "& .cm-codeblock-last": {
1673
- borderBottomLeftRadius: ".5rem",
1674
- borderBottomRightRadius: ".5rem"
1738
+ display: "inline-block",
1739
+ "&::after": {
1740
+ borderBottomLeftRadius: ".5rem",
1741
+ borderBottomRightRadius: ".5rem"
1742
+ }
1675
1743
  }
1676
1744
  });
1677
1745
  var getLineRange = (lines, from, to) => {
@@ -1715,7 +1783,7 @@ var buildDecorations = (view) => {
1715
1783
  }));
1716
1784
  } else {
1717
1785
  builder.add(block.from, block.from, Decoration3.line({
1718
- class: mx2("cm-code cm-codeblock")
1786
+ class: mx2("cm-code cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1719
1787
  }));
1720
1788
  }
1721
1789
  }
@@ -1743,16 +1811,156 @@ var code2 = () => {
1743
1811
  ];
1744
1812
  };
1745
1813
 
1746
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1814
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1815
+ import { snippet } from "@codemirror/autocomplete";
1747
1816
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
1817
  import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
- import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1818
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1819
+ var setHeading = (level) => (view) => {
1820
+ const { selection: { ranges }, doc } = view.state;
1821
+ const changes = [];
1822
+ for (const range of ranges) {
1823
+ const { number } = doc.lineAt(range.anchor);
1824
+ const { from, to } = doc.line(number);
1825
+ const line = doc.sliceString(from, to);
1826
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1827
+ const current = marks?.length ?? 0;
1828
+ if (level !== current) {
1829
+ changes.push({
1830
+ from,
1831
+ to: from + current + (spaces?.length ?? 0),
1832
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1833
+ });
1834
+ }
1835
+ }
1836
+ if (changes.length) {
1837
+ view.dispatch({
1838
+ changes
1839
+ });
1840
+ }
1841
+ return true;
1842
+ };
1843
+ var toggleStyle = (mark2) => (view) => {
1844
+ const { ranges } = view.state.selection;
1845
+ for (const range of ranges) {
1846
+ if (range.from === range.to) {
1847
+ return false;
1848
+ }
1849
+ view.dispatch({
1850
+ changes: [
1851
+ {
1852
+ from: range.from,
1853
+ insert: mark2
1854
+ },
1855
+ {
1856
+ from: range.to,
1857
+ insert: mark2
1858
+ }
1859
+ ]
1860
+ });
1861
+ }
1862
+ return true;
1863
+ };
1864
+ var snippets = {
1865
+ codeblock: snippet([
1866
+ "```#{lang}",
1867
+ " #{}",
1868
+ "```"
1869
+ ].join("\n")),
1870
+ table: snippet([
1871
+ "| #{col1} | #{col2} |",
1872
+ "| ---- | ---- |",
1873
+ "| #{val1} | #{val2} |",
1874
+ "| #{val3} | #{val4} |"
1875
+ ].join("\n"))
1876
+ };
1877
+ var insertCodeblock = (view) => {
1878
+ const { selection: { main }, doc } = view.state;
1879
+ const { number } = doc.lineAt(main.anchor);
1880
+ const { from } = doc.line(number);
1881
+ snippets.codeblock(view, null, from, from);
1882
+ };
1883
+ var insertTable = (view) => {
1884
+ const { selection: { main }, doc } = view.state;
1885
+ const { number } = doc.lineAt(main.anchor);
1886
+ const { from } = doc.line(number);
1887
+ snippets.table(view, null, from, from);
1888
+ };
1889
+ var toggleBold = toggleStyle("**");
1890
+ var toggleItalic = toggleStyle("_");
1891
+ var toggleStrikethrough = toggleStyle("~~");
1892
+ var toggleList = (view) => {
1893
+ };
1894
+ var formatting = (options = {}) => {
1895
+ return [
1896
+ keymap5.of([
1897
+ {
1898
+ key: "meta-b",
1899
+ run: toggleBold
1900
+ }
1901
+ ]),
1902
+ styling()
1903
+ ];
1904
+ };
1905
+ var styling = () => {
1906
+ const buildDecorations7 = (view) => {
1907
+ const builder = new RangeSetBuilder2();
1908
+ const { state } = view;
1909
+ const cursor = state.selection.main.head;
1910
+ const replace = (node, marks) => {
1911
+ if (cursor <= node.from || cursor >= node.to) {
1912
+ for (const mark2 of marks) {
1913
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1914
+ }
1915
+ }
1916
+ };
1917
+ for (const { from, to } of view.visibleRanges) {
1918
+ syntaxTree2(state).iterate({
1919
+ enter: (node) => {
1920
+ switch (node.name) {
1921
+ case "Emphasis":
1922
+ case "StrongEmphasis": {
1923
+ const marks = node.node.getChildren("EmphasisMark");
1924
+ replace(node, marks);
1925
+ break;
1926
+ }
1927
+ case "Strikethrough": {
1928
+ const marks = node.node.getChildren("StrikethroughMark");
1929
+ replace(node, marks);
1930
+ break;
1931
+ }
1932
+ }
1933
+ },
1934
+ from,
1935
+ to
1936
+ });
1937
+ }
1938
+ return builder.finish();
1939
+ };
1940
+ return [
1941
+ ViewPlugin4.fromClass(class {
1942
+ constructor(view) {
1943
+ this.decorations = buildDecorations7(view);
1944
+ }
1945
+ update(update2) {
1946
+ this.decorations = buildDecorations7(update2.view);
1947
+ }
1948
+ }, {
1949
+ decorations: (value) => value.decorations
1950
+ })
1951
+ ];
1952
+ };
1953
+
1954
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1955
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1956
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1957
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1750
1958
  var buildDecorations2 = (view) => {
1751
- const builder = new RangeSetBuilder2();
1959
+ const builder = new RangeSetBuilder3();
1752
1960
  const { state } = view;
1753
1961
  const cursor = state.selection.main.head;
1754
1962
  for (const { from, to } of view.visibleRanges) {
1755
- syntaxTree2(state).iterate({
1963
+ syntaxTree3(state).iterate({
1756
1964
  enter: (node) => {
1757
1965
  switch (node.name) {
1758
1966
  case "ATXHeading1":
@@ -1763,8 +1971,8 @@ var buildDecorations2 = (view) => {
1763
1971
  case "ATXHeading6": {
1764
1972
  const mark2 = node.node.getChild("HeaderMark");
1765
1973
  if (mark2) {
1766
- if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
- builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1974
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1975
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1768
1976
  }
1769
1977
  }
1770
1978
  }
@@ -1778,7 +1986,7 @@ var buildDecorations2 = (view) => {
1778
1986
  };
1779
1987
  var heading2 = () => {
1780
1988
  return [
1781
- ViewPlugin4.fromClass(class {
1989
+ ViewPlugin5.fromClass(class {
1782
1990
  constructor(view) {
1783
1991
  this.decorations = buildDecorations2(view);
1784
1992
  }
@@ -1792,9 +2000,9 @@ var heading2 = () => {
1792
2000
  };
1793
2001
 
1794
2002
  // 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";
2003
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2004
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2005
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1798
2006
  var styles4 = EditorView9.baseTheme({
1799
2007
  "& .cm-hr": {
1800
2008
  // Note that block-level decorations should not have vertical margins,
@@ -1808,15 +2016,15 @@ var HorizontalRuleWidget = class extends WidgetType3 {
1808
2016
  return el;
1809
2017
  }
1810
2018
  };
1811
- var decoration = Decoration5.replace({
2019
+ var decoration = Decoration6.replace({
1812
2020
  widget: new HorizontalRuleWidget()
1813
2021
  });
1814
2022
  var buildDecorations3 = (view) => {
1815
- const builder = new RangeSetBuilder3();
2023
+ const builder = new RangeSetBuilder4();
1816
2024
  const { state } = view;
1817
2025
  const cursor = state.selection.main.head;
1818
2026
  for (const { from, to } of view.visibleRanges) {
1819
- syntaxTree3(state).iterate({
2027
+ syntaxTree4(state).iterate({
1820
2028
  enter: (node) => {
1821
2029
  if (node.name === "HorizontalRule") {
1822
2030
  if (cursor <= node.from || cursor >= node.to) {
@@ -1832,7 +2040,7 @@ var buildDecorations3 = (view) => {
1832
2040
  };
1833
2041
  var hr = () => {
1834
2042
  return [
1835
- ViewPlugin5.fromClass(class {
2043
+ ViewPlugin6.fromClass(class {
1836
2044
  constructor(view) {
1837
2045
  this.decorations = buildDecorations3(view);
1838
2046
  }
@@ -1847,13 +2055,13 @@ var hr = () => {
1847
2055
  };
1848
2056
 
1849
2057
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2058
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1851
2059
  import { StateField as StateField3 } from "@codemirror/state";
1852
- import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2060
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
1853
2061
  var image = (options = {}) => {
1854
2062
  return StateField3.define({
1855
2063
  create: (state) => {
1856
- return Decoration6.set(buildDecorations4(0, state.doc.length, state));
2064
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
1857
2065
  },
1858
2066
  update: (value, tr) => {
1859
2067
  if (!tr.docChanged && !tr.selection) {
@@ -1882,14 +2090,14 @@ var image = (options = {}) => {
1882
2090
  var buildDecorations4 = (from, to, state) => {
1883
2091
  const decorations = [];
1884
2092
  const cursor = state.selection.main.head;
1885
- syntaxTree4(state).iterate({
2093
+ syntaxTree5(state).iterate({
1886
2094
  enter: (node) => {
1887
2095
  if (node.name === "Image") {
1888
2096
  const urlNode = node.node.getChild("URL");
1889
2097
  if (urlNode) {
1890
2098
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
2099
  const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
- decorations.push(Decoration6.replace({
2100
+ decorations.push(Decoration7.replace({
1893
2101
  block: true,
1894
2102
  widget: new ImageWidget(url)
1895
2103
  }).range(hide ? node.from : node.to, node.to));
@@ -1919,13 +2127,13 @@ var ImageWidget = class extends WidgetType4 {
1919
2127
  };
1920
2128
 
1921
2129
  // 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";
2130
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2131
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2132
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
1925
2133
  import { tooltipContent } from "@dxos/react-ui-theme";
1926
2134
  var link = (options = {}) => {
1927
2135
  const extensions = [
1928
- ViewPlugin6.fromClass(class {
2136
+ ViewPlugin7.fromClass(class {
1929
2137
  constructor(view) {
1930
2138
  this.decorations = buildDecorations5(view, options);
1931
2139
  }
@@ -1944,7 +2152,7 @@ var link = (options = {}) => {
1944
2152
  // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
2153
  // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
2154
  hoverTooltip2((view, pos, side) => {
1947
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
2155
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
1948
2156
  let link2 = null;
1949
2157
  for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
2158
  link2 = node.name === "Link" ? node : null;
@@ -2021,27 +2229,27 @@ var LinkText = class extends WidgetType5 {
2021
2229
  }
2022
2230
  };
2023
2231
  var buildDecorations5 = (view, options) => {
2024
- const builder = new RangeSetBuilder4();
2232
+ const builder = new RangeSetBuilder5();
2025
2233
  const { state } = view;
2026
2234
  const cursor = state.selection.main.head;
2027
2235
  for (const { from, to } of view.visibleRanges) {
2028
- syntaxTree5(state).iterate({
2236
+ syntaxTree6(state).iterate({
2029
2237
  enter: (node) => {
2030
2238
  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) : "";
2239
+ const marks = node.node.getChildren("LinkMark");
2240
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2033
2241
  const urlNode = node.node.getChild("URL");
2034
2242
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
2243
  if (!url) {
2036
2244
  return false;
2037
2245
  }
2038
2246
  if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
- builder.add(node.from, node.to, Decoration7.replace({
2247
+ builder.add(node.from, node.to, Decoration8.replace({
2040
2248
  widget: new LinkText(text, options.link ? url : void 0)
2041
2249
  }));
2042
2250
  }
2043
2251
  if (options.onRender) {
2044
- builder.add(node.to, node.to, Decoration7.replace({
2252
+ builder.add(node.to, node.to, Decoration8.replace({
2045
2253
  widget: new LinkButton(url, options.onRender)
2046
2254
  }));
2047
2255
  }
@@ -2056,9 +2264,9 @@ var buildDecorations5 = (view, options) => {
2056
2264
  };
2057
2265
 
2058
2266
  // 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";
2267
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2268
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2269
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2062
2270
  var table = (options = {}) => {
2063
2271
  return StateField4.define({
2064
2272
  create: (state) => update(state, options),
@@ -2067,7 +2275,7 @@ var table = (options = {}) => {
2067
2275
  });
2068
2276
  };
2069
2277
  var update = (state, options) => {
2070
- const builder = new RangeSetBuilder5();
2278
+ const builder = new RangeSetBuilder6();
2071
2279
  const cursor = state.selection.main.head;
2072
2280
  const tables = [];
2073
2281
  const getTable = () => tables[tables.length - 1];
@@ -2075,7 +2283,7 @@ var update = (state, options) => {
2075
2283
  const table2 = getTable();
2076
2284
  return table2.rows?.[table2.rows.length - 1];
2077
2285
  };
2078
- syntaxTree6(state).iterate({
2286
+ syntaxTree7(state).iterate({
2079
2287
  enter: (node) => {
2080
2288
  switch (node.name) {
2081
2289
  case "Table": {
@@ -2107,10 +2315,10 @@ var update = (state, options) => {
2107
2315
  });
2108
2316
  tables.forEach((table2) => {
2109
2317
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2110
- hide && builder.add(table2.from, table2.to, Decoration8.replace({
2318
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2111
2319
  widget: new TableWidget(table2)
2112
2320
  }));
2113
- builder.add(table2.from, table2.to, Decoration8.mark({
2321
+ builder.add(table2.from, table2.to, Decoration9.mark({
2114
2322
  class: "cm-table"
2115
2323
  }));
2116
2324
  });
@@ -2154,9 +2362,9 @@ var TableWidget = class extends WidgetType6 {
2154
2362
  };
2155
2363
 
2156
2364
  // 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";
2365
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2366
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2367
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2160
2368
  var styles5 = EditorView12.baseTheme({
2161
2369
  "& .cm-task": {
2162
2370
  color: getToken("extend.colors.blue.500")
@@ -2203,23 +2411,23 @@ var CheckboxWidget = class extends WidgetType7 {
2203
2411
  return false;
2204
2412
  }
2205
2413
  };
2206
- var checkedDecoration = Decoration9.replace({
2414
+ var checkedDecoration = Decoration10.replace({
2207
2415
  widget: new CheckboxWidget(true)
2208
2416
  });
2209
- var uncheckedDecoration = Decoration9.replace({
2417
+ var uncheckedDecoration = Decoration10.replace({
2210
2418
  widget: new CheckboxWidget(false)
2211
2419
  });
2212
2420
  var buildDecorations6 = (view) => {
2213
- const builder = new RangeSetBuilder6();
2421
+ const builder = new RangeSetBuilder7();
2214
2422
  const { state } = view;
2215
2423
  const cursor = state.selection.main.head;
2216
2424
  for (const { from, to } of view.visibleRanges) {
2217
- syntaxTree7(state).iterate({
2425
+ syntaxTree8(state).iterate({
2218
2426
  enter: (node) => {
2219
2427
  if (node.name === "TaskMarker") {
2220
2428
  if (cursor < node.from || cursor > node.to) {
2221
2429
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
- builder.add(node.from - 2, node.from - 1, Decoration9.mark({
2430
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2223
2431
  class: "cm-task"
2224
2432
  }));
2225
2433
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2234,7 +2442,7 @@ var buildDecorations6 = (view) => {
2234
2442
  };
2235
2443
  var tasklist = (options = {}) => {
2236
2444
  return [
2237
- ViewPlugin7.fromClass(class {
2445
+ ViewPlugin8.fromClass(class {
2238
2446
  constructor(view) {
2239
2447
  this.decorations = buildDecorations6(view);
2240
2448
  }
@@ -2285,7 +2493,7 @@ var outliner = (options = {}) => {
2285
2493
  };
2286
2494
 
2287
2495
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2288
- import { keymap as keymap5 } from "@codemirror/view";
2496
+ import { keymap as keymap6 } from "@codemirror/view";
2289
2497
  var defaultItems = [
2290
2498
  "hello world!",
2291
2499
  "this is a test.",
@@ -2295,7 +2503,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2295
2503
  let t;
2296
2504
  let idx = 0;
2297
2505
  return [
2298
- keymap5.of([
2506
+ keymap6.of([
2299
2507
  {
2300
2508
  // Reset.
2301
2509
  key: "alt-meta-'",
@@ -2372,8 +2580,9 @@ var defaultTheme = {
2372
2580
  outline: "none"
2373
2581
  },
2374
2582
  ".cm-scroller": {
2375
- overflow: "visible",
2376
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
2583
+ // overflow: 'visible',
2584
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
2585
+ lineHeight: 1.4
2377
2586
  },
2378
2587
  ".cm-content": {
2379
2588
  padding: 0,
@@ -2407,7 +2616,7 @@ var defaultTheme = {
2407
2616
  paddingInline: 0
2408
2617
  },
2409
2618
  ".cm-activeLine": {
2410
- background: "inherit"
2619
+ background: "transparent"
2411
2620
  },
2412
2621
  //
2413
2622
  // Selection
@@ -2578,15 +2787,12 @@ var codeTheme = {
2578
2787
  };
2579
2788
 
2580
2789
  // 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";
2790
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2582
2791
  var EditorModes = [
2583
2792
  "default",
2584
2793
  "vim"
2585
2794
  ];
2586
2795
  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
2796
  const { themeMode } = useThemeContext();
2591
2797
  const rootRef = useRef(null);
2592
2798
  const [view, setView] = useState(null);
@@ -2594,7 +2800,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2594
2800
  view
2595
2801
  ]);
2596
2802
  useEffect2(() => {
2597
- if (autoFocus) {
2803
+ if (autoFocus && !view?.hasFocus) {
2804
+ if (view?.state.selection.main?.from === 0) {
2805
+ const { to } = view.state.doc.lineAt(0);
2806
+ view?.dispatch({
2807
+ selection: {
2808
+ anchor: to
2809
+ }
2810
+ });
2811
+ }
2598
2812
  view?.focus();
2599
2813
  }
2600
2814
  }, [
@@ -2612,8 +2826,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2612
2826
  // TODO(burdon): Doesn't catch errors in keymap functions.
2613
2827
  EditorView13.exceptionSink.of((err) => {
2614
2828
  log2.catch(err, void 0, {
2615
- F: __dxlog_file5,
2616
- L: 123,
2829
+ F: __dxlog_file3,
2830
+ L: 134,
2617
2831
  S: void 0,
2618
2832
  C: (f, a) => f(...a)
2619
2833
  });
@@ -2630,15 +2844,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2630
2844
  class: slots.content?.className ?? ""
2631
2845
  }),
2632
2846
  // State.
2847
+ EditorView13.editable.of(!readonly),
2633
2848
  EditorState2.readOnly.of(!!readonly),
2634
2849
  // Storage and replication.
2635
2850
  // NOTE: This must come before user extensions.
2636
2851
  model.extension,
2637
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
2852
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2638
2853
  editorMode === "vim" && vim(),
2639
2854
  // Custom.
2640
2855
  ...extensions
2641
- ].filter(Boolean)
2856
+ ].filter(isNotFalsy3)
2642
2857
  });
2643
2858
  const newView = new EditorView13({
2644
2859
  state,
@@ -2675,7 +2890,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2675
2890
  }
2676
2891
  case "Escape": {
2677
2892
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2678
- rootRef.current?.focus();
2893
+ view?.focus();
2679
2894
  }
2680
2895
  break;
2681
2896
  }
@@ -2688,9 +2903,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2688
2903
  key: model.id,
2689
2904
  role: "none",
2690
2905
  tabIndex: 0,
2691
- onKeyUp: handleKeyUp,
2692
2906
  ...slots.root,
2693
- ...editorMode !== "vim" && tabsterDOMAttribute,
2907
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2908
+ onKeyUp: handleKeyUp,
2694
2909
  ref: rootRef
2695
2910
  });
2696
2911
  });
@@ -2701,7 +2916,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2701
2916
  ref: forwardedRef,
2702
2917
  readonly,
2703
2918
  extensions: [
2704
- basicBundle({
2919
+ createBasicBundle({
2705
2920
  themeMode,
2706
2921
  placeholder: placeholder3,
2707
2922
  lineWrapping
@@ -2720,7 +2935,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2720
2935
  ref: forwardedRef,
2721
2936
  readonly,
2722
2937
  extensions: [
2723
- markdownBundle({
2938
+ createMarkdownExtensions({
2724
2939
  themeMode,
2725
2940
  placeholder: placeholder3
2726
2941
  }),
@@ -2747,12 +2962,173 @@ var defaultMarkdownSlots = {
2747
2962
  ...defaultSlots
2748
2963
  };
2749
2964
 
2965
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2966
+ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
2967
+ import { createContext } from "@radix-ui/react-context";
2968
+ import React2 from "react";
2969
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
2970
+ import { getSize } from "@dxos/react-ui-theme";
2971
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2972
+ var ToolbarRoot = ({ children, onAction }) => {
2973
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2974
+ onAction
2975
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2976
+ density: "fine"
2977
+ }, /* @__PURE__ */ React2.createElement("div", {
2978
+ role: "toolbar",
2979
+ className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
2980
+ }, children)));
2981
+ };
2982
+ var ToolbarButton = ({ Icon, onClick, title }) => {
2983
+ const { onAction } = useToolbarContext("ToolbarButton");
2984
+ const handleClick = () => {
2985
+ const action = onClick();
2986
+ if (action) {
2987
+ onAction?.(action);
2988
+ }
2989
+ };
2990
+ return /* @__PURE__ */ React2.createElement(Button, {
2991
+ variant: "ghost",
2992
+ classNames: "p-2",
2993
+ onClick: handleClick,
2994
+ title
2995
+ }, /* @__PURE__ */ React2.createElement(Icon, {
2996
+ className: getSize(5)
2997
+ }));
2998
+ };
2999
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3000
+ className: "grow"
3001
+ });
3002
+ var MarkdownHeading = () => {
3003
+ const { onAction } = useToolbarContext("MarkdownFormatting");
3004
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3005
+ defaultValue: "0",
3006
+ onValueChange: (value) => onAction?.({
3007
+ type: "heading",
3008
+ data: parseInt(value)
3009
+ })
3010
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3011
+ classNames: "w-[8rem]"
3012
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3013
+ value: "0"
3014
+ }, "Paragraph"), [
3015
+ 1,
3016
+ 2,
3017
+ 3,
3018
+ 4,
3019
+ 5,
3020
+ 6
3021
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3022
+ key: level,
3023
+ value: String(level)
3024
+ }, "Heading ", level))))));
3025
+ };
3026
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3027
+ role: "none"
3028
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3029
+ Icon: TextB,
3030
+ title: "Bold",
3031
+ onClick: () => ({
3032
+ type: "bold"
3033
+ })
3034
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3035
+ Icon: TextItalic,
3036
+ title: "Italic",
3037
+ onClick: () => ({
3038
+ type: "italic"
3039
+ })
3040
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3041
+ Icon: TextStrikethrough,
3042
+ title: "Strike-through",
3043
+ onClick: () => ({
3044
+ type: "strikethrough"
3045
+ })
3046
+ }));
3047
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3048
+ role: "none"
3049
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3050
+ Icon: ListBullets,
3051
+ title: "Bullet list",
3052
+ onClick: () => ({
3053
+ type: "list"
3054
+ })
3055
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3056
+ Icon: ListNumbers,
3057
+ title: "Numbered list",
3058
+ onClick: () => ({
3059
+ type: "list-ordered"
3060
+ })
3061
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3062
+ Icon: ListChecks,
3063
+ title: "Task list",
3064
+ onClick: () => ({
3065
+ type: "list-tasks"
3066
+ })
3067
+ }));
3068
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3069
+ role: "none"
3070
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3071
+ Icon: CaretRight,
3072
+ title: "Block quite",
3073
+ onClick: () => ({
3074
+ type: "blockquote"
3075
+ })
3076
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3077
+ Icon: Code,
3078
+ title: "Code block",
3079
+ onClick: () => ({
3080
+ type: "codeblock"
3081
+ })
3082
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3083
+ Icon: Table2,
3084
+ title: "Table",
3085
+ onClick: () => ({
3086
+ type: "table"
3087
+ })
3088
+ }));
3089
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3090
+ role: "none"
3091
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3092
+ Icon: Link,
3093
+ title: "Link",
3094
+ onClick: () => ({
3095
+ type: "link"
3096
+ })
3097
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3098
+ Icon: At,
3099
+ title: "Mention",
3100
+ onClick: () => ({
3101
+ type: "mention"
3102
+ })
3103
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3104
+ Icon: Image,
3105
+ title: "Image",
3106
+ onClick: () => ({
3107
+ type: "image"
3108
+ })
3109
+ }));
3110
+ 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));
3111
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
3112
+ Icon: ChatText,
3113
+ title: "Create comment",
3114
+ onClick: () => ({
3115
+ type: "comment"
3116
+ })
3117
+ });
3118
+ var Toolbar = {
3119
+ Root: ToolbarRoot,
3120
+ Button: ToolbarButton,
3121
+ Separator: ToolbarSeparator,
3122
+ Markdown: MarkdownStandard,
3123
+ Extended: MarkdownExtended
3124
+ };
3125
+
2750
3126
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2751
3127
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2752
3128
  import { Context as Context2 } from "@dxos/context";
2753
- import { invariant as invariant4 } from "@dxos/invariant";
3129
+ import { invariant as invariant2 } from "@dxos/invariant";
2754
3130
  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";
3131
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2756
3132
  var DEBOUNCE_INTERVAL = 100;
2757
3133
  var SpaceAwarenessProvider = class {
2758
3134
  constructor(params) {
@@ -2792,7 +3168,7 @@ var SpaceAwarenessProvider = class {
2792
3168
  log3.debug("failed to query awareness", {
2793
3169
  err
2794
3170
  }, {
2795
- F: __dxlog_file6,
3171
+ F: __dxlog_file4,
2796
3172
  L: 89,
2797
3173
  S: this,
2798
3174
  C: (f, a) => f(...a)
@@ -2808,8 +3184,8 @@ var SpaceAwarenessProvider = class {
2808
3184
  return Array.from(this._remoteStates.values());
2809
3185
  }
2810
3186
  update(position) {
2811
- invariant4(this._postTask, void 0, {
2812
- F: __dxlog_file6,
3187
+ invariant2(this._postTask, void 0, {
3188
+ F: __dxlog_file4,
2813
3189
  L: 104,
2814
3190
  S: this,
2815
3191
  A: [
@@ -2825,8 +3201,8 @@ var SpaceAwarenessProvider = class {
2825
3201
  this._postTask.schedule();
2826
3202
  }
2827
3203
  _handleQueryMessage() {
2828
- invariant4(this._postTask, void 0, {
2829
- F: __dxlog_file6,
3204
+ invariant2(this._postTask, void 0, {
3205
+ F: __dxlog_file4,
2830
3206
  L: 115,
2831
3207
  S: this,
2832
3208
  A: [
@@ -2837,8 +3213,8 @@ var SpaceAwarenessProvider = class {
2837
3213
  this._postTask.schedule();
2838
3214
  }
2839
3215
  _handlePostMessage(message) {
2840
- invariant4(message.kind === "post", void 0, {
2841
- F: __dxlog_file6,
3216
+ invariant2(message.kind === "post", void 0, {
3217
+ F: __dxlog_file4,
2842
3218
  L: 120,
2843
3219
  S: this,
2844
3220
  A: [
@@ -2858,9 +3234,50 @@ var modelState = StateField5.define({
2858
3234
  update: (model) => model
2859
3235
  });
2860
3236
 
2861
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3237
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3238
+ var useActionHandler = (view) => {
3239
+ return (action) => {
3240
+ if (!view) {
3241
+ return;
3242
+ }
3243
+ switch (action.type) {
3244
+ case "heading":
3245
+ setHeading(parseInt(action.data))(view);
3246
+ break;
3247
+ case "bold":
3248
+ toggleBold(view);
3249
+ break;
3250
+ case "italic":
3251
+ toggleItalic(view);
3252
+ break;
3253
+ case "strikethrough":
3254
+ toggleStrikethrough(view);
3255
+ break;
3256
+ case "list":
3257
+ toggleList(view);
3258
+ break;
3259
+ case "codeblock":
3260
+ insertCodeblock(view);
3261
+ break;
3262
+ case "table":
3263
+ insertTable(view);
3264
+ break;
3265
+ case "comment":
3266
+ createComment(view);
3267
+ break;
3268
+ }
3269
+ setTimeout(() => {
3270
+ view.focus();
3271
+ view.dispatch({
3272
+ selection: view.state.selection
3273
+ });
3274
+ });
3275
+ };
3276
+ };
3277
+
3278
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
2862
3279
  import { useState as useState2 } from "react";
2863
- var useTextEditor = () => {
3280
+ var useEditorView = () => {
2864
3281
  const [view, setView] = useState2(null);
2865
3282
  return [
2866
3283
  (ref) => {
@@ -2870,19 +3287,125 @@ var useTextEditor = () => {
2870
3287
  ];
2871
3288
  };
2872
3289
 
3290
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3291
+ import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3292
+ import { EditorView as EditorView14 } from "@codemirror/view";
3293
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
3294
+ import { log as log4 } from "@dxos/log";
3295
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3296
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3297
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3298
+ const onUpdate = useRef2();
3299
+ const [view, setView] = useState3();
3300
+ const parentRef = useRef2(null);
3301
+ useEffect3(() => {
3302
+ if (parentRef.current) {
3303
+ const state = EditorState3.create({
3304
+ doc,
3305
+ selection,
3306
+ extensions: [
3307
+ // TODO(burdon): Doesn't catch errors in keymap functions.
3308
+ EditorView14.exceptionSink.of((err) => {
3309
+ log4.catch(err, void 0, {
3310
+ F: __dxlog_file5,
3311
+ L: 57,
3312
+ S: void 0,
3313
+ C: (f, a) => f(...a)
3314
+ });
3315
+ }),
3316
+ extensions,
3317
+ EditorView14.updateListener.of(() => {
3318
+ onUpdate.current?.();
3319
+ })
3320
+ ].filter(isNotFalsy4)
3321
+ });
3322
+ const view2 = new EditorView14({
3323
+ parent: parentRef.current,
3324
+ scrollTo,
3325
+ state,
3326
+ // NOTE: Uncomment to debug/monitor all transactions.
3327
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
3328
+ dispatchTransactions: (trs, view3) => {
3329
+ if (debug) {
3330
+ logChanges(trs);
3331
+ }
3332
+ view3.update(trs);
3333
+ }
3334
+ });
3335
+ setView(view2);
3336
+ }
3337
+ return () => {
3338
+ view?.destroy();
3339
+ };
3340
+ }, [
3341
+ parentRef
3342
+ ]);
3343
+ useEffect3(() => {
3344
+ if (view) {
3345
+ if (!selection && !view.state.selection.main.anchor) {
3346
+ selection = EditorSelection.single(view.state.doc.line(1).to);
3347
+ }
3348
+ if (selection || scrollTo) {
3349
+ onUpdate.current = () => {
3350
+ onUpdate.current = void 0;
3351
+ view.dispatch({
3352
+ selection,
3353
+ effects: scrollTo && [
3354
+ scrollTo
3355
+ ],
3356
+ scrollIntoView: !scrollTo
3357
+ });
3358
+ };
3359
+ }
3360
+ if (autoFocus) {
3361
+ view.focus();
3362
+ }
3363
+ }
3364
+ }, [
3365
+ view,
3366
+ autoFocus,
3367
+ selection,
3368
+ scrollTo
3369
+ ]);
3370
+ return {
3371
+ parentRef,
3372
+ view
3373
+ };
3374
+ };
3375
+ var createDataExtensions = ({ readonly = false } = {}) => {
3376
+ return [
3377
+ //
3378
+ EditorState3.readOnly.of(readonly),
3379
+ EditorView14.editable.of(!readonly)
3380
+ ];
3381
+ };
3382
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3383
+ return [
3384
+ //
3385
+ EditorView14.baseTheme(defaultTheme),
3386
+ theme && EditorView14.theme(theme),
3387
+ EditorView14.darkTheme.of(themeMode === "dark"),
3388
+ EditorView14.editorAttributes.of({
3389
+ class: slots?.editor?.className ?? ""
3390
+ }),
3391
+ EditorView14.contentAttributes.of({
3392
+ class: slots?.content?.className ?? ""
3393
+ })
3394
+ ].filter(isNotFalsy4);
3395
+ };
3396
+
2873
3397
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2874
3398
  import get4 from "lodash.get";
2875
- import { useEffect as useEffect3, useState as useState3 } from "react";
3399
+ import { useEffect as useEffect4, useState as useState4 } from "react";
2876
3400
  import { generateName } from "@dxos/display-name";
2877
3401
  import { getRawDoc } from "@dxos/echo-schema";
2878
- import { invariant as invariant5 } from "@dxos/invariant";
3402
+ import { invariant as invariant3 } from "@dxos/invariant";
2879
3403
  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";
3404
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2882
3405
  var useTextModel = (props) => {
2883
3406
  const { identity, space, text } = props;
2884
- const [model, setModel] = useState3();
2885
- useEffect3(() => setModel(createModel(props)), [
3407
+ const [model, setModel] = useState4();
3408
+ useEffect4(() => setModel(createModel(props)), [
2886
3409
  identity,
2887
3410
  space,
2888
3411
  text
@@ -2890,9 +3413,9 @@ var useTextModel = (props) => {
2890
3413
  return model;
2891
3414
  };
2892
3415
  var createModel = ({ space, identity, text }) => {
2893
- invariant5(isAutomergeObject(text), void 0, {
2894
- F: __dxlog_file7,
2895
- L: 35,
3416
+ invariant3(isAutomergeObject(text), void 0, {
3417
+ F: __dxlog_file6,
3418
+ L: 34,
2896
3419
  S: void 0,
2897
3420
  A: [
2898
3421
  "isAutomergeObject(text)",
@@ -2903,7 +3426,7 @@ var createModel = ({ space, identity, text }) => {
2903
3426
  const doc = getRawDoc(obj, [
2904
3427
  obj.field
2905
3428
  ]);
2906
- const awarenessProvider = space && new SpaceAwarenessProvider({
3429
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2907
3430
  space,
2908
3431
  channel: `automerge.awareness.${obj.id}`,
2909
3432
  info: {
@@ -2913,19 +3436,22 @@ var createModel = ({ space, identity, text }) => {
2913
3436
  },
2914
3437
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2915
3438
  });
3439
+ const extensions = [
3440
+ //
3441
+ modelState.init(() => model),
3442
+ automerge({
3443
+ handle: doc.handle,
3444
+ path: doc.path
3445
+ })
3446
+ ];
3447
+ if (awarenessProvider2) {
3448
+ extensions.push(awareness(awarenessProvider2));
3449
+ }
2916
3450
  const model = {
2917
3451
  id: obj.id,
2918
3452
  content: doc,
2919
3453
  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),
3454
+ extension: extensions,
2929
3455
  peer: identity ? {
2930
3456
  id: identity.identityKey.toHex(),
2931
3457
  name: identity.profile?.displayName
@@ -2934,7 +3460,6 @@ var createModel = ({ space, identity, text }) => {
2934
3460
  return model;
2935
3461
  };
2936
3462
  export {
2937
- AwarenessProvider,
2938
3463
  BaseTextEditor,
2939
3464
  Cursor,
2940
3465
  Doc,
@@ -2944,30 +3469,39 @@ export {
2944
3469
  SpaceAwarenessProvider,
2945
3470
  TextEditor,
2946
3471
  TextKind,
3472
+ Toolbar,
2947
3473
  YText,
2948
3474
  YXmlFragment,
2949
3475
  autocomplete,
2950
- automerge3 as automerge,
3476
+ automerge,
2951
3477
  awareness,
2952
- basicBundle,
3478
+ awarenessProvider,
2953
3479
  blast,
2954
3480
  callbackWrapper,
2955
3481
  code2 as code,
2956
3482
  codeTheme,
2957
3483
  comments,
3484
+ createBasicBundle,
3485
+ createComment,
3486
+ createDataExtensions,
3487
+ createMarkdownExtensions,
3488
+ createThemeExtensions,
2958
3489
  defaultMarkdownSlots,
2959
3490
  defaultOptions,
2960
3491
  defaultSlots,
2961
3492
  defaultTextSlots,
2962
3493
  defaultTheme,
3494
+ focusComment,
3495
+ formatting,
2963
3496
  getToken,
2964
3497
  heading2 as heading,
2965
3498
  hr,
2966
3499
  image,
3500
+ insertCodeblock,
3501
+ insertTable,
2967
3502
  link,
2968
3503
  listener,
2969
3504
  logChanges,
2970
- markdownBundle,
2971
3505
  markdownHighlightStyle,
2972
3506
  markdownTags,
2973
3507
  markdownTagsExtensions,
@@ -2976,15 +3510,23 @@ export {
2976
3510
  modelState,
2977
3511
  outliner,
2978
3512
  setComments,
2979
- setFocus,
3513
+ setHeading,
2980
3514
  setSelection,
2981
3515
  table,
2982
3516
  tags2 as tags,
2983
3517
  tasklist,
2984
3518
  textTheme,
3519
+ toggleBold,
3520
+ toggleItalic,
3521
+ toggleList,
3522
+ toggleStrikethrough,
3523
+ toggleStyle,
2985
3524
  typewriter,
3525
+ useActionHandler,
2986
3526
  useComments,
3527
+ useEditorView,
2987
3528
  useTextEditor,
2988
- useTextModel
3529
+ useTextModel,
3530
+ useToolbarContext
2989
3531
  };
2990
3532
  //# sourceMappingURL=index.mjs.map