@dxos/react-ui-editor 0.3.10-main.dcfebef → 0.3.10-main.e38869c

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 (78) hide show
  1. package/dist/lib/browser/index.mjs +1181 -372
  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 +23 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +3 -0
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
  11. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +2 -1
  12. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
  13. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  14. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  15. package/dist/types/src/components/TextEditor/extensions/code.d.ts +2 -0
  16. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +1 -0
  17. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +2 -2
  18. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -1
  19. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +2 -1
  20. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -1
  21. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +1 -1
  22. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
  23. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +24 -0
  24. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +1 -0
  25. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  26. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  27. package/dist/types/src/components/TextEditor/extensions/index.d.ts +6 -0
  28. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  29. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  30. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  31. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +10 -1
  32. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  33. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +9 -3
  34. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  35. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  36. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  37. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  38. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  39. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -1
  40. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  41. package/dist/types/src/components/TextEditor/themes/default.d.ts +5 -0
  42. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  43. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useTextModel.d.ts +6 -0
  45. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  46. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -2
  47. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  48. package/dist/types/src/styles/markdown.d.ts +3 -2
  49. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  50. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  51. package/package.json +23 -21
  52. package/src/components/TextEditor/MarkdownEditor.stories.tsx +124 -32
  53. package/src/components/TextEditor/TextEditor.stories.tsx +12 -1
  54. package/src/components/TextEditor/TextEditor.tsx +31 -24
  55. package/src/components/TextEditor/extensions/autocomplete.ts +5 -20
  56. package/src/components/TextEditor/extensions/basic.ts +6 -4
  57. package/src/components/TextEditor/extensions/blast.ts +373 -0
  58. package/src/components/TextEditor/extensions/code.ts +99 -0
  59. package/src/components/TextEditor/extensions/comments.ts +29 -46
  60. package/src/components/TextEditor/extensions/demo.ts +4 -1
  61. package/src/components/TextEditor/extensions/experimental.tsx +15 -36
  62. package/src/components/TextEditor/extensions/highlight.ts +128 -0
  63. package/src/components/TextEditor/extensions/image.ts +67 -0
  64. package/src/components/TextEditor/extensions/index.ts +6 -0
  65. package/src/components/TextEditor/extensions/link.ts +17 -25
  66. package/src/components/TextEditor/extensions/listener.ts +3 -3
  67. package/src/components/TextEditor/extensions/markdown/bundle.ts +45 -37
  68. package/src/components/TextEditor/extensions/markdown/highlight.ts +148 -108
  69. package/src/components/TextEditor/extensions/mention.ts +31 -0
  70. package/src/components/TextEditor/extensions/table.ts +119 -0
  71. package/src/components/TextEditor/extensions/tasklist.ts +42 -28
  72. package/src/components/TextEditor/themes/default.ts +98 -4
  73. package/src/hooks/automerge/automerge.stories.tsx +1 -0
  74. package/src/hooks/useTextModel.ts +9 -0
  75. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -2
  76. package/src/styles/markdown.ts +15 -8
  77. package/src/testing/replicator.ts +1 -0
  78. 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
  ]
@@ -42,20 +42,451 @@ var autocomplete = ({ onSearch }) => {
42
42
  import { closeBrackets } from "@codemirror/autocomplete";
43
43
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
44
44
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
45
- import { EditorView, placeholder } from "@codemirror/view";
46
- var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
45
+ import { drawSelection, EditorView, placeholder } from "@codemirror/view";
46
+ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
47
+ EditorView.lineWrapping,
47
48
  // TODO(burdon): Compare with minimalSetup.
48
49
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
49
50
  bracketMatching(),
50
51
  closeBrackets(),
51
- EditorView.lineWrapping,
52
+ drawSelection(),
52
53
  _placeholder && placeholder(_placeholder),
53
54
  // TODO(burdon): Is this required?
54
55
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
55
56
  ].filter(Boolean);
56
57
 
