@dxos/react-ui-editor 0.3.11-main.8be6c8a → 0.3.11-main.8ef1cc6

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 (81) hide show
  1. package/dist/lib/browser/index.mjs +306 -402
  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 +3 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +4 -9
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +4 -4
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  11. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  12. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  14. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/defs.d.ts +4 -4
  16. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/index.d.ts +0 -1
  18. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/semaphore.d.ts +4 -9
  20. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  21. package/dist/types/src/extensions/code.d.ts.map +1 -1
  22. package/dist/types/src/extensions/comments.d.ts +14 -5
  23. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  24. package/dist/types/src/{util → extensions}/cursor.d.ts +9 -1
  25. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  26. package/dist/types/src/extensions/hr.d.ts.map +1 -1
  27. package/dist/types/src/extensions/index.d.ts +2 -0
  28. package/dist/types/src/extensions/index.d.ts.map +1 -1
  29. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  30. package/dist/types/src/extensions/outliner.d.ts +4 -0
  31. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  32. package/dist/types/src/extensions/tasklist.d.ts +5 -1
  33. package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
  34. package/dist/types/src/extensions/yjs/cursor.d.ts +1 -1
  35. package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -1
  36. package/dist/types/src/hooks/index.d.ts +1 -1
  37. package/dist/types/src/hooks/index.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useTextModel.d.ts +0 -5
  39. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  40. package/dist/types/src/hooks/yjs.d.ts.map +1 -1
  41. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  42. package/dist/types/src/themes/default.d.ts.map +1 -1
  43. package/dist/types/src/util.d.ts.map +1 -0
  44. package/package.json +25 -24
  45. package/src/components/TextEditor/MarkdownEditor.stories.tsx +28 -26
  46. package/src/components/TextEditor/TextEditor.stories.tsx +10 -43
  47. package/src/components/TextEditor/TextEditor.tsx +31 -41
  48. package/src/components/TextEditor/comments.stories.tsx +355 -0
  49. package/src/extensions/automerge/automerge.ts +12 -14
  50. package/src/extensions/automerge/cursor.ts +2 -1
  51. package/src/extensions/automerge/defs.ts +13 -9
  52. package/src/extensions/automerge/index.ts +0 -1
  53. package/src/extensions/automerge/semaphore.ts +10 -12
  54. package/src/extensions/awareness.ts +27 -33
  55. package/src/extensions/code.ts +24 -18
  56. package/src/extensions/comments.ts +128 -140
  57. package/src/extensions/cursor.ts +46 -0
  58. package/src/extensions/hr.ts +8 -9
  59. package/src/extensions/image.ts +2 -2
  60. package/src/extensions/index.ts +2 -0
  61. package/src/extensions/link.ts +7 -7
  62. package/src/extensions/markdown/highlight.ts +0 -8
  63. package/src/extensions/outliner.ts +12 -0
  64. package/src/extensions/table.ts +2 -2
  65. package/src/extensions/tasklist.ts +54 -77
  66. package/src/extensions/yjs/cursor.ts +2 -1
  67. package/src/extensions/yjs/yjs.test.ts +1 -1
  68. package/src/extensions/yjs/yjs.ts +2 -2
  69. package/src/hooks/index.ts +2 -1
  70. package/src/hooks/useTextModel.ts +3 -6
  71. package/src/hooks/yjs.ts +1 -23
  72. package/src/styles/markdown.ts +0 -2
  73. package/src/themes/default.ts +17 -50
  74. package/dist/types/src/util/cursor.d.ts.map +0 -1
  75. package/dist/types/src/util/index.d.ts +0 -3
  76. package/dist/types/src/util/index.d.ts.map +0 -1
  77. package/dist/types/src/util/util.d.ts.map +0 -1
  78. package/src/util/cursor.ts +0 -29
  79. package/src/util/index.ts +0 -6
  80. /package/dist/types/src/{util/util.d.ts → util.d.ts} +0 -0
  81. /package/src/{util/util.ts → util.ts} +0 -0
@@ -51,7 +51,7 @@ var autocomplete = ({ onSearch }) => {
51
51
  };
52
52
 
53
53
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
54
- import { Facet as Facet2, StateField } from "@codemirror/state";
54
+ import { StateField } from "@codemirror/state";
55
55
  import { ViewPlugin } from "@codemirror/view";
56
56
  import { next as A } from "@dxos/automerge/automerge";
57
57
 
@@ -92,7 +92,7 @@ var cursorConverter = (handle, path) => ({
92
92
  });
93
93
 
94
94
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
95
- import { Annotation, StateEffect } from "@codemirror/state";
95
+ import { Annotation, StateEffect, Facet } from "@codemirror/state";
96
96
  var effectType = StateEffect.define({});
