@dxos/react-ui-editor 0.3.10-main.5515dfe → 0.3.10-main.560f82b

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 (108) hide show
  1. package/dist/lib/browser/index.mjs +1049 -285
  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 +22 -1
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +3 -1
  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/basic.d.ts +2 -1
  11. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
  12. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  13. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  14. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +18 -0
  15. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +11 -0
  17. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
  19. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  20. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  21. package/dist/types/src/components/TextEditor/extensions/index.d.ts +6 -0
  22. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  23. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  24. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +4 -2
  25. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  26. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +2 -1
  27. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  28. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +6 -2
  29. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  30. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  31. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  32. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  33. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  34. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -4
  35. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  36. package/dist/types/src/components/TextEditor/themes/default.d.ts +8 -0
  37. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  38. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.d.ts +3 -0
  39. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -0
  40. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +16 -0
  41. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
  42. package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
  43. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +1 -1
  44. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
  45. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
  46. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +5 -0
  47. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +1 -0
  48. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +6 -0
  49. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +1 -0
  50. package/dist/types/src/hooks/automerge/translation/index.d.ts +3 -0
  51. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +1 -0
  52. package/dist/types/src/hooks/index.d.ts +0 -1
  53. package/dist/types/src/hooks/index.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useTextModel.d.ts +3 -4
  55. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  56. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +1 -1
  57. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  58. package/dist/types/src/styles/markdown.d.ts +3 -2
  59. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  60. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  61. package/package.json +20 -19
  62. package/src/components/TextEditor/MarkdownEditor.stories.tsx +137 -31
  63. package/src/components/TextEditor/TextEditor.tsx +52 -25
  64. package/src/components/TextEditor/extensions/autocomplete.ts +9 -22
  65. package/src/components/TextEditor/extensions/basic.ts +2 -1
  66. package/src/components/TextEditor/extensions/blast.ts +375 -0
  67. package/src/components/TextEditor/extensions/comments.ts +190 -0
  68. package/src/components/TextEditor/extensions/demo.ts +69 -0
  69. package/src/components/TextEditor/extensions/experimental.tsx +1 -0
  70. package/src/components/TextEditor/extensions/image.ts +68 -0
  71. package/src/components/TextEditor/extensions/index.ts +6 -0
  72. package/src/components/TextEditor/extensions/link.ts +25 -25
  73. package/src/components/TextEditor/extensions/listener.ts +4 -3
  74. package/src/components/TextEditor/extensions/markdown/bundle.ts +18 -18
  75. package/src/components/TextEditor/extensions/markdown/highlight.ts +118 -103
  76. package/src/components/TextEditor/extensions/mention.ts +31 -0
  77. package/src/components/TextEditor/extensions/table.ts +123 -0
  78. package/src/components/TextEditor/extensions/tasklist.ts +89 -81
  79. package/src/components/TextEditor/themes/default.ts +69 -2
  80. package/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.ts +12 -9
  81. package/src/{automerge → hooks/automerge}/automerge.stories.tsx +5 -3
  82. package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +1 -0
  83. package/src/{automerge/automerge-plugin/amToCodemirror.ts → hooks/automerge/translation/automerge-to-codemirror.ts} +10 -2
  84. package/src/{automerge/automerge-plugin/codeMirrorToAm.ts → hooks/automerge/translation/codemirror-to-automerge.ts} +7 -4
  85. package/src/hooks/automerge/translation/index.ts +6 -0
  86. package/src/hooks/index.ts +0 -1
  87. package/src/hooks/useTextModel.ts +49 -41
  88. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +1 -1
  89. package/src/styles/markdown.ts +14 -8
  90. package/src/testing/replicator.ts +6 -7
  91. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
  92. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts +0 -6
  93. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
  94. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +0 -7
  95. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
  96. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
  97. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
  98. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
  99. package/dist/types/src/automerge/automerge.stories.d.ts +0 -9
  100. package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
  101. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  102. package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
  103. package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
  104. package/src/hooks/useCollaboration.ts +0 -45
  105. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
  106. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
  107. package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
  108. package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +1 -1
