@dxos/react-ui-editor 0.3.10-main.90b298e → 0.3.10-main.c5193cc

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 (62) hide show
  1. package/dist/lib/browser/index.mjs +729 -153
  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 +19 -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 +2 -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.map +1 -1
  9. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +2 -1
  10. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
  11. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  12. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  13. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +1 -1
  14. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -1
  15. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +2 -1
  16. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -1
  17. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  18. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  19. package/dist/types/src/components/TextEditor/extensions/index.d.ts +4 -0
  20. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  21. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  22. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  23. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +2 -1
  24. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  25. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +6 -2
  26. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  27. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  28. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  29. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  30. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  31. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -1
  32. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  33. package/dist/types/src/components/TextEditor/themes/default.d.ts +5 -0
  34. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  35. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +8 -1
  36. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
  37. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +1 -1
  38. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  39. package/dist/types/src/styles/markdown.d.ts +3 -2
  40. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  41. package/package.json +20 -20
  42. package/src/components/TextEditor/MarkdownEditor.stories.tsx +101 -25
  43. package/src/components/TextEditor/TextEditor.tsx +13 -23
  44. package/src/components/TextEditor/extensions/autocomplete.ts +5 -20
  45. package/src/components/TextEditor/extensions/basic.ts +2 -1
  46. package/src/components/TextEditor/extensions/blast.ts +387 -0
  47. package/src/components/TextEditor/extensions/comments.ts +8 -7
  48. package/src/components/TextEditor/extensions/demo.ts +4 -1
  49. package/src/components/TextEditor/extensions/image.ts +67 -0
  50. package/src/components/TextEditor/extensions/index.ts +4 -0
  51. package/src/components/TextEditor/extensions/link.ts +16 -23
  52. package/src/components/TextEditor/extensions/listener.ts +3 -3
  53. package/src/components/TextEditor/extensions/markdown/bundle.ts +28 -17
  54. package/src/components/TextEditor/extensions/markdown/highlight.ts +118 -104
  55. package/src/components/TextEditor/extensions/mention.ts +31 -0
  56. package/src/components/TextEditor/extensions/table.ts +123 -0
  57. package/src/components/TextEditor/extensions/tasklist.ts +34 -20
  58. package/src/components/TextEditor/themes/default.ts +54 -2
  59. package/src/hooks/automerge/automerge.stories.tsx +3 -2
  60. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +1 -1
  61. package/src/styles/markdown.ts +14 -8
  62. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
