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

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 (90) hide show
  1. package/dist/lib/browser/index.mjs +808 -252
  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/automerge.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/{comments.stories.d.ts → hooks.stories.d.ts} +9 -12
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  16. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  17. package/dist/types/src/components/index.d.ts +1 -0
  18. package/dist/types/src/components/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  21. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  23. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  24. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  29. package/dist/types/src/extensions/awareness.d.ts +1 -1
  30. package/dist/types/src/extensions/basic.d.ts +1 -1
  31. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  32. package/dist/types/src/extensions/comments.d.ts +6 -2
  33. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  35. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -1
  37. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  38. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  40. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  41. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  42. package/dist/types/src/hooks/index.d.ts +2 -0
  43. package/dist/types/src/hooks/index.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  45. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  46. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  47. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  48. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  49. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  50. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  51. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  52. package/dist/types/src/index.d.ts +1 -1
  53. package/dist/types/src/index.d.ts.map +1 -1
  54. package/dist/types/src/themes/default.d.ts.map +1 -1
  55. package/package.json +38 -25
  56. package/src/components/TextEditor/MarkdownEditor.stories.tsx +10 -16
  57. package/src/components/TextEditor/TextEditor.tsx +27 -16
  58. package/src/components/TextEditor/automerge.stories.tsx +1 -2
  59. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  60. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  61. package/src/components/Toolbar/Toolbar.tsx +166 -0
  62. package/src/components/Toolbar/index.ts +5 -0
  63. package/src/components/index.ts +1 -0
  64. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  65. package/src/extensions/automerge/automerge.test.ts +13 -0
  66. package/src/extensions/automerge/automerge.ts +26 -11
  67. package/src/extensions/automerge/cursor.ts +3 -3
  68. package/src/extensions/automerge/defs.ts +9 -9
  69. package/src/extensions/automerge/semaphore.ts +17 -19
  70. package/src/extensions/automerge/update-automerge.ts +12 -6
  71. package/src/extensions/automerge/update-codemirror.ts +5 -5
  72. package/src/extensions/awareness.ts +7 -4
  73. package/src/extensions/basic.ts +15 -13
  74. package/src/extensions/blast.ts +4 -1
  75. package/src/extensions/comments.ts +113 -65
  76. package/src/extensions/markdown/bundle.ts +10 -5
  77. package/src/extensions/markdown/code.ts +36 -13
  78. package/src/extensions/markdown/formatting.ts +188 -0
  79. package/src/extensions/markdown/heading.ts +1 -1
  80. package/src/extensions/markdown/index.ts +1 -0
  81. package/src/extensions/markdown/link.ts +12 -2
  82. package/src/hooks/index.ts +2 -0
  83. package/src/hooks/useActionHandler.ts +62 -0
  84. package/src/hooks/useEditorView.ts +29 -0
  85. package/src/hooks/useTextEditor.ts +143 -19
  86. package/src/hooks/useTextModel.ts +28 -9
  87. package/src/index.ts +1 -1
  88. package/src/themes/default.ts +9 -4
  89. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  90. package/src/components/TextEditor/comments.stories.tsx +0 -357
@@ -1,6 +1,7 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
3
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
+ import { keymap as keymap7 } from "@codemirror/view";
4
5
 
5
6
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
7
  import { tags as tags2 } from "@lezer/highlight";
@@ -8,13 +9,13 @@ import { tags as tags2 } from "@lezer/highlight";
8
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
10
  import { EditorState as EditorState2 } from "@codemirror/state";
10
11
  import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
12
12
  import { vim } from "@replit/codemirror-vim";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
14
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
15
15
  import { log as log2 } from "@dxos/log";
16
16
  import { useThemeContext } from "@dxos/react-ui";
17
- import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { focusRing, mx as mx3 } from "@dxos/react-ui-theme";
18
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
18
19
 
19
20
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
21
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -53,14 +54,14 @@ var autocomplete = ({ onSearch }) => {
53
54
  };
54
55
 
55
56
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
57
+ import { invertedEffects } from "@codemirror/commands";
56
58
  import { StateField } from "@codemirror/state";
57
59
  import { EditorView, ViewPlugin } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
- import { invariant } from "@dxos/invariant";
60
+ import { next as A4 } from "@dxos/automerge/automerge";
60
61
 
61
62
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
62
63
  import get from "lodash.get";
63
- import { next as automerge } from "@dxos/automerge/automerge";
64
+ import { next as A } from "@dxos/automerge/automerge";
64
65
  var cursorConverter = (handle, path) => ({
65
66
  // TODO(burdon): Handle assoc to associate with a previous character.
66
67
  toCursor: (pos) => {
@@ -72,7 +73,7 @@ var cursorConverter = (handle, path) => ({
72
73
  if (typeof value === "string" && value.length <= pos) {
73
74
  return "end";
74
75
  }
75
- return automerge.getCursor(doc, path.slice(), pos);
76
+ return A.getCursor(doc, path.slice(), pos);
76
77
  },
77
78
  fromCursor: (cursor) => {
78
79
  if (cursor === "") {
@@ -90,26 +91,28 @@ var cursorConverter = (handle, path) => ({
90
91
  return 0;
91
92
  }
92
93
  }
93
- return automerge.getCursorPosition(doc, path.slice(), cursor);
94
+ return A.getCursorPosition(doc, path.slice(), cursor);
94
95
  }
95
96
  });
96
97
 
97
98
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
98
99
  import { Annotation, StateEffect } from "@codemirror/state";
99
- var effectType = StateEffect.define({});
100
- var reconcileAnnotationType = Annotation.define();
101
100
  var getPath = (state, field) => state.field(field).path;
102
101
  var getLastHeads = (state, field) => state.field(field).lastHeads;
103
- var updateHeads = (newHeads) => effectType.of({
102
+ var updateHeadsEffect = StateEffect.define({});
103
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
104
104
  newHeads
105
105
  });
106
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
106
+ var reconcileAnnotation = Annotation.define();
107
+ var isReconcile = (tr) => {
108
+ return !!tr.annotation(reconcileAnnotation);
109
+ };
107
110
 
108
111
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
109
- import { next as automerge2 } from "@dxos/automerge/automerge";
112
+ import { next as A3 } from "@dxos/automerge/automerge";
110
113
 
111
114
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
112
- import { next as am } from "@dxos/automerge/automerge";
115
+ import { next as A2 } from "@dxos/automerge/automerge";
113
116
  var updateAutomerge = (field, handle, transactions, state) => {
114
117
  const { lastHeads, path } = state.field(field);
115
118
  let hasChanges = false;
@@ -122,11 +125,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
122
125
  return void 0;
123
126
  }
124
127
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
+ const invertedTransactions = [];
125
129
  for (const tr of transactions) {
126
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
127
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
130
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
131
+ invertedTransactions.push({
132
+ from: fromA,
133
+ del: toA - fromA,
134
+ insert
135
+ });
128
136
  });
129
137
  }
138
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
139
+ A2.splice(doc, path.slice(), from, del, insert.toString());
140
+ });
130
141
  });
131
142
  return newHeads ?? void 0;
132
143
  };
@@ -141,13 +152,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
141
152
  selection = selection.map(changeSet, 1);
142
153
  view.dispatch({
143
154
  changes: changeSet,
144
- annotations: reconcileAnnotationType.of({})
155
+ annotations: reconcileAnnotation.of(false)
145
156
  });
146
157
  }
147
158
  }
148
159
  view.dispatch({
149
160
  selection,
150
- annotations: reconcileAnnotationType.of({})
161
+ annotations: reconcileAnnotation.of(false)
151
162
  });
152
163
  };