58
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/blast.ts
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 = defaultOptions) => {
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.state.selection.main.head;
127
+ if (current !== last) {
128
+ last = current;
129
+ const { element, point } = getPoint(update4.view, current - 1);
130
+ if (element && point) {
131
+ blaster.spawn({
132
+ element,
133
+ point
134
+ });
135
+ }
136
+ }
137
+ }),
138
+ keymap2.of([
139
+ {
140
+ any: (_, event) => {
141
+ if (blaster) {
142
+ if (event.key === "Enter" && event.shiftKey) {
143
+ blaster.shake({
144
+ time: 0.8
145
+ });
146
+ return true;
147
+ }
148
+ }
149
+ return false;
150
+ }
151
+ }
152
+ ])
153
+ ];
154
+ };
155
+ var Blaster = class {
156
+ constructor(_node, _options) {
157
+ this._node = _node;
158
+ this._options = _options;
159
+ this._running = false;
160
+ this._shakeTime = 0;
161
+ this._shakeTimeMax = 0;
162
+ this._particles = [];
163
+ this._particlePointer = 0;
164
+ this._lastPoint = {
165
+ x: 0,
166
+ y: 0
167
+ };
168
+ this.shake = throttle(({ time }) => {
169
+ this._shakeTime = this._shakeTimeMax || time;
170
+ this._shakeTimeMax = time;
171
+ }, 100);
172
+ this.spawn = throttle(({ element, point }) => {
173
+ const color = getRGBComponents(element, this._options.color);
174
+ const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
175
+ const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
176
+ this._lastPoint = point;
177
+ for (let i = numParticles; i--; i > 0) {
178
+ this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);
179
+ this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;
180
+ }
181
+ }, 100);
182
+ this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
183
+ }
184
+ get node() {
185
+ return this._node;
186
+ }
187
+ initialize() {
188
+ invariant(!this._canvas && !this._ctx, void 0, {
189
+ F: __dxlog_file,
190
+ L: 138,
191
+ S: this,
192
+ A: [
193
+ "!this._canvas && !this._ctx",
194
+ ""
195
+ ]
196
+ });
197
+ this._canvas = document.createElement("canvas");
198
+ this._canvas.id = "code-blast-canvas";
199
+ this._canvas.style.position = "absolute";
200
+ this._canvas.style.zIndex = "0";
201
+ this._canvas.style.pointerEvents = "none";
202
+ this._ctx = this._canvas.getContext("2d");
203
+ const parent = this._node.parentElement?.parentElement;
204
+ parent?.appendChild(this._canvas);
205
+ this.resize();
206
+ }
207
+ destroy() {
208
+ this.stop();
209
+ if (this._canvas) {
210
+ this._canvas.remove();
211
+ this._canvas = void 0;
212
+ this._ctx = void 0;
213
+ }
214
+ }
215
+ resize() {
216
+ if (this._node.parentElement && this._canvas) {
217
+ const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
218
+ this._canvas.style.top = `${y}px`;
219
+ this._canvas.style.left = `${x}px`;
220
+ this._canvas.width = width;
221
+ this._canvas.height = height;
222
+ }
223
+ }
224
+ start() {
225
+ invariant(this._canvas && this._ctx, void 0, {
226
+ F: __dxlog_file,
227
+ L: 177,
228
+ S: this,
229
+ A: [
230
+ "this._canvas && this._ctx",
231
+ ""
232
+ ]
233
+ });
234
+ this._running = true;
235
+ this.loop();
236
+ }
237
+ stop() {
238
+ this._running = false;
239
+ this._node.style.transform = "translate(0px, 0px)";
240
+ }
241
+ loop() {
242
+ if (!this._running || !this._canvas || !this._ctx) {
243
+ return;
244
+ }
245
+ this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);
246
+ const now = (/* @__PURE__ */ new Date()).getTime();
247
+ this._lastTime ??= now;
248
+ const dt = (now - this._lastTime) / 1e3;
249
+ this._lastTime = now;
250
+ if (this._shakeTime > 0) {
251
+ this._shakeTime -= dt;
252
+ const magnitude = this._shakeTime / this._shakeTimeMax * this._options.shakeIntensity;
253
+ const shakeX = random(-magnitude, magnitude);
254
+ const shakeY = random(-magnitude, magnitude);
255
+ this._node.style.transform = `translate(${shakeX}px,${shakeY}px)`;
256
+ }
257
+ this.drawParticles();
258
+ requestAnimationFrame(this.loop.bind(this));
259
+ }
260
+ drawParticles() {
261
+ for (let i = this._particles.length; i--; i > 0) {
262
+ const particle = this._particles[i];
263
+ if (!particle) {
264
+ continue;
265
+ }
266
+ if (particle.alpha < 0.01 || particle.size <= 0.5) {
267
+ continue;
268
+ }
269
+ this._effect.update(this._ctx, particle);
270
+ }
271
+ }
272
+ };
273
+ var Effect = class {
274
+ constructor(_options) {
275
+ this._options = _options;
276
+ }
277
+ };
278
+ var Effect1 = class extends Effect {
279
+ create(x, y, color) {
280
+ return {
281
+ x,
282
+ y: y + 10,
283
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
284
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
285
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
286
+ color,
287
+ alpha: 1
288
+ };
289
+ }
290
+ update(ctx, particle) {
291
+ particle.vy += this._options.particleGravity;
292
+ particle.x += particle.vx;
293
+ particle.y += particle.vy;
294
+ particle.alpha *= this._options.particleAlphaFadeout;
295
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
296
+ ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);
297
+ }
298
+ };
299
+ var Effect2 = class extends Effect {
300
+ create(x, y, color) {
301
+ return {
302
+ x,
303
+ y: y + 10,
304
+ vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),
305
+ vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),
306
+ size: random(this._options.particleSize.min, this._options.particleSize.max),
307
+ color,
308
+ alpha: 1,
309
+ theta: random(0, 360) * Math.PI / 180,
310
+ drag: 0.92,
311
+ wander: 0.15
312
+ };
313
+ }
314
+ update(ctx, particle) {
315
+ particle.vy += this._options.particleGravity;
316
+ particle.x += particle.vx;
317
+ particle.y += particle.vy;
318
+ particle.vx *= particle.drag;
319
+ particle.vy *= particle.drag;
320
+ particle.theta += random(-0.5, 0.5);
321
+ particle.vx += Math.sin(particle.theta) * 0.1;
322
+ particle.vy += Math.cos(particle.theta) * 0.1;
323
+ particle.size *= this._options.particleShrinkRate;
324
+ particle.alpha *= this._options.particleAlphaFadeout;
325
+ ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;
326
+ ctx.beginPath();
327
+ ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);
328
+ ctx.fill();
329
+ }
330
+ };
331
+ function throttle(callback, limit) {
332
+ let wait = false;
333
+ return function(...args) {
334
+ if (!wait) {
335
+ callback.apply(this, args);
336
+ wait = true;
337
+ setTimeout(() => {
338
+ wait = false;
339
+ }, limit);
340
+ }
341
+ };
342
+ }
343
+ var getRGBComponents = (node, color) => {
344
+ if (typeof color === "function") {
345
+ return color();
346
+ }
347
+ const bgColor = getComputedStyle(node).color;
348
+ if (bgColor) {
349
+ const x = bgColor.match(/(\d+), (\d+), (\d+)/)?.slice(1);
350
+ if (x) {
351
+ return x;
352
+ }
353
+ }
354
+ return [
355
+ 50,
356
+ 50,
357
+ 50
358
+ ];
359
+ };
360
+ var random = (min, max) => {
361
+ if (!max) {
362
+ max = min;
363
+ min = 0;
364
+ }
365
+ return min + ~~(Math.random() * (max - min + 1));
366
+ };
367
+
368
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/code.ts
369
+ import { ViewPlugin, EditorView as EditorView3, Decoration } from "@codemirror/view";
370
+ import get from "lodash.get";
371
+ import { mx as mx2 } from "@dxos/react-ui-theme";
372
+
373
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
374
+ import { mx } from "@dxos/react-ui-theme";
375
+ var headings = {
376
+ 1: [
377
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
378
+ "-ml-[10px]"
379
+ ],
380
+ 2: [
381
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
382
+ "-ml-[8px]"
383
+ ],
384
+ 3: [
385
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
386
+ "-ml-[7px]"
387
+ ],
388
+ 4: [
389
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
390
+ "-ml-[6px]"
391
+ ],
392
+ 5: [
393
+ "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
394
+ "-ml-[5px]"
395
+ ],
396
+ 6: [
397
+ "mbs-4 mbe-2 font-medium text-inherit no-underline",
398
+ "-ml-[4px]"
399
+ ]
400
+ };
401
+ var heading = (level, readonly) => {
402
+ const [style, offset] = headings[level];
403
+ return mx(style, readonly && offset);
404
+ };
405
+ var light = "text-neutral-200 dark:text-neutral-800";
406
+ var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
407
+ var bold = "font-bold";
408
+ var italic = "italic";
409
+ var strikethrough = "line-through";
410
+ var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
411
+ var codeMark = "font-mono text-primary-500";
412
+ var inlineUrl = mx(code, "px-1");
413
+ var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
414
+ var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800";
415
+
416
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
417
+ import { tailwindConfig } from "@dxos/react-ui-theme";
418
+ var tokens = tailwindConfig({}).theme;
419
+
420
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/code.ts
421
+ var CODE_REGEX = /```[\s\S]*?```/gs;
422
+ var styles = EditorView3.baseTheme({
423
+ "& .cm-code-block": {
424
+ display: "block",
425
+ paddingInline: "4px !important"
426
+ },
427
+ "&light .cm-code-block": {
428
+ background: get(tokens, "extend.colors.neutral.50")
429
+ },
430
+ "&dark .cm-code-block": {
431
+ background: get(tokens, "extend.colors.neutral.850")
432
+ },
433
+ "& .cm-code-block-first": {
434
+ paddingTop: "4px !important",
435
+ borderTopLeftRadius: "4px",
436
+ borderTopRightRadius: "4px"
437
+ },
438
+ "& .cm-code-block-last": {
439
+ paddingBottom: "4px !important",
440
+ borderBottomLeftRadius: "4px",
441
+ borderBottomRightRadius: "4px"
442
+ }
443
+ });
444
+ var getLineRange = (lines, from, to) => {
445
+ const start = lines.findIndex((line) => line.from >= from);
446
+ const end = lines.findIndex((line) => line.to >= to);
447
+ return [
448
+ start,
449
+ end
450
+ ];
451
+ };
452
+ var code2 = () => {
453
+ const getDecorations = (view) => {
454
+ const decorations = [];
455
+ if (view.state.readOnly) {
456
+ const text = view.state.doc.sliceString(0);
457
+ const matches = text.matchAll(CODE_REGEX);
458
+ const blocks = view.viewportLineBlocks;
459
+ for (const match of matches) {
460
+ const range = getLineRange(blocks, match.index, match.index + match[0].length);
461
+ for (let i = range[0]; i <= range[1]; i++) {
462
+ const block = blocks[i];
463
+ decorations.push(Decoration.line({
464
+ class: mx2("cm-code-block", i === range[0] && "cm-code-block-first", i === range[1] && "cm-code-block-last")
465
+ }).range(block.from));
466
+ }
467
+ }
468
+ }
469
+ return Decoration.set(decorations);
470
+ };
471
+ return [
472
+ ViewPlugin.fromClass(class {
473
+ constructor(view) {
474
+ this.decorations = getDecorations(view);
475
+ }
476
+ update(update4) {
477
+ if (update4.docChanged || update4.viewportChanged) {
478
+ this.decorations = getDecorations(update4.view);
479
+ }
480
+ }
481
+ }, {
482
+ decorations: (value) => value.decorations
483
+ }),
484
+ styles
485
+ ];
486
+ };
487
+
57
488
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
58
- import { keymap as keymap2 } from "@codemirror/view";
489
+ import { keymap as keymap3 } from "@codemirror/view";
59
490
  var defaultItems = [
60
491
  "hello world!",
61
492
  "this is a test.",
@@ -65,7 +496,7 @@ var demo = ({ delay = 75, items = defaultItems } = {}) => {
65
496
  let t;
66
497
  let idx = 0;
67
498
  return [
68
- keymap2.of([
499
+ keymap3.of([
69
500
  {
70
501
  // Reset.
71
502
  key: "alt-meta-'",
@@ -112,18 +543,30 @@ var demo = ({ delay = 75, items = defaultItems } = {}) => {
112
543
  };
113
544
 
114
545
  // 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";
546
+ import { keymap as keymap4, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
116
547
  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";
548
+ import { invariant as invariant2 } from "@dxos/invariant";
549
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
550
+ var styles2 = EditorView4.baseTheme({
551
+ "& .cm-bookmark": {
552
+ cursor: "pointer",
553
+ margin: "4px",
554
+ padding: "4px",
555
+ backgroundColor: "yellow"
556
+ },
557
+ "& .cm-bookmark-selected": {
558
+ backgroundColor: "orange"
559
+ }
560
+ });
119
561
  var BookmarkWidget = class extends WidgetType {
120
- constructor(_pos, _id) {
562
+ constructor(_pos, _id, _handleClick) {
121
563
  super();
122
564
  this._pos = _pos;
123
565
  this._id = _id;
124
- invariant(this._id, void 0, {
125
- F: __dxlog_file,
126
- L: 41,
566
+ this._handleClick = _handleClick;
567
+ invariant2(this._id, void 0, {
568
+ F: __dxlog_file2,
569
+ L: 37,
127
570
  S: this,
128
571
  A: [
129
572
  "this._id",
@@ -137,65 +580,57 @@ var BookmarkWidget = class extends WidgetType {
137
580
  toDOM() {
138
581
  const span = document.createElement("span");
139
582
  span.className = "cm-bookmark";
140
- span.textContent = "\u{1F4AC}";
583
+ span.textContent = "\xA7";
584
+ span.onclick = () => this._handleClick();
141
585
  return span;
142
586
  }
143
587
  };
144
- var styles = EditorView2.baseTheme({
145
- "& .cm-bookmark": {
146
- cursor: "pointer",
147
- margin: "4px",
148
- padding: "4px",
149
- backgroundColor: "yellow"
150
- },
151
- "& .cm-bookmark-selected": {
152
- backgroundColor: "orange"
153
- }
154
- });
155
588
  var comments = (options = {}) => {
156
589
  let active;
157
590
  const bookmarkMatcher = new MatchDecorator({
158
591
  regexp: /\[\^(\w+)\]/g,
159
592
  decoration: (match, view, pos) => {
160
593
  const id = match[1];
161
- return Decoration.replace({
594
+ return Decoration2.replace({
162
595
  id,
163
- widget: new BookmarkWidget(pos, id)
596
+ widget: new BookmarkWidget(pos, id, () => handleUpdate({
597
+ active: id
598
+ }))
164
599
  });
165
600
  }
166
601
  });
167
- const bookmarks = ViewPlugin.fromClass(class {
602
+ const bookmarks = ViewPlugin2.fromClass(class {
168
603
  constructor(view) {
169
604
  this.bookmarks = bookmarkMatcher.createDeco(view);
170
605
  }
171
- update(update2) {
172
- this.bookmarks = bookmarkMatcher.updateDeco(update2, this.bookmarks);
606
+ update(update4) {
607
+ this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
173
608
  }
174
609
  }, {
175
610
  decorations: (instance) => instance.bookmarks,
176
- provide: (plugin) => EditorView2.atomicRanges.of((view) => {
177
- return view.plugin(plugin)?.bookmarks || Decoration.none;
611
+ provide: (plugin) => EditorView4.atomicRanges.of((view) => {
612
+ return view.plugin(plugin)?.bookmarks || Decoration2.none;
178
613
  })
179
614
  });
180
- const doUpdate = debounce((data) => {
615
+ const handleUpdate = debounce((data) => {
181
616
  options.onUpdate?.(data);
182
617
  }, 200);
183
618
  return [
184
- keymap3.of([
619
+ keymap4.of([
185
620
  {
186
621
  key: options?.key ?? "shift-meta-c",
187
622
  run: (view) => {
188
- const id = options.onCreate?.();
623
+ const { from, to, head } = view.state.selection.main;
624
+ const id = options.onCreate?.(from, to);
189
625
  if (id) {
190
- const pos = view.state.selection.main.head;
191
626
  const tag = `[^${id}]`;
192
627
  view.dispatch({
193
628
  changes: {
194
- from: pos,
629
+ from: head,
195
630
  insert: tag
196
631
  },
197
632
  selection: {
198
- anchor: pos + tag.length
633
+ anchor: head + tag.length
199
634
  }
200
635
  });
201
636
  return true;
@@ -206,20 +641,23 @@ var comments = (options = {}) => {
206
641
  ]),
207
642
  bookmarks,
208
643
  // Monitor cursor movement.
209
- EditorView2.updateListener.of((update2) => {
644
+ EditorView4.updateListener.of((update4) => {
210
645
  active = void 0;
211
646
  if (!options.onUpdate) {
212
647
  return;
213
648
  }
214
- const view = update2.view;
215
- const pos = view.state.selection.main.head;
649
+ const { view, state } = update4;
650
+ const pos = state.selection.main.head;
216
651
  const decorations = [];
217
652
  const rangeSet = view.plugin(bookmarks)?.bookmarks;
218
653
  let closest = Infinity;
219
- const { from, to } = view.visibleRanges?.[0] ?? {
220
- from: 0,
221
- to: view.state.doc.length
222
- };
654
+ const { from, to } = (
655
+ /* view.visibleRanges?.[0] ?? */
656
+ {
657
+ from: 0,
658
+ to: state.doc.length
659
+ }
660
+ );
223
661
  rangeSet?.between(from, to, (from2, to2, value) => {
224
662
  decorations.push({
225
663
  from: from2,
@@ -233,7 +671,7 @@ var comments = (options = {}) => {
233
671
  }
234
672
  });
235
673
  if (decorations.length) {
236
- doUpdate({
674
+ handleUpdate({
237
675
  active,
238
676
  items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
239
677
  id,
@@ -243,15 +681,170 @@ var comments = (options = {}) => {
243
681
  });
244
682
  }
245
683
  }),
246
- styles
684
+ styles2
247
685
  ];
248
686
  };
249
687
 
250
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
688
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/highlight.ts
689
+ import { StateEffect, StateField } from "@codemirror/state";
690
+ import { Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
691
+ import sortBy from "lodash.sortby";
692
+ import { useEffect } from "react";
693
+ var styles3 = EditorView5.baseTheme({
694
+ "& .cm-highlight": {
695
+ backgroundColor: "yellow"
696
+ },
697
+ "& .cm-highlight::before": {
698
+ content: "]"
699
+ },
700
+ "& .cm-highlight-active": {
701
+ backgroundColor: "lime"
702
+ }
703
+ });
704
+ var marks = {
705
+ highlight: Decoration3.mark({
706
+ class: "cm-highlight"
707
+ }),
708
+ HighlightActive: Decoration3.mark({
709
+ class: "cm-highlight-active"
710
+ })
711
+ };
712
+ var setHighlights = StateEffect.define();
713
+ var useHighlights = (view, ranges = []) => {
714
+ useEffect(() => {
715
+ view?.dispatch({
716
+ effects: setHighlights.of(ranges)
717
+ });
718
+ }, [
719
+ view,
720
+ ranges
721
+ ]);
722
+ };
723
+ var highlightStateField = StateField.define({
724
+ create: () => [],
725
+ update: (threads, tr) => {
726
+ for (const effect of tr.effects) {
727
+ if (effect.is(setHighlights)) {
728
+ return effect.value;
729
+ }
730
+ }
731
+ return threads;
732
+ }
733
+ });
734
+ var highlightDecorations = EditorView5.decorations.compute([
735
+ highlightStateField
736
+ ], (state) => {
737
+ const selectionRanges = state.field(highlightStateField);
738
+ const decorations = sortBy(selectionRanges ?? [], (selection) => selection.from)?.flatMap((selection) => {
739
+ const range = {
740
+ from: selection.from,
741
+ to: selection.to + 1
742
+ };
743
+ if (selection.to > selection.from) {
744
+ if (selection.active) {
745
+ return marks.HighlightActive.range(range.from, range.to);
746
+ } else {
747
+ return marks.highlight.range(range.from, range.to);
748
+ }
749
+ } else {
750
+ return [];
751
+ }
752
+ }) ?? [];
753
+ return Decoration3.set(decorations);
754
+ });
755
+ var listener = (options) => {
756
+ return EditorView5.updateListener.of((update4) => {
757
+ const { view, state } = update4;
758
+ const { head } = state.selection.main;
759
+ const ranges = state.field(highlightStateField);
760
+ let mod = false;
761
+ const newRanges = ranges.map((range) => {
762
+ const active = head >= range.from && head <= range.to;
763
+ if (active !== range.active) {
764
+ mod = true;
765
+ return {
766
+ ...range,
767
+ active
768
+ };
769
+ } else {
770
+ return range;
771
+ }
772
+ });
773
+ if (mod) {
774
+ view.dispatch({
775
+ effects: setHighlights.of(newRanges)
776
+ });
777
+ options?.onChange?.(newRanges.find((range) => range.active)?.id);
778
+ }
779
+ });
780
+ };
781
+ var highlight = (options = {}) => {
782
+ return [
783
+ highlightStateField,
784
+ highlightDecorations,
785
+ listener(options),
786
+ styles3
787
+ ];
788
+ };
789
+
790
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/image.ts
251
791
  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 {
792
+ import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
793
+ import { Decoration as Decoration4, EditorView as EditorView6, WidgetType as WidgetType2 } from "@codemirror/view";
794
+ var image = (options = {}) => {
795
+ return StateField2.define({
796
+ create: (state) => update(state, options),
797
+ update: (_, tr) => update(tr.state, options),
798
+ provide: (field) => EditorView6.decorations.from(field)
799
+ });
800
+ };
801
+ var update = (state, options) => {
802
+ const builder = new RangeSetBuilder();
803
+ const cursor = state.selection.main.head;
804
+ syntaxTree(state).iterate({
805
+ enter: (node) => {
806
+ if (node.name === "Image") {
807
+ const urlNode = node.node.getChild("URL");
808
+ if (urlNode) {
809
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
810
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
811
+ builder.add(hide ? node.from : node.to, node.to, Decoration4.replace({
812
+ widget: new ImageWidget(url)
813
+ }));
814
+ }
815
+ }
816
+ }
817
+ });
818
+ return builder.finish();
819
+ };
820
+ var ImageWidget = class extends WidgetType2 {
821
+ constructor(_url) {
822
+ super();
823
+ this._url = _url;
824
+ }
825
+ eq(other) {
826
+ return this._url === other?._url;
827
+ }
828
+ toDOM(view) {
829
+ const img = document.createElement("img");
830
+ img.setAttribute("src", this._url);
831
+ img.setAttribute("class", "cm-image");
832
+ return img;
833
+ }
834
+ };
835
+
836
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
837
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
838
+ import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
839
+ import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
840
+ var link = (options = {}) => {
841
+ return StateField3.define({
842
+ create: (state) => update2(state, options),
843
+ update: (_, tr) => update2(tr.state, options),
844
+ provide: (field) => EditorView7.decorations.from(field)
845
+ });
846
+ };
847
+ var LinkButton = class extends WidgetType3 {
255
848
  constructor(_pos, _url, _onAttach) {
256
849
  super();
257
850
  this._pos = _pos;
@@ -267,7 +860,7 @@ var LinkButton = class extends WidgetType2 {
267
860
  return el;
268
861
  }
269
862
  };
270
- var LinkText = class extends WidgetType2 {
863
+ var LinkText = class extends WidgetType3 {
271
864
  constructor(_pos, _text, _url) {
272
865
  super();
273
866
  this._pos = _pos;
@@ -297,26 +890,26 @@ var LinkText = class extends WidgetType2 {
297
890
  return link2;
298
891
  }
299
892
  };
300
- var update = (state, options) => {
301
- const builder = new RangeSetBuilder();
893
+ var update2 = (state, options) => {
894
+ const builder = new RangeSetBuilder2();
302
895
  const cursor = state.selection.main.head;
303
- syntaxTree(state).iterate({
896
+ syntaxTree2(state).iterate({
304
897
  enter: (node) => {
305
898
  if (node.name === "Link") {
306
- const marks = node.node.getChildren("LinkMark");
307
- const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
899
+ const marks2 = node.node.getChildren("LinkMark");
900
+ const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
308
901
  const urlNode = node.node.getChild("URL");
309
902
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
310
903
  if (!url) {
311
904
  return false;
312
905
  }
313
- if (cursor < node.from || cursor > node.to) {
314
- builder.add(node.from, node.to, Decoration2.replace({
906
+ if (state.readOnly || cursor < node.from || cursor > node.to) {
907
+ builder.add(node.from, node.to, Decoration5.replace({
315
908
  widget: new LinkText(node.from, text, options.link ? url : void 0)
316
909
  }));
317
910
  }
318
911
  if (options.onRender) {
319
- builder.add(node.to, node.to, Decoration2.replace({
912
+ builder.add(node.to, node.to, Decoration5.replace({
320
913
  widget: new LinkButton(node.from, url, options.onRender)
321
914
  }));
322
915
  }
@@ -326,69 +919,39 @@ var update = (state, options) => {
326
919
  });
327
920
  return builder.finish();
328
921
  };
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
922
 
337
923
  // 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());
924
+ import { StateField as StateField4 } from "@codemirror/state";
925
+ var listener2 = ({ onChange }) => {
926
+ return StateField4.define({
927
+ create: () => null,
928
+ update: (_value, transaction) => {
929
+ if (transaction.docChanged && onChange) {
930
+ onChange(transaction.newDoc.toString());
931
+ }
932
+ return null;
344
933
  }
345
- return null;
346
- }
347
- });
934
+ });
935
+ };
348
936
 
349
937
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
350
938
  import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
351
- import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
939
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
352
940
  import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
353
941
  import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
354
942
  import { languages } from "@codemirror/language-data";
355
- import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
943
+ import { searchKeymap } from "@codemirror/search";
356
944
  import { EditorState } from "@codemirror/state";
357
945
  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";
946
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView8, highlightActiveLine, keymap as keymap5, placeholder as placeholder2 } from "@codemirror/view";
359
947
 
360
948
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
361
949
  import { markdownLanguage } from "@codemirror/lang-markdown";
362
950
  import { HighlightStyle } from "@codemirror/language";
363
951
  import { tags, styleTags, Tag } from "@lezer/highlight";
364
- import get from "lodash.get";
365
-
366
- // packages/ui/react-ui-editor/src/styles/markdown.ts
367
- 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"
375
- };
376
- var light = "text-neutral-200 dark:text-neutral-800";
377
- var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
378
- var bold = "font-bold";
379
- var italic = "italic";
380
- var strikethrough = "line-through";
381
- var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
382
- var codeMark = "font-mono text-primary-500";
383
- var inlineUrl = mx(code, "px-1");
384
- 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";
386
-
387
- // packages/ui/react-ui-editor/src/styles/tokens.ts
388
- import { tailwindConfig } from "@dxos/react-ui-theme";
389
- var tokens = tailwindConfig({}).theme;
390
-
391
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
952
+ import { Table } from "@lezer/markdown";
953
+ import get2 from "lodash.get";
954
+ import { mx as mx3 } from "@dxos/react-ui-theme";
392
955
  var markdownTags = {
393
956
  Blockquote: Tag.define(),
394
957
  CodeMark: Tag.define(),
@@ -401,182 +964,202 @@ var markdownTags = {
401
964
  LinkReference: Tag.define(),
402
965
  ListMark: Tag.define(),
403
966
  QuoteMark: Tag.define(),
404
- URL: Tag.define()
967
+ URL: Tag.define(),
968
+ // Custom.
969
+ TableCell: Tag.define()
405
970
  };
406
- var markdownTagsExtension = {
407
- props: [
408
- styleTags(markdownTags)
409
- ]
410
- };
411
- var markdownHighlightStyle = HighlightStyle.define([
412
- {
413
- tag: [
414
- tags.keyword,
415
- tags.name,
416
- tags.deleted,
417
- tags.character,
418
- tags.propertyName,
419
- tags.macroName,
420
- tags.color,
421
- tags.constant(tags.name),
422
- tags.standard(tags.name),
423
- tags.definition(tags.name),
424
- tags.separator,
425
- tags.typeName,
426
- tags.className,
427
- tags.number,
428
- tags.changed,
429
- tags.annotation,
430
- tags.modifier,
431
- tags.self,
432
- tags.namespace,
433
- tags.operator,
434
- tags.operatorKeyword,
435
- tags.escape,
436
- tags.regexp,
437
- tags.special(tags.string),
438
- tags.meta,
439
- tags.comment,
440
- tags.atom,
441
- tags.bool,
442
- tags.special(tags.variableName),
443
- tags.processingInstruction,
444
- tags.string,
445
- tags.inserted,
446
- tags.invalid
447
- ],
448
- color: "inherit !important"
449
- },
450
- // Markdown marks.
451
- {
452
- tag: [
453
- tags.meta,
454
- tags.processingInstruction,
455
- markdownTags.CodeMark,
456
- markdownTags.EmphasisMark,
457
- markdownTags.HeaderMark,
458
- markdownTags.LinkLabel,
459
- markdownTags.LinkReference,
460
- markdownTags.ListMark,
461
- markdownTags.QuoteMark
462
- ],
463
- class: mark
464
- },
465
- // Headings.
466
- {
467
- tag: tags.heading1,
468
- class: heading[1]
469
- },
470
- {
471
- tag: tags.heading2,
472
- class: heading[2]
473
- },
474
- {
475
- tag: tags.heading3,
476
- class: heading[3]
477
- },
478
- {
479
- tag: tags.heading4,
480
- class: heading[4]
481
- },
482
- {
483
- tag: tags.heading5,
484
- class: heading[5]
485
- },
486
- {
487
- tag: tags.heading6,
488
- class: heading[6]
489
- },
490
- // Emphasis.
491
- {
492
- tag: tags.emphasis,
493
- class: italic
494
- },
495
- {
496
- tag: tags.strong,
497
- class: bold
498
- },
499
- {
500
- tag: tags.strikethrough,
501
- class: strikethrough
502
- },
503
- // Naked URLs.
504
- {
505
- tag: [
506
- markdownTags.URL
507
- ],
508
- class: inlineUrl
509
- },
510
- // E.g., code block language (after ```).
511
- {
512
- tag: [
513
- tags.function(tags.variableName),
514
- tags.labelName
515
- ],
516
- class: codeMark
517
- },
518
- // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
519
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
520
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
521
- // the code, but all other CSS properties will be inherited.
522
- // IMPORTANT: Therefore, the fenced code block will use the base editor font.
523
- {
524
- tag: [
525
- markdownTags.CodeText,
526
- markdownTags.InlineCode
527
- ],
528
- class: code
529
- },
530
- {
531
- tag: [
532
- markdownTags.QuoteMark
533
- ],
534
- class: blockquote
535
- },
536
- {
537
- tag: [
538
- markdownTags.HorizontalRule
539
- ],
540
- class: horizontalRule
541
- }
542
- ], {
543
- scope: markdownLanguage,
544
- all: {
545
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
971
+ Table.defineNodes?.forEach((node) => {
972
+ switch (node?.name) {
973
+ case "TableCell": {
974
+ node.style = markdownTags.TableCell;
975
+ break;
976
+ }
546
977
  }
547
978
  });
979
+ var markdownTagsExtensions = [
980
+ Table,
981
+ {
982
+ props: [
983
+ styleTags(markdownTags)
984
+ ]
985
+ }
986
+ ];
987
+ var markdownHighlightStyle = (readonly) => {
988
+ return HighlightStyle.define([
989
+ {
990
+ tag: [
991
+ tags.keyword,
992
+ tags.name,
993
+ tags.deleted,
994
+ tags.character,
995
+ tags.propertyName,
996
+ tags.macroName,
997
+ tags.color,
998
+ tags.constant(tags.name),
999
+ tags.standard(tags.name),
1000
+ tags.definition(tags.name),
1001
+ tags.separator,
1002
+ tags.typeName,
1003
+ tags.className,
1004
+ tags.number,
1005
+ tags.changed,
1006
+ tags.annotation,
1007
+ tags.modifier,
1008
+ tags.self,
1009
+ tags.namespace,
1010
+ tags.operator,
1011
+ tags.operatorKeyword,
1012
+ tags.escape,
1013
+ tags.regexp,
1014
+ tags.special(tags.string),
1015
+ tags.meta,
1016
+ tags.comment,
1017
+ tags.atom,
1018
+ tags.bool,
1019
+ tags.special(tags.variableName),
1020
+ tags.processingInstruction,
1021
+ tags.string,
1022
+ tags.inserted,
1023
+ tags.invalid
1024
+ ],
1025
+ color: "inherit !important"
1026
+ },
1027
+ // Markdown marks.
1028
+ {
1029
+ tag: [
1030
+ tags.meta,
1031
+ tags.processingInstruction,
1032
+ markdownTags.LinkLabel,
1033
+ markdownTags.LinkReference,
1034
+ markdownTags.ListMark
1035
+ ],
1036
+ class: mark
1037
+ },
1038
+ // Markdown marks.
1039
+ {
1040
+ tag: [
1041
+ markdownTags.CodeMark,
1042
+ markdownTags.HeaderMark,
1043
+ markdownTags.QuoteMark,
1044
+ markdownTags.EmphasisMark
1045
+ ],
1046
+ class: readonly ? "hidden" : mark
1047
+ },
1048
+ // E.g., code block language (after ```).
1049
+ {
1050
+ tag: [
1051
+ tags.function(tags.variableName),
1052
+ tags.labelName
1053
+ ],
1054
+ class: readonly ? "hidden" : codeMark
1055
+ },
1056
+ {
1057
+ tag: [
1058
+ tags.monospace
1059
+ ],
1060
+ class: "font-mono"
1061
+ },
1062
+ // Headings.
1063
+ {
1064
+ tag: tags.heading1,
1065
+ class: heading(1, readonly)
1066
+ },
1067
+ {
1068
+ tag: tags.heading2,
1069
+ class: heading(2, readonly)
1070
+ },
1071
+ {
1072
+ tag: tags.heading3,
1073
+ class: heading(3, readonly)
1074
+ },
1075
+ {
1076
+ tag: tags.heading4,
1077
+ class: heading(4, readonly)
1078
+ },
1079
+ {
1080
+ tag: tags.heading5,
1081
+ class: heading(5, readonly)
1082
+ },
1083
+ {
1084
+ tag: tags.heading6,
1085
+ class: heading(6, readonly)
1086
+ },
1087
+ // Emphasis.
1088
+ {
1089
+ tag: tags.emphasis,
1090
+ class: italic
1091
+ },
1092
+ {
1093
+ tag: tags.strong,
1094
+ class: bold
1095
+ },
1096
+ {
1097
+ tag: tags.strikethrough,
1098
+ class: strikethrough
1099
+ },
1100
+ // Naked URLs.
1101
+ {
1102
+ tag: [
1103
+ markdownTags.URL
1104
+ ],
1105
+ class: inlineUrl
1106
+ },
1107
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
1108
+ // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
1109
+ // when a language is specified. In this case, the syntax highlighting extensions will colorize
1110
+ // the code, but all other CSS properties will be inherited.
1111
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font.
1112
+ {
1113
+ tag: [
1114
+ markdownTags.CodeText,
1115
+ markdownTags.InlineCode
1116
+ ],
1117
+ class: code
1118
+ },
1119
+ {
1120
+ tag: [
1121
+ markdownTags.QuoteMark
1122
+ ],
1123
+ class: blockquote
1124
+ },
1125
+ // TODO(burdon): Replace with extension.
1126
+ {
1127
+ tag: [
1128
+ markdownTags.HorizontalRule
1129
+ ],
1130
+ class: mx3(horizontalRule, readonly && "-indent-[100rem]")
1131
+ },
1132
+ // TODO(burdon): Only if being edited.
1133
+ {
1134
+ tag: [
1135
+ markdownTags.TableCell
1136
+ ],
1137
+ class: "font-mono"
1138
+ }
1139
+ ], {
1140
+ scope: markdownLanguage,
1141
+ all: {
1142
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1143
+ }
1144
+ });
1145
+ };
548
1146
 
549
1147
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
550
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1148
+ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
551
1149
  return [
1150
+ EditorState.allowMultipleSelections.of(true),
1151
+ EditorState.tabSize.of(2),
1152
+ EditorView8.lineWrapping,
1153
+ customKeymap(),
552
1154
  bracketMatching2(),
553
1155
  closeBrackets2(),
554
- _placeholder && placeholder2(_placeholder),
555
- EditorState.allowMultipleSelections.of(true),
556
- EditorView4.lineWrapping,
557
1156
  crosshairCursor(),
558
1157
  dropCursor(),
559
- drawSelection(),
1158
+ drawSelection2(),
560
1159
  highlightActiveLine(),
561
- highlightActiveLineGutter(),
562
- highlightSelectionMatches(),
563
- highlightSpecialChars(),
564
1160
  history(),
565
1161
  indentOnInput(),
566
- rectangularSelection(),
567
- // https://codemirror.net/docs/ref/#view.keymap
568
- keymap4.of([
569
- // https://codemirror.net/docs/ref/#commands.defaultKeymap
570
- ...defaultKeymap,
571
- // https://codemirror.net/docs/ref/#commands.historyKeymap
572
- ...historyKeymap,
573
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
574
- ...closeBracketsKeymap,
575
- // https://codemirror.net/docs/ref/#search.searchKeymap
576
- ...searchKeymap,
577
- // https://codemirror.net/docs/ref/#commands.indentWithTab
578
- indentWithTab
579
- ]),
1162
+ _placeholder && placeholder2(_placeholder),
580
1163
  // Main extension.
581
1164
  // https://github.com/codemirror/lang-markdown
582
1165
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -591,20 +1174,156 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
591
1174
  codeLanguages: languages,
592
1175
  // Parser extensions.
593
1176
  extensions: [
594
- // GFM, // TODO(burdon): Provided by default?
595
- markdownTagsExtension
1177
+ // GFM provided by default.
1178
+ markdownTagsExtensions
596
1179
  ]
597
1180
  }),
1181
+ // https://github.com/codemirror/theme-one-dark
1182
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
598
1183
  // Custom styles.
599
- syntaxHighlighting2(markdownHighlightStyle),
600
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
601
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
1184
+ syntaxHighlighting2(markdownHighlightStyle(readonly))
602
1185
  ].filter(Boolean);
603
1186
  };
1187
+ var customKeymap = () => keymap5.of([
1188
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
1189
+ indentWithTab,
1190
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
1191
+ ...standardKeymap,
1192
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
1193
+ ...historyKeymap,
1194
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1195
+ ...closeBracketsKeymap,
1196
+ // https://codemirror.net/docs/ref/#search.searchKeymap
1197
+ ...searchKeymap
1198
+ ]);
1199
+
1200
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/mention.ts
1201
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
1202
+ var mention = ({ onSearch }) => {
1203
+ return autocompletion2({
1204
+ closeOnBlur: false,
1205
+ override: [
1206
+ (context) => {
1207
+ const match = context.matchBefore(/@(\w+)?/);
1208
+ if (!match || match.from === match.to && !context.explicit) {
1209
+ return null;
1210
+ }
1211
+ console.log(match);
1212
+ return {
1213
+ from: match.from,
1214
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
1215
+ label: `@${value}`
1216
+ }))
1217
+ };
1218
+ }
1219
+ ]
1220
+ });
1221
+ };
1222
+
1223
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/table.ts
1224
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1225
+ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField5 } from "@codemirror/state";
1226
+ import { Decoration as Decoration6, EditorView as EditorView9, WidgetType as WidgetType4 } from "@codemirror/view";
1227
+ var table = (options = {}) => {
1228
+ return StateField5.define({
1229
+ create: (state) => update3(state, options),
1230
+ update: (_, tr) => update3(tr.state, options),
1231
+ provide: (field) => EditorView9.decorations.from(field)
1232
+ });
1233
+ };
1234
+ var update3 = (state, options) => {
1235
+ const builder = new RangeSetBuilder3();
1236
+ const cursor = state.selection.main.head;
1237
+ const tables = [];
1238
+ const getTable = () => tables[tables.length - 1];
1239
+ const getRow = () => {
1240
+ const table2 = getTable();
1241
+ return table2.rows?.[table2.rows.length - 1];
1242
+ };
1243
+ syntaxTree3(state).iterate({
1244
+ enter: (node) => {
1245
+ switch (node.name) {
1246
+ case "Table": {
1247
+ tables.push({
1248
+ from: node.from,
1249
+ to: node.to
1250
+ });
1251
+ break;
1252
+ }
1253
+ case "TableHeader": {
1254
+ getTable().header = [];
1255
+ break;
1256
+ }
1257
+ case "TableRow": {
1258
+ (getTable().rows ??= []).push([]);
1259
+ break;
1260
+ }
1261
+ case "TableCell": {
1262
+ const row = getRow();
1263
+ if (row) {
1264
+ row.push(state.sliceDoc(node.from, node.to));
1265
+ } else {
1266
+ getTable().header?.push(state.sliceDoc(node.from, node.to));
1267
+ }
1268
+ break;
1269
+ }
1270
+ }
1271
+ }
1272
+ });
1273
+ tables.forEach((table2) => {
1274
+ const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
1275
+ hide && builder.add(table2.from, table2.to, Decoration6.replace({
1276
+ widget: new TableWidget(table2)
1277
+ }));
1278
+ builder.add(table2.from, table2.to, Decoration6.mark({
1279
+ class: "cm-table"
1280
+ }));
1281
+ });
1282
+ return builder.finish();
1283
+ };
1284
+ var TableWidget = class extends WidgetType4 {
1285
+ constructor(_table) {
1286
+ super();
1287
+ this._table = _table;
1288
+ }
1289
+ eq(other) {
1290
+ return this._table.from === other?._table?.from;
1291
+ }
1292
+ toDOM(view) {
1293
+ const table2 = document.createElement("table");
1294
+ {
1295
+ const header = table2.appendChild(document.createElement("thead"));
1296
+ const tr = header.appendChild(document.createElement("tr"));
1297
+ this._table.header?.forEach((cell) => {
1298
+ const th = document.createElement("th");
1299
+ th.setAttribute("class", "cm-table-head");
1300
+ tr.appendChild(th).textContent = cell;
1301
+ });
1302
+ }
1303
+ {
1304
+ const body = table2.appendChild(document.createElement("tbody"));
1305
+ this._table.rows?.forEach((row) => {
1306
+ const tr = body.appendChild(document.createElement("tr"));
1307
+ row.forEach((cell) => {
1308
+ const td = document.createElement("td");
1309
+ td.setAttribute("class", "cm-table-cell");
1310
+ tr.appendChild(td).textContent = cell;
1311
+ });
1312
+ });
1313
+ }
1314
+ return table2;
1315
+ }
1316
+ };
604
1317
 
605
1318
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
606
- import { EditorView as EditorView5, Decoration as Decoration3, WidgetType as WidgetType3, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
607
- var CheckboxWidget = class extends WidgetType3 {
1319
+ import { EditorView as EditorView10, Decoration as Decoration7, WidgetType as WidgetType5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1320
+ var styles4 = EditorView10.baseTheme({
1321
+ "& .cm-task-item": {
1322
+ paddingLeft: "4px",
1323
+ paddingRight: "8px"
1324
+ }
1325
+ });
1326
+ var CheckboxWidget = class extends WidgetType5 {
608
1327
  constructor(_pos, _checked, _indent, _onCheck) {
609
1328
  super();
610
1329
  this._pos = _pos;
@@ -623,30 +1342,29 @@ var CheckboxWidget = class extends WidgetType3 {
623
1342
  const input = wrap.appendChild(document.createElement("input"));
624
1343
  input.type = "checkbox";
625
1344
  input.checked = this._checked;
626
- input.onchange = (event) => {
627
- this._onCheck(event.target.checked);
628
- return true;
629
- };
1345
+ if (!this._onCheck) {
1346
+ input.setAttribute("disabled", "true");
1347
+ }
1348
+ if (this._onCheck) {
1349
+ input.onchange = (event) => {
1350
+ this._onCheck?.(event.target.checked);
1351
+ return true;
1352
+ };
1353
+ }
630
1354
  return wrap;
631
1355
  }
632
1356
  ignoreEvent() {
633
1357
  return false;
634
1358
  }
635
1359
  };
636
- var styles2 = EditorView5.baseTheme({
637
- "& .cm-task-item": {
638
- paddingLeft: "4px",
639
- paddingRight: "8px"
640
- }
641
- });
642
- var tasklist = () => {
1360
+ var tasklist = (options = {}) => {
643
1361
  const taskMatcher = new MatchDecorator2({
644
1362
  regexp: /^(\s*)- \[([ xX])\]\s/g,
645
1363
  decoration: (match, view, pos) => {
646
1364
  const indent = Math.floor(match[1].length / 2);
647
1365
  const checked = match[2] === "x" || match[2] === "X";
648
- return Decoration3.replace({
649
- widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
1366
+ return Decoration7.replace({
1367
+ widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
650
1368
  const idx = pos + match[0].indexOf("[") + 1;
651
1369
  view.dispatch({
652
1370
  changes: {
@@ -663,22 +1381,22 @@ var tasklist = () => {
663
1381
  });
664
1382
  }
665
1383
  });
666
- const tasks = ViewPlugin2.fromClass(class {
1384
+ const tasks = ViewPlugin3.fromClass(class {
667
1385
  constructor(view) {
668
1386
  this.tasks = taskMatcher.createDeco(view);
669
1387
  }
670
- update(update2) {
671
- this.tasks = taskMatcher.updateDeco(update2, this.tasks);
1388
+ update(update4) {
1389
+ this.tasks = taskMatcher.updateDeco(update4, this.tasks);
672
1390
  }
673
1391
  }, {
674
- decorations: (instance) => instance.tasks,
675
- provide: (plugin) => EditorView5.atomicRanges.of((view) => {
676
- return view.plugin(plugin)?.tasks || Decoration3.none;
1392
+ decorations: (value) => value.tasks,
1393
+ provide: (plugin) => EditorView10.atomicRanges.of((view) => {
1394
+ return view.plugin(plugin)?.tasks || Decoration7.none;
677
1395
  })
678
1396
  });
679
1397
  return [
680
1398
  tasks,
681
- styles2
1399
+ styles4
682
1400
  ];
683
1401
  };
684
1402
 
@@ -727,7 +1445,7 @@ var tooltip = ({ onHover, regexp = markdownLinkRegexp }) => hoverTooltip((view,
727
1445
  });
728
1446
 
729
1447
  // packages/ui/react-ui-editor/src/components/TextEditor/themes/default.ts
730
- import get2 from "lodash.get";
1448
+ import get3 from "lodash.get";
731
1449
  var defaultTheme = {
732
1450
  //
733
1451
  // Main layout:
@@ -744,7 +1462,8 @@ var defaultTheme = {
744
1462
  outline: "none"
745
1463
  },
746
1464
  "& .cm-scroller": {
747
- overflow: "visible"
1465
+ overflow: "visible",
1466
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
748
1467
  },
749
1468
  "& .cm-content": {
750
1469
  padding: 0,
@@ -752,20 +1471,20 @@ var defaultTheme = {
752
1471
  fontSize: "16px"
753
1472
  },
754
1473
  "&light .cm-content": {
755
- color: get2(tokens, "extend.colors.neutral.900", "black"),
1474
+ color: get3(tokens, "extend.colors.neutral.900", "black"),
756
1475
  caretColor: "black"
757
1476
  },
758
1477
  "&dark .cm-content": {
759
- color: get2(tokens, "extend.colors.neutral.100", "white"),
1478
+ color: get3(tokens, "extend.colors.neutral.100", "white"),
760
1479
  caretColor: "white"
761
1480
  },
762
1481
  //
763
1482
  // Cursor
764
1483
  //
765
- "&light .cm-cursor": {
1484
+ "&light .cm-cursor, &light .cm-dropCursor": {
766
1485
  borderLeft: "2px solid black"
767
1486
  },
768
- "&dark .cm-cursor": {
1487
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
769
1488
  borderLeft: "2px solid white"
770
1489
  },
771
1490
  "& .cm-placeholder": {
@@ -785,16 +1504,16 @@ var defaultTheme = {
785
1504
  // selection
786
1505
  //
787
1506
  "&light .cm-selectionBackground, &light.cm-focused .cm-selectionBackground": {
788
- background: get2(tokens, "extend.colors.primary.150", "#00ffff")
1507
+ background: get3(tokens, "extend.colors.primary.150", "#00ffff")
789
1508
  },
790
1509
  "&dark .cm-selectionBackground, &dark.cm-focused .cm-selectionBackground": {
791
- background: get2(tokens, "extend.colors.primary.850", "#00ffff")
1510
+ background: get3(tokens, "extend.colors.primary.850", "#00ffff")
792
1511
  },
793
1512
  "&light .cm-selectionMatch": {
794
- background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
1513
+ background: get3(tokens, "extend.colors.primary.250", "#00ffff") + "44"
795
1514
  },
796
1515
  "&dark .cm-selectionMatch": {
797
- background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
1516
+ background: get3(tokens, "extend.colors.primary.600", "#00ffff") + "44"
798
1517
  },
799
1518
  //
800
1519
  // collaboration
@@ -827,12 +1546,12 @@ var defaultTheme = {
827
1546
  // link
828
1547
  //
829
1548
  "& .cm-link": {
830
- color: get2(tokens, "extend.colors.primary.500"),
1549
+ color: get3(tokens, "extend.colors.primary.500"),
831
1550
  textDecorationLine: "underline",
832
1551
  textDecorationThickness: "1px",
833
1552
  textUnderlineOffset: "2px",
834
1553
  borderRadius: ".125rem",
835
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1554
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
836
1555
  },
837
1556
  //
838
1557
  // tooltip
@@ -840,14 +1559,61 @@ var defaultTheme = {
840
1559
  "& .cm-tooltip": {
841
1560
  border: "none"
842
1561
  },
843
- // '& .cm-tooltip-below': {},
844
- // '& .cm-tooltip-autocomplete': {},
1562
+ "& .cm-tooltip-below": {},
1563
+ //
1564
+ // autocomplete
1565
+ //
1566
+ "& .cm-tooltip-autocomplete": {
1567
+ marginTop: "4px",
1568
+ marginLeft: "-3px"
1569
+ },
1570
+ "& .cm-tooltip-autocomplete ul li": {},
1571
+ "& .cm-tooltip-autocomplete ul li[aria-selected]": {},
1572
+ "& .cm-completionIcon": {
1573
+ display: "none"
1574
+ },
1575
+ "& .cm-completionLabel": {
1576
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1577
+ },
1578
+ "& .cm-completionMatchedText": {
1579
+ textDecoration: "none"
1580
+ },
1581
+ //
1582
+ // widgets
1583
+ //
1584
+ "& .cm-widgetBuffer": {
1585
+ display: "none",
1586
+ height: 0
1587
+ },
1588
+ //
1589
+ // table
1590
+ //
1591
+ "& .cm-table *": {
1592
+ fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
1593
+ textDecoration: "none !important"
1594
+ },
1595
+ "& .cm-table-head": {
1596
+ padding: "2px 16px 2px 0px",
1597
+ borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
1598
+ fontWeight: 100,
1599
+ textAlign: "left",
1600
+ color: get3(tokens, "extend.colors.neutral.500")
1601
+ },
1602
+ "& .cm-table-cell": {
1603
+ padding: "2px 16px 2px 0px"
1604
+ },
1605
+ //
1606
+ // image
1607
+ //
1608
+ "& .cm-image": {
1609
+ margin: "0.5rem 0"
1610
+ },
845
1611
  //
846
1612
  // font size
847
1613
  // 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.
848
1614
  //
849
- ...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
850
- const height = get2(tokens, [
1615
+ ...Object.keys(get3(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
1616
+ const height = get3(tokens, [
851
1617
  "extend",
852
1618
  "fontSize",
853
1619
  fontSize,
@@ -864,46 +1630,81 @@ var defaultTheme = {
864
1630
  height
865
1631
  };
866
1632
  return acc;
867
- }, {})
1633
+ }, {}),
1634
+ /**
1635
+ * Panels
1636
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
1637
+ *
1638
+ * Find/replace panel.
1639
+ * <div class="cm-announced">...</div>
1640
+ * <div class="cm-scroller">...</div>
1641
+ * <div class="cm-panels cm-panels-bottom">
1642
+ * <div class="cm-search cm-panel">
1643
+ * <input class="cm-textfield" />
1644
+ * <button class="cm-button">...</button>
1645
+ * <label><input type="checkbox" />...</label>
1646
+ * </div>
1647
+ * </div
1648
+ */
1649
+ "& .cm-panels": {
1650
+ border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`,
1651
+ borderBottom: "none"
1652
+ },
1653
+ "& .cm-panel": {
1654
+ background: get3(tokens, "extend.colors.neutral.50"),
1655
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1656
+ },
1657
+ "& .cm-button": {
1658
+ margin: "4px",
1659
+ fontFamily: get3(tokens, "fontFamily.body", []).join(","),
1660
+ background: get3(tokens, "extend.colors.neutral.100"),
1661
+ border: "none"
1662
+ },
1663
+ "& .cm-searchMatch": {
1664
+ backgroundColor: get3(tokens, "extend.colors.yellow.100")
1665
+ },
1666
+ "& .cm-searchMatch.cm-searchMatch-selected": {
1667
+ backgroundColor: get3(tokens, "extend.colors.primary.100")
1668
+ }
868
1669
  };
869
1670
  var textTheme = {
870
1671
  "& .cm-scroller": {
871
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1672
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
872
1673
  },
873
1674
  "& .cm-placeholder": {
874
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1675
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
875
1676
  }
876
1677
  };
877
1678
  var markdownTheme = {
878
1679
  // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
879
1680
  "& .cm-placeholder": {
880
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1681
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
881
1682
  }
882
1683
  };
883
1684
  var codeTheme = {
884
1685
  "& .cm-scroller": {
885
- fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1686
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
886
1687
  },
887
1688
  "& .cm-placeholder": {
888
- fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1689
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
889
1690
  }
890
1691
  };
891
1692
 
892
1693
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
893
1694
  import { EditorState as EditorState2 } from "@codemirror/state";
894
- import { EditorView as EditorView6 } from "@codemirror/view";
1695
+ import { EditorView as EditorView11 } from "@codemirror/view";
895
1696
  import { useFocusableGroup } from "@fluentui/react-tabster";
896
1697
  import { vim } from "@replit/codemirror-vim";
897
- import defaultsDeep from "lodash.defaultsdeep";
898
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
1698
+ import defaultsDeep2 from "lodash.defaultsdeep";
1699
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
899
1700
  import { generateName } from "@dxos/display-name";
900
1701
  import { useThemeContext } from "@dxos/react-ui";
901
- import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
1702
+ import { getColorForValue, inputSurface, mx as mx4 } from "@dxos/react-ui-theme";
902
1703
  var EditorModes = [
903
1704
  "default",
904
1705
  "vim"
905
1706
  ];
906
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
1707
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
907
1708
  const { themeMode } = useThemeContext();
908
1709
  const tabsterDOMAttribute = useFocusableGroup({
909
1710
  tabBehavior: "limited"
@@ -921,7 +1722,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
921
1722
  root
922
1723
  ]);
923
1724
  const { awareness, peer } = model;
924
- useEffect(() => {
1725
+ useEffect2(() => {
925
1726
  if (awareness && peer) {
926
1727
  awareness.setLocalStateField("user", {
927
1728
  name: peer.name ?? generateName(peer.id),
@@ -941,20 +1742,22 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
941
1742
  peer,
942
1743
  themeMode
943
1744
  ]);
944
- useEffect(() => {
1745
+ useEffect2(() => {
945
1746
  if (!root) {
946
1747
  return;
947
1748
  }
948
1749
  const state2 = EditorState2.create({
949
1750
  doc: model.text(),
950
1751
  extensions: [
1752
+ readonly && EditorState2.readOnly.of(readonly),
951
1753
  // TODO(burdon): Factor out VIM mode?
952
1754
  editorMode === "vim" && vim(),
953
1755
  // Theme.
954
- EditorView6.baseTheme(defaultTheme),
955
- EditorView6.theme(slots?.editor?.theme ?? {}),
1756
+ // TODO(burdon): Make optional.
1757
+ EditorView11.baseTheme(defaultTheme),
1758
+ EditorView11.theme(slots?.editor?.theme ?? {}),
956
1759
  // TODO(burdon): themeMode doesn't change in storybooks.
957
- EditorView6.darkTheme.of(themeMode === "dark"),
1760
+ EditorView11.darkTheme.of(themeMode === "dark"),
958
1761
  // Storage and replication.
959
1762
  model.extension,
960
1763
  // Custom.
@@ -962,7 +1765,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
962
1765
  ].filter(Boolean)
963
1766
  });
964
1767
  view?.destroy();
965
- setView(new EditorView6({
1768
+ setView(new EditorView11({
966
1769
  state: state2,
967
1770
  parent: root
968
1771
  }));
@@ -976,6 +1779,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
976
1779
  root,
977
1780
  model,
978
1781
  themeMode,
1782
+ readonly,
979
1783
  editorMode
980
1784
  ]);
981
1785
  const handleKeyUp = useCallback((event) => {
@@ -1003,43 +1807,36 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
1003
1807
  onKeyUp: handleKeyUp
1004
1808
  });
1005
1809
  });
1006
- var maybeDebug = () => {
1007
- const items = localStorage.getItem("dxos.composer.demo");
1008
- if (items) {
1009
- return demo({
1010
- items: items.split(",")
1011
- });
1012
- }
1013
- return [];
1014
- };
1015
- var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1810
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1016
1811
  const { themeMode } = useThemeContext();
1017
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
1812
+ const slots = defaultsDeep2({}, _slots, defaultTextSlots);
1018
1813
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1019
1814
  ref: forwardedRef,
1815
+ readonly,
1020
1816
  extensions: [
1021
1817
  basicBundle({
1818
+ readonly,
1022
1819
  themeMode,
1023
1820
  placeholder: slots?.editor?.placeholder
1024
1821
  }),
1025
- maybeDebug(),
1026
1822
  ...extensions
1027
1823
  ],
1028
1824
  slots,
1029
1825
  ...props
1030
1826
  });
1031
1827
  });
1032
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1828
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1033
1829
  const { themeMode } = useThemeContext();
1034
- const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
1830
+ const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
1035
1831
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1036
1832
  ref: forwardedRef,
1833
+ readonly,
1037
1834
  extensions: [
1038
1835
  markdownBundle({
1836
+ readonly,
1039
1837
  themeMode,
1040
1838
  placeholder: slots?.editor?.placeholder
1041
1839
  }),
1042
- maybeDebug(),
1043
1840
  ...extensions
1044
1841
  ],
1045
1842
  slots,
@@ -1048,7 +1845,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
1048
1845
  });
1049
1846
  var defaultSlots = {
1050
1847
  root: {
1051
- className: mx2("p-2", inputSurface)
1848
+ className: mx4("p-2", inputSurface)
1052
1849
  }
1053
1850
  };
1054
1851
  var defaultTextSlots = {
@@ -1065,15 +1862,15 @@ var defaultMarkdownSlots = {
1065
1862
  };
1066
1863
 
1067
1864
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1068
- import get3 from "lodash.get";
1069
- import { useEffect as useEffect2, useState as useState2 } from "react";
1865
+ import get4 from "lodash.get";
1866
+ import { useEffect as useEffect3, useState as useState2 } from "react";
1070
1867
  import { yCollab } from "y-codemirror.next";
1071
- import { invariant as invariant2 } from "@dxos/invariant";
1868
+ import { invariant as invariant3 } from "@dxos/invariant";
1072
1869
  import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
1073
1870
 
1074
1871
  // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1075
- import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
1076
- import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1872
+ import { Annotation, Facet, StateEffect as StateEffect2, StateField as StateField6 } from "@codemirror/state";
1873
+ import { ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1077
1874
  import * as automerge2 from "@dxos/automerge/automerge";
1078
1875
 
1079
1876
  // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
@@ -1251,7 +2048,7 @@ var PatchSemaphore = class {
1251
2048
  };
1252
2049
 
1253
2050
  // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1254
- var effectType = StateEffect.define({});
2051
+ var effectType = StateEffect2.define({});
1255
2052
  var updateHeads = (newHeads) => effectType.of({
1256
2053
  newHeads
1257
2054
  });
@@ -1261,7 +2058,7 @@ var semaphoreFacet = Facet.define({
1261
2058
  combine: (values) => values.at(-1)
1262
2059
  });
1263
2060
  var automergePlugin = (handle, path) => {
1264
- const stateField = StateField3.define({
2061
+ const stateField = StateField6.define({
1265
2062
  create: () => ({
1266
2063
  lastHeads: automerge2.getHeads(handle.docSync()),
1267
2064
  unreconciledTransactions: [],
@@ -1291,7 +2088,7 @@ var automergePlugin = (handle, path) => {
1291
2088
  }
1292
2089
  });
1293
2090
  const semaphore = new PatchSemaphore(stateField);
1294
- const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
2091
+ const viewPlugin = ViewPlugin4.fromClass(class AutomergeCodemirrorViewPlugin {
1295
2092
  constructor(view) {
1296
2093
  this._handleChange = () => {
1297
2094
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
@@ -1299,8 +2096,8 @@ var automergePlugin = (handle, path) => {
1299
2096
  this._view = view;
1300
2097
  handle.addListener("change", this._handleChange);
1301
2098
  }
1302
- update(update2) {
1303
- if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
2099
+ update(update4) {
2100
+ if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
1304
2101
  queueMicrotask(() => {
1305
2102
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
1306
2103
  });
@@ -1322,7 +2119,7 @@ var reconcileAnnotationType = Annotation.define();
1322
2119
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1323
2120
 
1324
2121
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1325
- import * as random from "lib0/random";
2122
+ import * as random2 from "lib0/random";
1326
2123
  var cursorColors = [
1327
2124
  {
1328
2125
  color: "#30bced",
@@ -1357,7 +2154,7 @@ var cursorColors = [
1357
2154
  light: "#1be7ff33"
1358
2155
  }
1359
2156
  ];
1360
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
2157
+ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1361
2158
 
1362
2159
  // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1363
2160
  import * as decoding from "lib0/decoding";
@@ -1365,7 +2162,7 @@ import * as encoding from "lib0/encoding";
1365
2162
  import { Observable } from "lib0/observable";
1366
2163
  import * as awarenessProtocol from "y-protocols/awareness";
1367
2164
  import { log } from "@dxos/log";
1368
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
2165
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1369
2166
  var messageAwareness = 1;
1370
2167
  var messageQueryAwareness = 3;
1371
2168
  var SpaceAwarenessProvider = class extends Observable {
@@ -1402,7 +2199,7 @@ var SpaceAwarenessProvider = class extends Observable {
1402
2199
  removed,
1403
2200
  origin
1404
2201
  }, {
1405
- F: __dxlog_file2,
2202
+ F: __dxlog_file3,
1406
2203
  L: 67,
1407
2204
  S: this,
1408
2205
  C: (f, a) => f(...a)
@@ -1415,7 +2212,7 @@ var SpaceAwarenessProvider = class extends Observable {
1415
2212
  }
1416
2213
  _handleSpaceMessage({ payload }) {
1417
2214
  log("space message", payload, {
1418
- F: __dxlog_file2,
2215
+ F: __dxlog_file3,
1419
2216
  L: 79,
1420
2217
  S: this,
1421
2218
  C: (f, a) => f(...a)
@@ -1460,14 +2257,14 @@ var SpaceAwarenessProvider = class extends Observable {
1460
2257
  };
1461
2258
 
1462
2259
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1463
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2260
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
1464
2261
  var useTextModel = ({ identity, space, text }) => {
1465
2262
  const [model, setModel] = useState2(() => createModel({
1466
2263
  identity,
1467
2264
  space,
1468
2265
  text
1469
2266
  }));
1470
- useEffect2(() => {
2267
+ useEffect3(() => {
1471
2268
  setModel(createModel({
1472
2269
  identity,
1473
2270
  space,
@@ -1492,9 +2289,9 @@ var createModel = (options) => {
1492
2289
  }
1493
2290
  };
1494
2291
  var createYjsModel = ({ identity, space, text }) => {
1495
- invariant2(text?.doc && text?.content, void 0, {
1496
- F: __dxlog_file3,
1497
- L: 74,
2292
+ invariant3(text?.doc && text?.content, void 0, {
2293
+ F: __dxlog_file4,
2294
+ L: 81,
1498
2295
  S: void 0,
1499
2296
  A: [
1500
2297
  "text?.doc && text?.content",
@@ -1510,6 +2307,7 @@ var createYjsModel = ({ identity, space, text }) => {
1510
2307
  id: text.doc.guid,
1511
2308
  content: text.content,
1512
2309
  text: () => text.content.toString(),
2310
+ ranges: [],
1513
2311
  extension: yCollab(text.content, provider?.awareness),
1514
2312
  awareness: provider?.awareness,
1515
2313
  peer: identity ? {
@@ -1526,7 +2324,8 @@ var createAutomergeModel = ({ identity, text }) => {
1526
2324
  return {
1527
2325
  id: obj.id,
1528
2326
  content: doc,
1529
- text: () => get3(doc.handle.docSync(), doc.path),
2327
+ text: () => get4(doc.handle.docSync(), doc.path),
2328
+ ranges: [],
1530
2329
  extension: automergePlugin(doc.handle, doc.path),
1531
2330
  peer: identity ? {
1532
2331
  id: identity.identityKey.toHex(),
@@ -1546,25 +2345,35 @@ export {
1546
2345
  YXmlFragment,
1547
2346
  autocomplete,
1548
2347
  basicBundle,
2348
+ blast,
2349
+ code2 as code,
1549
2350
  codeTheme,
1550
2351
  comments,
1551
2352
  cursorColor,
1552
2353
  defaultMarkdownSlots,
2354
+ defaultOptions,
1553
2355
  defaultSlots,
1554
2356
  defaultTextSlots,
1555
2357
  defaultTheme,
1556
2358
  demo,
2359
+ highlight,
2360
+ highlightDecorations,
2361
+ highlightStateField,
2362
+ image,
1557
2363
  link,
1558
- listener,
2364
+ listener2 as listener,
1559
2365
  markdownBundle,
1560
2366
  markdownHighlightStyle,
1561
2367
  markdownTags,
1562
- markdownTagsExtension,
2368
+ markdownTagsExtensions,
1563
2369
  markdownTheme,
2370
+ mention,
2371
+ table,
1564
2372
  tags2 as tags,
1565
2373
  tasklist,
1566
2374
  textTheme,
1567
2375
  tooltip,
2376
+ useHighlights,
1568
2377
  useTextModel
1569
2378
  };
1570
2379
  //# sourceMappingURL=index.mjs.map