@@ -24,13 +24,13 @@ var autocomplete = ({ onSearch }) => {
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: onSearch(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,8 +54,333 @@ 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/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
+ effect: 2,
65
+ maxParticles: 200,
66
+ particleGravity: 0.08,
67
+ particleAlphaFadeout: 0.996,
68
+ particleSize: {
69
+ min: 2,
70
+ max: 8
71
+ },
72
+ particleNumRange: {
73
+ min: 5,
74
+ max: 10
75
+ },
76
+ particleVelocityRange: {
77
+ x: [
78
+ -1,
79
+ 1
80
+ ],
81
+ y: [
82
+ -3.5,
83
+ -1.5
84
+ ]
85
+ },
86
+ particleShrinkRate: 0.95,
87
+ shakeIntensity: 5
88
+ };
89
+ var blast = (options) => {
90
+ let blaster;
91
+ let last = 0;
92
+ const getPoint = (view, pos) => {
93
+ const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
94
+ const element = document.elementFromPoint(x, y);
95
+ const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
96
+ const point = {
97
+ x: x - left,
98
+ y: y - top
99
+ };
100
+ return {
101
+ element,
102
+ point
103
+ };
104
+ };
105
+ return [
106
+ // Cursor moved.
107
+ EditorView2.updateListener.of((update4) => {
108
+ if (blaster?.node !== update4.view.scrollDOM) {
109
+ if (blaster) {
110
+ blaster.destroy();
111
+ }
112
+ blaster = new Blaster(update4.view.scrollDOM, defaultsDeep({
113
+ particleGravity: 0.2,
114
+ particleShrinkRate: 0.995,
115
+ color: () => [
116
+ 100 + Math.random() * 100,
117
+ 0,
118
+ 0
119
+ ]
120
+ }, options, defaultOptions));
121
+ blaster.initialize();
122
+ blaster.start();
123
+ } else {
124
+ blaster.resize();
125
+ }
126
+ const current = update4.view.state.selection.main.head;
127
+ if (current !== last) {
128
+ last = current;
129
+ const { element, point } = getPoint(update4.view, current);
130
+ if (element && point) {
131
+ blaster.spawn({
132
+ element,
133
+ point
134
+ });
135
+ }
136
+ }
137
+ }),
138
+ // Document changed.
139
+ StateField.define({
140
+ create: () => null,
141
+ update: (_value, transaction) => {
142
+ if (blaster && transaction.docChanged) {
143
+ const view = EditorView2.findFromDOM(blaster.node);
144
+ const { element, point } = getPoint(view, transaction.selection.main.head);
145
+ if (element && point) {
146
+ blaster.spawn({
147
+ element,
148
+ point
149
+ });
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ }),
155
+ keymap2.of([
156
+ {
157
+ any: (view, event) => {
158
+ if (blaster && event.key === "Enter" && event.shiftKey) {
159
+ blaster.shake({
160
+ time: 0.4
161
+ });
162
+ }
163
+ return false;
164
+ }
165
+ }
166
+ ])
167
+ ];
168
+ };
169
+ var Blaster = class {
170
+ constructor(_node, _options) {
171
+ this._node = _node;
172
+ this._options = _options;
173
+ this._running = false;
174
+ this._shakeTime = 0;
175
+ this._shakeTimeMax = 0;
176
+ this._particles = [];
177
+ this._particlePointer = 0;
178
+ this._lastPoint = {
179
+ x: 0,
180
+ y: 0
181
+ };
182
+ this.shake = throttle(({ time }) => {
183
+ this._shakeTime = this._shakeTimeMax;
184
+ this._shakeTimeMax = time;
185
+ }, 100);
186
+ this.spawn = throttle(({ element, point }) => {
187
+ const color = getRGBComponents(element, this._options.color);
188
+ const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
189
+ const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
190
+ this._lastPoint = point;
191
+ for (let i = numParticles; i--; i > 0) {
192
+ this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);
193
+ this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;
194
+ }
195
+ }, 100);
196
+ this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
197
+ }
198
+ get node() {
199
+ return this._node;
200
+ }
201
+ initialize() {
202
+ invariant(!this._canvas && !this._ctx, void 0, {
203
+ F: __dxlog_file,
204
+ L: 152,
205
+ S: this,
206
+ A: [
207
+ "!this._canvas && !this._ctx",
208
+ ""
209
+ ]
210
+ });
211
+ this._canvas = document.createElement("canvas");
212
+ this._canvas.id = "code-blast-canvas";
213
+ this._canvas.style.position = "absolute";
214
+ this._canvas.style.zIndex = "0";
215
+ this._canvas.style.pointerEvents = "none";
216
+ this._ctx = this._canvas.getContext("2d");
217
+ const parent = this._node.parentElement?.parentElement;
218
+ parent?.appendChild(this._canvas);
219
+ this.resize();
220
+ }
221
+ destroy() {
222
+ this.stop();
223
+ if (this._canvas) {
224
+ this._canvas.remove();
225
+ this._canvas = void 0;
226
+ this._ctx = void 0;
227
+ }
228
+ }
229
+ resize() {
230
+ if (this._node.parentElement && this._canvas) {
231
+ const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
232
+ this._canvas.style.top = `${y}px`;
233
+ this._canvas.style.left = `${x}px`;
234
+ this._canvas.width = width;
235
+ this._canvas.height = height;
236
+ }
237
+ }
238
+ start() {
239
+ invariant(this._canvas && this._ctx, void 0, {
240
+ F: __dxlog_file,
241
+ L: 191,
242
+ S: this,
243
+ A: [
244
+ "this._canvas && this._ctx",
245
+ ""
246
+ ]
247
+ });
248
+ this._running = true;
249
+ this.loop();
250
+ }
251
+ stop() {
252
+ this._running = false;
253
+ this._node.style.transform = "translate(0px, 0px)";
254
+ }
255
+ loop() {
256
+ if (!this._running || !this._canvas || !this._ctx) {
257
+ return;
258
+ }
259
+ this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);
260
+ const now = (/* @__PURE__ */ new Date()).getTime();
261
+ this._lastTime ??= now;
262
+ const dt = (now - this._lastTime) / 1e3;
263
+ this._lastTime = now;
264
+ if (this._shakeTime > 0) {
265
+ this._shakeTime -= dt;
266
+ const magnitude = this._shakeTime / this._shakeTimeMax * this._options.shakeIntensity;
267
+ const shakeX = random(-magnitude, magnitude);
268
+ const shakeY = random(-magnitude, magnitude);
269
+ this._node.style.transform = `translate(${shakeX}px,${shakeY}px)`;
270
+ }
271
+ this.drawParticles();
272
+ requestAnimationFrame(this.loop.bind(this));
273
+ }
274
+ drawParticles() {
275
+ for (let i = this._particles.length; i--; i > 0) {
276
+ const particle = this._particles[i];
277
+ if (!particle) {
278
+ continue;
279
+ }
280
+ if (particle.alpha < 0.01 || particle.size <= 0.5) {
281
+ continue;
282
+ }
283
+ this._effect.update(this._ctx, particle);
284
+ }
285
+ }
286
+ };
287
+ var Effect = class {
288
+ constructor(_options) {
289
+ this._options = _options;
290
+ }
291
+ };
292
+ var Effect1 = class extends Effect {
293
+ create(x, y, color) {
294
+ return {
295
+ x,
296
+ y: y + 10,
297
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
298
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
299
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
300
+ color,
301
+ alpha: 1
302
+ };
303
+ }
304
+ update(ctx, particle) {
305
+ particle.vy += this._options.particleGravity;
306
+ particle.x += particle.vx;
307
+ particle.y += particle.vy;
308
+ particle.alpha *= this._options.particleAlphaFadeout;
309
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
310
+ ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);
311
+ }
312
+ };
313
+ var Effect2 = class extends Effect {
314
+ create(x, y, color) {
315
+ return {
316
+ x,
317
+ y: y + 10,
318
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
319
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
320
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
321
+ color,
322
+ alpha: 1,
323
+ theta: random(0, 360) * Math.PI / 180,
324
+ drag: 0.92,
325
+ wander: 0.15
326
+ };
327
+ }
328
+ update(ctx, particle) {
329
+ particle.vy += this._options.particleGravity;
330
+ particle.x += particle.vx;
331
+ particle.y += particle.vy;
332
+ particle.vx *= particle.drag;
333
+ particle.vy *= particle.drag;
334
+ particle.theta += random(-0.5, 0.5);
335
+ particle.vx += Math.sin(particle.theta) * 0.1;
336
+ particle.vy += Math.cos(particle.theta) * 0.1;
337
+ particle.size *= this._options.particleShrinkRate;
338
+ particle.alpha *= this._options.particleAlphaFadeout;
339
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
340
+ ctx.beginPath();
341
+ ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);
342
+ ctx.fill();
343
+ }
344
+ };
345
+ function throttle(callback, limit) {
346
+ let wait = false;
347
+ return function(...args) {
348
+ if (!wait) {
349
+ callback.apply(this, args);
350
+ wait = true;
351
+ setTimeout(() => {
352
+ wait = false;
353
+ }, limit);
354
+ }
355
+ };
356
+ }
357
+ var getRGBComponents = (node, color) => {
358
+ if (typeof color === "function") {
359
+ return color();
360
+ }
361
+ const bgColor = getComputedStyle(node).color;
362
+ if (bgColor) {
363
+ const x = bgColor.match(/(\d+), (\d+), (\d+)/)?.slice(1);
364
+ if (x) {
365
+ return x;
366
+ }
367
+ }
368
+ return [
369
+ 50,
370
+ 50,
371
+ 50
372
+ ];
373
+ };
374
+ var random = (min, max) => {
375
+ if (!max) {
376
+ max = min;
377
+ min = 0;
378
+ }
379
+ return min + ~~(Math.random() * (max - min + 1));
380
+ };
381
+
57
382
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
58
- import { keymap as keymap2 } from "@codemirror/view";
383
+ import { keymap as keymap3 } from "@codemirror/view";
59
384
  var defaultItems = [
60
385
  "hello world!",
61
386
  "this is a test.",
@@ -65,7 +390,7 @@ var demo = ({ delay = 75, items = defaultItems } = {}) => {
65
390
  let t;
66
391
  let idx = 0;
67
392
  return [
68
- keymap2.of([
393
+ keymap3.of([
69
394
  {
70
395
  // Reset.
71
396
  key: "alt-meta-'",
@@ -112,17 +437,18 @@ var demo = ({ delay = 75, items = defaultItems } = {}) => {
112
437
  };
113
438
 
114
439
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
115
- import { keymap as keymap3, Decoration, EditorView as EditorView2, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
440
+ import { keymap as keymap4, Decoration, EditorView as EditorView3, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
116
441
  import { debounce } from "@dxos/async";
117
- import { invariant } from "@dxos/invariant";
118
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
442
+ import { invariant as invariant2 } from "@dxos/invariant";
443
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
119
444
  var BookmarkWidget = class extends WidgetType {
120
- constructor(_pos, _id) {
445
+ constructor(_pos, _id, _handleClick) {
121
446
  super();
122
447
  this._pos = _pos;
123
448
  this._id = _id;
124
- invariant(this._id, void 0, {
125
- F: __dxlog_file,
449
+ this._handleClick = _handleClick;
450
+ invariant2(this._id, void 0, {
451
+ F: __dxlog_file2,
126
452
  L: 41,
127
453
  S: this,
128
454
  A: [
@@ -138,10 +464,11 @@ var BookmarkWidget = class extends WidgetType {
138
464
  const span = document.createElement("span");
139
465
  span.className = "cm-bookmark";
140
466
  span.textContent = "\u{1F4AC}";
467
+ span.onclick = () => this._handleClick();
141
468
  return span;
142
469
  }
143
470
  };
144
- var styles = EditorView2.baseTheme({
471
+ var styles = EditorView3.baseTheme({
145
472
  "& .cm-bookmark": {
146
473
  cursor: "pointer",
147
474
  margin: "4px",
@@ -160,7 +487,9 @@ var comments = (options = {}) => {
160
487
  const id = match[1];
161
488
  return Decoration.replace({
162
489
  id,
163
- widget: new BookmarkWidget(pos, id)
490
+ widget: new BookmarkWidget(pos, id, () => handleUpdate({
491
+ active: id
492
+ }))
164
493
  });
165
494
  }
166
495
  });
@@ -168,20 +497,20 @@ var comments = (options = {}) => {
168
497
  constructor(view) {
169
498
  this.bookmarks = bookmarkMatcher.createDeco(view);
170
499
  }
171
- update(update2) {
172
- this.bookmarks = bookmarkMatcher.updateDeco(update2, this.bookmarks);
500
+ update(update4) {
501
+ this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
173
502
  }
174
503
  }, {
175
504
  decorations: (instance) => instance.bookmarks,
176
- provide: (plugin) => EditorView2.atomicRanges.of((view) => {
505
+ provide: (plugin) => EditorView3.atomicRanges.of((view) => {
177
506
  return view.plugin(plugin)?.bookmarks || Decoration.none;
178
507
  })
179
508
  });
180
- const doUpdate = debounce((data) => {
509
+ const handleUpdate = debounce((data) => {
181
510
  options.onUpdate?.(data);
182
511
  }, 200);
183
512
  return [
184
- keymap3.of([
513
+ keymap4.of([
185
514
  {
186
515
  key: options?.key ?? "shift-meta-c",
187
516
  run: (view) => {
@@ -206,20 +535,23 @@ var comments = (options = {}) => {
206
535
  ]),
207
536
  bookmarks,
208
537
  // Monitor cursor movement.
209
- EditorView2.updateListener.of((update2) => {
538
+ EditorView3.updateListener.of((update4) => {
210
539
  active = void 0;
211
540
  if (!options.onUpdate) {
212
541
  return;
213
542
  }
214
- const view = update2.view;
543
+ const view = update4.view;
215
544
  const pos = view.state.selection.main.head;
216
545
  const decorations = [];
217
546
  const rangeSet = view.plugin(bookmarks)?.bookmarks;
218
547
  let closest = Infinity;
219
- const { from, to } = view.visibleRanges?.[0] ?? {
220
- from: 0,
221
- to: view.state.doc.length
222
- };
548
+ const { from, to } = (
549
+ /* view.visibleRanges?.[0] ?? */
550
+ {
551
+ from: 0,
552
+ to: view.state.doc.length
553
+ }
554
+ );
223
555
  rangeSet?.between(from, to, (from2, to2, value) => {
224
556
  decorations.push({
225
557
  from: from2,
@@ -233,7 +565,7 @@ var comments = (options = {}) => {
233
565
  }
234
566
  });
235
567
  if (decorations.length) {
236
- doUpdate({
568
+ handleUpdate({
237
569
  active,
238
570
  items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
239
571
  id,
@@ -247,11 +579,64 @@ var comments = (options = {}) => {
247
579
  ];
248
580
  };
249
581
 
250
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
582
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/image.ts
251
583
  import { syntaxTree } from "@codemirror/language";
252
- import { RangeSetBuilder, StateField } from "@codemirror/state";
253
- import { Decoration as Decoration2, EditorView as EditorView3, WidgetType as WidgetType2 } from "@codemirror/view";
254
- var LinkButton = class extends WidgetType2 {
584
+ import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
585
+ import { Decoration as Decoration2, EditorView as EditorView4, WidgetType as WidgetType2 } from "@codemirror/view";
586
+ var image = (options = {}) => {
587
+ return StateField2.define({
588
+ create: (state) => update(state, options),
589
+ update: (_, tr) => update(tr.state, options),
590
+ provide: (field) => EditorView4.decorations.from(field)
591
+ });
592
+ };
593
+ var update = (state, options) => {
594
+ const builder = new RangeSetBuilder();
595
+ const cursor = state.selection.main.head;
596
+ syntaxTree(state).iterate({
597
+ enter: (node) => {
598
+ if (node.name === "Image") {
599
+ const urlNode = node.node.getChild("URL");
600
+ if (urlNode) {
601
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
602
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
603
+ builder.add(hide ? node.from : node.to, node.to, Decoration2.replace({
604
+ widget: new ImageWidget(url)
605
+ }));
606
+ }
607
+ }
608
+ }
609
+ });
610
+ return builder.finish();
611
+ };
612
+ var ImageWidget = class extends WidgetType2 {
613
+ constructor(_url) {
614
+ super();
615
+ this._url = _url;
616
+ }
617
+ eq(other) {
618
+ return this._url === other?._url;
619
+ }
620
+ toDOM(view) {
621
+ const img = document.createElement("img");
622
+ img.setAttribute("src", this._url);
623
+ img.setAttribute("class", "cm-image");
624
+ return img;
625
+ }
626
+ };
627
+
628
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
629
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
630
+ import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
631
+ import { Decoration as Decoration3, EditorView as EditorView5, WidgetType as WidgetType3 } from "@codemirror/view";
632
+ var link = (options = {}) => {
633
+ return StateField3.define({
634
+ create: (state) => update2(state, options),
635
+ update: (_, tr) => update2(tr.state, options),
636
+ provide: (field) => EditorView5.decorations.from(field)
637
+ });
638
+ };
639
+ var LinkButton = class extends WidgetType3 {
255
640
  constructor(_pos, _url, _onAttach) {
256
641
  super();
257
642
  this._pos = _pos;
@@ -267,7 +652,7 @@ var LinkButton = class extends WidgetType2 {
267
652
  return el;
268
653
  }
269
654
  };
270
- var LinkText = class extends WidgetType2 {
655
+ var LinkText = class extends WidgetType3 {
271
656
  constructor(_pos, _text, _url) {
272
657
  super();
273
658
  this._pos = _pos;
@@ -297,10 +682,10 @@ var LinkText = class extends WidgetType2 {
297
682
  return link2;
298
683
  }
299
684
  };
300
- var update = (state, options) => {
301
- const builder = new RangeSetBuilder();
685
+ var update2 = (state, options) => {
686
+ const builder = new RangeSetBuilder2();
302
687
  const cursor = state.selection.main.head;
303
- syntaxTree(state).iterate({
688
+ syntaxTree2(state).iterate({
304
689
  enter: (node) => {
305
690
  if (node.name === "Link") {
306
691
  const marks = node.node.getChildren("LinkMark");
@@ -311,12 +696,12 @@ var update = (state, options) => {
311
696
  return false;
312
697
  }
313
698
  if (cursor < node.from || cursor > node.to) {
314
- builder.add(node.from, node.to, Decoration2.replace({
699
+ builder.add(node.from, node.to, Decoration3.replace({
315
700
  widget: new LinkText(node.from, text, options.link ? url : void 0)
316
701
  }));
317
702
  }
318
703
  if (options.onRender) {
319
- builder.add(node.to, node.to, Decoration2.replace({
704
+ builder.add(node.to, node.to, Decoration3.replace({
320
705
  widget: new LinkButton(node.from, url, options.onRender)
321
706
  }));
322
707
  }
@@ -326,52 +711,70 @@ var update = (state, options) => {
326
711
  });
327
712
  return builder.finish();
328
713
  };
329
- var link = (options = {}) => {
330
- return StateField.define({
331
- create: (state) => update(state, options),
332
- update: (_, tr) => update(tr.state, options),
333
- provide: (field) => EditorView3.decorations.from(field)
334
- });
335
- };
336
714
 
337
715
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/listener.ts
338
- import { StateField as StateField2 } from "@codemirror/state";
339
- var listener = ({ onChange }) => StateField2.define({
340
- create: () => null,
341
- update: (_value, transaction) => {
342
- if (transaction.docChanged && onChange) {
343
- onChange(transaction.newDoc.toString());
716
+ import { StateField as StateField4 } from "@codemirror/state";
717
+ var listener = ({ onChange }) => {
718
+ return StateField4.define({
719
+ create: () => null,
720
+ update: (_value, transaction) => {
721
+ if (transaction.docChanged && onChange) {
722
+ onChange(transaction.newDoc.toString());
723
+ }
724
+ return null;
344
725
  }
345
- return null;
346
- }
347
- });
726
+ });
727
+ };
348
728
 
349
729
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
350
730
  import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
351
- import { defaultKeymap, history, indentWithTab } from "@codemirror/commands";
731
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
352
732
  import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
353
733
  import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
354
734
  import { languages } from "@codemirror/language-data";
355
735
  import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
356
736
  import { EditorState } from "@codemirror/state";
357
737
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
358
- import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap4, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
738
+ import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView6, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap5, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
359
739
 
360
740
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
361
741
  import { markdownLanguage } from "@codemirror/lang-markdown";
362
742
  import { HighlightStyle } from "@codemirror/language";
363
743
  import { tags, styleTags, Tag } from "@lezer/highlight";
364
744
  import get from "lodash.get";
745
+ import { mx as mx2 } from "@dxos/react-ui-theme";
365
746
 
366
747
  // packages/ui/react-ui-editor/src/styles/markdown.ts
367
748
  import { mx } from "@dxos/react-ui-theme";
368
- var heading = {
369
- 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
370
- 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
371
- 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
372
- 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
373
- 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
374
- 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
749
+ var headings = {
750
+ 1: [
751
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
752
+ "-ml-1.5"
753
+ ],
754
+ 2: [
755
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
756
+ "-ml-1.5"
757
+ ],
758
+ 3: [
759
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
760
+ "-ml-1.5"
761
+ ],
762
+ 4: [
763
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
764
+ "-ml-[5px]"
765
+ ],
766
+ 5: [
767
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
768
+ "-ml-[5px]"
769
+ ],
770
+ 6: [
771
+ "mbs-4 mbe-2 font-medium text-inherit no-underline",
772
+ "-ml-[4px]"
773
+ ]
774
+ };
775
+ var heading = (level, readonly) => {
776
+ const [style, offset] = headings[level];
777
+ return mx(style, readonly && offset);
375
778
  };
376
779
  var light = "text-neutral-200 dark:text-neutral-800";
377
780
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -382,7 +785,7 @@ var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
382
785
  var codeMark = "font-mono text-primary-500";
383
786
  var inlineUrl = mx(code, "px-1");
384
787
  var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
385
- var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-500/50";
788
+ var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800";
386
789
 
387
790
  // packages/ui/react-ui-editor/src/styles/tokens.ts
388
791
  import { tailwindConfig } from "@dxos/react-ui-theme";
@@ -408,7 +811,7 @@ var markdownTagsExtension = {
408
811
  styleTags(markdownTags)
409
812
  ]
410
813
  };
411
- var markdownHighlightStyle = HighlightStyle.define([
814
+ var markdownHighlightStyle = (readonly) => HighlightStyle.define([
412
815
  {
413
816
  tag: [
414
817
  tags.keyword,
@@ -452,40 +855,54 @@ var markdownHighlightStyle = HighlightStyle.define([
452
855
  tag: [
453
856
  tags.meta,
454
857
  tags.processingInstruction,
455
- markdownTags.CodeMark,
456
- markdownTags.EmphasisMark,
457
- markdownTags.HeaderMark,
458
858
  markdownTags.LinkLabel,
459
859
  markdownTags.LinkReference,
460
- markdownTags.ListMark,
461
- markdownTags.QuoteMark
860
+ markdownTags.ListMark
462
861
  ],
463
862
  class: mark
464
863
  },
864
+ // Markdown marks.
865
+ {
866
+ tag: [
867
+ markdownTags.CodeMark,
868
+ markdownTags.HeaderMark,
869
+ markdownTags.QuoteMark,
870
+ markdownTags.EmphasisMark
871
+ ],
872
+ class: readonly ? "hidden" : mark
873
+ },
874
+ // E.g., code block language (after ```).
875
+ {
876
+ tag: [
877
+ tags.function(tags.variableName),
878
+ tags.labelName
879
+ ],
880
+ class: readonly ? "hidden" : codeMark
881
+ },
465
882
  // Headings.
466
883
  {
467
884
  tag: tags.heading1,
468
- class: heading[1]
885
+ class: heading(1, readonly)
469
886
  },
470
887
  {
471
888
  tag: tags.heading2,
472
- class: heading[2]
889
+ class: heading(2, readonly)
473
890
  },
474
891
  {
475
892
  tag: tags.heading3,
476
- class: heading[3]
893
+ class: heading(3, readonly)
477
894
  },
478
895
  {
479
896
  tag: tags.heading4,
480
- class: heading[4]
897
+ class: heading(4, readonly)
481
898
  },
482
899
  {
483
900
  tag: tags.heading5,
484
- class: heading[5]
901
+ class: heading(5, readonly)
485
902
  },
486
903
  {
487
904
  tag: tags.heading6,
488
- class: heading[6]
905
+ class: heading(6, readonly)
489
906
  },
490
907
  // Emphasis.
491
908
  {
@@ -507,14 +924,6 @@ var markdownHighlightStyle = HighlightStyle.define([
507
924
  ],
508
925
  class: inlineUrl
509
926
  },
510
- // E.g., code block language (after ```).
511
- {
512
- tag: [
513
- tags.function(tags.variableName),
514
- tags.labelName
515
- ],
516
- class: codeMark
517
- },
518
927
  // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
519
928
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
520
929
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
@@ -537,7 +946,7 @@ var markdownHighlightStyle = HighlightStyle.define([
537
946
  tag: [
538
947
  markdownTags.HorizontalRule
539
948
  ],
540
- class: horizontalRule
949
+ class: mx2(horizontalRule, readonly && "-indent-[100rem]")
541
950
  }
542
951
  ], {
543
952
  scope: markdownLanguage,
@@ -547,13 +956,13 @@ var markdownHighlightStyle = HighlightStyle.define([
547
956
  });
548
957
 
549
958
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
550
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
959
+ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
551
960
  return [
552
961
  bracketMatching2(),
553
962
  closeBrackets2(),
554
963
  _placeholder && placeholder2(_placeholder),
555
964
  EditorState.allowMultipleSelections.of(true),
556
- EditorView4.lineWrapping,
965
+ EditorView6.lineWrapping,
557
966
  crosshairCursor(),
558
967
  dropCursor(),
559
968
  drawSelection(),
@@ -564,16 +973,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
564
973
  history(),
565
974
  indentOnInput(),
566
975
  rectangularSelection(),
567
- // TODO(burdon): Review.
568
- keymap4.of([
569
- ...closeBracketsKeymap,
570
- ...defaultKeymap,
571
- // ...foldKeymap,
572
- // ...historyKeymap,
573
- // ...lintKeymap,
574
- ...searchKeymap,
575
- indentWithTab
576
- ]),
976
+ customkeymap(),
577
977
  // Main extension.
578
978
  // https://github.com/codemirror/lang-markdown
579
979
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -588,20 +988,148 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
588
988
  codeLanguages: languages,
589
989
  // Parser extensions.
590
990
  extensions: [
591
- // GFM, // TODO(burdon): Provided by default?
991
+ // GFM provided by default.
592
992
  markdownTagsExtension
593
993
  ]
594
994
  }),
995
+ // https://github.com/codemirror/theme-one-dark
996
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
595
997
  // Custom styles.
596
- syntaxHighlighting2(markdownHighlightStyle),
597
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
598
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
998
+ syntaxHighlighting2(markdownHighlightStyle(readonly))
599
999
  ].filter(Boolean);
600
1000
  };
1001
+ var customkeymap = () => keymap5.of([
1002
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
1003
+ indentWithTab,
1004
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
1005
+ ...standardKeymap,
1006
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
1007
+ ...historyKeymap,
1008
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1009
+ ...closeBracketsKeymap,
1010
+ // https://codemirror.net/docs/ref/#search.searchKeymap
1011
+ ...searchKeymap
1012
+ ]);
1013
+
1014
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/mention.ts
1015
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
1016
+ var mention = ({ onSearch }) => {
1017
+ return autocompletion2({
1018
+ closeOnBlur: false,
1019
+ override: [
1020
+ (context) => {
1021
+ const match = context.matchBefore(/@(\w+)?/);
1022
+ if (!match || match.from === match.to && !context.explicit) {
1023
+ return null;
1024
+ }
1025
+ console.log(match);
1026
+ return {
1027
+ from: match.from,
1028
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
1029
+ label: `@${value}`
1030
+ }))
1031
+ };
1032
+ }
1033
+ ]
1034
+ });
1035
+ };
1036
+
1037
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/table.ts
1038
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1039
+ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField5 } from "@codemirror/state";
1040
+ import { Decoration as Decoration4, EditorView as EditorView7, WidgetType as WidgetType4 } from "@codemirror/view";
1041
+ var table = (options = {}) => {
1042
+ return StateField5.define({
1043
+ create: (state) => update3(state, options),
1044
+ update: (_, tr) => update3(tr.state, options),
1045
+ provide: (field) => EditorView7.decorations.from(field)
1046
+ });
1047
+ };
1048
+ var update3 = (state, options) => {
1049
+ const builder = new RangeSetBuilder3();
1050
+ if (state.readOnly) {
1051
+ const tables = [];
1052
+ const getTable = () => tables[tables.length - 1];
1053
+ const getRow = () => {
1054
+ const table2 = getTable();
1055
+ return table2.rows?.[table2.rows.length - 1];
1056
+ };
1057
+ syntaxTree3(state).iterate({
1058
+ enter: (node) => {
1059
+ switch (node.name) {
1060
+ case "Table": {
1061
+ tables.push({
1062
+ from: node.from
1063
+ });
1064
+ break;
1065
+ }
1066
+ case "TableHeader": {
1067
+ getTable().header = [];
1068
+ break;
1069
+ }
1070
+ case "TableRow": {
1071
+ (getTable().rows ??= []).push([]);
1072
+ break;
1073
+ }
1074
+ case "TableCell": {
1075
+ const row = getRow();
1076
+ if (row) {
1077
+ row.push(state.sliceDoc(node.from, node.to));
1078
+ } else {
1079
+ getTable().header?.push(state.sliceDoc(node.from, node.to));
1080
+ }
1081
+ break;
1082
+ }
1083
+ case "TableDelimiter": {
1084
+ getTable().to = node.to;
1085
+ break;
1086
+ }
1087
+ }
1088
+ }
1089
+ });
1090
+ tables.forEach((table2) => builder.add(table2.from, table2.to, Decoration4.replace({
1091
+ widget: new TableWidget(table2)
1092
+ })));
1093
+ }
1094
+ return builder.finish();
1095
+ };
1096
+ var TableWidget = class extends WidgetType4 {
1097
+ constructor(_table) {
1098
+ super();
1099
+ this._table = _table;
1100
+ }
1101
+ eq(other) {
1102
+ return this._table.from === other?._table?.from;
1103
+ }
1104
+ toDOM(view) {
1105
+ const table2 = document.createElement("table");
1106
+ {
1107
+ const header = table2.appendChild(document.createElement("thead"));
1108
+ const row = header.appendChild(document.createElement("tr"));
1109
+ this._table.header?.forEach((cell) => {
1110
+ const th = document.createElement("th");
1111
+ th.setAttribute("class", "cm-table-head");
1112
+ row.appendChild(th).textContent = cell;
1113
+ });
1114
+ }
1115
+ {
1116
+ const body = table2.appendChild(document.createElement("tbody"));
1117
+ this._table.rows?.forEach((row) => {
1118
+ const tr = body.appendChild(document.createElement("tr"));
1119
+ row.forEach((cell) => {
1120
+ const td = document.createElement("td");
1121
+ td.setAttribute("class", "cm-table-cell");
1122
+ tr.appendChild(td).textContent = cell;
1123
+ });
1124
+ });
1125
+ }
1126
+ return table2;
1127
+ }
1128
+ };
601
1129
 
602
1130
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
603
- import { EditorView as EditorView5, Decoration as Decoration3, WidgetType as WidgetType3, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
604
- var CheckboxWidget = class extends WidgetType3 {
1131
+ import { EditorView as EditorView8, Decoration as Decoration5, WidgetType as WidgetType5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
1132
+ var CheckboxWidget = class extends WidgetType5 {
605
1133
  constructor(_pos, _checked, _indent, _onCheck) {
606
1134
  super();
607
1135
  this._pos = _pos;
@@ -620,30 +1148,35 @@ var CheckboxWidget = class extends WidgetType3 {
620
1148
  const input = wrap.appendChild(document.createElement("input"));
621
1149
  input.type = "checkbox";
622
1150
  input.checked = this._checked;
623
- input.onchange = (event) => {
624
- this._onCheck(event.target.checked);
625
- return true;
626
- };
1151
+ if (!this._onCheck) {
1152
+ input.setAttribute("disabled", "true");
1153
+ }
1154
+ if (this._onCheck) {
1155
+ input.onchange = (event) => {
1156
+ this._onCheck?.(event.target.checked);
1157
+ return true;
1158
+ };
1159
+ }
627
1160
  return wrap;
628
1161
  }
629
1162
  ignoreEvent() {
630
1163
  return false;
631
1164
  }
632
1165
  };
633
- var styles2 = EditorView5.baseTheme({
1166
+ var styles2 = EditorView8.baseTheme({
634
1167
  "& .cm-task-item": {
635
1168
  paddingLeft: "4px",
636
1169
  paddingRight: "8px"
637
1170
  }
638
1171
  });
639
- var tasklist = () => {
1172
+ var tasklist = (options = {}) => {
640
1173
  const taskMatcher = new MatchDecorator2({
641
1174
  regexp: /^(\s*)- \[([ xX])\]\s/g,
642
1175
  decoration: (match, view, pos) => {
643
1176
  const indent = Math.floor(match[1].length / 2);
644
1177
  const checked = match[2] === "x" || match[2] === "X";
645
- return Decoration3.replace({
646
- widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
1178
+ return Decoration5.replace({
1179
+ widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
647
1180
  const idx = pos + match[0].indexOf("[") + 1;
648
1181
  view.dispatch({
649
1182
  changes: {
@@ -664,13 +1197,13 @@ var tasklist = () => {
664
1197
  constructor(view) {
665
1198
  this.tasks = taskMatcher.createDeco(view);
666
1199
  }
667
- update(update2) {
668
- this.tasks = taskMatcher.updateDeco(update2, this.tasks);
1200
+ update(update4) {
1201
+ this.tasks = taskMatcher.updateDeco(update4, this.tasks);
669
1202
  }
670
1203
  }, {
671
1204
  decorations: (instance) => instance.tasks,
672
- provide: (plugin) => EditorView5.atomicRanges.of((view) => {
673
- return view.plugin(plugin)?.tasks || Decoration3.none;
1205
+ provide: (plugin) => EditorView8.atomicRanges.of((view) => {
1206
+ return view.plugin(plugin)?.tasks || Decoration5.none;
674
1207
  })
675
1208
  });
676
1209
  return [
@@ -837,8 +1370,51 @@ var defaultTheme = {
837
1370
  "& .cm-tooltip": {
838
1371
  border: "none"
839
1372
  },
840
- // '& .cm-tooltip-below': {},
841
- // '& .cm-tooltip-autocomplete': {},
1373
+ "& .cm-tooltip-below": {},
1374
+ //
1375
+ // autocomplete
1376
+ //
1377
+ "& .cm-tooltip-autocomplete": {
1378
+ marginTop: "4px",
1379
+ marginLeft: "-3px"
1380
+ },
1381
+ "& .cm-tooltip-autocomplete ul li": {},
1382
+ "& .cm-tooltip-autocomplete ul li[aria-selected]": {},
1383
+ "& .cm-completionIcon": {
1384
+ display: "none"
1385
+ },
1386
+ "& .cm-completionLabel": {
1387
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1388
+ },
1389
+ "& .cm-completionMatchedText": {
1390
+ textDecoration: "none"
1391
+ },
1392
+ //
1393
+ // widgets
1394
+ //
1395
+ "& .cm-widgetBuffer": {
1396
+ display: "none",
1397
+ height: 0
1398
+ },
1399
+ //
1400
+ // table
1401
+ //
1402
+ "& .cm-table-head": {
1403
+ padding: "2px 16px 2px 0px",
1404
+ borderBottom: `1px solid ${get2(tokens, "extend.colors.neutral.500")}`,
1405
+ fontWeight: 100,
1406
+ textAlign: "left",
1407
+ color: get2(tokens, "extend.colors.neutral.500")
1408
+ },
1409
+ "& .cm-table-cell": {
1410
+ padding: "2px 16px 2px 0px"
1411
+ },
1412
+ //
1413
+ // image
1414
+ //
1415
+ "& .cm-image": {
1416
+ margin: "0.5rem 0"
1417
+ },
842
1418
  //
843
1419
  // font size
844
1420
  // 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.
@@ -888,19 +1464,19 @@ var codeTheme = {
888
1464
 
889
1465
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
890
1466
  import { EditorState as EditorState2 } from "@codemirror/state";
891
- import { EditorView as EditorView6 } from "@codemirror/view";
1467
+ import { EditorView as EditorView9 } from "@codemirror/view";
892
1468
  import { useFocusableGroup } from "@fluentui/react-tabster";
893
1469
  import { vim } from "@replit/codemirror-vim";
894
- import defaultsDeep from "lodash.defaultsdeep";
1470
+ import defaultsDeep2 from "lodash.defaultsdeep";
895
1471
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
896
1472
  import { generateName } from "@dxos/display-name";
897
1473
  import { useThemeContext } from "@dxos/react-ui";
898
- import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
1474
+ import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
899
1475
  var EditorModes = [
900
1476
  "default",
901
1477
  "vim"
902
1478
  ];
903
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
1479
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
904
1480
  const { themeMode } = useThemeContext();
905
1481
  const tabsterDOMAttribute = useFocusableGroup({
906
1482
  tabBehavior: "limited"
@@ -945,13 +1521,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
945
1521
  const state2 = EditorState2.create({
946
1522
  doc: model.text(),
947
1523
  extensions: [
1524
+ readonly && EditorState2.readOnly.of(readonly),
948
1525
  // TODO(burdon): Factor out VIM mode?
949
1526
  editorMode === "vim" && vim(),
950
1527
  // Theme.
951
- EditorView6.baseTheme(defaultTheme),
952
- EditorView6.theme(slots?.editor?.theme ?? {}),
1528
+ EditorView9.baseTheme(defaultTheme),
1529
+ EditorView9.theme(slots?.editor?.theme ?? {}),
953
1530
  // TODO(burdon): themeMode doesn't change in storybooks.
954
- EditorView6.darkTheme.of(themeMode === "dark"),
1531
+ EditorView9.darkTheme.of(themeMode === "dark"),
955
1532
  // Storage and replication.
956
1533
  model.extension,
957
1534
  // Custom.
@@ -959,7 +1536,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
959
1536
  ].filter(Boolean)
960
1537
  });
961
1538
  view?.destroy();
962
- setView(new EditorView6({
1539
+ setView(new EditorView9({
963
1540
  state: state2,
964
1541
  parent: root
965
1542
  }));
@@ -973,6 +1550,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
973
1550
  root,
974
1551
  model,
975
1552
  themeMode,
1553
+ readonly,
976
1554
  editorMode
977
1555
  ]);
978
1556
  const handleKeyUp = useCallback((event) => {
@@ -1000,43 +1578,36 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
1000
1578
  onKeyUp: handleKeyUp
1001
1579
  });
1002
1580
  });
1003
- var maybeDebug = () => {
1004
- const items = localStorage.getItem("dxos.composer.demo");
1005
- if (items) {
1006
- return demo({
1007
- items: items.split(",")
1008
- });
1009
- }
1010
- return [];
1011
- };
1012
- var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1581
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1013
1582
  const { themeMode } = useThemeContext();
1014
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
1583
+ const slots = defaultsDeep2({}, _slots, defaultTextSlots);
1015
1584
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1016
1585
  ref: forwardedRef,
1586
+ readonly,
1017
1587
  extensions: [
1018
1588
  basicBundle({
1589
+ readonly,
1019
1590
  themeMode,
1020
1591
  placeholder: slots?.editor?.placeholder
1021
1592
  }),
1022
- maybeDebug(),
1023
1593
  ...extensions
1024
1594
  ],
1025
1595
  slots,
1026
1596
  ...props
1027
1597
  });
1028
1598
  });
1029
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1599
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1030
1600
  const { themeMode } = useThemeContext();
1031
- const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
1601
+ const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
1032
1602
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1033
1603
  ref: forwardedRef,
1604
+ readonly,
1034
1605
  extensions: [
1035
1606
  markdownBundle({
1607
+ readonly,
1036
1608
  themeMode,
1037
1609
  placeholder: slots?.editor?.placeholder
1038
1610
  }),
1039
- maybeDebug(),
1040
1611
  ...extensions
1041
1612
  ],
1042
1613
  slots,
@@ -1045,7 +1616,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
1045
1616
  });
1046
1617
  var defaultSlots = {
1047
1618
  root: {
1048
- className: mx2("p-2", inputSurface)
1619
+ className: mx3("p-2", inputSurface)
1049
1620
  }
1050
1621
  };
1051
1622
  var defaultTextSlots = {
@@ -1065,11 +1636,11 @@ var defaultMarkdownSlots = {
1065
1636
  import get3 from "lodash.get";
1066
1637
  import { useEffect as useEffect2, useState as useState2 } from "react";
1067
1638
  import { yCollab } from "y-codemirror.next";
1068
- import { invariant as invariant2 } from "@dxos/invariant";
1639
+ import { invariant as invariant3 } from "@dxos/invariant";
1069
1640
  import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
1070
1641
 
1071
1642
  // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1072
- import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
1643
+ import { Annotation, Facet, StateEffect, StateField as StateField6 } from "@codemirror/state";
1073
1644
  import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1074
1645
  import * as automerge2 from "@dxos/automerge/automerge";
1075
1646
 
@@ -1258,7 +1829,7 @@ var semaphoreFacet = Facet.define({
1258
1829
  combine: (values) => values.at(-1)
1259
1830
  });
1260
1831
  var automergePlugin = (handle, path) => {
1261
- const stateField = StateField3.define({
1832
+ const stateField = StateField6.define({
1262
1833
  create: () => ({
1263
1834
  lastHeads: automerge2.getHeads(handle.docSync()),
1264
1835
  unreconciledTransactions: [],
@@ -1296,8 +1867,8 @@ var automergePlugin = (handle, path) => {
1296
1867
  this._view = view;
1297
1868
  handle.addListener("change", this._handleChange);
1298
1869
  }
1299
- update(update2) {
1300
- if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
1870
+ update(update4) {
1871
+ if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
1301
1872
  queueMicrotask(() => {
1302
1873
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
1303
1874
  });
@@ -1319,7 +1890,7 @@ var reconcileAnnotationType = Annotation.define();
1319
1890
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1320
1891
 
1321
1892
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1322
- import * as random from "lib0/random";
1893
+ import * as random2 from "lib0/random";
1323
1894
  var cursorColors = [
1324
1895
  {
1325
1896
  color: "#30bced",
@@ -1354,7 +1925,7 @@ var cursorColors = [
1354
1925
  light: "#1be7ff33"
1355
1926
  }
1356
1927
  ];
1357
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
1928
+ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1358
1929
 
1359
1930
  // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1360
1931
  import * as decoding from "lib0/decoding";
@@ -1362,7 +1933,7 @@ import * as encoding from "lib0/encoding";
1362
1933
  import { Observable } from "lib0/observable";
1363
1934
  import * as awarenessProtocol from "y-protocols/awareness";
1364
1935
  import { log } from "@dxos/log";
1365
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1936
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1366
1937
  var messageAwareness = 1;
1367
1938
  var messageQueryAwareness = 3;
1368
1939
  var SpaceAwarenessProvider = class extends Observable {
@@ -1399,7 +1970,7 @@ var SpaceAwarenessProvider = class extends Observable {
1399
1970
  removed,
1400
1971
  origin
1401
1972
  }, {
1402
- F: __dxlog_file2,
1973
+ F: __dxlog_file3,
1403
1974
  L: 67,
1404
1975
  S: this,
1405
1976
  C: (f, a) => f(...a)
@@ -1412,7 +1983,7 @@ var SpaceAwarenessProvider = class extends Observable {
1412
1983
  }
1413
1984
  _handleSpaceMessage({ payload }) {
1414
1985
  log("space message", payload, {
1415
- F: __dxlog_file2,
1986
+ F: __dxlog_file3,
1416
1987
  L: 79,
1417
1988
  S: this,
1418
1989
  C: (f, a) => f(...a)
@@ -1457,7 +2028,7 @@ var SpaceAwarenessProvider = class extends Observable {
1457
2028
  };
1458
2029
 
1459
2030
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1460
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2031
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
1461
2032
  var useTextModel = ({ identity, space, text }) => {
1462
2033
  const [model, setModel] = useState2(() => createModel({
1463
2034
  identity,
@@ -1489,8 +2060,8 @@ var createModel = (options) => {
1489
2060
  }
1490
2061
  };
1491
2062
  var createYjsModel = ({ identity, space, text }) => {
1492
- invariant2(text?.doc && text?.content, void 0, {
1493
- F: __dxlog_file3,
2063
+ invariant3(text?.doc && text?.content, void 0, {
2064
+ F: __dxlog_file4,
1494
2065
  L: 74,
1495
2066
  S: void 0,
1496
2067
  A: [
@@ -1543,14 +2114,17 @@ export {
1543
2114
  YXmlFragment,
1544
2115
  autocomplete,
1545
2116
  basicBundle,
2117
+ blast,
1546
2118
  codeTheme,
1547
2119
  comments,
1548
2120
  cursorColor,
1549
2121
  defaultMarkdownSlots,
2122
+ defaultOptions,
1550
2123
  defaultSlots,
1551
2124
  defaultTextSlots,
1552
2125
  defaultTheme,
1553
2126
  demo,
2127
+ image,
1554
2128
  link,
1555
2129
  listener,
1556
2130
  markdownBundle,
@@ -1558,6 +2132,8 @@ export {
1558
2132
  markdownTags,
1559
2133
  markdownTagsExtension,
1560
2134
  markdownTheme,
2135
+ mention,
2136
+ table,
1561
2137
  tags2 as tags,
1562
2138
  tasklist,
1563
2139
  textTheme,