@dxos/react-ui-editor 0.3.10-main.11cdcf4 → 0.3.10-main.279fbbe

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 (74) hide show
  1. package/dist/lib/browser/index.mjs +319 -210
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +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 +1 -0
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts +3 -2
  9. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +14 -0
  11. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
  12. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
  13. package/dist/types/src/components/TextEditor/extensions/index.d.ts +1 -0
  14. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  15. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  16. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  17. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  18. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  19. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +1 -4
  20. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  21. package/dist/types/src/components/TextEditor/themes/default.d.ts +3 -0
  22. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  23. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -0
  24. package/dist/types/src/hooks/automerge/amToCodemirror.d.ts.map +1 -0
  25. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
  26. package/dist/types/src/hooks/automerge/codeMirrorToAm.d.ts.map +1 -0
  27. package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
  28. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
  29. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
  30. package/dist/types/src/hooks/index.d.ts +0 -1
  31. package/dist/types/src/hooks/index.d.ts.map +1 -1
  32. package/dist/types/src/hooks/useTextModel.d.ts +3 -4
  33. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  34. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  35. package/package.json +19 -19
  36. package/src/components/TextEditor/MarkdownEditor.stories.tsx +37 -6
  37. package/src/components/TextEditor/TextEditor.tsx +21 -17
  38. package/src/components/TextEditor/extensions/autocomplete.ts +5 -3
  39. package/src/components/TextEditor/extensions/comments.ts +173 -0
  40. package/src/components/TextEditor/extensions/experimental.tsx +1 -0
  41. package/src/components/TextEditor/extensions/index.ts +1 -0
  42. package/src/components/TextEditor/extensions/link.ts +10 -2
  43. package/src/components/TextEditor/extensions/listener.ts +1 -0
  44. package/src/components/TextEditor/extensions/markdown/bundle.ts +6 -14
  45. package/src/components/TextEditor/extensions/markdown/highlight.ts +2 -1
  46. package/src/components/TextEditor/extensions/tasklist.ts +73 -79
  47. package/src/components/TextEditor/themes/default.ts +15 -0
  48. package/src/{automerge → hooks/automerge}/automerge.stories.tsx +2 -1
  49. package/src/hooks/index.ts +0 -1
  50. package/src/hooks/useTextModel.ts +49 -41
  51. package/src/testing/replicator.ts +6 -7
  52. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
  53. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
  54. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
  55. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
  56. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
  57. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
  58. package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
  59. package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
  60. package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
  61. package/src/hooks/useCollaboration.ts +0 -45
  62. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.d.ts +0 -0
  63. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/amToCodemirror.d.ts +0 -0
  64. /package/dist/types/src/{automerge → hooks/automerge}/automerge.stories.d.ts +0 -0
  65. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/codeMirrorToAm.d.ts +0 -0
  66. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
  67. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +0 -0
  68. /package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
  69. /package/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.ts +0 -0
  70. /package/src/{automerge/automerge-plugin → hooks/automerge}/amToCodemirror.ts +0 -0
  71. /package/src/{automerge/automerge-plugin → hooks/automerge}/codeMirrorToAm.ts +0 -0
  72. /package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
  73. /package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +0 -0
  74. /package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
- import { Doc, YText as YText2, YXmlFragment } from "@dxos/text-model";
3
+ import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
4
 
5
5
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
6
  import { tags as tags2 } from "@lezer/highlight";
@@ -8,7 +8,7 @@ import { tags as tags2 } from "@lezer/highlight";
8
8
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/autocomplete.ts
9
9
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
10
10
  import { keymap } from "@codemirror/view";
11
- var autocomplete = ({ getOptions }) => {
11
+ var autocomplete = ({ onSearch }) => {
12
12
  return [
13
13
  // https://codemirror.net/docs/ref/#view.keymap
14
14
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -30,7 +30,7 @@ var autocomplete = ({ getOptions }) => {
30
30
  }
31
31
  return {
32
32
  from: word.from,
33
- options: getOptions(word.text.toLowerCase())
33
+ options: onSearch(word.text.toLowerCase())
34
34
  };
35
35
  }
36
36
  ]
@@ -54,11 +54,130 @@ var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
54
54
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
55
55
  ].filter(Boolean);