@@ -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
@@ -24,13 +24,13 @@ var autocomplete = ({ getOptions }) => {
24
24
  // TODO(burdon): Optional decoration via addToOptions
25
25
  override: [
26
26
  (context) => {
27
- const word = context.matchBefore(/\w*/);
28
- if (!word || word.from === word.to && !context.explicit) {
27
+ const match = context.matchBefore(/\w*/);
28
+ if (!match || match.from === match.to && !context.explicit) {
29
29
  return null;
30
30
  }
31
31
  return {
32
- from: word.from,
33
- options: getOptions(word.text.toLowerCase())
32
+ from: match.from,
33
+ options: onSearch(match.text.toLowerCase())
34
34
  };
35
35
  }
36
36
  ]
@@ -43,7 +43,7 @@ import { closeBrackets } from "@codemirror/autocomplete";
43
43
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
44
44
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
45
45
  import { EditorView, placeholder } from "@codemirror/view";
46
- var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
46
+ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
47
47
  // TODO(burdon): Compare with minimalSetup.
48
48
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
49
49
  bracketMatching(),
@@ -54,11 +54,571 @@ 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/link.ts
57
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/blast.ts
58
+ import { StateField } from "@codemirror/state";
59
+ import { EditorView as EditorView2, keymap as keymap2 } from "@codemirror/view";
60
+ import defaultsDeep from "lodash.defaultsdeep";
61
+ import { invariant } from "@dxos/invariant";
62
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/blast.ts";
63
+ var defaultOptions = {
64
+ maxParticles: 200,
65
+ particleGravity: 0.08,
66
+ particleAlphaFadeout: 0.996,
67
+ particleSize: {
68
+ min: 2,
69
+ max: 8
70
+ },
71
+ particleNumRange: {
72
+ min: 5,
73
+ max: 10
74
+ },
75
+ particleVelocityRange: {
76
+ x: [
77
+ -1,
78
+ 1
79
+ ],
80
+ y: [
81
+ -3.5,
82
+ -1.5
83
+ ]
84
+ },
85
+ particleShrinkRate: 0.95,
86
+ shakeIntensity: 5
87
+ };
88
+ var blast = (options) => {
89
+ let blaster;
90
+ let last = 0;
91
+ const getPoint = (view, pos) => {
92
+ const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
93
+ const element = document.elementFromPoint(x, y);
94
+ const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
95
+ const point = {
96
+ x: x - left,
97
+ y: y - top
98
+ };
99
+ return {
100
+ element,
101
+ point
102
+ };
103
+ };
104
+ return [
105
+ // Cursor moved.
106
+ EditorView2.updateListener.of((update4) => {
107
+ if (blaster?.node !== update4.view.scrollDOM) {
108
+ if (blaster) {
109
+ blaster.destroy();
110
+ }
111
+ blaster = new Blaster(update4.view.scrollDOM, defaultsDeep({}, options, defaultOptions));
112
+ blaster.initialize();
113
+ blaster.start();
114
+ } else {
115
+ blaster.resize();
116
+ }
117
+ const current = update4.view.state.selection.main.head;
118
+ if (current !== last) {
119
+ last = current;
120
+ const { element, point } = getPoint(update4.view, current);
121
+ if (element && point) {
122
+ blaster.spawn({
123
+ element,
124
+ point
125
+ });
126
+ }
127
+ }
128
+ }),
129
+ // Document changed.
130
+ StateField.define({
131
+ create: () => null,
132
+ update: (_value, transaction) => {
133
+ if (blaster && transaction.docChanged) {
134
+ const view = EditorView2.findFromDOM(blaster.node);
135
+ const { element, point } = getPoint(view, transaction.selection.main.head);
136
+ if (element && point) {
137
+ blaster.spawn({
138
+ element,
139
+ point
140
+ });
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ }),
146
+ keymap2.of([
147
+ {
148
+ any: (view, event) => {
149
+ if (blaster && event.key === "Enter" && event.shiftKey) {
150
+ blaster.shake({
151
+ time: 0.4
152
+ });
153
+ }
154
+ return false;
155
+ }
156
+ }
157
+ ])
158
+ ];
159
+ };
160
+ var Blaster = class {
161
+ constructor(_node, _options) {
162
+ this._node = _node;
163
+ this._options = _options;
164
+ this._running = false;
165
+ this._shakeTime = 0;
166
+ this._shakeTimeMax = 0;
167
+ this._particles = [];
168
+ this._particlePointer = 0;
169
+ this._lastPoint = {
170
+ x: 0,
171
+ y: 0
172
+ };
173
+ this.shake = throttle(({ time }) => {
174
+ this._shakeTime = this._shakeTimeMax;
175
+ this._shakeTimeMax = time;
176
+ }, 100);
177
+ this.spawn = throttle(({ element, point }) => {
178
+ const color = getRGBComponents(element, this._options.color);
179
+ const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
180
+ const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
181
+ this._lastPoint = point;
182
+ for (let i = numParticles; i--; i > 0) {
183
+ this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);
184
+ this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;
185
+ }
186
+ }, 100);
187
+ this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
188
+ }
189
+ get node() {
190
+ return this._node;
191
+ }
192
+ initialize() {
193
+ invariant(!this._canvas && !this._ctx, void 0, {
194
+ F: __dxlog_file,
195
+ L: 140,
196
+ S: this,
197
+ A: [
198
+ "!this._canvas && !this._ctx",
199
+ ""
200
+ ]
201
+ });
202
+ this._canvas = document.createElement("canvas");
203
+ this._canvas.id = "code-blast-canvas";
204
+ this._canvas.style.position = "absolute";
205
+ this._canvas.style.zIndex = "0";
206
+ this._canvas.style.pointerEvents = "none";
207
+ this._ctx = this._canvas.getContext("2d");
208
+ const parent = this._node.parentElement?.parentElement;
209
+ parent?.appendChild(this._canvas);
210
+ this.resize();
211
+ }
212
+ destroy() {
213
+ this.stop();
214
+ if (this._canvas) {
215
+ this._canvas.remove();
216
+ this._canvas = void 0;
217
+ this._ctx = void 0;
218
+ }
219
+ }
220
+ resize() {
221
+ if (this._node.parentElement && this._canvas) {
222
+ const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
223
+ this._canvas.style.top = `${y}px`;
224
+ this._canvas.style.left = `${x}px`;
225
+ this._canvas.width = width;
226
+ this._canvas.height = height;
227
+ }
228
+ }
229
+ start() {
230
+ invariant(this._canvas && this._ctx, void 0, {
231
+ F: __dxlog_file,
232
+ L: 179,
233
+ S: this,
234
+ A: [
235
+ "this._canvas && this._ctx",
236
+ ""
237
+ ]
238
+ });
239
+ this._running = true;
240
+ this.loop();
241
+ }
242
+ stop() {
243
+ this._running = false;
244
+ this._node.style.transform = "translate(0px, 0px)";
245
+ }
246
+ loop() {
247
+ if (!this._running || !this._canvas || !this._ctx) {
248
+ return;
249
+ }
250
+ this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);
251
+ const now = (/* @__PURE__ */ new Date()).getTime();
252
+ this._lastTime ??= now;
253
+ const dt = (now - this._lastTime) / 1e3;
254
+ this._lastTime = now;
255
+ if (this._shakeTime > 0) {
256
+ this._shakeTime -= dt;
257
+ const magnitude = this._shakeTime / this._shakeTimeMax * this._options.shakeIntensity;
258
+ const shakeX = random(-magnitude, magnitude);
259
+ const shakeY = random(-magnitude, magnitude);
260
+ this._node.style.transform = `translate(${shakeX}px,${shakeY}px)`;
261
+ }
262
+ this.drawParticles();
263
+ requestAnimationFrame(this.loop.bind(this));
264
+ }
265
+ drawParticles() {
266
+ for (let i = this._particles.length; i--; i > 0) {
267
+ const particle = this._particles[i];
268
+ if (!particle) {
269
+ continue;
270
+ }
271
+ if (particle.alpha < 0.01 || particle.size <= 0.5) {
272
+ continue;
273
+ }
274
+ this._effect.update(this._ctx, particle);
275
+ }
276
+ }
277
+ };
278
+ var Effect = class {
279
+ constructor(_options) {
280
+ this._options = _options;
281
+ }
282
+ };
283
+ var Effect1 = class extends Effect {
284
+ create(x, y, color) {
285
+ return {
286
+ x,
287
+ y: y + 10,
288
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
289
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
290
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
291
+ color,
292
+ alpha: 1
293
+ };
294
+ }
295
+ update(ctx, particle) {
296
+ particle.vy += this._options.particleGravity;
297
+ particle.x += particle.vx;
298
+ particle.y += particle.vy;
299
+ particle.alpha *= this._options.particleAlphaFadeout;
300
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
301
+ ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);
302
+ }
303
+ };
304
+ var Effect2 = class extends Effect {
305
+ create(x, y, color) {
306
+ return {
307
+ x,
308
+ y: y + 10,
309
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
310
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
311
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
312
+ color,
313
+ alpha: 1,
314
+ theta: random(0, 360) * Math.PI / 180,
315
+ drag: 0.92,
316
+ wander: 0.15
317
+ };
318
+ }
319
+ update(ctx, particle) {
320
+ particle.vy += this._options.particleGravity;
321
+ particle.x += particle.vx;
322
+ particle.y += particle.vy;
323
+ particle.vx *= particle.drag;
324
+ particle.vy *= particle.drag;
325
+ particle.theta += random(-0.5, 0.5);
326
+ particle.vx += Math.sin(particle.theta) * 0.1;
327
+ particle.vy += Math.cos(particle.theta) * 0.1;
328
+ particle.size *= this._options.particleShrinkRate;
329
+ particle.alpha *= this._options.particleAlphaFadeout;
330
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
331
+ ctx.beginPath();
332
+ ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);
333
+ ctx.fill();
334
+ }
335
+ };
336
+ function throttle(callback, limit) {
337
+ let wait = false;
338
+ return function(...args) {
339
+ if (!wait) {
340
+ callback.apply(this, args);
341
+ wait = true;
342
+ setTimeout(() => {
343
+ wait = false;
344
+ }, limit);
345
+ }
346
+ };
347
+ }
348
+ var getRGBComponents = (node, color) => {
349
+ if (typeof color === "function") {
350
+ return color();
351
+ }
352
+ const bgColor = getComputedStyle(node).color;
353
+ if (bgColor) {
354
+ const x = bgColor.match(/(\d+), (\d+), (\d+)/)?.slice(1);
355
+ if (x) {
356
+ return x;
357
+ }
358
+ }
359
+ return [
360
+ 50,
361
+ 50,
362
+ 50
363
+ ];
364
+ };
365
+ var random = (min, max) => {
366
+ if (!max) {
367
+ max = min;
368
+ min = 0;
369
+ }
370
+ return min + ~~(Math.random() * (max - min + 1));
371
+ };
372
+
373
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
374
+ import { keymap as keymap3 } from "@codemirror/view";
375
+ var defaultItems = [
376
+ "hello world!",
377
+ "this is a test.",
378
+ "this is [DXOS](https://dxos.org)"
379
+ ];
380
+ var demo = ({ delay = 75, items = defaultItems } = {}) => {
381
+ let t;
382
+ let idx = 0;
383
+ return [
384
+ keymap3.of([
385
+ {
386
+ // Reset.
387
+ key: "alt-meta-'",
388
+ run: (view) => {
389
+ clearTimeout(t);
390
+ idx = 0;
391
+ return true;
392
+ }
393
+ },
394
+ {
395
+ // Next prompt.
396
+ // TODO(burdon): Press 1-9 to select prompt?
397
+ key: "shift-meta-'",
398
+ run: (view) => {
399
+ clearTimeout(t);
400
+ const text = items[idx++];
401
+ if (idx === items?.length) {
402
+ idx = 0;
403
+ }
404
+ let i = 0;
405
+ const insert = (d = 0) => {
406
+ t = setTimeout(() => {
407
+ const pos = view.state.selection.main.head;
408
+ view.dispatch({
409
+ changes: {
410
+ from: pos,
411
+ insert: text[i++]
412
+ },
413
+ selection: {
414
+ anchor: pos + 1
415
+ }
416
+ });
417
+ if (i < text.length) {
418
+ insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
419
+ }
420
+ }, d);
421
+ };
422
+ insert();
423
+ return true;
424
+ }
425
+ }
426
+ ])
427
+ ];
428
+ };
429
+
430
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
431
+ import { keymap as keymap4, Decoration, EditorView as EditorView3, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
432
+ import { debounce } from "@dxos/async";
433
+ import { invariant as invariant2 } from "@dxos/invariant";
434
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
435
+ var BookmarkWidget = class extends WidgetType {
436
+ constructor(_pos, _id) {
437
+ super();
438
+ this._pos = _pos;
439
+ this._id = _id;
440
+ invariant2(this._id, void 0, {
441
+ F: __dxlog_file2,
442
+ L: 41,
443
+ S: this,
444
+ A: [
445
+ "this._id",
446
+ ""
447
+ ]
448
+ });
449
+ }
450
+ eq(other) {
451
+ return this._id === other._id;
452
+ }
453
+ toDOM() {
454
+ const span = document.createElement("span");
455
+ span.className = "cm-bookmark";
456
+ span.textContent = "\u{1F4AC}";
457
+ return span;
458
+ }
459
+ };
460
+ var styles = EditorView3.baseTheme({
461
+ "& .cm-bookmark": {
462
+ cursor: "pointer",
463
+ margin: "4px",
464
+ padding: "4px",
465
+ backgroundColor: "yellow"
466
+ },
467
+ "& .cm-bookmark-selected": {
468
+ backgroundColor: "orange"
469
+ }
470
+ });
471
+ var comments = (options = {}) => {
472
+ let active;
473
+ const bookmarkMatcher = new MatchDecorator({
474
+ regexp: /\[\^(\w+)\]/g,
475
+ decoration: (match, view, pos) => {
476
+ const id = match[1];
477
+ return Decoration.replace({
478
+ id,
479
+ widget: new BookmarkWidget(pos, id)
480
+ });
481
+ }
482
+ });
483
+ const bookmarks = ViewPlugin.fromClass(class {
484
+ constructor(view) {
485
+ this.bookmarks = bookmarkMatcher.createDeco(view);
486
+ }
487
+ update(update4) {
488
+ this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
489
+ }
490
+ }, {
491
+ decorations: (instance) => instance.bookmarks,
492
+ provide: (plugin) => EditorView3.atomicRanges.of((view) => {
493
+ return view.plugin(plugin)?.bookmarks || Decoration.none;
494
+ })
495
+ });
496
+ const doUpdate = debounce((data) => {
497
+ options.onUpdate?.(data);
498
+ }, 200);
499
+ return [
500
+ keymap4.of([
501
+ {
502
+ key: options?.key ?? "shift-meta-c",
503
+ run: (view) => {
504
+ const id = options.onCreate?.();
505
+ if (id) {
506
+ const pos = view.state.selection.main.head;
507
+ const tag = `[^${id}]`;
508
+ view.dispatch({
509
+ changes: {
510
+ from: pos,
511
+ insert: tag
512
+ },
513
+ selection: {
514
+ anchor: pos + tag.length
515
+ }
516
+ });
517
+ return true;
518
+ }
519
+ return false;
520
+ }
521
+ }
522
+ ]),
523
+ bookmarks,
524
+ // Monitor cursor movement.
525
+ EditorView3.updateListener.of((update4) => {
526
+ active = void 0;
527
+ if (!options.onUpdate) {
528
+ return;
529
+ }
530
+ const view = update4.view;
531
+ const pos = view.state.selection.main.head;
532
+ const decorations = [];
533
+ const rangeSet = view.plugin(bookmarks)?.bookmarks;
534
+ let closest = Infinity;
535
+ const { from, to } = view.visibleRanges?.[0] ?? {
536
+ from: 0,
537
+ to: view.state.doc.length
538
+ };
539
+ rangeSet?.between(from, to, (from2, to2, value) => {
540
+ decorations.push({
541
+ from: from2,
542
+ to: to2,
543
+ value
544
+ });
545
+ const d = Math.min(Math.abs(pos - from2), Math.abs(pos - to2));
546
+ if (d < closest) {
547
+ active = value.spec.id;
548
+ closest = d;
549
+ }
550
+ });
551
+ if (decorations.length) {
552
+ doUpdate({
553
+ active,
554
+ items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
555
+ id,
556
+ pos: from2,
557
+ location: view.coordsAtPos(from2)
558
+ }))
559
+ });
560
+ }
561
+ }),
562
+ styles
563
+ ];
564
+ };
565
+
566
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/image.ts
58
567
  import { syntaxTree } from "@codemirror/language";