153
164
  var handlePatch = (patch, target, state) => {
@@ -168,7 +179,7 @@ var handleInsert = (target, patch) => {
168
179
  if (index == null) {
169
180
  return [];
170
181
  }
171
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
182
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
172
183
  return [
173
184
  {
174
185
  from: index,
@@ -246,36 +257,37 @@ var PatchSemaphore = class {
246
257
  this._state = _state;
247
258
  this._inReconcile = false;
248
259
  }
249
- // NOTE: Cannot destruct view.state.
250
260
  reconcile(view) {
251
- if (this._inReconcile) {
252
- return;
261
+ if (!this._inReconcile) {
262
+ this._inReconcile = true;
263
+ this.doReconcile(view);
264
+ this._inReconcile = false;
253
265
  }
254
- this._inReconcile = true;
266
+ }
267
+ doReconcile(view) {
255
268
  const path = getPath(view.state, this._state);
256
269
  const oldHeads = getLastHeads(view.state, this._state);
257
270
  let selection = view.state.selection;
258
- const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
271
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
259
272
  const toInvert = transactions.slice().reverse();
260
- for (const tx of toInvert) {
261
- const inverted = tx.changes.invert(tx.startState.doc);
273
+ for (const tr of toInvert) {
274
+ const inverted = tr.changes.invert(tr.startState.doc);
262
275
  selection = selection.map(inverted);
263
276
  view.dispatch({
264
277
  changes: inverted,
265
- annotations: reconcileAnnotationType.of(true)
278
+ annotations: reconcileAnnotation.of(true)
266
279
  });
267
280
  }
268
281
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
282
  if (newHeads === null || newHeads === void 0) {
270
- newHeads = automerge2.getHeads(this._handle.docSync());
283
+ newHeads = A3.getHeads(this._handle.docSync());
271
284
  }
272
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
285
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
273
286
  updateCodeMirror(view, selection, path, diff);
274
287
  view.dispatch({
275
288
  effects: updateHeads(newHeads),
276
- annotations: reconcileAnnotationType.of({})
289
+ annotations: reconcileAnnotation.of(false)
277
290
  });
278
- this._inReconcile = false;
279
291
  }
280
292
  };
281
293
 
@@ -317,12 +329,11 @@ var Cursor = class _Cursor {
317
329
  };
318
330
 
319
331
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
320
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
321
- var automerge3 = ({ handle, path }) => {
322
- const state = StateField.define({
332
+ var automerge = ({ handle, path }) => {
333
+ const syncState = StateField.define({
323
334
  create: () => ({
324
335
  path: path.slice(),
325
- lastHeads: A.getHeads(handle.docSync()),
336
+ lastHeads: A4.getHeads(handle.docSync()),
326
337
  unreconciledTransactions: []
327
338
  }),
328
339
  update: (value, tr) => {
@@ -333,7 +344,7 @@ var automerge3 = ({ handle, path }) => {
333
344
  };
334
345
  let clearUnreconciled = false;
335
346
  for (const effect of tr.effects) {
336
- if (effect.is(effectType)) {
347
+ if (effect.is(updateHeadsEffect)) {
337
348
  result.lastHeads = effect.value.newHeads;
338
349
  clearUnreconciled = true;
339
350
  }
@@ -341,29 +352,21 @@ var automerge3 = ({ handle, path }) => {
341
352
  if (clearUnreconciled) {
342
353
  result.unreconciledTransactions = [];
343
354
  } else {
344
- if (!isReconcileTx(tr)) {
355
+ if (!isReconcile(tr)) {
345
356
  result.unreconciledTransactions.push(tr);
346
357
  }
347
358
  }
348
359
  return result;
349
360
  }
350
361
  });
351
- const semaphore = new PatchSemaphore(handle, state);
362
+ const semaphore = new PatchSemaphore(handle, syncState);
352
363
  return [
353
364
  Cursor.converter.of(cursorConverter(handle, path)),
365
+ syncState,
354
366
  // Reconcile external updates.
355
367
  ViewPlugin.fromClass(class {
356
368
  constructor(_view) {
357
369
  this._view = _view;
358
- invariant(this._view, void 0, {
359
- F: __dxlog_file,
360
- L: 66,
361
- S: this,
362
- A: [
363
- "this._view",
364
- ""
365
- ]
366
- });
367
370
  handle.addListener("change", this._handleChange.bind(this));
368
371
  }
369
372
  destroy() {
@@ -379,7 +382,17 @@ var automerge3 = ({ handle, path }) => {
379
382
  semaphore.reconcile(view);
380
383
  }
381
384
  }),
382
- state
385
+ // TODO(burdon): Record undo transactions.
386
+ // See https://github.com/yjs/y-codemirror.next/tree/main
387
+ // https://codemirror.net/examples/inverted-effect
388
+ invertedEffects.of((tr) => {
389
+ const effects = [];
390
+ if (!tr.changes.empty) {
391
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
392
+ });
393
+ }
394
+ return effects;
395
+ })
383
396
  ];
384
397
  };
385
398
 
@@ -398,13 +411,13 @@ var dummyProvider = {
398
411
  update: () => {
399
412
  }
400
413
  };
401
- var AwarenessProvider = Facet2.define({
414
+ var awarenessProvider = Facet2.define({
402
415
  combine: (providers) => providers[0] ?? dummyProvider
403
416
  });
404
417
  var RemoteSelectionChangedAnnotation = Annotation2.define();
405
418
  var awareness = (provider = dummyProvider) => {
406
419
  return [
407
- AwarenessProvider.of(provider),
420
+ awarenessProvider.of(provider),
408
421
  ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
409
422
  decorations: (value) => value.decorations
410
423
  }),
@@ -418,7 +431,7 @@ var RemoteSelectionsDecorator = class {
418
431
  this._lastAnchor = void 0;
419
432
  this._lastHead = void 0;
420
433
  this._cursorConverter = view.state.facet(Cursor.converter);
421
- this._provider = view.state.facet(AwarenessProvider);
434
+ this._provider = view.state.facet(awarenessProvider);
422
435
  this._provider.open();
423
436
  this._provider.remoteStateChange.on(this._ctx, () => {
424
437
  view.dispatch({
@@ -624,24 +637,25 @@ import { history } from "@codemirror/commands";
624
637
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
625
638
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
626
639
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
627
- var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
628
- lineWrapping ? EditorView3.lineWrapping : [],
640
+ import { isNotFalsy } from "@dxos/util";
641
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
642
+ lineWrapping && EditorView3.lineWrapping,
629
643
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
630
644
  bracketMatching(),
631
645
  closeBrackets(),
632
646
  drawSelection(),
633
647
  highlightActiveLine(),
634
648
  history(),
635
- _placeholder ? placeholder(_placeholder) : [],
649
+ _placeholder && placeholder(_placeholder),
636
650
  // https://github.com/codemirror/theme-one-dark
637
651
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
638
- ];
652
+ ].filter(isNotFalsy);
639
653
 
640
654
  // packages/ui/react-ui-editor/src/extensions/blast.ts
641
655
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
642
656
  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";
657
+ import { invariant } from "@dxos/invariant";
658
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
645
659
  var defaultOptions = {
646
660
  effect: 2,
647
661
  maxParticles: 200,
@@ -767,9 +781,9 @@ var Blaster = class {
767
781
  return this._node;
768
782
  }
769
783
  initialize() {
770
- invariant2(!this._canvas && !this._ctx, void 0, {
771
- F: __dxlog_file2,
772
- L: 138,
784
+ invariant(!this._canvas && !this._ctx, void 0, {
785
+ F: __dxlog_file,
786
+ L: 141,
773
787
  S: this,
774
788
  A: [
775
789
  "!this._canvas && !this._ctx",
@@ -804,9 +818,9 @@ var Blaster = class {
804
818
  }
805
819
  }
806
820
  start() {
807
- invariant2(this._canvas && this._ctx, void 0, {
808
- F: __dxlog_file2,
809
- L: 177,
821
+ invariant(this._canvas && this._ctx, void 0, {
822
+ F: __dxlog_file,
823
+ L: 180,
810
824
  S: this,
811
825
  A: [
812
826
  "this._canvas && this._ctx",
@@ -948,12 +962,12 @@ var random = (min, max) => {
948
962
  };
949
963
 
950
964
  // packages/ui/react-ui-editor/src/extensions/comments.ts
951
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
965
+ import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
966
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
952
967
  import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
953
968
  import sortBy from "lodash.sortby";
954
969
  import { useEffect } from "react";
955
970
  import { debounce } from "@dxos/async";
956
- import { invariant as invariant3 } from "@dxos/invariant";
957
971
  import { nonNullable } from "@dxos/util";
958
972
 
959
973
  // packages/ui/react-ui-editor/src/styles/cursors.ts
@@ -1025,13 +1039,13 @@ var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue)
1025
1039
 
1026
1040
  // packages/ui/react-ui-editor/src/util.ts
1027
1041
  import { log } from "@dxos/log";
1028
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1042
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1029
1043
  var callbackWrapper = (fn) => (...args) => {
1030
1044
  try {
1031
1045
  return fn(...args);
1032
1046
  } catch (err) {
1033
1047
  log.catch(err, void 0, {
1034
- F: __dxlog_file3,
1048
+ F: __dxlog_file2,
1035
1049
  L: 18,
1036
1050
  S: void 0,
1037
1051
  C: (f, a) => f(...a)
@@ -1059,7 +1073,6 @@ var logChanges = (trs) => {
1059
1073
  };
1060
1074
 
1061
1075
  // 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
1076
  var styles2 = EditorView5.baseTheme({
1064
1077
  "& .cm-comment": {
1065
1078
  backgroundColor: getToken("extend.colors.yellow.50")
@@ -1068,15 +1081,13 @@ var styles2 = EditorView5.baseTheme({
1068
1081
  backgroundColor: getToken("extend.colors.yellow.100")
1069
1082
  }
1070
1083
  });
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) => {
1084
+ var commentMark = Decoration2.mark({
1085
+ class: "cm-comment"
1086
+ });
1087
+ var commentCurrentMark = Decoration2.mark({
1088
+ class: "cm-comment-current"
1089
+ });
1090
+ var focusComment = (view, id, center = true) => {
1080
1091
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1081
1092
  if (!comment?.comment.cursor) {
1082
1093
  return;
@@ -1138,7 +1149,7 @@ var commentsState = StateField2.define({
1138
1149
  return value;
1139
1150
  }
1140
1151
  });
1141
- var highlightDecorations = EditorView5.decorations.compute([
1152
+ var commentsDecorations = EditorView5.decorations.compute([
1142
1153
  commentsState
1143
1154
  ], (state) => {
1144
1155
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -1149,9 +1160,9 @@ var highlightDecorations = EditorView5.decorations.compute([
1149
1160
  return void 0;
1150
1161
  }
1151
1162
  if (comment.comment.id === current) {
1152
- return marks.highlightActive.range(range.from, range.to);
1163
+ return commentCurrentMark.range(range.from, range.to);
1153
1164
  } else {
1154
- return marks.highlight.range(range.from, range.to);
1165
+ return commentMark.range(range.from, range.to);
1155
1166
  }
1156
1167
  }).filter(nonNullable);
1157
1168
  return Decoration2.set(decorations);
@@ -1180,84 +1191,124 @@ var trackPastedComments = (onUpdate) => {
1180
1191
  cut: handleTrack,
1181
1192
  copy: handleTrack
1182
1193
  }),
1183
- // Handle paste.
1184
- EditorView5.updateListener.of(({ view, state, transactions }) => {
1194
+ // Track deleted comments.
1195
+ invertedEffects2.of((tr) => {
1196
+ const { comments: comments2 } = tr.startState.field(commentsState);
1197
+ const effects = [];
1198
+ tr.changes.iterChangedRanges((fromA, toA) => {
1199
+ for (const { comment: { id }, range: { from, to } } of comments2) {
1200
+ if (from < to && from >= fromA && to <= toA) {
1201
+ effects.push(restoreCommentEffect.of({
1202
+ id,
1203
+ from,
1204
+ to
1205
+ }));
1206
+ }
1207
+ }
1208
+ });
1209
+ return effects;
1210
+ }),
1211
+ // Handle paste or the undo of comment deletion.
1212
+ EditorView5.updateListener.of((update2) => {
1213
+ const restore = [];
1214
+ for (let i = 0; i < update2.transactions.length; i++) {
1215
+ const tr = update2.transactions[i];
1216
+ for (let j = 0; j < restore.length; j++) {
1217
+ restore[j] = mapTrackedComment(restore[j], tr.changes);
1218
+ }
1219
+ for (const effect of tr.effects) {
1220
+ if (effect.is(restoreCommentEffect)) {
1221
+ restore.push(effect.value);
1222
+ }
1223
+ }
1224
+ }
1185
1225
  if (tracked) {
1186
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1226
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1187
1227
  if (paste) {
1188
1228
  let found = -1;
1189
1229
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1190
1230
  if (text.eq(tracked.text)) {
1191
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1192
- fromB = transactions[i].changes.mapPos(fromB);
1231
+ for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1232
+ fromB = update2.transactions[i].changes.mapPos(fromB);
1193
1233
  }
1194
1234
  found = fromB;
1195
1235
  }
1196
1236
  });
1197
1237
  if (found > -1) {
1198
1238
  for (const moved of tracked.comments) {
1199
- const range = {
1239
+ restore.push({
1240
+ id: moved.id,
1200
1241
  from: found + moved.from,
1201
1242
  to: found + moved.to
1202
- };
1203
- const cursor = Cursor.getCursorFromRange(state, range);
1204
- onUpdate(moved.id, cursor);
1243
+ });
1205
1244
  }
1206
1245
  }
1207
1246
  tracked = null;
1208
1247
  }
1209
1248
  }
1249
+ for (const comment of restore) {
1250
+ const { comments: comments2 } = update2.startState.field(commentsState);
1251
+ const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
1252
+ if (!exists) {
1253
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
1254
+ onUpdate(comment.id, cursor);
1255
+ }
1256
+ }
1210
1257
  })
1211
1258
  ];
1212
1259
  };
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
- ]
1260
+ var mapTrackedComment = (comment, changes) => ({
1261
+ id: comment.id,
1262
+ from: changes.mapPos(comment.from, 1),
1263
+ to: changes.mapPos(comment.to, 1)
1264
+ });
1265
+ var restoreCommentEffect = StateEffect2.define({
1266
+ map: mapTrackedComment
1267
+ });
1268
+ var optionsFacet = Facet3.define({
1269
+ combine: (providers) => providers[0]
1270
+ });
1271
+ var createComment = (view) => {
1272
+ const options = view.state.facet(optionsFacet);
1273
+ const { from, to } = view.state.selection.main;
1274
+ if (from === to) {
1275
+ return false;
1276
+ }
1277
+ if (to === view.state.doc.length) {
1278
+ view.dispatch({
1279
+ changes: {
1280
+ from: to,
1281
+ insert: "\n"
1282
+ }
1225
1283
  });
1226
- const { from, to } = view.state.selection.main;
1227
- if (from === to) {
1228
- return false;
1229
- }
1230
- if (to === view.state.doc.length) {
1284
+ }
1285
+ const cursor = Cursor.getCursorFromRange(view.state, {
1286
+ from,
1287
+ to
1288
+ });
1289
+ if (cursor) {
1290
+ const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1291
+ if (id) {
1231
1292
  view.dispatch({
1232
- changes: {
1233
- from: to,
1234
- insert: "\n"
1293
+ effects: setSelection.of({
1294
+ current: id
1295
+ }),
1296
+ selection: {
1297
+ anchor: to
1235
1298
  }
1236
1299
  });
1300
+ return true;
1237
1301
  }
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
- };
1302
+ }
1303
+ return false;
1304
+ };
1305
+ var comments = (options = {}) => {
1306
+ const { key: shortcut = "meta-'" } = options;
1307
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1258
1308
  return [
1309
+ optionsFacet.of(options),
1259
1310
  commentsState,
1260
- highlightDecorations,
1311
+ commentsDecorations,
1261
1312
  styles2,
1262
1313
  //
1263
1314
  // Keymap.
@@ -1265,7 +1316,7 @@ var comments = (options = {}) => {
1265
1316
  options.onCreate ? keymap3.of([
1266
1317
  {
1267
1318
  key: shortcut,
1268
- run: callbackWrapper(createCommentThread)
1319
+ run: callbackWrapper(createComment)
1269
1320
  }
1270
1321
  ]) : [],
1271
1322
  //
@@ -1403,6 +1454,7 @@ import { searchKeymap } from "@codemirror/search";
1403
1454
  import { EditorState } from "@codemirror/state";
1404
1455
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1405
1456
  import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1457
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1406
1458
 
1407
1459
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1408
1460
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -1593,11 +1645,11 @@ var markdownHighlightStyle = (readonly) => {
1593
1645
  };
1594
1646
 
1595
1647
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1596
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1648
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1597
1649
  return [
1650
+ lineWrapping && EditorView7.lineWrapping,
1598
1651
  EditorState.allowMultipleSelections.of(true),
1599
1652
  EditorState.tabSize.of(2),
1600
- EditorView7.lineWrapping,
1601
1653
  // https://github.com/codemirror/basic-setup
1602
1654
  bracketMatching2(),
1603
1655
  closeBrackets2(),
@@ -1607,7 +1659,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1607
1659
  highlightActiveLine2(),
1608
1660
  history2(),
1609
1661
  indentOnInput(),
1610
- _placeholder ? placeholder2(_placeholder) : [],
1662
+ _placeholder && placeholder2(_placeholder),
1611
1663
  // Main extension.
1612
1664
  // https://github.com/codemirror/lang-markdown
1613
1665
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1642,7 +1694,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1642
1694
  // https://codemirror.net/docs/ref/#commands.standardKeymap
1643
1695
  ...standardKeymap
1644
1696
  ])
1645
- ];
1697
+ ].filter(isNotFalsy2);
1646
1698
  };
1647
1699
 
1648
1700
  // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
@@ -1652,26 +1704,43 @@ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Dec
1652
1704
  import { mx as mx2 } from "@dxos/react-ui-theme";
1653
1705
  var styles3 = EditorView8.baseTheme({
1654
1706
  "& .cm-code": {
1655
- paddingInline: "1rem !important",
1656
1707
  fontFamily: getToken("fontFamily.mono", []).join(",")
1657
1708
  },
1658
1709
  "& .cm-codeblock": {
1659
- display: "inline-block",
1660
- width: "100%"
1710
+ width: "100%",
1711
+ paddingInline: "1rem !important",
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
+ display: "inline-block",
1733
+ "&::after": {
1734
+ borderTopLeftRadius: ".5rem",
1735
+ borderTopRightRadius: ".5rem"
1736
+ }
1671
1737
  },
1672
1738
  "& .cm-codeblock-last": {
1673
- borderBottomLeftRadius: ".5rem",
1674
- borderBottomRightRadius: ".5rem"
1739
+ display: "inline-block",
1740
+ "&::after": {
1741
+ borderBottomLeftRadius: ".5rem",
1742
+ borderBottomRightRadius: ".5rem"
1743
+ }
1675
1744
  }
1676
1745
  });
1677
1746
  var getLineRange = (lines, from, to) => {
@@ -1715,7 +1784,7 @@ var buildDecorations = (view) => {
1715
1784
  }));
1716
1785
  } else {
1717
1786
  builder.add(block.from, block.from, Decoration3.line({
1718
- class: mx2("cm-code cm-codeblock")
1787
+ class: mx2("cm-code cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1719
1788
  }));
1720
1789
  }
1721
1790
  }
@@ -1743,16 +1812,156 @@ var code2 = () => {
1743
1812
  ];
1744
1813
  };
1745
1814
 
1746
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1815
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1816
+ import { snippet } from "@codemirror/autocomplete";
1747
1817
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
1818
  import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
- import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1819
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1820
+ var setHeading = (level) => (view) => {
1821
+ const { selection: { ranges }, doc } = view.state;
1822
+ const changes = [];
1823
+ for (const range of ranges) {
1824
+ const { number } = doc.lineAt(range.anchor);
1825
+ const { from, to } = doc.line(number);
1826
+ const line = doc.sliceString(from, to);
1827
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1828
+ const current = marks?.length ?? 0;
1829
+ if (level !== current) {
1830
+ changes.push({
1831
+ from,
1832
+ to: from + current + (spaces?.length ?? 0),
1833
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1834
+ });
1835
+ }
1836
+ }
1837
+ if (changes.length) {
1838
+ view.dispatch({
1839
+ changes
1840
+ });
1841
+ }
1842
+ return true;
1843
+ };
1844
+ var toggleStyle = (mark2) => (view) => {
1845
+ const { ranges } = view.state.selection;
1846
+ for (const range of ranges) {
1847
+ if (range.from === range.to) {
1848
+ return false;
1849
+ }
1850
+ view.dispatch({
1851
+ changes: [
1852
+ {
1853
+ from: range.from,
1854
+ insert: mark2
1855
+ },
1856
+ {
1857
+ from: range.to,
1858
+ insert: mark2
1859
+ }
1860
+ ]
1861
+ });
1862
+ }
1863
+ return true;
1864
+ };
1865
+ var snippets = {
1866
+ codeblock: snippet([
1867
+ "```#{lang}",
1868
+ " #{}",
1869
+ "```"
1870
+ ].join("\n")),
1871
+ table: snippet([
1872
+ "| #{col1} | #{col2} |",
1873
+ "| ---- | ---- |",
1874
+ "| #{val1} | #{val2} |",
1875
+ "| #{val3} | #{val4} |"
1876
+ ].join("\n"))
1877
+ };
1878
+ var insertCodeblock = (view) => {
1879
+ const { selection: { main }, doc } = view.state;
1880
+ const { number } = doc.lineAt(main.anchor);
1881
+ const { from } = doc.line(number);
1882
+ snippets.codeblock(view, null, from, from);
1883
+ };
1884
+ var insertTable = (view) => {
1885
+ const { selection: { main }, doc } = view.state;
1886
+ const { number } = doc.lineAt(main.anchor);
1887
+ const { from } = doc.line(number);
1888
+ snippets.table(view, null, from, from);
1889
+ };
1890
+ var toggleBold = toggleStyle("**");
1891
+ var toggleItalic = toggleStyle("_");
1892
+ var toggleStrikethrough = toggleStyle("~~");
1893
+ var toggleList = (view) => {
1894
+ };
1895
+ var formatting = (options = {}) => {
1896
+ return [
1897
+ keymap5.of([
1898
+ {
1899
+ key: "meta-b",
1900
+ run: toggleBold
1901
+ }
1902
+ ]),
1903
+ styling()
1904
+ ];
1905
+ };
1906
+ var styling = () => {
1907
+ const buildDecorations7 = (view) => {
1908
+ const builder = new RangeSetBuilder2();
1909
+ const { state } = view;
1910
+ const cursor = state.selection.main.head;
1911
+ const replace = (node, marks) => {
1912
+ if (cursor <= node.from || cursor >= node.to) {
1913
+ for (const mark2 of marks) {
1914
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1915
+ }
1916
+ }
1917
+ };
1918
+ for (const { from, to } of view.visibleRanges) {
1919
+ syntaxTree2(state).iterate({
1920
+ enter: (node) => {
1921
+ switch (node.name) {
1922
+ case "Emphasis":
1923
+ case "StrongEmphasis": {
1924
+ const marks = node.node.getChildren("EmphasisMark");
1925
+ replace(node, marks);
1926
+ break;
1927
+ }
1928
+ case "Strikethrough": {
1929
+ const marks = node.node.getChildren("StrikethroughMark");
1930
+ replace(node, marks);
1931
+ break;
1932
+ }
1933
+ }
1934
+ },
1935
+ from,
1936
+ to
1937
+ });
1938
+ }
1939
+ return builder.finish();
1940
+ };
1941
+ return [
1942
+ ViewPlugin4.fromClass(class {
1943
+ constructor(view) {
1944
+ this.decorations = buildDecorations7(view);
1945
+ }
1946
+ update(update2) {
1947
+ this.decorations = buildDecorations7(update2.view);
1948
+ }
1949
+ }, {
1950
+ decorations: (value) => value.decorations
1951
+ })
1952
+ ];
1953
+ };
1954
+
1955
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1956
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1957
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1958
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1750
1959
  var buildDecorations2 = (view) => {
1751
- const builder = new RangeSetBuilder2();
1960
+ const builder = new RangeSetBuilder3();
1752
1961
  const { state } = view;
1753
1962
  const cursor = state.selection.main.head;
1754
1963
  for (const { from, to } of view.visibleRanges) {
1755
- syntaxTree2(state).iterate({
1964
+ syntaxTree3(state).iterate({
1756
1965
  enter: (node) => {
1757
1966
  switch (node.name) {
1758
1967
  case "ATXHeading1":
@@ -1763,8 +1972,8 @@ var buildDecorations2 = (view) => {
1763
1972
  case "ATXHeading6": {
1764
1973
  const mark2 = node.node.getChild("HeaderMark");
1765
1974
  if (mark2) {
1766
- if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
- builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1975
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1976
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1768
1977
  }
1769
1978
  }
1770
1979
  }
@@ -1778,7 +1987,7 @@ var buildDecorations2 = (view) => {
1778
1987
  };
1779
1988
  var heading2 = () => {
1780
1989
  return [
1781
- ViewPlugin4.fromClass(class {
1990
+ ViewPlugin5.fromClass(class {
1782
1991
  constructor(view) {
1783
1992
  this.decorations = buildDecorations2(view);
1784
1993
  }
@@ -1792,9 +2001,9 @@ var heading2 = () => {
1792
2001
  };
1793
2002
 
1794
2003
  // 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";
2004
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2005
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2006
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1798
2007
  var styles4 = EditorView9.baseTheme({
1799
2008
  "& .cm-hr": {
1800
2009
  // Note that block-level decorations should not have vertical margins,
@@ -1808,15 +2017,15 @@ var HorizontalRuleWidget = class extends WidgetType3 {
1808
2017
  return el;
1809
2018
  }
1810
2019
  };
1811
- var decoration = Decoration5.replace({
2020
+ var decoration = Decoration6.replace({
1812
2021
  widget: new HorizontalRuleWidget()
1813
2022
  });
1814
2023
  var buildDecorations3 = (view) => {
1815
- const builder = new RangeSetBuilder3();
2024
+ const builder = new RangeSetBuilder4();
1816
2025
  const { state } = view;
1817
2026
  const cursor = state.selection.main.head;
1818
2027
  for (const { from, to } of view.visibleRanges) {
1819
- syntaxTree3(state).iterate({
2028
+ syntaxTree4(state).iterate({
1820
2029
  enter: (node) => {
1821
2030
  if (node.name === "HorizontalRule") {
1822
2031
  if (cursor <= node.from || cursor >= node.to) {
@@ -1832,7 +2041,7 @@ var buildDecorations3 = (view) => {
1832
2041
  };
1833
2042
  var hr = () => {
1834
2043
  return [
1835
- ViewPlugin5.fromClass(class {
2044
+ ViewPlugin6.fromClass(class {
1836
2045
  constructor(view) {
1837
2046
  this.decorations = buildDecorations3(view);
1838
2047
  }
@@ -1847,13 +2056,13 @@ var hr = () => {
1847
2056
  };
1848
2057
 
1849
2058
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2059
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1851
2060
  import { StateField as StateField3 } from "@codemirror/state";
1852
- import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2061
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
1853
2062
  var image = (options = {}) => {
1854
2063
  return StateField3.define({
1855
2064
  create: (state) => {
1856
- return Decoration6.set(buildDecorations4(0, state.doc.length, state));
2065
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
1857
2066
  },
1858
2067
  update: (value, tr) => {
1859
2068
  if (!tr.docChanged && !tr.selection) {
@@ -1882,14 +2091,14 @@ var image = (options = {}) => {
1882
2091
  var buildDecorations4 = (from, to, state) => {
1883
2092
  const decorations = [];
1884
2093
  const cursor = state.selection.main.head;
1885
- syntaxTree4(state).iterate({
2094
+ syntaxTree5(state).iterate({
1886
2095
  enter: (node) => {
1887
2096
  if (node.name === "Image") {
1888
2097
  const urlNode = node.node.getChild("URL");
1889
2098
  if (urlNode) {
1890
2099
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
2100
  const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
- decorations.push(Decoration6.replace({
2101
+ decorations.push(Decoration7.replace({
1893
2102
  block: true,
1894
2103
  widget: new ImageWidget(url)
1895
2104
  }).range(hide ? node.from : node.to, node.to));
@@ -1919,13 +2128,13 @@ var ImageWidget = class extends WidgetType4 {
1919
2128
  };
1920
2129
 
1921
2130
  // 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";
2131
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2132
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2133
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
1925
2134
  import { tooltipContent } from "@dxos/react-ui-theme";
1926
2135
  var link = (options = {}) => {
1927
2136
  const extensions = [
1928
- ViewPlugin6.fromClass(class {
2137
+ ViewPlugin7.fromClass(class {
1929
2138
  constructor(view) {
1930
2139
  this.decorations = buildDecorations5(view, options);
1931
2140
  }
@@ -1944,7 +2153,7 @@ var link = (options = {}) => {
1944
2153
  // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
2154
  // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
2155
  hoverTooltip2((view, pos, side) => {
1947
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
2156
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
1948
2157
  let link2 = null;
1949
2158
  for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
2159
  link2 = node.name === "Link" ? node : null;
@@ -2021,27 +2230,27 @@ var LinkText = class extends WidgetType5 {
2021
2230
  }
2022
2231
  };
2023
2232
  var buildDecorations5 = (view, options) => {
2024
- const builder = new RangeSetBuilder4();
2233
+ const builder = new RangeSetBuilder5();
2025
2234
  const { state } = view;
2026
2235
  const cursor = state.selection.main.head;
2027
2236
  for (const { from, to } of view.visibleRanges) {
2028
- syntaxTree5(state).iterate({
2237
+ syntaxTree6(state).iterate({
2029
2238
  enter: (node) => {
2030
2239
  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) : "";
2240
+ const marks = node.node.getChildren("LinkMark");
2241
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2033
2242
  const urlNode = node.node.getChild("URL");
2034
2243
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
2244
  if (!url) {
2036
2245
  return false;
2037
2246
  }
2038
2247
  if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
- builder.add(node.from, node.to, Decoration7.replace({
2248
+ builder.add(node.from, node.to, Decoration8.replace({
2040
2249
  widget: new LinkText(text, options.link ? url : void 0)
2041
2250
  }));
2042
2251
  }
2043
2252
  if (options.onRender) {
2044
- builder.add(node.to, node.to, Decoration7.replace({
2253
+ builder.add(node.to, node.to, Decoration8.replace({
2045
2254
  widget: new LinkButton(url, options.onRender)
2046
2255
  }));
2047
2256
  }
@@ -2056,9 +2265,9 @@ var buildDecorations5 = (view, options) => {
2056
2265
  };
2057
2266
 
2058
2267
  // 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";
2268
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2269
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2270
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2062
2271
  var table = (options = {}) => {
2063
2272
  return StateField4.define({
2064
2273
  create: (state) => update(state, options),
@@ -2067,7 +2276,7 @@ var table = (options = {}) => {
2067
2276
  });
2068
2277
  };
2069
2278
  var update = (state, options) => {
2070
- const builder = new RangeSetBuilder5();
2279
+ const builder = new RangeSetBuilder6();
2071
2280
  const cursor = state.selection.main.head;
2072
2281
  const tables = [];
2073
2282
  const getTable = () => tables[tables.length - 1];
@@ -2075,7 +2284,7 @@ var update = (state, options) => {
2075
2284
  const table2 = getTable();
2076
2285
  return table2.rows?.[table2.rows.length - 1];
2077
2286
  };
2078
- syntaxTree6(state).iterate({
2287
+ syntaxTree7(state).iterate({
2079
2288
  enter: (node) => {
2080
2289
  switch (node.name) {
2081
2290
  case "Table": {
@@ -2107,10 +2316,10 @@ var update = (state, options) => {
2107
2316
  });
2108
2317
  tables.forEach((table2) => {
2109
2318
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2110
- hide && builder.add(table2.from, table2.to, Decoration8.replace({
2319
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2111
2320
  widget: new TableWidget(table2)
2112
2321
  }));
2113
- builder.add(table2.from, table2.to, Decoration8.mark({
2322
+ builder.add(table2.from, table2.to, Decoration9.mark({
2114
2323
  class: "cm-table"
2115
2324
  }));
2116
2325
  });
@@ -2154,9 +2363,9 @@ var TableWidget = class extends WidgetType6 {
2154
2363
  };
2155
2364
 
2156
2365
  // 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";
2366
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2367
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2368
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2160
2369
  var styles5 = EditorView12.baseTheme({
2161
2370
  "& .cm-task": {
2162
2371
  color: getToken("extend.colors.blue.500")
@@ -2203,23 +2412,23 @@ var CheckboxWidget = class extends WidgetType7 {
2203
2412
  return false;
2204
2413
  }
2205
2414
  };
2206
- var checkedDecoration = Decoration9.replace({
2415
+ var checkedDecoration = Decoration10.replace({
2207
2416
  widget: new CheckboxWidget(true)
2208
2417
  });
2209
- var uncheckedDecoration = Decoration9.replace({
2418
+ var uncheckedDecoration = Decoration10.replace({
2210
2419
  widget: new CheckboxWidget(false)
2211
2420
  });
2212
2421
  var buildDecorations6 = (view) => {
2213
- const builder = new RangeSetBuilder6();
2422
+ const builder = new RangeSetBuilder7();
2214
2423
  const { state } = view;
2215
2424
  const cursor = state.selection.main.head;
2216
2425
  for (const { from, to } of view.visibleRanges) {
2217
- syntaxTree7(state).iterate({
2426
+ syntaxTree8(state).iterate({
2218
2427
  enter: (node) => {
2219
2428
  if (node.name === "TaskMarker") {
2220
2429
  if (cursor < node.from || cursor > node.to) {
2221
2430
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
- builder.add(node.from - 2, node.from - 1, Decoration9.mark({
2431
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2223
2432
  class: "cm-task"
2224
2433
  }));
2225
2434
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2234,7 +2443,7 @@ var buildDecorations6 = (view) => {
2234
2443
  };
2235
2444
  var tasklist = (options = {}) => {
2236
2445
  return [
2237
- ViewPlugin7.fromClass(class {
2446
+ ViewPlugin8.fromClass(class {
2238
2447
  constructor(view) {
2239
2448
  this.decorations = buildDecorations6(view);
2240
2449
  }
@@ -2285,7 +2494,7 @@ var outliner = (options = {}) => {
2285
2494
  };
2286
2495
 
2287
2496
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2288
- import { keymap as keymap5 } from "@codemirror/view";
2497
+ import { keymap as keymap6 } from "@codemirror/view";
2289
2498
  var defaultItems = [
2290
2499
  "hello world!",
2291
2500
  "this is a test.",
@@ -2295,7 +2504,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2295
2504
  let t;
2296
2505
  let idx = 0;
2297
2506
  return [
2298
- keymap5.of([
2507
+ keymap6.of([
2299
2508
  {
2300
2509
  // Reset.
2301
2510
  key: "alt-meta-'",
@@ -2372,8 +2581,9 @@ var defaultTheme = {
2372
2581
  outline: "none"
2373
2582
  },
2374
2583
  ".cm-scroller": {
2375
- overflow: "visible",
2376
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
2584
+ // overflow: 'visible',
2585
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
2586
+ lineHeight: 1.4
2377
2587
  },
2378
2588
  ".cm-content": {
2379
2589
  padding: 0,
@@ -2397,8 +2607,11 @@ var defaultTheme = {
2397
2607
  "&dark .cm-cursor, &dark .cm-dropCursor": {
2398
2608
  borderLeft: "2px solid white"
2399
2609
  },
2400
- ".cm-placeholder": {
2401
- fontWeight: 100
2610
+ "&light .cm-placeholder": {
2611
+ color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
2612
+ },
2613
+ "&dark .cm-placeholder": {
2614
+ color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2402
2615
  },
2403
2616
  //
2404
2617
  // line
@@ -2407,7 +2620,7 @@ var defaultTheme = {
2407
2620
  paddingInline: 0
2408
2621
  },
2409
2622
  ".cm-activeLine": {
2410
- background: "inherit"
2623
+ background: "transparent"
2411
2624
  },
2412
2625
  //
2413
2626
  // Selection
@@ -2578,15 +2791,12 @@ var codeTheme = {
2578
2791
  };
2579
2792
 
2580
2793
  // 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";
2794
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2582
2795
  var EditorModes = [
2583
2796
  "default",
2584
2797
  "vim"
2585
2798
  ];
2586
2799
  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
2800
  const { themeMode } = useThemeContext();
2591
2801
  const rootRef = useRef(null);
2592
2802
  const [view, setView] = useState(null);
@@ -2594,7 +2804,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2594
2804
  view
2595
2805
  ]);
2596
2806
  useEffect2(() => {
2597
- if (autoFocus) {
2807
+ if (autoFocus && !view?.hasFocus) {
2808
+ if (view?.state.selection.main?.from === 0) {
2809
+ const { to } = view.state.doc.lineAt(0);
2810
+ view?.dispatch({
2811
+ selection: {
2812
+ anchor: to
2813
+ }
2814
+ });
2815
+ }
2598
2816
  view?.focus();
2599
2817
  }
2600
2818
  }, [
@@ -2612,8 +2830,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2612
2830
  // TODO(burdon): Doesn't catch errors in keymap functions.
2613
2831
  EditorView13.exceptionSink.of((err) => {
2614
2832
  log2.catch(err, void 0, {
2615
- F: __dxlog_file5,
2616
- L: 123,
2833
+ F: __dxlog_file3,
2834
+ L: 134,
2617
2835
  S: void 0,
2618
2836
  C: (f, a) => f(...a)
2619
2837
  });
@@ -2630,15 +2848,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2630
2848
  class: slots.content?.className ?? ""
2631
2849
  }),
2632
2850
  // State.
2851
+ EditorView13.editable.of(!readonly),
2633
2852
  EditorState2.readOnly.of(!!readonly),
2634
2853
  // Storage and replication.
2635
2854
  // NOTE: This must come before user extensions.
2636
2855
  model.extension,
2637
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
2856
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2638
2857
  editorMode === "vim" && vim(),
2639
2858
  // Custom.
2640
2859
  ...extensions
2641
- ].filter(Boolean)
2860
+ ].filter(isNotFalsy3)
2642
2861
  });
2643
2862
  const newView = new EditorView13({
2644
2863
  state,
@@ -2675,7 +2894,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2675
2894
  }
2676
2895
  case "Escape": {
2677
2896
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2678
- rootRef.current?.focus();
2897
+ view?.focus();
2679
2898
  }
2680
2899
  break;
2681
2900
  }
@@ -2688,9 +2907,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2688
2907
  key: model.id,
2689
2908
  role: "none",
2690
2909
  tabIndex: 0,
2691
- onKeyUp: handleKeyUp,
2692
2910
  ...slots.root,
2693
- ...editorMode !== "vim" && tabsterDOMAttribute,
2911
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2912
+ onKeyUp: handleKeyUp,
2694
2913
  ref: rootRef
2695
2914
  });
2696
2915
  });
@@ -2701,7 +2920,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2701
2920
  ref: forwardedRef,
2702
2921
  readonly,
2703
2922
  extensions: [
2704
- basicBundle({
2923
+ createBasicBundle({
2705
2924
  themeMode,
2706
2925
  placeholder: placeholder3,
2707
2926
  lineWrapping
@@ -2720,7 +2939,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2720
2939
  ref: forwardedRef,
2721
2940
  readonly,
2722
2941
  extensions: [
2723
- markdownBundle({
2942
+ createMarkdownExtensions({
2724
2943
  themeMode,
2725
2944
  placeholder: placeholder3
2726
2945
  }),
@@ -2733,11 +2952,10 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2733
2952
  });
2734
2953
  var defaultSlots = {
2735
2954
  root: {
2736
- // TODO(burdon): Add focusRing by default/as property?
2737
- className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
2955
+ className: mx3("grow", focusRing)
2738
2956
  },
2739
2957
  editor: {
2740
- className: "h-full p-2"
2958
+ className: "bs-full"
2741
2959
  }
2742
2960
  };
2743
2961
  var defaultTextSlots = {
@@ -2747,12 +2965,173 @@ var defaultMarkdownSlots = {
2747
2965
  ...defaultSlots
2748
2966
  };
2749
2967
 
2968
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2969
+ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
2970
+ import { createContext } from "@radix-ui/react-context";
2971
+ import React2 from "react";
2972
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
2973
+ import { getSize } from "@dxos/react-ui-theme";
2974
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2975
+ var ToolbarRoot = ({ children, onAction }) => {
2976
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2977
+ onAction
2978
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2979
+ density: "fine"
2980
+ }, /* @__PURE__ */ React2.createElement("div", {
2981
+ role: "toolbar",
2982
+ className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
2983
+ }, children)));
2984
+ };
2985
+ var ToolbarButton = ({ Icon, onClick, title }) => {
2986
+ const { onAction } = useToolbarContext("ToolbarButton");
2987
+ const handleClick = () => {
2988
+ const action = onClick();
2989
+ if (action) {
2990
+ onAction?.(action);
2991
+ }
2992
+ };
2993
+ return /* @__PURE__ */ React2.createElement(Button, {
2994
+ variant: "ghost",
2995
+ classNames: "p-2",
2996
+ onClick: handleClick,
2997
+ title
2998
+ }, /* @__PURE__ */ React2.createElement(Icon, {
2999
+ className: getSize(5)
3000
+ }));
3001
+ };
3002
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3003
+ className: "grow"
3004
+ });
3005
+ var MarkdownHeading = () => {
3006
+ const { onAction } = useToolbarContext("MarkdownFormatting");
3007
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3008
+ defaultValue: "0",
3009
+ onValueChange: (value) => onAction?.({
3010
+ type: "heading",
3011
+ data: parseInt(value)
3012
+ })
3013
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3014
+ classNames: "w-[8rem]"
3015
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3016
+ value: "0"
3017
+ }, "Paragraph"), [
3018
+ 1,
3019
+ 2,
3020
+ 3,
3021
+ 4,
3022
+ 5,
3023
+ 6
3024
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3025
+ key: level,
3026
+ value: String(level)
3027
+ }, "Heading ", level))))));
3028
+ };
3029
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3030
+ role: "none"
3031
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3032
+ Icon: TextB,
3033
+ title: "Bold",
3034
+ onClick: () => ({
3035
+ type: "bold"
3036
+ })
3037
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3038
+ Icon: TextItalic,
3039
+ title: "Italic",
3040
+ onClick: () => ({
3041
+ type: "italic"
3042
+ })
3043
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3044
+ Icon: TextStrikethrough,
3045
+ title: "Strike-through",
3046
+ onClick: () => ({
3047
+ type: "strikethrough"
3048
+ })
3049
+ }));
3050
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3051
+ role: "none"
3052
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3053
+ Icon: ListBullets,
3054
+ title: "Bullet list",
3055
+ onClick: () => ({
3056
+ type: "list"
3057
+ })
3058
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3059
+ Icon: ListNumbers,
3060
+ title: "Numbered list",
3061
+ onClick: () => ({
3062
+ type: "list-ordered"
3063
+ })
3064
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3065
+ Icon: ListChecks,
3066
+ title: "Task list",
3067
+ onClick: () => ({
3068
+ type: "list-tasks"
3069
+ })
3070
+ }));
3071
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3072
+ role: "none"
3073
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3074
+ Icon: CaretRight,
3075
+ title: "Block quite",
3076
+ onClick: () => ({
3077
+ type: "blockquote"
3078
+ })
3079
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3080
+ Icon: Code,
3081
+ title: "Code block",
3082
+ onClick: () => ({
3083
+ type: "codeblock"
3084
+ })
3085
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3086
+ Icon: Table2,
3087
+ title: "Table",
3088
+ onClick: () => ({
3089
+ type: "table"
3090
+ })
3091
+ }));
3092
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3093
+ role: "none"
3094
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3095
+ Icon: Link,
3096
+ title: "Link",
3097
+ onClick: () => ({
3098
+ type: "link"
3099
+ })
3100
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3101
+ Icon: At,
3102
+ title: "Mention",
3103
+ onClick: () => ({
3104
+ type: "mention"
3105
+ })
3106
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3107
+ Icon: Image,
3108
+ title: "Image",
3109
+ onClick: () => ({
3110
+ type: "image"
3111
+ })
3112
+ }));
3113
+ 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));
3114
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
3115
+ Icon: ChatText,
3116
+ title: "Create comment",
3117
+ onClick: () => ({
3118
+ type: "comment"
3119
+ })
3120
+ });
3121
+ var Toolbar = {
3122
+ Root: ToolbarRoot,
3123
+ Button: ToolbarButton,
3124
+ Separator: ToolbarSeparator,
3125
+ Markdown: MarkdownStandard,
3126
+ Extended: MarkdownExtended
3127
+ };
3128
+
2750
3129
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2751
3130
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2752
3131
  import { Context as Context2 } from "@dxos/context";
2753
- import { invariant as invariant4 } from "@dxos/invariant";
3132
+ import { invariant as invariant2 } from "@dxos/invariant";
2754
3133
  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";
3134
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2756
3135
  var DEBOUNCE_INTERVAL = 100;
2757
3136
  var SpaceAwarenessProvider = class {
2758
3137
  constructor(params) {
@@ -2792,7 +3171,7 @@ var SpaceAwarenessProvider = class {
2792
3171
  log3.debug("failed to query awareness", {
2793
3172
  err
2794
3173
  }, {
2795
- F: __dxlog_file6,
3174
+ F: __dxlog_file4,
2796
3175
  L: 89,
2797
3176
  S: this,
2798
3177
  C: (f, a) => f(...a)
@@ -2808,8 +3187,8 @@ var SpaceAwarenessProvider = class {
2808
3187
  return Array.from(this._remoteStates.values());
2809
3188
  }
2810
3189
  update(position) {
2811
- invariant4(this._postTask, void 0, {
2812
- F: __dxlog_file6,
3190
+ invariant2(this._postTask, void 0, {
3191
+ F: __dxlog_file4,
2813
3192
  L: 104,
2814
3193
  S: this,
2815
3194
  A: [
@@ -2825,8 +3204,8 @@ var SpaceAwarenessProvider = class {
2825
3204
  this._postTask.schedule();
2826
3205
  }
2827
3206
  _handleQueryMessage() {
2828
- invariant4(this._postTask, void 0, {
2829
- F: __dxlog_file6,
3207
+ invariant2(this._postTask, void 0, {
3208
+ F: __dxlog_file4,
2830
3209
  L: 115,
2831
3210
  S: this,
2832
3211
  A: [
@@ -2837,8 +3216,8 @@ var SpaceAwarenessProvider = class {
2837
3216
  this._postTask.schedule();
2838
3217
  }
2839
3218
  _handlePostMessage(message) {
2840
- invariant4(message.kind === "post", void 0, {
2841
- F: __dxlog_file6,
3219
+ invariant2(message.kind === "post", void 0, {
3220
+ F: __dxlog_file4,
2842
3221
  L: 120,
2843
3222
  S: this,
2844
3223
  A: [
@@ -2858,9 +3237,50 @@ var modelState = StateField5.define({
2858
3237
  update: (model) => model
2859
3238
  });
2860
3239
 
2861
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3240
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3241
+ var useActionHandler = (view) => {
3242
+ return (action) => {
3243
+ if (!view) {
3244
+ return;
3245
+ }
3246
+ switch (action.type) {
3247
+ case "heading":
3248
+ setHeading(parseInt(action.data))(view);
3249
+ break;
3250
+ case "bold":
3251
+ toggleBold(view);
3252
+ break;
3253
+ case "italic":
3254
+ toggleItalic(view);
3255
+ break;
3256
+ case "strikethrough":
3257
+ toggleStrikethrough(view);
3258
+ break;
3259
+ case "list":
3260
+ toggleList(view);
3261
+ break;
3262
+ case "codeblock":
3263
+ insertCodeblock(view);
3264
+ break;
3265
+ case "table":
3266
+ insertTable(view);
3267
+ break;
3268
+ case "comment":
3269
+ createComment(view);
3270
+ break;
3271
+ }
3272
+ setTimeout(() => {
3273
+ view.focus();
3274
+ view.dispatch({
3275
+ selection: view.state.selection
3276
+ });
3277
+ });
3278
+ };
3279
+ };
3280
+
3281
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
2862
3282
  import { useState as useState2 } from "react";
2863
- var useTextEditor = () => {
3283
+ var useEditorView = () => {
2864
3284
  const [view, setView] = useState2(null);
2865
3285
  return [
2866
3286
  (ref) => {
@@ -2870,29 +3290,144 @@ var useTextEditor = () => {
2870
3290
  ];
2871
3291
  };
2872
3292
 
3293
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3294
+ import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3295
+ import { EditorView as EditorView14 } from "@codemirror/view";
3296
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
3297
+ import { log as log4 } from "@dxos/log";
3298
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3299
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3300
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3301
+ const onUpdate = useRef2();
3302
+ const [view, setView] = useState3();
3303
+ const parentRef = useRef2(null);
3304
+ useEffect3(() => {
3305
+ if (parentRef.current) {
3306
+ const state = EditorState3.create({
3307
+ doc,
3308
+ selection,
3309
+ extensions: [
3310
+ // TODO(burdon): Doesn't catch errors in keymap functions.
3311
+ EditorView14.exceptionSink.of((err) => {
3312
+ log4.catch(err, void 0, {
3313
+ F: __dxlog_file5,
3314
+ L: 57,
3315
+ S: void 0,
3316
+ C: (f, a) => f(...a)
3317
+ });
3318
+ }),
3319
+ extensions,
3320
+ EditorView14.updateListener.of(() => {
3321
+ onUpdate.current?.();
3322
+ })
3323
+ ].filter(isNotFalsy4)
3324
+ });
3325
+ const view2 = new EditorView14({
3326
+ parent: parentRef.current,
3327
+ scrollTo,
3328
+ state,
3329
+ // NOTE: Uncomment to debug/monitor all transactions.
3330
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
3331
+ dispatchTransactions: (trs, view3) => {
3332
+ if (debug) {
3333
+ logChanges(trs);
3334
+ }
3335
+ view3.update(trs);
3336
+ }
3337
+ });
3338
+ setView(view2);
3339
+ }
3340
+ return () => {
3341
+ view?.destroy();
3342
+ };
3343
+ }, [
3344
+ parentRef
3345
+ ]);
3346
+ useEffect3(() => {
3347
+ if (view) {
3348
+ if (!selection && !view.state.selection.main.anchor) {
3349
+ selection = EditorSelection.single(view.state.doc.line(1).to);
3350
+ }
3351
+ if (selection || scrollTo) {
3352
+ onUpdate.current = () => {
3353
+ onUpdate.current = void 0;
3354
+ view.dispatch({
3355
+ selection,
3356
+ effects: scrollTo && [
3357
+ scrollTo
3358
+ ],
3359
+ scrollIntoView: !scrollTo
3360
+ });
3361
+ };
3362
+ }
3363
+ if (autoFocus) {
3364
+ view.focus();
3365
+ }
3366
+ }
3367
+ }, [
3368
+ view,
3369
+ autoFocus,
3370
+ selection,
3371
+ scrollTo
3372
+ ]);
3373
+ return {
3374
+ parentRef,
3375
+ view
3376
+ };
3377
+ };
3378
+ var createDataExtensions = ({ readonly = false } = {}) => {
3379
+ return [
3380
+ //
3381
+ EditorState3.readOnly.of(readonly),
3382
+ EditorView14.editable.of(!readonly)
3383
+ ];
3384
+ };
3385
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3386
+ return [
3387
+ //
3388
+ EditorView14.baseTheme(defaultTheme),
3389
+ theme && EditorView14.theme(theme),
3390
+ EditorView14.darkTheme.of(themeMode === "dark"),
3391
+ EditorView14.editorAttributes.of({
3392
+ class: slots?.editor?.className ?? ""
3393
+ }),
3394
+ EditorView14.contentAttributes.of({
3395
+ class: slots?.content?.className ?? ""
3396
+ })
3397
+ ].filter(isNotFalsy4);
3398
+ };
3399
+
2873
3400
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2874
3401
  import get4 from "lodash.get";
2875
- import { useEffect as useEffect3, useState as useState3 } from "react";
3402
+ import { useEffect as useEffect4, useState as useState4 } from "react";
2876
3403
  import { generateName } from "@dxos/display-name";
2877
3404
  import { getRawDoc } from "@dxos/echo-schema";
2878
- import { invariant as invariant5 } from "@dxos/invariant";
3405
+ import { invariant as invariant3 } from "@dxos/invariant";
2879
3406
  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";
3407
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2882
3408
  var useTextModel = (props) => {
2883
3409
  const { identity, space, text } = props;
2884
- const [model, setModel] = useState3();
2885
- useEffect3(() => setModel(createModel(props)), [
3410
+ const [model, setModel] = useState4();
3411
+ useEffect4(() => setModel(createModel(props)), [
2886
3412
  identity,
2887
3413
  space,
2888
3414
  text
2889
3415
  ]);
2890
3416
  return model;
2891
3417
  };
3418
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
3419
+ const [content, setContent] = useState4(defaultContent ?? "");
3420
+ return {
3421
+ id,
3422
+ content,
3423
+ setContent,
3424
+ text: () => content
3425
+ };
3426
+ };
2892
3427
  var createModel = ({ space, identity, text }) => {
2893
- invariant5(isAutomergeObject(text), void 0, {
2894
- F: __dxlog_file7,
2895
- L: 35,
3428
+ invariant3(isAutomergeObject(text), void 0, {
3429
+ F: __dxlog_file6,
3430
+ L: 50,
2896
3431
  S: void 0,
2897
3432
  A: [
2898
3433
  "isAutomergeObject(text)",
@@ -2903,7 +3438,7 @@ var createModel = ({ space, identity, text }) => {
2903
3438
  const doc = getRawDoc(obj, [
2904
3439
  obj.field
2905
3440
  ]);
2906
- const awarenessProvider = space && new SpaceAwarenessProvider({
3441
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2907
3442
  space,
2908
3443
  channel: `automerge.awareness.${obj.id}`,
2909
3444
  info: {
@@ -2913,19 +3448,22 @@ var createModel = ({ space, identity, text }) => {
2913
3448
  },
2914
3449
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2915
3450
  });
3451
+ const extensions = [
3452
+ //
3453
+ modelState.init(() => model),
3454
+ automerge({
3455
+ handle: doc.handle,
3456
+ path: doc.path
3457
+ })
3458
+ ];
3459
+ if (awarenessProvider2) {
3460
+ extensions.push(awareness(awarenessProvider2));
3461
+ }
2916
3462
  const model = {
2917
3463
  id: obj.id,
2918
3464
  content: doc,
2919
3465
  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),
3466
+ extension: extensions,
2929
3467
  peer: identity ? {
2930
3468
  id: identity.identityKey.toHex(),
2931
3469
  name: identity.profile?.displayName
@@ -2934,7 +3472,6 @@ var createModel = ({ space, identity, text }) => {
2934
3472
  return model;
2935
3473
  };
2936
3474
  export {
2937
- AwarenessProvider,
2938
3475
  BaseTextEditor,
2939
3476
  Cursor,
2940
3477
  Doc,
@@ -2944,30 +3481,40 @@ export {
2944
3481
  SpaceAwarenessProvider,
2945
3482
  TextEditor,
2946
3483
  TextKind,
3484
+ Toolbar,
2947
3485
  YText,
2948
3486
  YXmlFragment,
2949
3487
  autocomplete,
2950
- automerge3 as automerge,
3488
+ automerge,
2951
3489
  awareness,
2952
- basicBundle,
3490
+ awarenessProvider,
2953
3491
  blast,
2954
3492
  callbackWrapper,
2955
3493
  code2 as code,
2956
3494
  codeTheme,
2957
3495
  comments,
3496
+ createBasicBundle,
3497
+ createComment,
3498
+ createDataExtensions,
3499
+ createMarkdownExtensions,
3500
+ createThemeExtensions,
2958
3501
  defaultMarkdownSlots,
2959
3502
  defaultOptions,
2960
3503
  defaultSlots,
2961
3504
  defaultTextSlots,
2962
3505
  defaultTheme,
3506
+ focusComment,
3507
+ formatting,
2963
3508
  getToken,
2964
3509
  heading2 as heading,
2965
3510
  hr,
2966
3511
  image,
3512
+ insertCodeblock,
3513
+ insertTable,
3514
+ keymap7 as keymap,
2967
3515
  link,
2968
3516
  listener,
2969
3517
  logChanges,
2970
- markdownBundle,
2971
3518
  markdownHighlightStyle,
2972
3519
  markdownTags,
2973
3520
  markdownTagsExtensions,
@@ -2976,15 +3523,24 @@ export {
2976
3523
  modelState,
2977
3524
  outliner,
2978
3525
  setComments,
2979
- setFocus,
3526
+ setHeading,
2980
3527
  setSelection,
2981
3528
  table,
2982
3529
  tags2 as tags,
2983
3530
  tasklist,
2984
3531
  textTheme,
3532
+ toggleBold,
3533
+ toggleItalic,
3534
+ toggleList,
3535
+ toggleStrikethrough,
3536
+ toggleStyle,
2985
3537
  typewriter,
3538
+ useActionHandler,
2986
3539
  useComments,
3540
+ useEditorView,
3541
+ useInMemoryTextModel,
2987
3542
  useTextEditor,
2988
- useTextModel
3543
+ useTextModel,
3544
+ useToolbarContext
2989
3545
  };
2990
3546
  //# sourceMappingURL=index.mjs.map