97
97
  var updateHeads = (newHeads) => effectType.of({
98
98
  newHeads
@@ -101,6 +101,9 @@ var getLastHeads = (state, field) => state.field(field).lastHeads;
101
101
  var getPath = (state, field) => state.field(field).path;
102
102
  var reconcileAnnotationType = Annotation.define();
103
103
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
104
+ var semaphoreFacet = Facet.define({
105
+ combine: (values) => values.at(-1)
106
+ });
104
107
 
105
108
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
106
109
  import { next as automerge2 } from "@dxos/automerge/automerge";
@@ -237,11 +240,11 @@ var charPath = (textPath, candidatePath) => {
237
240
 
238
241
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
239
242
  var PatchSemaphore = class {
240
- constructor(_field) {
243
+ constructor(_handle, _field) {
244
+ this._handle = _handle;
241
245
  this._field = _field;
242
246
  this._inReconcile = false;
243
- this._queue = [];
244
- this.reconcile = (handle, view) => {
247
+ this.reconcile = (view) => {
245
248
  if (this._inReconcile) {
246
249
  return;
247
250
  }
@@ -259,11 +262,11 @@ var PatchSemaphore = class {
259
262
  annotations: reconcileAnnotationType.of(true)
260
263
  });
261
264
  }
262
- let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
265
+ let newHeads = updateAutomerge(this._field, this._handle, transactions, view.state);
263
266
  if (newHeads === null || newHeads === void 0) {
264
- newHeads = automerge2.getHeads(handle.docSync());
267
+ newHeads = automerge2.getHeads(this._handle.docSync());
265
268
  }
266
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
269
+ const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
267
270
  updateCodeMirror(view, selection, path, diff);
268
271
  view.dispatch({
269
272
  effects: updateHeads(newHeads),
@@ -274,36 +277,42 @@ var PatchSemaphore = class {
274
277
  }
275
278
  };
276
279
 
277
- // packages/ui/react-ui-editor/src/util/cursor.ts
278
- import { Facet } from "@codemirror/state";
279
- var DEFAULT_CURSOR_CONVERTER = {
280
+ // packages/ui/react-ui-editor/src/extensions/cursor.ts
281
+ import { Facet as Facet2 } from "@codemirror/state";
282
+ var defaultCursorConverter = {
280
283
  toCursor: (position) => position.toString(),
281
284
  fromCursor: (cursor) => parseInt(cursor)
282
285
  };
283
- var CursorConverter = Facet.define({
284
- combine: (providers) => providers[0] ?? DEFAULT_CURSOR_CONVERTER
285
- });
286
-
287
- // packages/ui/react-ui-editor/src/util/util.ts
288
- import { log } from "@dxos/log";
289
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/util.ts";
290
- var callbackWrapper = (fn) => (...args) => {
291
- try {
292
- return fn(...args);
293
- } catch (error) {
294
- log.catch(error, void 0, {
295
- F: __dxlog_file,
296
- L: 16,
297
- S: void 0,
298
- C: (f, a) => f(...a)
286
+ var Cursor = class {
287
+ static {
288
+ this.converter = Facet2.define({
289
+ combine: (providers) => providers[0] ?? defaultCursorConverter
299
290
  });
300
291
  }
292
+ static {
293
+ this.getCursorFromRange = (cursorConverter3, range) => {
294
+ const from = cursorConverter3.toCursor(range.from);
295
+ const to = cursorConverter3.toCursor(range.to, -1);
296
+ return [
297
+ from,
298
+ to
299
+ ].join(":");
300
+ };
301
+ }
302
+ static {
303
+ this.getRangeFromCursor = (cursorConverter3, cursor) => {
304
+ const parts = cursor.split(":");
305
+ const from = cursorConverter3.fromCursor(parts[0]);
306
+ const to = cursorConverter3.fromCursor(parts[1]);
307
+ return from !== void 0 && to !== void 0 ? {
308
+ from,
309
+ to
310
+ } : void 0;
311
+ };
312
+ }
301
313
  };
302
314
 
303
315
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
304
- var semaphoreFacet = Facet2.define({
305
- combine: (values) => values.at(-1)
306
- });
307
316
  var automerge3 = ({ handle, path }) => {
308
317
  const stateField = StateField.define({
309
318
  create: () => ({
@@ -334,19 +343,19 @@ var automerge3 = ({ handle, path }) => {
334
343
  return result;
335
344
  }
336
345
  });
337
- const semaphore = new PatchSemaphore(stateField);
338
- const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
346
+ const semaphore = new PatchSemaphore(handle, stateField);
347
+ const viewPlugin = ViewPlugin.fromClass(class AutomergeViewPlugin {
339
348
  constructor(_view) {
340
349
  this._view = _view;
341
350
  this._handleChange = () => {
342
- this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
351
+ this._view.state.facet(semaphoreFacet).reconcile(this._view);
343
352
  };
344
353
  handle.addListener("change", this._handleChange);
345
354
  }
346
355
  update(update2) {
347
356
  if (update2.transactions.length > 0 && update2.transactions.some((tr) => !isReconcileTx(tr))) {
348
357
  queueMicrotask(() => {
349
- this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
358
+ this._view.state.facet(semaphoreFacet).reconcile(this._view);
350
359
  });
351
360
  }
352
361
  }
@@ -356,7 +365,7 @@ var automerge3 = ({ handle, path }) => {
356
365
  });
357
366
  return {
358
367
  extension: [
359
- CursorConverter.of(cursorConverter(handle, path)),
368
+ Cursor.converter.of(cursorConverter(handle, path)),
360
369
  semaphoreFacet.of(semaphore),
361
370
  stateField,
362
371
  viewPlugin
@@ -398,7 +407,7 @@ var RemoteSelectionsDecorator = class {
398
407
  this._ctx = new Context();
399
408
  this._lastAnchor = void 0;
400
409
  this._lastHead = void 0;
401
- this._cursorConverter = view.state.facet(CursorConverter);
410
+ this._cursorConverter = view.state.facet(Cursor.converter);
402
411
  this._provider = view.state.facet(AwarenessProvider);
403
412
  this._provider.open();
404
413
  this._provider.remoteStateChange.on(this._ctx, () => {
@@ -453,7 +462,7 @@ var RemoteSelectionsDecorator = class {
453
462
  attributes: {
454
463
  style: `background-color: ${lightColor}`
455
464
  },
456
- class: "cm-ySelection"
465
+ class: "cm-collab-selection"
457
466
  })
458
467
  });
459
468
  } else {
@@ -464,7 +473,7 @@ var RemoteSelectionsDecorator = class {
464
473
  attributes: {
465
474
  style: `background-color: ${color}`
466
475
  },
467
- class: "cm-ySelection"
476
+ class: "cm-collab-selection"
468
477
  })
469
478
  });
470
479
  decorations.push({
@@ -474,7 +483,7 @@ var RemoteSelectionsDecorator = class {
474
483
  attributes: {
475
484
  style: `background-color: ${color}`
476
485
  },
477
- class: "cm-ySelection"
486
+ class: "cm-collab-selection"
478
487
  })
479
488
  });
480
489
  for (let i = startLine.number + 1; i < endLine.number; i++) {
@@ -485,7 +494,7 @@ var RemoteSelectionsDecorator = class {
485
494
  value: Decoration.line({
486
495
  attributes: {
487
496
  style: `background-color: ${color}`,
488
- class: "cm-yLineSelection"
497
+ class: "cm-collab-selectionLine"
489
498
  }
490
499
  })
491
500
  });
@@ -505,23 +514,21 @@ var RemoteSelectionsDecorator = class {
505
514
  }
506
515
  };
507
516
  var RemoteCaretWidget = class extends WidgetType {
508
- constructor(name, color) {
517
+ constructor(_name, _color) {
509
518
  super();
510
- this.name = name;
511
- this.color = color;
512
- this.name = name;
513
- this.color = color;
519
+ this._name = _name;
520
+ this._color = _color;
514
521
  }
515
522
  toDOM() {
516
523
  const span = document.createElement("span");
517
- span.className = "cm-ySelectionCaret";
518
- span.style.backgroundColor = this.color;
519
- span.style.borderColor = this.color;
524
+ span.className = "cm-collab-selectionCaret";
525
+ span.style.backgroundColor = this._color;
526
+ span.style.borderColor = this._color;
520
527
  const dot = document.createElement("div");
521
- dot.className = "cm-ySelectionCaretDot";
528
+ dot.className = "cm-collab-selectionCaretDot";
522
529
  const info = document.createElement("div");
523
- info.className = "cm-ySelectionInfo";
524
- info.innerText = this.name;
530
+ info.className = "cm-collab-selectionInfo";
531
+ info.innerText = this._name;
525
532
  span.appendChild(document.createTextNode("\u2060"));
526
533
  span.appendChild(dot);
527
534
  span.appendChild(document.createTextNode("\u2060"));
@@ -529,15 +536,12 @@ var RemoteCaretWidget = class extends WidgetType {
529
536
  span.appendChild(document.createTextNode("\u2060"));
530
537
  return span;
531
538
  }
532
- eq(widget) {
533
- return widget.color === this.color;
534
- }
535
- compare(widget) {
536
- return widget.color === this.color;
537
- }
538
539
  updateDOM() {
539
540
  return false;
540
541
  }
542
+ eq(widget) {
543
+ return widget._color === this._color;
544
+ }
541
545
  get estimatedHeight() {
542
546
  return -1;
543
547
  }
@@ -546,12 +550,12 @@ var RemoteCaretWidget = class extends WidgetType {
546
550
  }
547
551
  };
548
552
  var styles = EditorView.baseTheme({
549
- ".cm-ySelection": {},
550
- ".cm-yLineSelection": {
553
+ ".cm-collab-selection": {},
554
+ ".cm-collab-selectionLine": {
551
555
  padding: 0,
552
556
  margin: "0px 2px 0px 4px"
553
557
  },
554
- ".cm-ySelectionCaret": {
558
+ ".cm-collab-selectionCaret": {
555
559
  position: "relative",
556
560
  borderLeft: "1px solid black",
557
561
  borderRight: "1px solid black",
@@ -560,7 +564,7 @@ var styles = EditorView.baseTheme({
560
564
  boxSizing: "border-box",
561
565
  display: "inline"
562
566
  },
563
- ".cm-ySelectionCaretDot": {
567
+ ".cm-collab-selectionCaretDot": {
564
568
  borderRadius: "50%",
565
569
  position: "absolute",
566
570
  width: ".4em",
@@ -571,11 +575,11 @@ var styles = EditorView.baseTheme({
571
575
  transition: "transform .3s ease-in-out",
572
576
  boxSizing: "border-box"
573
577
  },
574
- ".cm-ySelectionCaret:hover > .cm-ySelectionCaretDot": {
578
+ ".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
575
579
  transformOrigin: "bottom center",
576
580
  transform: "scale(0)"
577
581
  },
578
- ".cm-ySelectionInfo": {
582
+ ".cm-collab-selectionInfo": {
579
583
  position: "absolute",
580
584
  top: "-1.05em",
581
585
  left: "-1px",
@@ -591,12 +595,12 @@ var styles = EditorView.baseTheme({
591
595
  zIndex: 101,
592
596
  transition: "opacity .3s ease-in-out",
593
597
  backgroundColor: "inherit",
594
- // these should be separate
598
+ // These should be separate.
595
599
  opacity: 0,
596
600
  transitionDelay: "0s",
597
601
  whiteSpace: "nowrap"
598
602
  },
599
- ".cm-ySelectionCaret:hover > .cm-ySelectionInfo": {
603
+ ".cm-collab-selectionCaret:hover > .cm-collab-selectionInfo": {
600
604
  opacity: 1,
601
605
  transitionDelay: "0s"
602
606
  }
@@ -623,7 +627,7 @@ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
623
627
  import { EditorView as EditorView3, keymap as keymap2 } from "@codemirror/view";
624
628
  import defaultsDeep from "lodash.defaultsdeep";
625
629
  import { invariant } from "@dxos/invariant";
626
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
630
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
627
631
  var defaultOptions = {
628
632
  effect: 2,
629
633
  maxParticles: 200,
@@ -750,7 +754,7 @@ var Blaster = class {
750
754
  }
751
755
  initialize() {
752
756
  invariant(!this._canvas && !this._ctx, void 0, {
753
- F: __dxlog_file2,
757
+ F: __dxlog_file,
754
758
  L: 138,
755
759
  S: this,
756
760
  A: [
@@ -787,7 +791,7 @@ var Blaster = class {
787
791
  }
788
792
  start() {
789
793
  invariant(this._canvas && this._ctx, void 0, {
790
- F: __dxlog_file2,
794
+ F: __dxlog_file,
791
795
  L: 177,
792
796
  S: this,
793
797
  A: [
@@ -1055,20 +1059,18 @@ var getLineRange = (lines, from, to) => {
1055
1059
  ];
1056
1060
  };
1057
1061
  var code2 = () => {
1058
- const getDecorations = (view) => {
1062
+ const buildDecorations4 = (view) => {
1059
1063
  const decorations = [];
1060
- if (view.state.readOnly) {
1061
- const text = view.state.doc.sliceString(0);
1062
- const matches = text.matchAll(CODE_REGEX);
1063
- const blocks = view.viewportLineBlocks;
1064
- for (const match of matches) {
1065
- const range = getLineRange(blocks, match.index, match.index + match[0].length);
1066
- for (let i = range[0]; i <= range[1]; i++) {
1067
- const block = blocks[i];
1068
- decorations.push(Decoration2.line({
1069
- class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1070
- }).range(block.from));
1071
- }
1064
+ const text = view.state.doc.sliceString(0);
1065
+ const matches = text.matchAll(CODE_REGEX);
1066
+ const blocks = view.viewportLineBlocks;
1067
+ for (const match of matches) {
1068
+ const range = getLineRange(blocks, match.index, match.index + match[0].length);
1069
+ for (let i = range[0]; i <= range[1]; i++) {
1070
+ const block = blocks[i];
1071
+ decorations.push(Decoration2.line({
1072
+ class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1073
+ }).range(block.from));
1072
1074
  }
1073
1075
  }
1074
1076
  return Decoration2.set(decorations);
@@ -1076,11 +1078,16 @@ var code2 = () => {
1076
1078
  return [
1077
1079
  ViewPlugin3.fromClass(class {
1078
1080
  constructor(view) {
1079
- this.decorations = getDecorations(view);
1081
+ this.hasFocus = false;
1082
+ this.decorations = buildDecorations4(view);
1080
1083
  }
1081
1084
  update(update2) {
1082
- if (update2.docChanged || update2.viewportChanged) {
1083
- this.decorations = getDecorations(update2.view);
1085
+ if (
1086
+ // TODO(burdon): Generalize for other extensions.
1087
+ this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
1088
+ ) {
1089
+ this.hasFocus = update2.view.hasFocus;
1090
+ this.decorations = buildDecorations4(update2.view);
1084
1091
  }
1085
1092
  }
1086
1093
  }, {
@@ -1092,23 +1099,31 @@ var code2 = () => {
1092
1099
 
1093
1100
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1094
1101
  import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1095
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5, MatchDecorator, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
1102
+ import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
1096
1103
  import sortBy from "lodash.sortby";
1097
1104
  import { debounce } from "@dxos/async";
1098
1105
  import { invariant as invariant2 } from "@dxos/invariant";
1099
- import { log as log2 } from "@dxos/log";
1100
1106
  import { nonNullable } from "@dxos/util";
1107
+
1108
+ // packages/ui/react-ui-editor/src/util.ts
1109
+ import { log } from "@dxos/log";
1110
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1111
+ var callbackWrapper = (fn) => (...args) => {
1112
+ try {
1113
+ return fn(...args);
1114
+ } catch (error) {
1115
+ log.catch(error, void 0, {
1116
+ F: __dxlog_file2,
1117
+ L: 16,
1118
+ S: void 0,
1119
+ C: (f, a) => f(...a)
1120
+ });
1121
+ }
1122
+ };
1123
+
1124
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
1101
1125
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1102
1126
  var styles3 = EditorView5.baseTheme({
1103
- "& .cm-bookmark": {
1104
- cursor: "pointer",
1105
- margin: "4px",
1106
- padding: "4px",
1107
- backgroundColor: "yellow"
1108
- },
1109
- "& .cm-bookmark-selected": {
1110
- backgroundColor: "orange"
1111
- },
1112
1127
  "& .cm-comment": {
1113
1128
  backgroundColor: getToken("extend.colors.yellow.50")
1114
1129
  },
@@ -1147,7 +1162,7 @@ var commentsStateField = StateField2.define({
1147
1162
  ranges: []
1148
1163
  }),
1149
1164
  update: (value, tr) => {
1150
- const cursorConverter3 = tr.state.facet(CursorConverter);
1165
+ const cursorConverter3 = tr.state.facet(Cursor.converter);
1151
1166
  for (const effect of tr.effects) {
1152
1167
  if (effect.is(setSelection)) {
1153
1168
  return {
@@ -1158,7 +1173,7 @@ var commentsStateField = StateField2.define({
1158
1173
  if (effect.is(setCommentRange)) {
1159
1174
  const { comments: comments2 } = effect.value;
1160
1175
  const ranges = comments2.map((comment) => {
1161
- const range = getRangeFromCursor(cursorConverter3, comment.cursor);
1176
+ const range = Cursor.getRangeFromCursor(cursorConverter3, comment.cursor);
1162
1177
  return range && {
1163
1178
  ...comment,
1164
1179
  ...range
@@ -1196,58 +1211,89 @@ var highlightDecorations = EditorView5.decorations.compute([
1196
1211
  }).filter(nonNullable) ?? [];
1197
1212
  return Decoration3.set(decorations);
1198
1213
  });
1199
- var BookmarkWidget = class extends WidgetType2 {
1200
- constructor(_anchor, _id, _handleClick) {
1201
- super();
1202
- this._anchor = _anchor;
1203
- this._id = _id;
1204
- this._handleClick = _handleClick;
1205
- invariant2(this._id, void 0, {
1206
- F: __dxlog_file3,
1207
- L: 168,
1208
- S: this,
1209
- A: [
1210
- "this._id",
1211
- ""
1212
- ]
1213
- });
1214
- }
1215
- eq(other) {
1216
- return this._anchor === other._anchor && this._id === other._id;
1217
- }
1218
- toDOM() {
1219
- const span = document.createElement("span");
1220
- span.className = "cm-bookmark";
1221
- span.textContent = "\u203B";
1222
- if (this._handleClick) {
1223
- span.onclick = () => this._handleClick();
1214
+ var trackPastedComments = (onUpdate) => {
1215
+ let tracked = null;
1216
+ const handleTrack = (event, view) => {
1217
+ const comments2 = view.state.field(commentsStateField);
1218
+ const { main } = view.state.selection;
1219
+ const selectedRanges = comments2.ranges.filter((range) => range.from >= main.from && range.to <= main.to && range.from < range.to);
1220
+ if (!selectedRanges.length) {
1221
+ tracked = null;
1222
+ } else {
1223
+ tracked = {
1224
+ text: view.state.doc.slice(main.from, main.to),
1225
+ comments: selectedRanges.map((range) => ({
1226
+ id: range.id,
1227
+ from: range.from - main.from,
1228
+ to: range.to - main.from
1229
+ }))
1230
+ };
1224
1231
  }
1225
- return span;
1226
- }
1232
+ };
1233
+ return [
1234
+ EditorView5.domEventHandlers({
1235
+ cut: handleTrack,
1236
+ copy: handleTrack
1237
+ }),
1238
+ // Handle paste.
1239
+ EditorView5.updateListener.of(({ view, state, transactions }) => {
1240
+ if (tracked) {
1241
+ const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1242
+ if (paste) {
1243
+ let found = -1;
1244
+ paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1245
+ if (text.eq(tracked.text)) {
1246
+ for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1247
+ fromB = transactions[i].changes.mapPos(fromB);
1248
+ }
1249
+ found = fromB;
1250
+ }
1251
+ });
1252
+ if (found > -1) {
1253
+ const comments2 = tracked.comments;
1254
+ view.state.facet(semaphoreFacet).reconcile(view);
1255
+ const active = state.field(commentsStateField).ranges;
1256
+ for (const moved of comments2) {
1257
+ if (active.some((range) => range.id === moved.id && range.from === range.to)) {
1258
+ const range = {
1259
+ from: found + moved.from,
1260
+ to: found + moved.to
1261
+ };
1262
+ const cursor = Cursor.getCursorFromRange(state.facet(Cursor.converter), range);
1263
+ onUpdate(moved.id, cursor);
1264
+ }
1265
+ }
1266
+ }
1267
+ tracked = null;
1268
+ }
1269
+ }
1270
+ })
1271
+ ];
1227
1272
  };
1228
1273
  var comments = (options = {}) => {
1274
+ const { key: shortcut = "meta-'" } = options;
1229
1275
  const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1230
1276
  const createCommentThread = (view) => {
1231
- const cursorConverter3 = view.state.facet(CursorConverter);
1277
+ const cursorConverter3 = view.state.facet(Cursor.converter);
1232
1278
  invariant2(options.onCreate, void 0, {
1233
1279
  F: __dxlog_file3,
1234
- L: 224,
1280
+ L: 264,
1235
1281
  S: void 0,
1236
1282
  A: [
1237
1283
  "options.onCreate",
1238
1284
  ""
1239
1285
  ]
1240
1286
  });
1241
- const { head, from, to } = view.state.selection.main;
1287
+ const { from, to } = view.state.selection.main;
1242
1288
  if (from === to) {
1243
1289
  return false;
1244
1290
  }
1245
- const relPos = getCursorFromRange(cursorConverter3, {
1291
+ const cursor = Cursor.getCursorFromRange(cursorConverter3, {
1246
1292
  from,
1247
1293
  to
1248
1294
  });
1249
- if (relPos) {
1250
- const id = callbackWrapper(options.onCreate)(relPos);
1295
+ if (cursor) {
1296
+ const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
1251
1297
  if (id) {
1252
1298
  view.dispatch({
1253
1299
  effects: setSelection.of({
@@ -1257,52 +1303,12 @@ var comments = (options = {}) => {
1257
1303
  anchor: from
1258
1304
  }
1259
1305
  });
1260
- if (options.footnote) {
1261
- const tag = `[^${id}]`;
1262
- view.dispatch({
1263
- changes: {
1264
- from: head,
1265
- insert: tag
1266
- },
1267
- selection: {
1268
- anchor: head + tag.length
1269
- }
1270
- });
1271
- }
1272
1306
  return true;
1273
1307
  }
1274
1308
  }
1275
1309
  return false;
1276
1310
  };
1277
- const bookmarksViewPlugin = ViewPlugin4.fromClass(class BookmarkViewPlugin {
1278
- static {
1279
- // Match markdown footnotes (option).
1280
- // https://www.markdownguide.org/extended-syntax/#footnotes
1281
- this.bookmarkMatcher = new MatchDecorator({
1282
- regexp: /\[\^(\w+)\]/g,
1283
- decoration: (match, view, pos) => {
1284
- const id = match[1];
1285
- return Decoration3.replace({
1286
- id,
1287
- widget: new BookmarkWidget(pos, id)
1288
- });
1289
- }
1290
- });
1291
- }
1292
- constructor(view) {
1293
- this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.createDeco(view);
1294
- }
1295
- update(update2) {
1296
- this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.updateDeco(update2, this.bookmarks);
1297
- }
1298
- }, {
1299
- decorations: (instance) => instance.bookmarks,
1300
- provide: (plugin) => EditorView5.atomicRanges.of((view) => {
1301
- return view.plugin(plugin)?.bookmarks || Decoration3.none;
1302
- })
1303
- });
1304
1311
  return [
1305
- bookmarksViewPlugin,
1306
1312
  commentsStateField,
1307
1313
  highlightDecorations,
1308
1314
  styles3,
@@ -1311,7 +1317,7 @@ var comments = (options = {}) => {
1311
1317
  //
1312
1318
  options.onCreate ? keymap3.of([
1313
1319
  {
1314
- key: options?.key ?? "meta-'",
1320
+ key: shortcut,
1315
1321
  run: callbackWrapper(createCommentThread)
1316
1322
  }
1317
1323
  ]) : [],
@@ -1328,7 +1334,7 @@ var comments = (options = {}) => {
1328
1334
  above: true,
1329
1335
  create: () => {
1330
1336
  const el = document.createElement("div");
1331
- options.onHover?.(el);
1337
+ options.onHover(el, shortcut);
1332
1338
  return {
1333
1339
  dom: el,
1334
1340
  offset: {
@@ -1341,7 +1347,8 @@ var comments = (options = {}) => {
1341
1347
  }
1342
1348
  return null;
1343
1349
  }, {
1344
- hideOnChange: true,
1350
+ // TODO(burdon): Hide on change triggered immediately?
1351
+ // hideOnChange: true,
1345
1352
  hoverTime: 1e3
1346
1353
  }) : [],
1347
1354
  //
@@ -1349,27 +1356,20 @@ var comments = (options = {}) => {
1349
1356
  // TODO(burdon): Is there a better (finer grained) way to do this?
1350
1357
  //
1351
1358
  EditorView5.updateListener.of(({ view, state, changes }) => {
1352
- const cursorConverter3 = view.state.facet(CursorConverter);
1359
+ const cursorConverter3 = view.state.facet(Cursor.converter);
1353
1360
  {
1354
1361
  let mod = false;
1355
1362
  const { active, ranges } = state.field(commentsStateField);
1356
1363
  changes.iterChanges((from, to, from2, to2) => {
1357
1364
  ranges.forEach((range) => {
1358
1365
  if (from2 === to2) {
1359
- const newRange = getRangeFromCursor(cursorConverter3, range.cursor);
1366
+ const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
1360
1367
  if (!newRange || newRange.to - newRange.from === 0) {
1361
- log2.info("deleted comment", {
1362
- thread: range.id
1363
- }, {
1364
- F: __dxlog_file3,
1365
- L: 358,
1366
- S: void 0,
1367
- C: (f, a) => f(...a)
1368
- });
1368
+ options.onDelete?.(range.id);
1369
1369
  }
1370
1370
  }
1371
1371
  if (from <= range.to) {
1372
- const newRange = getRangeFromCursor(cursorConverter3, range.cursor);
1372
+ const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
1373
1373
  Object.assign(range, newRange);
1374
1374
  mod = true;
1375
1375
  }
@@ -1415,29 +1415,13 @@ var comments = (options = {}) => {
1415
1415
  });
1416
1416
  }
1417
1417
  }
1418
- })
1418
+ }),
1419
+ options.onUpdate ? trackPastedComments(options.onUpdate) : []
1419
1420
  ];
1420
1421
  };
1421
- var getCursorFromRange = (cursorConverter3, range) => {
1422
- const from = cursorConverter3.toCursor(range.from);
1423
- const to = cursorConverter3.toCursor(range.to, -1);
1424
- return [
1425
- from,
1426
- to
1427
- ].join(":");
1428
- };
1429
- var getRangeFromCursor = (cursorConverter3, cursor) => {
1430
- const parts = cursor.split(":");
1431
- const from = cursorConverter3.fromCursor(parts[0]);
1432
- const to = cursorConverter3.fromCursor(parts[1]);
1433
- return from !== void 0 && to !== void 0 ? {
1434
- from,
1435
- to
1436
- } : void 0;
1437
- };
1438
1422
 
1439
1423
  // packages/ui/react-ui-editor/src/extensions/hr.ts
1440
- import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin5, WidgetType as WidgetType3 } from "@codemirror/view";
1424
+ import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
1441
1425
  var styles4 = EditorView6.baseTheme({
1442
1426
  "& .cm-hr": {
1443
1427
  // TODO(burdon): ???
@@ -1445,7 +1429,7 @@ var styles4 = EditorView6.baseTheme({
1445
1429
  borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
1446
1430
  }
1447
1431
  });
1448
- var HorizontalRuleWidget = class extends WidgetType3 {
1432
+ var HorizontalRuleWidget = class extends WidgetType2 {
1449
1433
  constructor(_pos) {
1450
1434
  super();
1451
1435
  this._pos = _pos;
@@ -1459,15 +1443,16 @@ var HorizontalRuleWidget = class extends WidgetType3 {
1459
1443
  return el;
1460
1444
  }
1461
1445
  };
1462
- var placeholderMatcher = new MatchDecorator2({
1463
- regexp: /^---$/g,
1446
+ var placeholderMatcher = new MatchDecorator({
1447
+ // regexp: /(?<=\n\n)---/gs,
1448
+ regexp: /^---$/gs,
1464
1449
  decoration: (match, view, pos) => Decoration4.replace({
1465
1450
  widget: new HorizontalRuleWidget(pos)
1466
1451
  })
1467
1452
  });
1468
1453
  var hr = () => [
1469
1454
  styles4,
1470
- ViewPlugin5.fromClass(class {
1455
+ ViewPlugin4.fromClass(class {
1471
1456
  constructor(view) {
1472
1457
  this.rules = placeholderMatcher.createDeco(view);
1473
1458
  }
@@ -1475,18 +1460,14 @@ var hr = () => [
1475
1460
  this.rules = placeholderMatcher.updateDeco(update2, this.rules);
1476
1461
  }
1477
1462
  }, {
1478
- decorations: (instance) => instance.rules,
1479
- // TODO(burdon): Is this really atomic (cursor can move into ---).
1480
- provide: (plugin) => EditorView6.atomicRanges.of((view) => {
1481
- return view.plugin(plugin)?.rules || Decoration4.none;
1482
- })
1463
+ decorations: (instance) => instance.rules
1483
1464
  })
1484
1465
  ];
1485
1466
 
1486
1467
  // packages/ui/react-ui-editor/src/extensions/image.ts
1487
1468
  import { syntaxTree } from "@codemirror/language";
1488
1469
  import { StateField as StateField3 } from "@codemirror/state";
1489
- import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType4 } from "@codemirror/view";
1470
+ import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
1490
1471
  var image = (options = {}) => {
1491
1472
  return StateField3.define({
1492
1473
  create: (state) => {
@@ -1538,7 +1519,7 @@ var buildDecorations = (from, to, state) => {
1538
1519
  });
1539
1520
  return decorations;
1540
1521
  };
1541
- var ImageWidget = class extends WidgetType4 {
1522
+ var ImageWidget = class extends WidgetType3 {
1542
1523
  constructor(_url) {
1543
1524
  super();
1544
1525
  this._url = _url;
@@ -1558,11 +1539,11 @@ var ImageWidget = class extends WidgetType4 {
1558
1539
  // packages/ui/react-ui-editor/src/extensions/link.ts
1559
1540
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1560
1541
  import { RangeSetBuilder } from "@codemirror/state";
1561
- import { Decoration as Decoration6, WidgetType as WidgetType5, hoverTooltip as hoverTooltip2, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
1542
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin5, WidgetType as WidgetType4 } from "@codemirror/view";
1562
1543
  import { tooltipContent } from "@dxos/react-ui-theme";
1563
1544
  var link = (options = {}) => {
1564
1545
  const extensions = [
1565
- ViewPlugin6.fromClass(class {
1546
+ ViewPlugin5.fromClass(class {
1566
1547
  constructor(view) {
1567
1548
  this.decorations = buildDecorations2(view, options);
1568
1549
  }
@@ -1613,7 +1594,7 @@ var link = (options = {}) => {
1613
1594
  }
1614
1595
  return extensions;
1615
1596
  };
1616
- var LinkButton = class extends WidgetType5 {
1597
+ var LinkButton = class extends WidgetType4 {
1617
1598
  constructor(_url, _onAttach) {
1618
1599
  super();
1619
1600
  this._url = _url;
@@ -1628,7 +1609,7 @@ var LinkButton = class extends WidgetType5 {
1628
1609
  return el;
1629
1610
  }
1630
1611
  };
1631
- var LinkText = class extends WidgetType5 {
1612
+ var LinkText = class extends WidgetType4 {
1632
1613
  constructor(_text, _url) {
1633
1614
  super();
1634
1615
  this._text = _text;
@@ -1728,7 +1709,6 @@ var markdownTags = {
1728
1709
  CodeText: Tag.define(),
1729
1710
  EmphasisMark: Tag.define(),
1730
1711
  HeaderMark: Tag.define(),
1731
- // HorizontalRule: Tag.define(),
1732
1712
  InlineCode: Tag.define(),
1733
1713
  LinkLabel: Tag.define(),
1734
1714
  LinkReference: Tag.define(),
@@ -1892,12 +1872,6 @@ var markdownHighlightStyle = (readonly) => {
1892
1872
  ],
1893
1873
  class: blockquote
1894
1874
  },
1895
- // TODO(burdon): Replace with extension.
1896
- // {
1897
- // tag: [markdownTags.HorizontalRule],
1898
- // class: mx(horizontalRule, readonly && '-indent-[100rem]'),
1899
- // },
1900
- // TODO(burdon): Only if being edited.
1901
1875
  {
1902
1876
  tag: [
1903
1877
  markdownTags.TableCell
@@ -1988,10 +1962,15 @@ var mention = ({ onSearch }) => {
1988
1962
  });
1989
1963
  };
1990
1964
 
1965
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
1966
+ var outliner = (options = {}) => {
1967
+ return [];
1968
+ };
1969
+
1991
1970
  // packages/ui/react-ui-editor/src/extensions/table.ts
1992
1971
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1993
1972
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
1994
- import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType6 } from "@codemirror/view";
1973
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
1995
1974
  var table = (options = {}) => {
1996
1975
  return StateField4.define({
1997
1976
  create: (state) => update(state, options),
@@ -2049,7 +2028,7 @@ var update = (state, options) => {
2049
2028
  });
2050
2029
  return builder.finish();
2051
2030
  };
2052
- var TableWidget = class extends WidgetType6 {
2031
+ var TableWidget = class extends WidgetType5 {
2053
2032
  constructor(_table) {
2054
2033
  super();
2055
2034
  this._table = _table;
@@ -2087,87 +2066,86 @@ var TableWidget = class extends WidgetType6 {
2087
2066
  };
2088
2067
 
2089
2068
  // packages/ui/react-ui-editor/src/extensions/tasklist.ts
2090
- import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType7, MatchDecorator as MatchDecorator3, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
2091
- var styles5 = EditorView11.baseTheme({
2092
- "& .cm-task-item": {
2093
- paddingLeft: "4px",
2094
- paddingRight: "8px"
2095
- }
2096
- });
2097
- var CheckboxWidget = class extends WidgetType7 {
2098
- constructor(_checked, _indent, _onCheck) {
2069
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2070
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
2071
+ import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
2072
+ var CheckboxWidget = class extends WidgetType6 {
2073
+ constructor(_checked) {
2099
2074
  super();
2100
2075
  this._checked = _checked;
2101
- this._indent = _indent;
2102
- this._onCheck = _onCheck;
2103
2076
  }
2104
2077
  eq(other) {
2105
- return this._checked === other._checked && this._indent === other._indent;
2078
+ return this._checked === other._checked;
2106
2079
  }
2107
2080
  toDOM(view) {
2108
- const wrap = document.createElement("span");
2109
- wrap.className = "cm-task-item";
2110
- wrap.setAttribute("aria-hidden", "true");
2111
- wrap.style.setProperty("margin-left", this._indent * 24 + "px");
2112
- const input = wrap.appendChild(document.createElement("input"));
2081
+ const input = document.createElement("input");
2113
2082
  input.type = "checkbox";
2114
2083
  input.checked = this._checked;
2115
- if (!this._onCheck) {
2084
+ if (view.state.readOnly) {
2116
2085
  input.setAttribute("disabled", "true");
2117
- }
2118
- if (this._onCheck) {
2119
- input.onchange = (event) => {
2120
- this._onCheck?.(event.target.checked);
2121
- return true;
2086
+ } else {
2087
+ input.onmousedown = (event) => {
2088
+ const pos = view.posAtDOM(input);
2089
+ const text = view.state.sliceDoc(pos, pos + 3);
2090
+ if (text === (this._checked ? "[x]" : "[ ]")) {
2091
+ view.dispatch({
2092
+ changes: {
2093
+ from: pos + 1,
2094
+ to: pos + 2,
2095
+ insert: this._checked ? " " : "x"
2096
+ }
2097
+ });
2098
+ event.preventDefault();
2099
+ }
2122
2100
  };
2123
2101
  }
2124
- return wrap;
2102
+ return input;
2125
2103
  }
2126
2104
  ignoreEvent() {
2127
2105
  return false;
2128
2106
  }
2129
2107
  };
2108
+ var checkedDeco = Decoration8.replace({
2109
+ widget: new CheckboxWidget(true)
2110
+ });
2111
+ var uncheckedDeco = Decoration8.replace({
2112
+ widget: new CheckboxWidget(false)
2113
+ });
2130
2114
  var tasklist = (options = {}) => {
2131
- const taskMatcher = new MatchDecorator3({
2132
- regexp: /^(\s*)- \[([ xX])\]\s/g,
2133
- decoration: (match, view, pos) => {
2134
- const indent = Math.floor(match[1].length / 2);
2135
- const checked = match[2] === "x" || match[2] === "X";
2136
- return Decoration8.replace({
2137
- widget: new CheckboxWidget(checked, indent, view.state.readOnly ? void 0 : (checked2) => {
2138
- const idx = pos + match[0].indexOf("[") + 1;
2139
- view.dispatch({
2140
- changes: {
2141
- from: idx,
2142
- to: idx + 1,
2143
- insert: checked2 ? "x" : " "
2144
- },
2145
- // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
2146
- selection: {
2147
- anchor: idx + 3
2148
- }
2149
- });
2150
- })
2151
- });
2152
- }
2153
- });
2154
- const tasks = ViewPlugin7.fromClass(class {
2115
+ return ViewPlugin6.fromClass(class {
2155
2116
  constructor(view) {
2156
- this.tasks = taskMatcher.createDeco(view);
2117
+ this.decorations = buildDecorations3(view);
2157
2118
  }
2158
2119
  update(update2) {
2159
- this.tasks = taskMatcher.updateDeco(update2, this.tasks);
2120
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2121
+ this.decorations = buildDecorations3(update2.view);
2122
+ }
2160
2123
  }
2161
2124
  }, {
2162
- decorations: (value) => value.tasks,
2125
+ decorations: (v) => v.decorations,
2126
+ // TODO(burdon): Is this still required?
2163
2127
  provide: (plugin) => EditorView11.atomicRanges.of((view) => {
2164
- return view.plugin(plugin)?.tasks || Decoration8.none;
2128
+ return view.plugin(plugin)?.decorations || Decoration8.none;
2165
2129
  })
2166
2130
  });
2167
- return [
2168
- tasks,
2169
- styles5
2170
- ];
2131
+ };
2132
+ var buildDecorations3 = (view) => {
2133
+ const builder = new RangeSetBuilder3();
2134
+ const { state } = view;
2135
+ const cursor = state.selection.main.head;
2136
+ for (const { from, to } of view.visibleRanges) {
2137
+ syntaxTree4(state).iterate({
2138
+ enter: (node) => {
2139
+ if (node.name === "TaskMarker" && (cursor < node.from || cursor > node.to)) {
2140
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2141
+ builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
2142
+ }
2143
+ },
2144
+ from,
2145
+ to
2146
+ });
2147
+ }
2148
+ return builder.finish();
2171
2149
  };
2172
2150
 
2173
2151
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
@@ -2244,7 +2222,7 @@ var cursorConverter2 = (text) => ({
2244
2222
 
2245
2223
  // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2246
2224
  var yjs = (content, awareness2) => [
2247
- CursorConverter.of(cursorConverter2(content)),
2225
+ Cursor.converter.of(cursorConverter2(content)),
2248
2226
  yCollab(content, awareness2)
2249
2227
  ];
2250
2228
 
@@ -2341,33 +2319,6 @@ var defaultTheme = {
2341
2319
  backgroundColor: get3(tokens, "extend.colors.yellow.700")
2342
2320
  },
2343
2321
  //
2344
- // collaboration
2345
- // TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
2346
- //
2347
- "&light .cm-ySelection, &light .cm-yLineSelection": {
2348
- mixBlendMode: "multiply"
2349
- },
2350
- "&dark .cm-ySelection, &dark .cm-yLineSelection": {
2351
- mixBlendMode: "screen"
2352
- },
2353
- ".cm-ySelectionInfo": {
2354
- padding: "2px 4px",
2355
- marginBlockStart: "-4px"
2356
- },
2357
- ".cm-ySelection, & .cm-selectionMatch": {
2358
- paddingBlockStart: ".15em",
2359
- paddingBlockEnd: ".15em"
2360
- },
2361
- ".cm-ySelectionCaret": {
2362
- display: "inline-block",
2363
- insetBlockStart: ".1em",
2364
- blockSize: "1.4em",
2365
- verticalAlign: "top"
2366
- },
2367
- ".cm-yLineSelection": {
2368
- margin: 0
2369
- },
2370
- //
2371
2322
  // link
2372
2323
  //
2373
2324
  ".cm-link": {
@@ -2404,13 +2355,6 @@ var defaultTheme = {
2404
2355
  textDecoration: "none"
2405
2356
  },
2406
2357
  //
2407
- // widgets
2408
- //
2409
- ".cm-widgetBuffer": {
2410
- display: "none",
2411
- height: 0
2412
- },
2413
- //
2414
2358
  // table
2415
2359
  //
2416
2360
  ".cm-table *": {
@@ -2441,27 +2385,17 @@ var defaultTheme = {
2441
2385
  },
2442
2386
  //
2443
2387
  // font size
2444
- // TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
2388
+ // TODO(thure): This appears to be the best or only way to set selection caret heights,
2389
+ // but it's far more verbose than it needs to be.
2445
2390
  //
2446
- ...Object.keys(get3(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
2447
- const height = get3(tokens, [
2448
- "extend",
2449
- "fontSize",
2450
- fontSize,
2451
- 1,
2452
- "lineHeight"
2453
- ]);
2454
- acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
2455
- height
2456
- };
2457
- acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
2458
- height
2459
- };
2460
- acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
2461
- height
2462
- };
2463
- return acc;
2464
- }, {}),
2391
+ // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
2392
+ // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
2393
+ //
2394
+ // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
2395
+ // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
2396
+ // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
2397
+ // return acc;
2398
+ // }, {}),
2465
2399
  /**
2466
2400
  * Panels
2467
2401
  * TODO(burdon): Needs styling attention (esp. dark mode).
@@ -2512,7 +2446,9 @@ var textTheme = {
2512
2446
  }
2513
2447
  };
2514
2448
  var markdownTheme = {
2515
- // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
2449
+ ".cm-scroller": {
2450
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2451
+ },
2516
2452
  ".cm-placeholder": {
2517
2453
  fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2518
2454
  }
@@ -2531,7 +2467,7 @@ var EditorModes = [
2531
2467
  "default",
2532
2468
  "vim"
2533
2469
  ];
2534
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, readonly, comments: comments2, extensions = [], editorMode, slots = defaultSlots }, forwardedRef) => {
2470
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, selection, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
2535
2471
  const tabsterDOMAttribute = useFocusableGroup({
2536
2472
  tabBehavior: "limited"
2537
2473
  });
@@ -2542,12 +2478,12 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2542
2478
  view
2543
2479
  ]);
2544
2480
  useEffect(() => {
2545
- if (view && focus) {
2546
- view.focus();
2481
+ if (autofocus) {
2482
+ view?.focus();
2547
2483
  }
2548
2484
  }, [
2549
- view,
2550
- focus
2485
+ autofocus,
2486
+ view
2551
2487
  ]);
2552
2488
  const { awareness: awareness2, peer } = model;
2553
2489
  useEffect(() => {
@@ -2571,7 +2507,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2571
2507
  themeMode
2572
2508
  ]);
2573
2509
  useEffect(() => {
2574
- if (view && comments2?.length) {
2510
+ if (view && comments2 !== void 0) {
2575
2511
  view.dispatch({
2576
2512
  effects: setCommentRange.of({
2577
2513
  model,
@@ -2589,6 +2525,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2589
2525
  }
2590
2526
  const state = EditorState2.create({
2591
2527
  doc: model.text(),
2528
+ // TODO(burdon): Composer should store and set selection when switching documents.
2592
2529
  selection,
2593
2530
  extensions: [
2594
2531
  readonly && EditorState2.readOnly.of(readonly),
@@ -2597,7 +2534,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2597
2534
  // Theme.
2598
2535
  // TODO(burdon): Make theme configurable.
2599
2536
  EditorView12.baseTheme(defaultTheme),
2600
- EditorView12.theme(slots?.editor?.theme ?? {}),
2537
+ EditorView12.theme(theme ?? {}),
2601
2538
  EditorView12.darkTheme.of(themeMode === "dark"),
2602
2539
  // Storage and replication.
2603
2540
  model.extension,
@@ -2636,14 +2573,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2636
2573
  ]);
2637
2574
  return /* @__PURE__ */ React.createElement("div", {
2638
2575
  key: model.id,
2639
- ref: rootRef,
2576
+ role: "none",
2640
2577
  tabIndex: 0,
2641
- ...slots?.root,
2578
+ onKeyUp: handleKeyUp,
2579
+ ...slots.root,
2642
2580
  ...editorMode !== "vim" && tabsterDOMAttribute,
2643
- onKeyUp: handleKeyUp
2581
+ ref: rootRef
2644
2582
  });
2645
2583
  });
2646
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots, ...props }, forwardedRef) => {
2584
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
2647
2585
  const { themeMode } = useThemeContext();
2648
2586
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
2649
2587
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2653,15 +2591,16 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots,
2653
2591
  basicBundle({
2654
2592
  readonly,
2655
2593
  themeMode,
2656
- placeholder: updatedSlots?.editor?.placeholder
2594
+ placeholder: placeholder3
2657
2595
  }),
2658
2596
  ...extensions
2659
2597
  ],
2598
+ theme,
2660
2599
  slots: updatedSlots,
2661
2600
  ...props
2662
2601
  });
2663
2602
  });
2664
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots, ...props }, forwardedRef) => {
2603
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
2665
2604
  const { themeMode } = useThemeContext();
2666
2605
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
2667
2606
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2671,30 +2610,25 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], sl
2671
2610
  markdownBundle({
2672
2611
  readonly,
2673
2612
  themeMode,
2674
- placeholder: updatedSlots?.editor?.placeholder
2613
+ placeholder: placeholder3
2675
2614
  }),
2676
2615
  ...extensions
2677
2616
  ],
2617
+ theme,
2678
2618
  slots: updatedSlots,
2679
2619
  ...props
2680
2620
  });
2681
2621
  });
2682
2622
  var defaultSlots = {
2683
2623
  root: {
2684
- className: mx3("p-2", inputSurface)
2624
+ className: mx3("w-full p-2 overflow-y-auto", inputSurface)
2685
2625
  }
2686
2626
  };
2687
2627
  var defaultTextSlots = {
2688
- ...defaultSlots,
2689
- editor: {
2690
- theme: textTheme
2691
- }
2628
+ ...defaultSlots
2692
2629
  };
2693
2630
  var defaultMarkdownSlots = {
2694
- ...defaultSlots,
2695
- editor: {
2696
- theme: markdownTheme
2697
- }
2631
+ ...defaultSlots
2698
2632
  };
2699
2633
 
2700
2634
  // packages/ui/react-ui-editor/src/hooks/automerge.ts
@@ -2706,7 +2640,7 @@ import { isNotNullOrUndefined } from "@dxos/util";
2706
2640
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2707
2641
  import { Context as Context2 } from "@dxos/context";
2708
2642
  import { invariant as invariant3 } from "@dxos/invariant";
2709
- import { log as log3 } from "@dxos/log";
2643
+ import { log as log2 } from "@dxos/log";
2710
2644
  var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2711
2645
  var DEBOUNCE_INTERVAL = 100;
2712
2646
  var SpaceAwarenessProvider = class {
@@ -2745,7 +2679,7 @@ var SpaceAwarenessProvider = class {
2745
2679
  void this._space.postMessage(this._channel, {
2746
2680
  kind: "query"
2747
2681
  }).catch((err) => {
2748
- log3.debug("failed to query awareness", {
2682
+ log2.debug("failed to query awareness", {
2749
2683
  err
2750
2684
  }, {
2751
2685
  F: __dxlog_file4,
@@ -2817,15 +2751,13 @@ import * as decoding from "lib0/decoding";
2817
2751
  import * as encoding from "lib0/encoding";
2818
2752
  import { Observable } from "lib0/observable";
2819
2753
  import * as YP from "y-protocols/awareness";
2820
- import * as Y2 from "yjs";
2821
2754
  import { invariant as invariant4 } from "@dxos/invariant";
2822
- import { log as log4 } from "@dxos/log";
2823
- import { arrayToString as arrayToString2, stringToArray as stringToArray2 } from "@dxos/util";
2755
+ import { log as log3 } from "@dxos/log";
2824
2756
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
2825
2757
  var createYjsModel = ({ identity, space, text }) => {
2826
2758
  invariant4(text?.doc && text?.content, void 0, {
2827
2759
  F: __dxlog_file5,
2828
- L: 23,
2760
+ L: 21,
2829
2761
  S: void 0,
2830
2762
  A: [
2831
2763
  "text?.doc && text?.content",
@@ -2841,34 +2773,6 @@ var createYjsModel = ({ identity, space, text }) => {
2841
2773
  id: text.doc.guid,
2842
2774
  content: text.content,
2843
2775
  text: () => text.content.toString(),
2844
- // https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
2845
- // TODO(dmaretskyi): Refactor as cursor-converter.
2846
- getCursorFromRange: (range) => {
2847
- const from = Y2.encodeRelativePosition(Y2.createRelativePositionFromTypeIndex(text.content, range.from));
2848
- const to = Y2.encodeRelativePosition(Y2.createRelativePositionFromTypeIndex(text.content, range.to, -1));
2849
- return [
2850
- arrayToString2(from),
2851
- arrayToString2(to)
2852
- ].join(":");
2853
- },
2854
- getRangeFromCursor: (cursor) => {
2855
- invariant4(text.doc, void 0, {
2856
- F: __dxlog_file5,
2857
- L: 40,
2858
- S: void 0,
2859
- A: [
2860
- "text.doc",
2861
- ""
2862
- ]
2863
- });
2864
- const parts = cursor.split(":");
2865
- const from = Y2.createAbsolutePositionFromRelativePosition(Y2.decodeRelativePosition(stringToArray2(parts[0])), text.doc);
2866
- const to = Y2.createAbsolutePositionFromRelativePosition(Y2.decodeRelativePosition(stringToArray2(parts[1])), text.doc);
2867
- return from && to ? {
2868
- from: from.index,
2869
- to: to.index
2870
- } : void 0;
2871
- },
2872
2776
  extension: [
2873
2777
  modelState.init(() => model),
2874
2778
  yjs(text.content, provider?.awareness)
@@ -2911,14 +2815,14 @@ var YAwarenessProvider = class extends Observable {
2911
2815
  return this._awareness;
2912
2816
  }
2913
2817
  _handleAwarenessUpdate({ added, updated, removed }, origin) {
2914
- log4("awareness update", {
2818
+ log3("awareness update", {
2915
2819
  added,
2916
2820
  updated,
2917
2821
  removed,
2918
2822
  origin
2919
2823
  }, {
2920
2824
  F: __dxlog_file5,
2921
- L: 122,
2825
+ L: 100,
2922
2826
  S: this,
2923
2827
  C: (f, a) => f(...a)
2924
2828
  });
@@ -2929,9 +2833,9 @@ var YAwarenessProvider = class extends Observable {
2929
2833
  void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
2930
2834
  }
2931
2835
  _handleSpaceMessage({ payload }) {
2932
- log4("space message", payload, {
2836
+ log3("space message", payload, {
2933
2837
  F: __dxlog_file5,
2934
- L: 131,
2838
+ L: 109,
2935
2839
  S: this,
2936
2840
  C: (f, a) => f(...a)
2937
2841
  });
@@ -3040,7 +2944,7 @@ var createAutomergeModel = ({ space, identity, text }) => {
3040
2944
  export {
3041
2945
  AwarenessProvider,
3042
2946
  BaseTextEditor,
3043
- CursorConverter,
2947
+ Cursor,
3044
2948
  Doc,
3045
2949
  EditorModes,
3046
2950
  MarkdownEditor,
@@ -3062,7 +2966,6 @@ export {
3062
2966
  comments,
3063
2967
  createAutomergeModel,
3064
2968
  createYjsModel,
3065
- cursorConverter,
3066
2969
  defaultMarkdownSlots,
3067
2970
  defaultOptions,
3068
2971
  defaultSlots,
@@ -3080,6 +2983,7 @@ export {
3080
2983
  markdownTheme,
3081
2984
  mention,
3082
2985
  modelState,
2986
+ outliner,
3083
2987
  setCommentRange,
3084
2988
  setFocus,
3085
2989
  setSelection,