59
- import { RangeSetBuilder, StateField } from "@codemirror/state";
60
- import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
61
- var LinkButton = class extends WidgetType {
568
+ import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
569
+ import { Decoration as Decoration2, EditorView as EditorView4, WidgetType as WidgetType2 } from "@codemirror/view";
570
+ var image = (options = {}) => {
571
+ return StateField2.define({
572
+ create: (state) => update(state, options),
573
+ update: (_, tr) => update(tr.state, options),
574
+ provide: (field) => EditorView4.decorations.from(field)
575
+ });
576
+ };
577
+ var update = (state, options) => {
578
+ const builder = new RangeSetBuilder();
579
+ syntaxTree(state).iterate({
580
+ enter: (node) => {
581
+ if (node.name === "Image") {
582
+ const urlNode = node.node.getChild("URL");
583
+ if (urlNode) {
584
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
585
+ builder.add(state.readOnly ? node.from : node.to, node.to, Decoration2.widget({
586
+ widget: new ImageWidget(url)
587
+ }));
588
+ }
589
+ }
590
+ }
591
+ });
592
+ return builder.finish();
593
+ };
594
+ var ImageWidget = class extends WidgetType2 {
595
+ constructor(_url) {
596
+ super();
597
+ this._url = _url;
598
+ }
599
+ eq(other) {
600
+ return this._url === other?._url;
601
+ }
602
+ toDOM(view) {
603
+ const img = document.createElement("img");
604
+ img.setAttribute("src", this._url);
605
+ img.setAttribute("class", "cm-image");
606
+ return img;
607
+ }
608
+ };
609
+
610
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
611
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
612
+ import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
613
+ import { Decoration as Decoration3, EditorView as EditorView5, WidgetType as WidgetType3 } from "@codemirror/view";
614
+ var link = (options = {}) => {
615
+ return StateField3.define({
616
+ create: (state) => update2(state, options),
617
+ update: (_, tr) => update2(tr.state, options),
618
+ provide: (field) => EditorView5.decorations.from(field)
619
+ });
620
+ };
621
+ var LinkButton = class extends WidgetType3 {
62
622
  constructor(_pos, _url, _onAttach) {
63
623
  super();
64
624
  this._pos = _pos;
@@ -74,7 +634,7 @@ var LinkButton = class extends WidgetType {
74
634
  return el;
75
635
  }
76
636
  };
77
- var LinkText = class extends WidgetType {
637
+ var LinkText = class extends WidgetType3 {
78
638
  constructor(_pos, _text, _url) {
79
639
  super();
80
640
  this._pos = _pos;
@@ -104,79 +664,99 @@ var LinkText = class extends WidgetType {
104
664
  return link2;
105
665
  }
106
666
  };
107
- var update = (state, options) => {
108
- const builder = new RangeSetBuilder();
667
+ var update2 = (state, options) => {
668
+ const builder = new RangeSetBuilder2();
109
669
  const cursor = state.selection.main.head;
110
- syntaxTree(state).iterate({
670
+ syntaxTree2(state).iterate({
111
671
  enter: (node) => {
112
672
  if (node.name === "Link") {
113
673
  const marks = node.node.getChildren("LinkMark");
114
674
  const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
115
675
  const urlNode = node.node.getChild("URL");
116
676
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
677
+ if (!url) {
678
+ return false;
679
+ }
117
680
  if (cursor < node.from || cursor > node.to) {
118
- builder.add(node.from, node.to, Decoration.replace({
681
+ builder.add(node.from, node.to, Decoration3.replace({
119
682
  widget: new LinkText(node.from, text, options.link ? url : void 0)
120
683
  }));
121
684
  }
122
685
  if (options.onRender) {
123
- builder.add(node.to, node.to, Decoration.replace({
686
+ builder.add(node.to, node.to, Decoration3.replace({
124
687
  widget: new LinkButton(node.from, url, options.onRender)
125
688
  }));
126
689
  }
690
+ return false;
127
691
  }
128
- return true;
129
692
  }
130
693
  });
131
694
  return builder.finish();
132
695
  };
133
- var link = (options = {}) => {
134
- return StateField.define({
135
- create: (state) => update(state, options),
136
- update: (_, tr) => update(tr.state, options),
137
- provide: (field) => EditorView2.decorations.from(field)
138
- });
139
- };
140
696
 
141
697
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/listener.ts
142
- import { StateField as StateField2 } from "@codemirror/state";
143
- var listener = (onChange) => StateField2.define({
144
- create: () => null,
145
- update: (_value, transaction) => {
146
- if (transaction.docChanged && onChange) {
147
- onChange(transaction.newDoc.toString());
698
+ import { StateField as StateField4 } from "@codemirror/state";
699
+ var listener = ({ onChange }) => {
700
+ return StateField4.define({
701
+ create: () => null,
702
+ update: (_value, transaction) => {
703
+ if (transaction.docChanged && onChange) {
704
+ onChange(transaction.newDoc.toString());
705
+ }
706
+ return null;
148
707
  }
149
- return null;
150
- }
151
- });
708
+ });
709
+ };
152
710
 
153
711
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
154
712
  import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
155
713
  import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
156
714
  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";
715
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
158
716
  import { languages } from "@codemirror/language-data";
159
- import { lintKeymap } from "@codemirror/lint";
160
717
  import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
161
718
  import { EditorState } from "@codemirror/state";
162
719
  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";
720
+ import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView6, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap5, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
164
721
 
165
722
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
166
723
  import { markdownLanguage } from "@codemirror/lang-markdown";
167
724
  import { HighlightStyle } from "@codemirror/language";
168
725
  import { tags, styleTags, Tag } from "@lezer/highlight";
169
726
  import get from "lodash.get";
727
+ import { mx as mx2 } from "@dxos/react-ui-theme";
170
728
 
171
729
  // packages/ui/react-ui-editor/src/styles/markdown.ts
172
730
  import { mx } from "@dxos/react-ui-theme";
173
- var heading = {
174
- 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
175
- 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
176
- 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
177
- 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
178
- 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
179
- 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
731
+ var headings = {
732
+ 1: [
733
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
734
+ "-ml-1.5"
735
+ ],
736
+ 2: [
737
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
738
+ "-ml-1.5"
739
+ ],
740
+ 3: [
741
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
742
+ "-ml-1.5"
743
+ ],
744
+ 4: [
745
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
746
+ "-ml-[5px]"
747
+ ],
748
+ 5: [
749
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
750
+ "-ml-[5px]"
751
+ ],
752
+ 6: [
753
+ "mbs-4 mbe-2 font-medium text-inherit no-underline",
754
+ "-ml-[4px]"
755
+ ]
756
+ };
757
+ var heading = (level, readonly) => {
758
+ const [style, offset] = headings[level];
759
+ return mx(style, readonly && offset);
180
760
  };
181
761
  var light = "text-neutral-200 dark:text-neutral-800";
182
762
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -187,7 +767,7 @@ var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
187
767
  var codeMark = "font-mono text-primary-500";
188
768
  var inlineUrl = mx(code, "px-1");
189
769
  var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
190
- var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-500/50";
770
+ var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800";
191
771
 
192
772
  // packages/ui/react-ui-editor/src/styles/tokens.ts
193
773
  import { tailwindConfig } from "@dxos/react-ui-theme";
@@ -213,7 +793,7 @@ var markdownTagsExtension = {
213
793
  styleTags(markdownTags)
214
794
  ]
215
795
  };
216
- var markdownHighlightStyle = HighlightStyle.define([
796
+ var markdownHighlightStyle = (readonly) => HighlightStyle.define([
217
797
  {
218
798
  tag: [
219
799
  tags.keyword,
@@ -257,40 +837,54 @@ var markdownHighlightStyle = HighlightStyle.define([
257
837
  tag: [
258
838
  tags.meta,
259
839
  tags.processingInstruction,
260
- markdownTags.CodeMark,
261
- markdownTags.EmphasisMark,
262
- markdownTags.HeaderMark,
263
840
  markdownTags.LinkLabel,
264
841
  markdownTags.LinkReference,
265
- markdownTags.ListMark,
266
- markdownTags.QuoteMark
842
+ markdownTags.ListMark
267
843
  ],
268
844
  class: mark
269
845
  },
846
+ // Markdown marks.
847
+ {
848
+ tag: [
849
+ markdownTags.CodeMark,
850
+ markdownTags.HeaderMark,
851
+ markdownTags.QuoteMark,
852
+ markdownTags.EmphasisMark
853
+ ],
854
+ class: readonly ? "hidden" : mark
855
+ },
856
+ // E.g., code block language (after ```).
857
+ {
858
+ tag: [
859
+ tags.function(tags.variableName),
860
+ tags.labelName
861
+ ],
862
+ class: readonly ? "hidden" : codeMark
863
+ },
270
864
  // Headings.
271
865
  {
272
866
  tag: tags.heading1,
273
- class: heading[1]
867
+ class: heading(1, readonly)
274
868
  },
275
869
  {
276
870
  tag: tags.heading2,
277
- class: heading[2]
871
+ class: heading(2, readonly)
278
872
  },
279
873
  {
280
874
  tag: tags.heading3,
281
- class: heading[3]
875
+ class: heading(3, readonly)
282
876
  },
283
877
  {
284
878
  tag: tags.heading4,
285
- class: heading[4]
879
+ class: heading(4, readonly)
286
880
  },
287
881
  {
288
882
  tag: tags.heading5,
289
- class: heading[5]
883
+ class: heading(5, readonly)
290
884
  },
291
885
  {
292
886
  tag: tags.heading6,
293
- class: heading[6]
887
+ class: heading(6, readonly)
294
888
  },
295
889
  // Emphasis.
296
890
  {
@@ -312,18 +906,11 @@ var markdownHighlightStyle = HighlightStyle.define([
312
906
  ],
313
907
  class: inlineUrl
314
908
  },
315
- // E.g., code block language (after ```).
316
- {
317
- tag: [
318
- tags.function(tags.variableName),
319
- tags.labelName
320
- ],
321
- class: codeMark
322
- },
323
- // The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
909
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
324
910
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
325
911
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
326
912
  // the code, but all other CSS properties will be inherited.
913
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font.
327
914
  {
328
915
  tag: [
329
916
  markdownTags.CodeText,
@@ -341,7 +928,7 @@ var markdownHighlightStyle = HighlightStyle.define([
341
928
  tag: [
342
929
  markdownTags.HorizontalRule
343
930
  ],
344
- class: horizontalRule
931
+ class: mx2(horizontalRule, readonly && "-indent-[100rem]")
345
932
  }
346
933
  ], {
347
934
  scope: markdownLanguage,
@@ -351,14 +938,13 @@ var markdownHighlightStyle = HighlightStyle.define([
351
938
  });
352
939
 
353
940
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
354
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
941
+ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
355
942
  return [
356
943
  bracketMatching2(),
357
944
  closeBrackets2(),
358
945
  _placeholder && placeholder2(_placeholder),
359
946
  EditorState.allowMultipleSelections.of(true),
360
- EditorView3.lineWrapping,
361
- // autocompletion(),
947
+ EditorView6.lineWrapping,
362
948
  crosshairCursor(),
363
949
  dropCursor(),
364
950
  drawSelection(),
@@ -369,14 +955,17 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
369
955
  history(),
370
956
  indentOnInput(),
371
957
  rectangularSelection(),
372
- keymap2.of([
373
- ...closeBracketsKeymap,
374
- // ...completionKeymap,
958
+ // https://codemirror.net/docs/ref/#view.keymap
959
+ keymap5.of([
960
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
375
961
  ...defaultKeymap,
376
- ...foldKeymap,
962
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
377
963
  ...historyKeymap,
378
- ...lintKeymap,
964
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
965
+ ...closeBracketsKeymap,
966
+ // https://codemirror.net/docs/ref/#search.searchKeymap
379
967
  ...searchKeymap,
968
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
380
969
  indentWithTab
381
970
  ]),
382
971
  // Main extension.
@@ -397,98 +986,213 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
397
986
  markdownTagsExtension
398
987
  ]
399
988
  }),
989
+ // https://github.com/codemirror/theme-one-dark
990
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
400
991
  // Custom styles.
401
- syntaxHighlighting2(markdownHighlightStyle),
402
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
403
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
992
+ syntaxHighlighting2(markdownHighlightStyle(readonly))
404
993
  ].filter(Boolean);
405
994
  };
406
995
 
996
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/mention.ts
997
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
998
+ var mention = ({ onSearch }) => {
999
+ return autocompletion2({
1000
+ closeOnBlur: false,
1001
+ override: [
1002
+ (context) => {
1003
+ const match = context.matchBefore(/@(\w+)?/);
1004
+ if (!match || match.from === match.to && !context.explicit) {
1005
+ return null;
1006
+ }
1007
+ console.log(match);
1008
+ return {
1009
+ from: match.from,
1010
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
1011
+ label: `@${value}`
1012
+ }))
1013
+ };
1014
+ }
1015
+ ]
1016
+ });
1017
+ };
1018
+
1019
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/table.ts
1020
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1021
+ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField5 } from "@codemirror/state";
1022
+ import { Decoration as Decoration4, EditorView as EditorView7, WidgetType as WidgetType4 } from "@codemirror/view";
1023
+ var table = (options = {}) => {
1024
+ return StateField5.define({
1025
+ create: (state) => update3(state, options),
1026
+ update: (_, tr) => update3(tr.state, options),
1027
+ provide: (field) => EditorView7.decorations.from(field)
1028
+ });
1029
+ };
1030
+ var update3 = (state, options) => {
1031
+ const builder = new RangeSetBuilder3();
1032
+ if (state.readOnly) {
1033
+ const tables = [];
1034
+ const getTable = () => tables[tables.length - 1];
1035
+ const getRow = () => {
1036
+ const table2 = getTable();
1037
+ return table2.rows?.[table2.rows.length - 1];
1038
+ };
1039
+ syntaxTree3(state).iterate({
1040
+ enter: (node) => {
1041
+ switch (node.name) {
1042
+ case "Table": {
1043
+ tables.push({
1044
+ from: node.from
1045
+ });
1046
+ break;
1047
+ }
1048
+ case "TableHeader": {
1049
+ getTable().header = [];
1050
+ break;
1051
+ }
1052
+ case "TableRow": {
1053
+ (getTable().rows ??= []).push([]);
1054
+ break;
1055
+ }
1056
+ case "TableCell": {
1057
+ const row = getRow();
1058
+ if (row) {
1059
+ row.push(state.sliceDoc(node.from, node.to));
1060
+ } else {
1061
+ getTable().header?.push(state.sliceDoc(node.from, node.to));
1062
+ }
1063
+ break;
1064
+ }
1065
+ case "TableDelimiter": {
1066
+ getTable().to = node.to;
1067
+ break;
1068
+ }
1069
+ }
1070
+ }
1071
+ });
1072
+ tables.forEach((table2) => builder.add(table2.from, table2.to, Decoration4.replace({
1073
+ widget: new TableWidget(table2)
1074
+ })));
1075
+ }
1076
+ return builder.finish();
1077
+ };
1078
+ var TableWidget = class extends WidgetType4 {
1079
+ constructor(_table) {
1080
+ super();
1081
+ this._table = _table;
1082
+ }
1083
+ eq(other) {
1084
+ return this._table.from === other?._table?.from;
1085
+ }
1086
+ toDOM(view) {
1087
+ const table2 = document.createElement("table");
1088
+ {
1089
+ const header = table2.appendChild(document.createElement("thead"));
1090
+ const row = header.appendChild(document.createElement("tr"));
1091
+ this._table.header?.forEach((cell) => {
1092
+ const th = document.createElement("th");
1093
+ th.setAttribute("class", "cm-table-head");
1094
+ row.appendChild(th).textContent = cell;
1095
+ });
1096
+ }
1097
+ {
1098
+ const body = table2.appendChild(document.createElement("tbody"));
1099
+ this._table.rows?.forEach((row) => {
1100
+ const tr = body.appendChild(document.createElement("tr"));
1101
+ row.forEach((cell) => {
1102
+ const td = document.createElement("td");
1103
+ td.setAttribute("class", "cm-table-cell");
1104
+ tr.appendChild(td).textContent = cell;
1105
+ });
1106
+ });
1107
+ }
1108
+ return table2;
1109
+ }
1110
+ };
1111
+
407
1112
  // 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) {
1113
+ import { EditorView as EditorView8, Decoration as Decoration5, WidgetType as WidgetType5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
1114
+ var CheckboxWidget = class extends WidgetType5 {
1115
+ constructor(_pos, _checked, _indent, _onCheck) {
413
1116
  super();
414
1117
  this._pos = _pos;
415
- this._getCursor = _getCursor;
416
1118
  this._checked = _checked;
1119
+ this._indent = _indent;
1120
+ this._onCheck = _onCheck;
417
1121
  }
418
1122
  eq(other) {
419
- return this._pos === other._pos && this._checked === other._checked;
1123
+ return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
420
1124
  }
421
1125
  toDOM(view) {
422
1126
  const wrap = document.createElement("span");
423
- wrap.setAttribute("aria-hidden", "true");
424
1127
  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
- });
441
- return true;
442
- };
1128
+ wrap.setAttribute("aria-hidden", "true");
1129
+ wrap.style.setProperty("margin-left", this._indent * 24 + "px");
1130
+ const input = wrap.appendChild(document.createElement("input"));
1131
+ input.type = "checkbox";
1132
+ input.checked = this._checked;
1133
+ if (!this._onCheck) {
1134
+ input.setAttribute("disabled", "true");
1135
+ }
1136
+ if (this._onCheck) {
1137
+ input.onchange = (event) => {
1138
+ this._onCheck?.(event.target.checked);
1139
+ return true;
1140
+ };
1141
+ }
443
1142
  return wrap;
444
1143
  }
445
- // TODO(burdon): Remove listener?
446
- // override destroy() {
447
- // console.log('destroy', this._pos);
448
- // }
449
1144
  ignoreEvent() {
450
1145
  return false;
451
1146
  }
452
1147
  };
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;
1148
+ var styles2 = EditorView8.baseTheme({
1149
+ "& .cm-task-item": {
1150
+ paddingLeft: "4px",
1151
+ paddingRight: "8px"
1152
+ }
1153
+ });
1154
+ var tasklist = (options = {}) => {
1155
+ const taskMatcher = new MatchDecorator2({
1156
+ regexp: /^(\s*)- \[([ xX])\]\s/g,
1157
+ decoration: (match, view, pos) => {
1158
+ const indent = Math.floor(match[1].length / 2);
1159
+ const checked = match[2] === "x" || match[2] === "X";
1160
+ return Decoration5.replace({
1161
+ widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
1162
+ const idx = pos + match[0].indexOf("[") + 1;
1163
+ view.dispatch({
1164
+ changes: {
1165
+ from: idx,
1166
+ to: idx + 1,
1167
+ insert: checked2 ? "x" : " "
1168
+ },
1169
+ // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
1170
+ selection: {
1171
+ anchor: idx + 3
1172
+ }
1173
+ });
1174
+ })
1175
+ });
1176
+ }
460
1177
  });
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)
1178
+ const tasks = ViewPlugin2.fromClass(class {
1179
+ constructor(view) {
1180
+ this.tasks = taskMatcher.createDeco(view);
1181
+ }
1182
+ update(update4) {
1183
+ this.tasks = taskMatcher.updateDeco(update4, this.tasks);
1184
+ }
1185
+ }, {
1186
+ decorations: (instance) => instance.tasks,
1187
+ provide: (plugin) => EditorView8.atomicRanges.of((view) => {
1188
+ return view.plugin(plugin)?.tasks || Decoration5.none;
480
1189
  })
1190
+ });
1191
+ return [
1192
+ tasks,
1193
+ styles2
481
1194
  ];
482
1195
  };
483
- var styles = EditorView4.baseTheme({
484
- "& .cm-task-item": {
485
- padding: "0 4px"
486
- }
487
- });
488
- var tasklist = () => [
489
- statefield(),
490
- styles
491
- ];
492
1196
 
493
1197
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
494
1198
  import { hoverTooltip } from "@codemirror/view";
@@ -648,8 +1352,51 @@ var defaultTheme = {
648
1352
  "& .cm-tooltip": {
649
1353
  border: "none"
650
1354
  },
651
- // '& .cm-tooltip-below': {},
652
- // '& .cm-tooltip-autocomplete': {},
1355
+ "& .cm-tooltip-below": {},
1356
+ //
1357
+ // autocomplete
1358
+ //
1359
+ "& .cm-tooltip-autocomplete": {
1360
+ marginTop: "4px",
1361
+ marginLeft: "-3px"
1362
+ },
1363
+ "& .cm-tooltip-autocomplete ul li": {},
1364
+ "& .cm-tooltip-autocomplete ul li[aria-selected]": {},
1365
+ "& .cm-completionIcon": {
1366
+ display: "none"
1367
+ },
1368
+ "& .cm-completionLabel": {
1369
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1370
+ },
1371
+ "& .cm-completionMatchedText": {
1372
+ textDecoration: "none"
1373
+ },
1374
+ //
1375
+ // widgets
1376
+ //
1377
+ "& .cm-widgetBuffer": {
1378
+ display: "none",
1379
+ height: 0
1380
+ },
1381
+ //
1382
+ // table
1383
+ //
1384
+ "& .cm-table-head": {
1385
+ padding: "2px 16px 2px 0px",
1386
+ borderBottom: `1px solid ${get2(tokens, "extend.colors.neutral.500")}`,
1387
+ fontWeight: 100,
1388
+ textAlign: "left",
1389
+ color: get2(tokens, "extend.colors.neutral.500")
1390
+ },
1391
+ "& .cm-table-cell": {
1392
+ padding: "2px 16px 2px 0px"
1393
+ },
1394
+ //
1395
+ // image
1396
+ //
1397
+ "& .cm-image": {
1398
+ margin: "0.5rem 0"
1399
+ },
653
1400
  //
654
1401
  // font size
655
1402
  // 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.
@@ -677,38 +1424,61 @@ var defaultTheme = {
677
1424
  var textTheme = {
678
1425
  "& .cm-scroller": {
679
1426
  fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1427
+ },
1428
+ "& .cm-placeholder": {
1429
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1430
+ }
1431
+ };
1432
+ var markdownTheme = {
1433
+ // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
1434
+ "& .cm-placeholder": {
1435
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
680
1436
  }
681
1437
  };
682
1438
  var codeTheme = {
683
1439
  "& .cm-scroller": {
684
1440
  fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1441
+ },
1442
+ "& .cm-placeholder": {
1443
+ fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
685
1444
  }
686
1445
  };
687
1446
 
688
1447
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
689
1448
  import { EditorState as EditorState2 } from "@codemirror/state";
690
- import { EditorView as EditorView5 } from "@codemirror/view";
1449
+ import { EditorView as EditorView9 } from "@codemirror/view";
691
1450
  import { useFocusableGroup } from "@fluentui/react-tabster";
692
1451
  import { vim } from "@replit/codemirror-vim";
693
- import defaultsDeep from "lodash.defaultsdeep";
1452
+ import defaultsDeep2 from "lodash.defaultsdeep";
694
1453
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
695
- import { yCollab } from "y-codemirror.next";
696
1454
  import { generateName } from "@dxos/display-name";
697
1455
  import { useThemeContext } from "@dxos/react-ui";
698
- import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
1456
+ import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
699
1457
  var EditorModes = [
700
1458
  "default",
701
1459
  "vim"
702
1460
  ];
703
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
1461
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
704
1462
  const { themeMode } = useThemeContext();
705
1463
  const tabsterDOMAttribute = useFocusableGroup({
706
1464
  tabBehavior: "limited"
707
1465
  });
708
- const { provider, peer } = model;
1466
+ const [root, setRoot] = useState(null);
1467
+ const [state, setState] = useState();
1468
+ const [view, setView] = useState();
1469
+ useImperativeHandle(forwardedRef, () => ({
1470
+ root,
1471
+ state,
1472
+ view
1473
+ }), [
1474
+ view,
1475
+ state,
1476
+ root
1477
+ ]);
1478
+ const { awareness, peer } = model;
709
1479
  useEffect(() => {
710
- if (provider && peer) {
711
- provider.awareness.setLocalStateField("user", {
1480
+ if (awareness && peer) {
1481
+ awareness.setLocalStateField("user", {
712
1482
  name: peer.name ?? generateName(peer.id),
713
1483
  color: getColorForValue({
714
1484
  value: peer.id,
@@ -722,22 +1492,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
722
1492
  });
723
1493
  }
724
1494
  }, [
725
- provider,
1495
+ awareness,
726
1496
  peer,
727
1497
  themeMode
728
1498
  ]);
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
1499
  useEffect(() => {
742
1500
  if (!root) {
743
1501
  return;
@@ -745,21 +1503,22 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
745
1503
  const state2 = EditorState2.create({
746
1504
  doc: model.text(),
747
1505
  extensions: [
1506
+ readonly && EditorState2.readOnly.of(readonly),
748
1507
  // TODO(burdon): Factor out VIM mode?
749
1508
  editorMode === "vim" && vim(),
750
1509
  // Theme.
751
- EditorView5.baseTheme(defaultTheme),
752
- EditorView5.theme(slots?.editor?.theme ?? {}),
1510
+ EditorView9.baseTheme(defaultTheme),
1511
+ EditorView9.theme(slots?.editor?.theme ?? {}),
753
1512
  // TODO(burdon): themeMode doesn't change in storybooks.
754
- EditorView5.darkTheme.of(themeMode === "dark"),
1513
+ EditorView9.darkTheme.of(themeMode === "dark"),
755
1514
  // Storage and replication.
756
- yCollab(model.content, model.provider?.awareness),
1515
+ model.extension,
757
1516
  // Custom.
758
1517
  ...extensions
759
1518
  ].filter(Boolean)
760
1519
  });
761
1520
  view?.destroy();
762
- setView(new EditorView5({
1521
+ setView(new EditorView9({
763
1522
  state: state2,
764
1523
  parent: root
765
1524
  }));
@@ -771,8 +1530,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
771
1530
  };
772
1531
  }, [
773
1532
  root,
774
- model.content,
1533
+ model,
775
1534
  themeMode,
1535
+ readonly,
776
1536
  editorMode
777
1537
  ]);
778
1538
  const handleKeyUp = useCallback((event) => {
@@ -800,32 +1560,47 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
800
1560
  onKeyUp: handleKeyUp
801
1561
  });
802
1562
  });
803
- var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1563
+ var maybeDebug = () => {
1564
+ const items = localStorage.getItem("dxos.composer.demo");
1565
+ if (items) {
1566
+ return demo({
1567
+ items: items.split(",")
1568
+ });
1569
+ }
1570
+ return [];
1571
+ };
1572
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
804
1573
  const { themeMode } = useThemeContext();
805
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
1574
+ const slots = defaultsDeep2({}, _slots, defaultTextSlots);
806
1575
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
807
1576
  ref: forwardedRef,
1577
+ readonly,
808
1578
  extensions: [
809
1579
  basicBundle({
1580
+ readonly,
810
1581
  themeMode,
811
1582
  placeholder: slots?.editor?.placeholder
812
1583
  }),
1584
+ maybeDebug(),
813
1585
  ...extensions
814
1586
  ],
815
1587
  slots,
816
1588
  ...props
817
1589
  });
818
1590
  });
819
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1591
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
820
1592
  const { themeMode } = useThemeContext();
821
- const slots = defaultsDeep({}, _slots, defaultSlots);
1593
+ const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
822
1594
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
823
1595
  ref: forwardedRef,
1596
+ readonly,
824
1597
  extensions: [
825
1598
  markdownBundle({
1599
+ readonly,
826
1600
  themeMode,
827
1601
  placeholder: slots?.editor?.placeholder
828
1602
  }),
1603
+ maybeDebug(),
829
1604
  ...extensions
830
1605
  ],
831
1606
  slots,
@@ -834,7 +1609,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
834
1609
  });
835
1610
  var defaultSlots = {
836
1611
  root: {
837
- className: mx2("p-2", inputSurface)
1612
+ className: mx3("p-2", inputSurface)
838
1613
  }
839
1614
  };
840
1615
  var defaultTextSlots = {
@@ -843,26 +1618,31 @@ var defaultTextSlots = {
843
1618
  theme: textTheme
844
1619
  }
845
1620
  };
1621
+ var defaultMarkdownSlots = {
1622
+ ...defaultSlots,
1623
+ editor: {
1624
+ theme: markdownTheme
1625
+ }
1626
+ };
846
1627
 
847
- // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
1628
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1629
+ import get3 from "lodash.get";
848
1630
  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";
1631
+ import { yCollab } from "y-codemirror.next";
1632
+ import { invariant as invariant3 } from "@dxos/invariant";
1633
+ import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
854
1634
 
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";
1635
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1636
+ import { Annotation, Facet, StateEffect, StateField as StateField6 } from "@codemirror/state";
1637
+ import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
858
1638
  import * as automerge2 from "@dxos/automerge/automerge";
859
1639
 
860
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
1640
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
861
1641
  import { next as automerge } from "@dxos/automerge/automerge";
862
1642
 
863
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
1643
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/automerge-to-codemirror.ts
864
1644
  import { ChangeSet } from "@codemirror/state";
865
- var amToCodemirror_default = (view, selection, target, patches) => {
1645
+ var automergeToCodemirror = (view, selection, target, patches) => {
866
1646
  for (const patch of patches) {
867
1647
  const changeSpec = handlePatch(patch, target, view.state);
868
1648
  if (changeSpec != null) {
@@ -967,9 +1747,9 @@ var charPath = (textPath, candidatePath) => {
967
1747
  return null;
968
1748
  };
969
1749
 
970
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
1750
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/codemirror-to-automerge.ts
971
1751
  import { next as am } from "@dxos/automerge/automerge";
972
- var codeMirrorToAm_default = (field, handle, transactions, state) => {
1752
+ var codemirrorToAutomerge = (field, handle, transactions, state) => {
973
1753
  const { lastHeads, path } = state.field(field);
974
1754
  let hasChanges = false;
975
1755
  for (const tr of transactions) {
@@ -990,7 +1770,7 @@ var codeMirrorToAm_default = (field, handle, transactions, state) => {
990
1770
  return newHeads ?? void 0;
991
1771
  };
992
1772
 
993
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
1773
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
994
1774
  var PatchSemaphore = class {
995
1775
  constructor(field) {
996
1776
  this._inReconcile = false;
@@ -1013,12 +1793,12 @@ var PatchSemaphore = class {
1013
1793
  annotations: reconcileAnnotationType.of(true)
1014
1794
  });
1015
1795
  }
1016
- let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
1796
+ let newHeads = codemirrorToAutomerge(this._field, handle, transactions, view.state);
1017
1797
  if (newHeads === null || newHeads === void 0) {
1018
1798
  newHeads = automerge.getHeads(handle.docSync());
1019
1799
  }
1020
1800
  const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
1021
- amToCodemirror_default(view, selection, path, diff);
1801
+ automergeToCodemirror(view, selection, path, diff);
1022
1802
  view.dispatch({
1023
1803
  effects: updateHeads(newHeads),
1024
1804
  annotations: reconcileAnnotationType.of({})
@@ -1031,7 +1811,7 @@ var PatchSemaphore = class {
1031
1811
  }
1032
1812
  };
1033
1813
 
1034
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
1814
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1035
1815
  var effectType = StateEffect.define({});
1036
1816
  var updateHeads = (newHeads) => effectType.of({
1037
1817
  newHeads
@@ -1042,7 +1822,7 @@ var semaphoreFacet = Facet.define({
1042
1822
  combine: (values) => values.at(-1)
1043
1823
  });
1044
1824
  var automergePlugin = (handle, path) => {
1045
- const stateField = StateField4.define({
1825
+ const stateField = StateField6.define({
1046
1826
  create: () => ({
1047
1827
  lastHeads: automerge2.getHeads(handle.docSync()),
1048
1828
  unreconciledTransactions: [],
@@ -1072,7 +1852,7 @@ var automergePlugin = (handle, path) => {
1072
1852
  }
1073
1853
  });
1074
1854
  const semaphore = new PatchSemaphore(stateField);
1075
- const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
1855
+ const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
1076
1856
  constructor(view) {
1077
1857
  this._handleChange = () => {
1078
1858
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
@@ -1080,8 +1860,8 @@ var automergePlugin = (handle, path) => {
1080
1860
  this._view = view;
1081
1861
  handle.addListener("change", this._handleChange);
1082
1862
  }
1083
- update(update2) {
1084
- if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
1863
+ update(update4) {
1864
+ if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
1085
1865
  queueMicrotask(() => {
1086
1866
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
1087
1867
  });
@@ -1102,47 +1882,8 @@ var automergePlugin = (handle, path) => {
1102
1882
  var reconcileAnnotationType = Annotation.define();
1103
1883
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1104
1884
 
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
1885
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1145
- import * as random from "lib0/random";
1886
+ import * as random2 from "lib0/random";
1146
1887
  var cursorColors = [
1147
1888
  {
1148
1889
  color: "#30bced",
@@ -1177,7 +1918,7 @@ var cursorColors = [
1177
1918
  light: "#1be7ff33"
1178
1919
  }
1179
1920
  ];
1180
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
1921
+ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1181
1922
 
1182
1923
  // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1183
1924
  import * as decoding from "lib0/decoding";
@@ -1185,7 +1926,7 @@ import * as encoding from "lib0/encoding";
1185
1926
  import { Observable } from "lib0/observable";
1186
1927
  import * as awarenessProtocol from "y-protocols/awareness";
1187
1928
  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";
1929
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1189
1930
  var messageAwareness = 1;
1190
1931
  var messageQueryAwareness = 3;
1191
1932
  var SpaceAwarenessProvider = class extends Observable {
@@ -1222,7 +1963,7 @@ var SpaceAwarenessProvider = class extends Observable {
1222
1963
  removed,
1223
1964
  origin
1224
1965
  }, {
1225
- F: __dxlog_file,
1966
+ F: __dxlog_file3,
1226
1967
  L: 67,
1227
1968
  S: this,
1228
1969
  C: (f, a) => f(...a)
@@ -1235,7 +1976,7 @@ var SpaceAwarenessProvider = class extends Observable {
1235
1976
  }
1236
1977
  _handleSpaceMessage({ payload }) {
1237
1978
  log("space message", payload, {
1238
- F: __dxlog_file,
1979
+ F: __dxlog_file3,
1239
1980
  L: 79,
1240
1981
  S: this,
1241
1982
  C: (f, a) => f(...a)
@@ -1280,57 +2021,74 @@ var SpaceAwarenessProvider = class extends Observable {
1280
2021
  };
1281
2022
 
1282
2023
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2024
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
1283
2025
  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({
2026
+ const [model, setModel] = useState2(() => createModel({
2027
+ identity,
2028
+ space,
2029
+ text
2030
+ }));
2031
+ useEffect2(() => {
2032
+ setModel(createModel({
2033
+ identity,
1292
2034
  space,
1293
- doc: text.doc,
1294
- channel: `yjs.awareness.${text.id}`
1295
- });
2035
+ text
2036
+ }));
1296
2037
  }, [
1297
2038
  identity,
1298
2039
  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
2040
  text
1313
2041
  ]);
1314
- if (!text?.doc || !text?.content) {
1315
- return void 0;
1316
- }
2042
+ return model;
2043
+ };
2044
+ var createModel = (options) => {
2045
+ const { text } = options;
1317
2046
  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
- };
2047
+ return createAutomergeModel(options);
2048
+ } else {
2049
+ if (!text?.doc) {
2050
+ return void 0;
2051
+ }
2052
+ return createYjsModel(options);
1329
2053
  }
2054
+ };
2055
+ var createYjsModel = ({ identity, space, text }) => {
2056
+ invariant3(text?.doc && text?.content, void 0, {
2057
+ F: __dxlog_file4,
2058
+ L: 74,
2059
+ S: void 0,
2060
+ A: [
2061
+ "text?.doc && text?.content",
2062
+ ""
2063
+ ]
2064
+ });
2065
+ const provider = space ? new SpaceAwarenessProvider({
2066
+ space,
2067
+ doc: text.doc,
2068
+ channel: `yjs.awareness.${text.id}`
2069
+ }) : void 0;
1330
2070
  return {
1331
2071
  id: text.doc.guid,
1332
2072
  content: text.content,
1333
2073
  text: () => text.content.toString(),
2074
+ extension: yCollab(text.content, provider?.awareness),
2075
+ awareness: provider?.awareness,
2076
+ peer: identity ? {
2077
+ id: identity.identityKey.toHex(),
2078
+ name: identity.profile?.displayName
2079
+ } : void 0
2080
+ };
2081
+ };
2082
+ var createAutomergeModel = ({ identity, text }) => {
2083
+ const obj = text;
2084
+ const doc = getRawDoc(obj, [
2085
+ obj.field
2086
+ ]);
2087
+ return {
2088
+ id: obj.id,
2089
+ content: doc,
2090
+ text: () => get3(doc.handle.docSync(), doc.path),
2091
+ extension: automergePlugin(doc.handle, doc.path),
1334
2092
  peer: identity ? {
1335
2093
  id: identity.identityKey.toHex(),
1336
2094
  name: identity.profile?.displayName
@@ -1345,28 +2103,34 @@ export {
1345
2103
  SpaceAwarenessProvider,
1346
2104
  TextEditor,
1347
2105
  TextKind,
1348
- YText2 as YText,
2106
+ YText,
1349
2107
  YXmlFragment,
1350
2108
  autocomplete,
1351
2109
  basicBundle,
2110
+ blast,
1352
2111
  codeTheme,
2112
+ comments,
1353
2113
  cursorColor,
2114
+ defaultMarkdownSlots,
2115
+ defaultOptions,
1354
2116
  defaultSlots,
1355
2117
  defaultTextSlots,
1356
2118
  defaultTheme,
2119
+ demo,
2120
+ image,
1357
2121
  link,
1358
2122
  listener,
1359
2123
  markdownBundle,
1360
2124
  markdownHighlightStyle,
1361
2125
  markdownTags,
1362
2126
  markdownTagsExtension,
1363
- statefield,
1364
- styles,
2127
+ markdownTheme,
2128
+ mention,
2129
+ table,
1365
2130
  tags2 as tags,
1366
2131
  tasklist,
1367
2132
  textTheme,
1368
2133
  tooltip,
1369
- useCollaboration,
1370
2134
  useTextModel
1371
2135
  };
1372
2136
  //# sourceMappingURL=index.mjs.map