56
56
 
57
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
58
+ import { Decoration, EditorView as EditorView2, keymap as keymap2, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
59
+ import { invariant } from "@dxos/invariant";
60
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
61
+ var BookmarkWidget = class extends WidgetType {
62
+ constructor(_pos, _id) {
63
+ super();
64
+ this._pos = _pos;
65
+ this._id = _id;
66
+ invariant(this._id, void 0, {
67
+ F: __dxlog_file,
68
+ L: 40,
69
+ S: this,
70
+ A: [
71
+ "this._id",
72
+ ""
73
+ ]
74
+ });
75
+ }
76
+ eq(other) {
77
+ return this._id === other._id;
78
+ }
79
+ // TODO(burdon): Click to select.
80
+ toDOM() {
81
+ const span = document.createElement("span");
82
+ span.className = "cm-bookmark";
83
+ span.textContent = "\u{1F4AC}";
84
+ return span;
85
+ }
86
+ };
87
+ var styles = EditorView2.baseTheme({
88
+ "& .cm-bookmark": {
89
+ cursor: "pointer",
90
+ margin: "4px",
91
+ padding: "4px",
92
+ backgroundColor: "yellow"
93
+ }
94
+ });
95
+ var comments = (options = {}) => {
96
+ const bookmarkMatcher = new MatchDecorator({
97
+ regexp: /\[\^(\w+)\]/g,
98
+ decoration: (match, view, pos) => {
99
+ const id = match[1];
100
+ return Decoration.replace({
101
+ id,
102
+ widget: new BookmarkWidget(pos, id)
103
+ });
104
+ }
105
+ });
106
+ const bookmarks = ViewPlugin.fromClass(class {
107
+ constructor(view) {
108
+ this.bookmarks = bookmarkMatcher.createDeco(view);
109
+ }
110
+ update(update2) {
111
+ this.bookmarks = bookmarkMatcher.updateDeco(update2, this.bookmarks);
112
+ }
113
+ }, {
114
+ decorations: (instance) => instance.bookmarks,
115
+ provide: (plugin) => EditorView2.atomicRanges.of((view) => {
116
+ return view.plugin(plugin)?.bookmarks || Decoration.none;
117
+ })
118
+ });
119
+ return [
120
+ keymap2.of([
121
+ {
122
+ key: options?.key ?? "alt-meta-c",
123
+ run: (view) => {
124
+ const id = options.onCreate?.();
125
+ if (id) {
126
+ const pos = view.state.selection.main.head;
127
+ const tag = `[^${id}]`;
128
+ view.dispatch({
129
+ changes: {
130
+ from: pos,
131
+ insert: tag
132
+ },
133
+ selection: {
134
+ anchor: pos + tag.length
135
+ }
136
+ });
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ }
142
+ ]),
143
+ bookmarks,
144
+ // Monitor cursor movement.
145
+ EditorView2.updateListener.of((update2) => {
146
+ const view = update2.view;
147
+ const pos = view.state.selection.main.head;
148
+ const decorations = [];
149
+ const rangeSet = view.plugin(bookmarks)?.bookmarks;
150
+ rangeSet?.between(pos, pos + 1, (from, to, value) => {
151
+ if (value.spec.widget) {
152
+ decorations.push({
153
+ from,
154
+ to,
155
+ value
156
+ });
157
+ }
158
+ });
159
+ if (decorations.length) {
160
+ const { from, value: { spec: { id } } } = decorations[0];
161
+ const location = view.coordsAtPos(from);
162
+ if (location) {
163
+ options.onUpdate?.({
164
+ items: decorations.map(({ value: { spec: { id: id2 } } }) => id2),
165
+ active: id,
166
+ pos,
167
+ location
168
+ });
169
+ }
170
+ }
171
+ }),
172
+ styles
173
+ ];
174
+ };
175
+
57
176
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
58
177
  import { syntaxTree } from "@codemirror/language";
59
178
  import { RangeSetBuilder, StateField } from "@codemirror/state";
60
- import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
61
- var LinkButton = class extends WidgetType {
179
+ import { Decoration as Decoration2, EditorView as EditorView3, WidgetType as WidgetType2 } from "@codemirror/view";
180
+ var LinkButton = class extends WidgetType2 {
62
181
  constructor(_pos, _url, _onAttach) {
63
182
  super();
64
183
  this._pos = _pos;
@@ -74,7 +193,7 @@ var LinkButton = class extends WidgetType {
74
193
  return el;
75
194
  }
76
195
  };
77
- var LinkText = class extends WidgetType {
196
+ var LinkText = class extends WidgetType2 {
78
197
  constructor(_pos, _text, _url) {
79
198
  super();
80
199
  this._pos = _pos;
@@ -114,18 +233,21 @@ var update = (state, options) => {
114
233
  const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
115
234
  const urlNode = node.node.getChild("URL");
116
235
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
236
+ if (!url) {
237
+ return false;
238
+ }
117
239
  if (cursor < node.from || cursor > node.to) {
118
- builder.add(node.from, node.to, Decoration.replace({
240
+ builder.add(node.from, node.to, Decoration2.replace({
119
241
  widget: new LinkText(node.from, text, options.link ? url : void 0)
120
242
  }));
121
243
  }
122
244
  if (options.onRender) {
123
- builder.add(node.to, node.to, Decoration.replace({
245
+ builder.add(node.to, node.to, Decoration2.replace({
124
246
  widget: new LinkButton(node.from, url, options.onRender)
125
247
  }));
126
248
  }
249
+ return false;
127
250
  }
128
- return true;
129
251
  }
130
252
  });
131
253
  return builder.finish();
@@ -134,7 +256,7 @@ var link = (options = {}) => {
134
256
  return StateField.define({
135
257
  create: (state) => update(state, options),
136
258
  update: (_, tr) => update(tr.state, options),
137
- provide: (field) => EditorView2.decorations.from(field)
259
+ provide: (field) => EditorView3.decorations.from(field)
138
260
  });
139
261
  };
140
262
 
@@ -152,15 +274,14 @@ var listener = (onChange) => StateField2.define({
152
274
 
153
275
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
154
276
  import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
155
- import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
277
+ import { defaultKeymap, history, indentWithTab } from "@codemirror/commands";
156
278
  import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
157
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, foldKeymap, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
279
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
158
280
  import { languages } from "@codemirror/language-data";
159
- import { lintKeymap } from "@codemirror/lint";
160
281
  import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
161
282
  import { EditorState } from "@codemirror/state";
162
283
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
163
- import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView3, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap2, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
284
+ import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap3, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
164
285
 
165
286
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
166
287
  import { markdownLanguage } from "@codemirror/lang-markdown";
@@ -320,10 +441,11 @@ var markdownHighlightStyle = HighlightStyle.define([
320
441
  ],
321
442
  class: codeMark
322
443
  },
323
- // The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
444
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
324
445
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
325
446
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
326
447
  // the code, but all other CSS properties will be inherited.
448
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font.
327
449
  {
328
450
  tag: [
329
451
  markdownTags.CodeText,
@@ -357,8 +479,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
357
479
  closeBrackets2(),
358
480
  _placeholder && placeholder2(_placeholder),
359
481
  EditorState.allowMultipleSelections.of(true),
360
- EditorView3.lineWrapping,
361
- // autocompletion(),
482
+ EditorView4.lineWrapping,
362
483
  crosshairCursor(),
363
484
  dropCursor(),
364
485
  drawSelection(),
@@ -369,13 +490,13 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
369
490
  history(),
370
491
  indentOnInput(),
371
492
  rectangularSelection(),
372
- keymap2.of([
493
+ // TODO(burdon): Review.
494
+ keymap3.of([
373
495
  ...closeBracketsKeymap,
374
- // ...completionKeymap,
375
496
  ...defaultKeymap,
376
- ...foldKeymap,
377
- ...historyKeymap,
378
- ...lintKeymap,
497
+ // ...foldKeymap,
498
+ // ...historyKeymap,
499
+ // ...lintKeymap,
379
500
  ...searchKeymap,
380
501
  indentWithTab
381
502
  ]),
@@ -405,90 +526,84 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
405
526
  };
406
527
 
407
528
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
408
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
409
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
410
- import { EditorView as EditorView4, Decoration as Decoration2, WidgetType as WidgetType2 } from "@codemirror/view";
411
- var CheckboxWidget = class extends WidgetType2 {
412
- constructor(_pos, _getCursor, _checked) {
529
+ import { EditorView as EditorView5, Decoration as Decoration3, WidgetType as WidgetType3, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
530
+ var CheckboxWidget = class extends WidgetType3 {
531
+ constructor(_pos, _checked, _indent, _onCheck) {
413
532
  super();
414
533
  this._pos = _pos;
415
- this._getCursor = _getCursor;
416
534
  this._checked = _checked;
535
+ this._indent = _indent;
536
+ this._onCheck = _onCheck;
417
537
  }
418
538
  eq(other) {
419
- return this._pos === other._pos && this._checked === other._checked;
539
+ return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
420
540
  }
421
541
  toDOM(view) {
422
542
  const wrap = document.createElement("span");
423
- wrap.setAttribute("aria-hidden", "true");
424
543
  wrap.className = "cm-task-item";
425
- const box = wrap.appendChild(document.createElement("input"));
426
- box.type = "checkbox";
427
- box.checked = this._checked;
428
- box.onclick = (event) => {
429
- this._checked = !isChecked(view.state, this._pos);
430
- view.dispatch({
431
- changes: {
432
- from: this._pos + 1,
433
- to: this._pos + 2,
434
- insert: this._checked ? "x" : " "
435
- },
436
- // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
437
- selection: {
438
- anchor: this._pos + 4
439
- }
440
- });
544
+ wrap.setAttribute("aria-hidden", "true");
545
+ wrap.style.setProperty("margin-left", this._indent * 24 + "px");
546
+ const input = wrap.appendChild(document.createElement("input"));
547
+ input.type = "checkbox";
548
+ input.checked = this._checked;
549
+ input.onchange = (event) => {
550
+ this._onCheck(event.target.checked);
441
551
  return true;
442
552
  };
443
553
  return wrap;
444
554
  }
445
- // TODO(burdon): Remove listener?
446
- // override destroy() {
447
- // console.log('destroy', this._pos);
448
- // }
449
555
  ignoreEvent() {
450
556
  return false;
451
557
  }
452
558
  };
453
- var isChecked = (state, pos) => {
454
- return state.sliceDoc(pos, pos + 3).toLowerCase() === "[x]";
455
- };
456
- var statefield = () => {
457
- let lastPosition = 0;
458
- const listener2 = EditorView4.updateListener.of((update2) => {
459
- lastPosition = update2.startState.selection.main.head;
559
+ var styles2 = EditorView5.baseTheme({
560
+ "& .cm-task-item": {
561
+ paddingLeft: "4px",
562
+ paddingRight: "8px"
563
+ }
564
+ });
565
+ var tasklist = () => {
566
+ const taskMatcher = new MatchDecorator2({
567
+ regexp: /^(\s*)- \[([ xX])\]\s/g,
568
+ decoration: (match, view, pos) => {
569
+ const indent = Math.floor(match[1].length / 2);
570
+ const checked = match[2] === "x" || match[2] === "X";
571
+ return Decoration3.replace({
572
+ widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
573
+ const idx = pos + match[0].indexOf("[") + 1;
574
+ view.dispatch({
575
+ changes: {
576
+ from: idx,
577
+ to: idx + 1,
578
+ insert: checked2 ? "x" : " "
579
+ },
580
+ // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
581
+ selection: {
582
+ anchor: idx + 3
583
+ }
584
+ });
585
+ })
586
+ });
587
+ }
460
588
  });
461
- const checkbox = (state) => {
462
- const builder = new RangeSetBuilder2();
463
- syntaxTree2(state).iterate({
464
- enter: (node) => {
465
- if (node.name === "TaskMarker") {
466
- builder.add(node.from, node.to, Decoration2.replace({
467
- widget: new CheckboxWidget(node.from, () => lastPosition, isChecked(state, node.from))
468
- }));
469
- }
470
- }
471
- });
472
- return builder.finish();
473
- };
474
- return [
475
- listener2,
476
- StateField3.define({
477
- create: (state) => checkbox(state),
478
- update: (_, tr) => checkbox(tr.state),
479
- provide: (field) => EditorView4.decorations.from(field)
589
+ const tasks = ViewPlugin2.fromClass(class {
590
+ constructor(view) {
591
+ this.tasks = taskMatcher.createDeco(view);
592
+ }
593
+ update(update2) {
594
+ this.tasks = taskMatcher.updateDeco(update2, this.tasks);
595
+ }
596
+ }, {
597
+ decorations: (instance) => instance.tasks,
598
+ provide: (plugin) => EditorView5.atomicRanges.of((view) => {
599
+ return view.plugin(plugin)?.tasks || Decoration3.none;
480
600
  })
601
+ });
602
+ return [
603
+ tasks,
604
+ styles2
481
605
  ];
482
606
  };
483
- var styles = EditorView4.baseTheme({
484
- "& .cm-task-item": {
485
- padding: "0 4px"
486
- }
487
- });
488
- var tasklist = () => [
489
- statefield(),
490
- styles
491
- ];
492
607
 
493
608
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
494
609
  import { hoverTooltip } from "@codemirror/view";
@@ -677,22 +792,33 @@ var defaultTheme = {
677
792
  var textTheme = {
678
793
  "& .cm-scroller": {
679
794
  fontFamily: get2(tokens, "fontFamily.body", []).join(",")
795
+ },
796
+ "& .cm-placeholder": {
797
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
798
+ }
799
+ };
800
+ var markdownTheme = {
801
+ // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
802
+ "& .cm-placeholder": {
803
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
680
804
  }
681
805
  };
682
806
  var codeTheme = {
683
807
  "& .cm-scroller": {
684
808
  fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
809
+ },
810
+ "& .cm-placeholder": {
811
+ fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
685
812
  }
686
813
  };
687
814
 
688
815
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
689
816
  import { EditorState as EditorState2 } from "@codemirror/state";
690
- import { EditorView as EditorView5 } from "@codemirror/view";
817
+ import { EditorView as EditorView6 } from "@codemirror/view";
691
818
  import { useFocusableGroup } from "@fluentui/react-tabster";
692
819
  import { vim } from "@replit/codemirror-vim";
693
820
  import defaultsDeep from "lodash.defaultsdeep";
694
821
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
695
- import { yCollab } from "y-codemirror.next";
696
822
  import { generateName } from "@dxos/display-name";
697
823
  import { useThemeContext } from "@dxos/react-ui";
698
824
  import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
@@ -705,10 +831,22 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
705
831
  const tabsterDOMAttribute = useFocusableGroup({
706
832
  tabBehavior: "limited"
707
833
  });
708
- const { provider, peer } = model;
834
+ const [root, setRoot] = useState(null);
835
+ const [state, setState] = useState();
836
+ const [view, setView] = useState();
837
+ useImperativeHandle(forwardedRef, () => ({
838
+ root,
839
+ state,
840
+ view
841
+ }), [
842
+ view,
843
+ state,
844
+ root
845
+ ]);
846
+ const { awareness, peer } = model;
709
847
  useEffect(() => {
710
- if (provider && peer) {
711
- provider.awareness.setLocalStateField("user", {
848
+ if (awareness && peer) {
849
+ awareness.setLocalStateField("user", {
712
850
  name: peer.name ?? generateName(peer.id),
713
851
  color: getColorForValue({
714
852
  value: peer.id,
@@ -722,22 +860,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
722
860
  });
723
861
  }
724
862
  }, [
725
- provider,
863
+ awareness,
726
864
  peer,
727
865
  themeMode
728
866
  ]);
729
- const [root, setRoot] = useState(null);
730
- const [state, setState] = useState();
731
- const [view, setView] = useState();
732
- useImperativeHandle(forwardedRef, () => ({
733
- root,
734
- state,
735
- view
736
- }), [
737
- view,
738
- state,
739
- root
740
- ]);
741
867
  useEffect(() => {
742
868
  if (!root) {
743
869
  return;
@@ -748,18 +874,18 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
748
874
  // TODO(burdon): Factor out VIM mode?
749
875
  editorMode === "vim" && vim(),
750
876
  // Theme.
751
- EditorView5.baseTheme(defaultTheme),
752
- EditorView5.theme(slots?.editor?.theme ?? {}),
877
+ EditorView6.baseTheme(defaultTheme),
878
+ EditorView6.theme(slots?.editor?.theme ?? {}),
753
879
  // TODO(burdon): themeMode doesn't change in storybooks.
754
- EditorView5.darkTheme.of(themeMode === "dark"),
880
+ EditorView6.darkTheme.of(themeMode === "dark"),
755
881
  // Storage and replication.
756
- yCollab(model.content, model.provider?.awareness),
882
+ model.extension,
757
883
  // Custom.
758
884
  ...extensions
759
885
  ].filter(Boolean)
760
886
  });
761
887
  view?.destroy();
762
- setView(new EditorView5({
888
+ setView(new EditorView6({
763
889
  state: state2,
764
890
  parent: root
765
891
  }));
@@ -771,7 +897,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
771
897
  };
772
898
  }, [
773
899
  root,
774
- model.content,
900
+ model,
775
901
  themeMode,
776
902
  editorMode
777
903
  ]);
@@ -818,7 +944,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, .
818
944
  });
819
945
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
820
946
  const { themeMode } = useThemeContext();
821
- const slots = defaultsDeep({}, _slots, defaultSlots);
947
+ const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
822
948
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
823
949
  ref: forwardedRef,
824
950
  extensions: [
@@ -843,24 +969,29 @@ var defaultTextSlots = {
843
969
  theme: textTheme
844
970
  }
845
971
  };
972
+ var defaultMarkdownSlots = {
973
+ ...defaultSlots,
974
+ editor: {
975
+ theme: markdownTheme
976
+ }
977
+ };
846
978
 
847
- // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
979
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
980
+ import get3 from "lodash.get";
848
981
  import { useEffect as useEffect2, useState as useState2 } from "react";
849
- import { yCollab as yCollab2 } from "y-codemirror.next";
850
- import { generateName as generateName2 } from "@dxos/display-name";
851
- import { isDocAccessor } from "@dxos/echo-schema";
852
- import { getColorForValue as getColorForValue2 } from "@dxos/react-ui-theme";
853
- import { YText } from "@dxos/text-model";
982
+ import { yCollab } from "y-codemirror.next";
983
+ import { invariant as invariant2 } from "@dxos/invariant";
984
+ import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
854
985
 
855
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
856
- import { Annotation, Facet, StateEffect, StateField as StateField4 } from "@codemirror/state";
857
- import { ViewPlugin } from "@codemirror/view";
986
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
987
+ import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
988
+ import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
858
989
  import * as automerge2 from "@dxos/automerge/automerge";
859
990
 
860
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
991
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
861
992
  import { next as automerge } from "@dxos/automerge/automerge";
862
993
 
863
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
994
+ // packages/ui/react-ui-editor/src/hooks/automerge/amToCodemirror.ts
864
995
  import { ChangeSet } from "@codemirror/state";
865
996
  var amToCodemirror_default = (view, selection, target, patches) => {
866
997
  for (const patch of patches) {
@@ -967,7 +1098,7 @@ var charPath = (textPath, candidatePath) => {
967
1098
  return null;
968
1099
  };
969
1100
 
970
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
1101
+ // packages/ui/react-ui-editor/src/hooks/automerge/codeMirrorToAm.ts
971
1102
  import { next as am } from "@dxos/automerge/automerge";
972
1103
  var codeMirrorToAm_default = (field, handle, transactions, state) => {
973
1104
  const { lastHeads, path } = state.field(field);
@@ -990,7 +1121,7 @@ var codeMirrorToAm_default = (field, handle, transactions, state) => {
990
1121
  return newHeads ?? void 0;
991
1122
  };
992
1123
 
993
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
1124
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
994
1125
  var PatchSemaphore = class {
995
1126
  constructor(field) {
996
1127
  this._inReconcile = false;
@@ -1031,7 +1162,7 @@ var PatchSemaphore = class {
1031
1162
  }
1032
1163
  };
1033
1164
 
1034
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
1165
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1035
1166
  var effectType = StateEffect.define({});
1036
1167
  var updateHeads = (newHeads) => effectType.of({
1037
1168
  newHeads
@@ -1042,7 +1173,7 @@ var semaphoreFacet = Facet.define({
1042
1173
  combine: (values) => values.at(-1)
1043
1174
  });
1044
1175
  var automergePlugin = (handle, path) => {
1045
- const stateField = StateField4.define({
1176
+ const stateField = StateField3.define({
1046
1177
  create: () => ({
1047
1178
  lastHeads: automerge2.getHeads(handle.docSync()),
1048
1179
  unreconciledTransactions: [],
@@ -1072,7 +1203,7 @@ var automergePlugin = (handle, path) => {
1072
1203
  }
1073
1204
  });
1074
1205
  const semaphore = new PatchSemaphore(stateField);
1075
- const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
1206
+ const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
1076
1207
  constructor(view) {
1077
1208
  this._handleChange = () => {
1078
1209
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
@@ -1102,45 +1233,6 @@ var automergePlugin = (handle, path) => {
1102
1233
  var reconcileAnnotationType = Annotation.define();
1103
1234
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1104
1235
 
1105
- // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
1106
- var useCollaboration = (model, themeMode) => {
1107
- const { content, provider, peer } = model;
1108
- const [extension] = useState2(() => {
1109
- if (content instanceof YText) {
1110
- return yCollab2(content, provider?.awareness);
1111
- } else if (isDocAccessor(content)) {
1112
- return automergePlugin(content.handle, content.path);
1113
- } else {
1114
- return void 0;
1115
- }
1116
- });
1117
- useEffect2(() => {
1118
- if (provider && peer) {
1119
- provider.awareness.setLocalStateField("user", {
1120
- name: peer.name ?? generateName2(peer.id),
1121
- color: getColorForValue2({
1122
- value: peer.id,
1123
- type: "color"
1124
- }),
1125
- colorLight: getColorForValue2({
1126
- value: peer.id,
1127
- themeMode,
1128
- type: "highlight"
1129
- })
1130
- });
1131
- }
1132
- }, [
1133
- provider,
1134
- peer,
1135
- themeMode
1136
- ]);
1137
- return extension;
1138
- };
1139
-
1140
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1141
- import { useMemo } from "react";
1142
- import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
1143
-
1144
1236
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1145
1237
  import * as random from "lib0/random";
1146
1238
  var cursorColors = [
@@ -1185,7 +1277,7 @@ import * as encoding from "lib0/encoding";
1185
1277
  import { Observable } from "lib0/observable";
1186
1278
  import * as awarenessProtocol from "y-protocols/awareness";
1187
1279
  import { log } from "@dxos/log";
1188
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1280
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1189
1281
  var messageAwareness = 1;
1190
1282
  var messageQueryAwareness = 3;
1191
1283
  var SpaceAwarenessProvider = class extends Observable {
@@ -1222,7 +1314,7 @@ var SpaceAwarenessProvider = class extends Observable {
1222
1314
  removed,
1223
1315
  origin
1224
1316
  }, {
1225
- F: __dxlog_file,
1317
+ F: __dxlog_file2,
1226
1318
  L: 67,
1227
1319
  S: this,
1228
1320
  C: (f, a) => f(...a)
@@ -1235,7 +1327,7 @@ var SpaceAwarenessProvider = class extends Observable {
1235
1327
  }
1236
1328
  _handleSpaceMessage({ payload }) {
1237
1329
  log("space message", payload, {
1238
- F: __dxlog_file,
1330
+ F: __dxlog_file2,
1239
1331
  L: 79,
1240
1332
  S: this,
1241
1333
  C: (f, a) => f(...a)
@@ -1280,57 +1372,74 @@ var SpaceAwarenessProvider = class extends Observable {
1280
1372
  };
1281
1373
 
1282
1374
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1375
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
1283
1376
  var useTextModel = ({ identity, space, text }) => {
1284
- const provider = useMemo(() => {
1285
- if (isActualAutomergeObject(text)) {
1286
- return void 0;
1287
- }
1288
- if (!space || !text?.doc) {
1289
- return void 0;
1290
- }
1291
- return new SpaceAwarenessProvider({
1377
+ const [model, setModel] = useState2(() => createModel({
1378
+ identity,
1379
+ space,
1380
+ text
1381
+ }));
1382
+ useEffect2(() => {
1383
+ setModel(createModel({
1384
+ identity,
1292
1385
  space,
1293
- doc: text.doc,
1294
- channel: `yjs.awareness.${text.id}`
1295
- });
1386
+ text
1387
+ }));
1296
1388
  }, [
1297
1389
  identity,
1298
1390
  space,
1299
- text,
1300
- text?.doc
1301
- ]);
1302
- const content = useMemo(() => {
1303
- if (isActualAutomergeObject(text)) {
1304
- const obj = text;
1305
- return getRawDoc(obj, [
1306
- obj.field
1307
- ]);
1308
- } else {
1309
- return text?.content;
1310
- }
1311
- }, [
1312
1391
  text
1313
1392
  ]);
1314
- if (!text?.doc || !text?.content) {
1315
- return void 0;
1316
- }
1393
+ return model;
1394
+ };
1395
+ var createModel = (options) => {
1396
+ const { text } = options;
1317
1397
  if (isActualAutomergeObject(text)) {
1318
- const obj = text;
1319
- return {
1320
- id: obj.id,
1321
- content,
1322
- text: () => content.toString(),
1323
- provider: void 0,
1324
- peer: identity ? {
1325
- id: identity.identityKey.toHex(),
1326
- name: identity.profile?.displayName
1327
- } : void 0
1328
- };
1398
+ return createAutomergeModel(options);
1399
+ } else {
1400
+ if (!text?.doc) {
1401
+ return void 0;
1402
+ }
1403
+ return createYjsModel(options);
1329
1404
  }
1405
+ };
1406
+ var createYjsModel = ({ identity, space, text }) => {
1407
+ invariant2(text?.doc && text?.content, void 0, {
1408
+ F: __dxlog_file3,
1409
+ L: 74,
1410
+ S: void 0,
1411
+ A: [
1412
+ "text?.doc && text?.content",
1413
+ ""
1414
+ ]
1415
+ });
1416
+ const provider = space ? new SpaceAwarenessProvider({
1417
+ space,
1418
+ doc: text.doc,
1419
+ channel: `yjs.awareness.${text.id}`
1420
+ }) : void 0;
1330
1421
  return {
1331
1422
  id: text.doc.guid,
1332
1423
  content: text.content,
1333
1424
  text: () => text.content.toString(),
1425
+ extension: yCollab(text.content, provider?.awareness),
1426
+ awareness: provider?.awareness,
1427
+ peer: identity ? {
1428
+ id: identity.identityKey.toHex(),
1429
+ name: identity.profile?.displayName
1430
+ } : void 0
1431
+ };
1432
+ };
1433
+ var createAutomergeModel = ({ identity, text }) => {
1434
+ const obj = text;
1435
+ const doc = getRawDoc(obj, [
1436
+ obj.field
1437
+ ]);
1438
+ return {
1439
+ id: obj.id,
1440
+ content: doc,
1441
+ text: () => get3(doc.handle.docSync(), doc.path),
1442
+ extension: automergePlugin(doc.handle, doc.path),
1334
1443
  peer: identity ? {
1335
1444
  id: identity.identityKey.toHex(),
1336
1445
  name: identity.profile?.displayName
@@ -1345,12 +1454,14 @@ export {
1345
1454
  SpaceAwarenessProvider,
1346
1455
  TextEditor,
1347
1456
  TextKind,
1348
- YText2 as YText,
1457
+ YText,
1349
1458
  YXmlFragment,
1350
1459
  autocomplete,
1351
1460
  basicBundle,
1352
1461
  codeTheme,
1462
+ comments,
1353
1463
  cursorColor,
1464
+ defaultMarkdownSlots,
1354
1465
  defaultSlots,
1355
1466
  defaultTextSlots,
1356
1467
  defaultTheme,
@@ -1360,13 +1471,11 @@ export {
1360
1471
  markdownHighlightStyle,
1361
1472
  markdownTags,
1362
1473
  markdownTagsExtension,
1363
- statefield,
1364
- styles,
1474
+ markdownTheme,
1365
1475
  tags2 as tags,
1366
1476
  tasklist,
1367
1477
  textTheme,
1368
1478
  tooltip,
1369
- useCollaboration,
1370
1479
  useTextModel
1371
1480
  };
1372
1481
  //# sourceMappingURL=index.mjs.map