@dxos/react-ui-editor 0.3.10-main.cb1d9e2 → 0.3.10-main.cc91b2d

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 (100) hide show
  1. package/dist/lib/browser/index.mjs +1185 -374
  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/PatchSemaphore.d.ts +3 -0
  44. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -1
  45. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +8 -1
  46. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
  47. package/dist/types/src/hooks/automerge/index.d.ts +1 -1
  48. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -1
  49. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -1
  50. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +5 -0
  51. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +1 -0
  52. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +6 -0
  53. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +1 -0
  54. package/dist/types/src/hooks/automerge/translation/index.d.ts +3 -0
  55. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +1 -0
  56. package/dist/types/src/hooks/useTextModel.d.ts +6 -0
  57. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  58. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -2
  59. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  60. package/dist/types/src/styles/markdown.d.ts +3 -2
  61. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  62. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  63. package/package.json +22 -20
  64. package/src/components/TextEditor/MarkdownEditor.stories.tsx +124 -32
  65. package/src/components/TextEditor/TextEditor.stories.tsx +12 -1
  66. package/src/components/TextEditor/TextEditor.tsx +31 -24
  67. package/src/components/TextEditor/extensions/autocomplete.ts +5 -20
  68. package/src/components/TextEditor/extensions/basic.ts +6 -4
  69. package/src/components/TextEditor/extensions/blast.ts +373 -0
  70. package/src/components/TextEditor/extensions/code.ts +99 -0
  71. package/src/components/TextEditor/extensions/comments.ts +29 -46
  72. package/src/components/TextEditor/extensions/demo.ts +4 -1
  73. package/src/components/TextEditor/extensions/experimental.tsx +15 -36
  74. package/src/components/TextEditor/extensions/highlight.ts +128 -0
  75. package/src/components/TextEditor/extensions/image.ts +67 -0
  76. package/src/components/TextEditor/extensions/index.ts +6 -0
  77. package/src/components/TextEditor/extensions/link.ts +17 -25
  78. package/src/components/TextEditor/extensions/listener.ts +3 -3
  79. package/src/components/TextEditor/extensions/markdown/bundle.ts +45 -34
  80. package/src/components/TextEditor/extensions/markdown/highlight.ts +148 -108
  81. package/src/components/TextEditor/extensions/mention.ts +31 -0
  82. package/src/components/TextEditor/extensions/table.ts +119 -0
  83. package/src/components/TextEditor/extensions/tasklist.ts +42 -28
  84. package/src/components/TextEditor/themes/default.ts +97 -4
  85. package/src/hooks/automerge/PatchSemaphore.ts +12 -9
  86. package/src/hooks/automerge/automerge.stories.tsx +4 -2
  87. package/src/hooks/automerge/index.ts +1 -1
  88. package/src/hooks/automerge/plugin.ts +1 -0
  89. package/src/hooks/automerge/{amToCodemirror.ts → translation/automerge-to-codemirror.ts} +10 -2
  90. package/src/hooks/automerge/{codeMirrorToAm.ts → translation/codemirror-to-automerge.ts} +7 -4
  91. package/src/hooks/automerge/translation/index.ts +6 -0
  92. package/src/hooks/useTextModel.ts +9 -0
  93. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -2
  94. package/src/styles/markdown.ts +15 -8
  95. package/src/testing/replicator.ts +1 -0
  96. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  97. package/dist/types/src/hooks/automerge/amToCodemirror.d.ts +0 -6
  98. package/dist/types/src/hooks/automerge/amToCodemirror.d.ts.map +0 -1
  99. package/dist/types/src/hooks/automerge/codeMirrorToAm.d.ts +0 -7
  100. package/dist/types/src/hooks/automerge/codeMirrorToAm.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, 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,179 +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
- // TODO(burdon): Review.
568
- keymap4.of([
569
- ...closeBracketsKeymap,
570
- ...defaultKeymap,
571
- // ...foldKeymap,
572
- // ...historyKeymap,
573
- // ...lintKeymap,
574
- ...searchKeymap,
575
- indentWithTab
576
- ]),
1162
+ _placeholder && placeholder2(_placeholder),
577
1163
  // Main extension.
578
1164
  // https://github.com/codemirror/lang-markdown
579
1165
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -588,20 +1174,156 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
588
1174
  codeLanguages: languages,
589
1175
  // Parser extensions.
590
1176
  extensions: [
591
- // GFM, // TODO(burdon): Provided by default?
592
- markdownTagsExtension
1177
+ // GFM provided by default.
1178
+ markdownTagsExtensions
593
1179
  ]
594
1180
  }),
1181
+ // https://github.com/codemirror/theme-one-dark
1182
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
595
1183
  // 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)
1184
+ syntaxHighlighting2(markdownHighlightStyle(readonly))
599
1185
  ].filter(Boolean);
600
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
+ };
601
1317
 
602
1318
  // 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 {
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 {
605
1327
  constructor(_pos, _checked, _indent, _onCheck) {
606
1328
  super();
607
1329
  this._pos = _pos;
@@ -620,30 +1342,29 @@ var CheckboxWidget = class extends WidgetType3 {
620
1342
  const input = wrap.appendChild(document.createElement("input"));
621
1343
  input.type = "checkbox";
622
1344
  input.checked = this._checked;
623
- input.onchange = (event) => {
624
- this._onCheck(event.target.checked);
625
- return true;
626
- };
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
+ }
627
1354
  return wrap;
628
1355
  }
629
1356
  ignoreEvent() {
630
1357
  return false;
631
1358
  }
632
1359
  };
633
- var styles2 = EditorView5.baseTheme({
634
- "& .cm-task-item": {
635
- paddingLeft: "4px",
636
- paddingRight: "8px"
637
- }
638
- });
639
- var tasklist = () => {
1360
+ var tasklist = (options = {}) => {
640
1361
  const taskMatcher = new MatchDecorator2({
641
1362
  regexp: /^(\s*)- \[([ xX])\]\s/g,
642
1363
  decoration: (match, view, pos) => {
643
1364
  const indent = Math.floor(match[1].length / 2);
644
1365
  const checked = match[2] === "x" || match[2] === "X";
645
- return Decoration3.replace({
646
- widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
1366
+ return Decoration7.replace({
1367
+ widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
647
1368
  const idx = pos + match[0].indexOf("[") + 1;
648
1369
  view.dispatch({
649
1370
  changes: {
@@ -660,22 +1381,22 @@ var tasklist = () => {
660
1381
  });
661
1382
  }
662
1383
  });
663
- const tasks = ViewPlugin2.fromClass(class {
1384
+ const tasks = ViewPlugin3.fromClass(class {
664
1385
  constructor(view) {
665
1386
  this.tasks = taskMatcher.createDeco(view);
666
1387
  }
667
- update(update2) {
668
- this.tasks = taskMatcher.updateDeco(update2, this.tasks);
1388
+ update(update4) {
1389
+ this.tasks = taskMatcher.updateDeco(update4, this.tasks);
669
1390
  }
670
1391
  }, {
671
- decorations: (instance) => instance.tasks,
672
- provide: (plugin) => EditorView5.atomicRanges.of((view) => {
673
- 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;
674
1395
  })
675
1396
  });
676
1397
  return [
677
1398
  tasks,
678
- styles2
1399
+ styles4
679
1400
  ];
680
1401
  };
681
1402
 
@@ -724,7 +1445,7 @@ var tooltip = ({ onHover, regexp = markdownLinkRegexp }) => hoverTooltip((view,
724
1445
  });
725
1446
 
726
1447
  // packages/ui/react-ui-editor/src/components/TextEditor/themes/default.ts
727
- import get2 from "lodash.get";
1448
+ import get3 from "lodash.get";
728
1449
  var defaultTheme = {
729
1450
  //
730
1451
  // Main layout:
@@ -749,20 +1470,20 @@ var defaultTheme = {
749
1470
  fontSize: "16px"
750
1471
  },
751
1472
  "&light .cm-content": {
752
- color: get2(tokens, "extend.colors.neutral.900", "black"),
1473
+ color: get3(tokens, "extend.colors.neutral.900", "black"),
753
1474
  caretColor: "black"
754
1475
  },
755
1476
  "&dark .cm-content": {
756
- color: get2(tokens, "extend.colors.neutral.100", "white"),
1477
+ color: get3(tokens, "extend.colors.neutral.100", "white"),
757
1478
  caretColor: "white"
758
1479
  },
759
1480
  //
760
1481
  // Cursor
761
1482
  //
762
- "&light .cm-cursor": {
1483
+ "&light .cm-cursor, &light .cm-dropCursor": {
763
1484
  borderLeft: "2px solid black"
764
1485
  },
765
- "&dark .cm-cursor": {
1486
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
766
1487
  borderLeft: "2px solid white"
767
1488
  },
768
1489
  "& .cm-placeholder": {
@@ -782,16 +1503,16 @@ var defaultTheme = {
782
1503
  // selection
783
1504
  //
784
1505
  "&light .cm-selectionBackground, &light.cm-focused .cm-selectionBackground": {
785
- background: get2(tokens, "extend.colors.primary.150", "#00ffff")
1506
+ background: get3(tokens, "extend.colors.primary.150", "#00ffff")
786
1507
  },
787
1508
  "&dark .cm-selectionBackground, &dark.cm-focused .cm-selectionBackground": {
788
- background: get2(tokens, "extend.colors.primary.850", "#00ffff")
1509
+ background: get3(tokens, "extend.colors.primary.850", "#00ffff")
789
1510
  },
790
1511
  "&light .cm-selectionMatch": {
791
- background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
1512
+ background: get3(tokens, "extend.colors.primary.250", "#00ffff") + "44"
792
1513
  },
793
1514
  "&dark .cm-selectionMatch": {
794
- background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
1515
+ background: get3(tokens, "extend.colors.primary.600", "#00ffff") + "44"
795
1516
  },
796
1517
  //
797
1518
  // collaboration
@@ -824,12 +1545,12 @@ var defaultTheme = {
824
1545
  // link
825
1546
  //
826
1547
  "& .cm-link": {
827
- color: get2(tokens, "extend.colors.primary.500"),
1548
+ color: get3(tokens, "extend.colors.primary.500"),
828
1549
  textDecorationLine: "underline",
829
1550
  textDecorationThickness: "1px",
830
1551
  textUnderlineOffset: "2px",
831
1552
  borderRadius: ".125rem",
832
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1553
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
833
1554
  },
834
1555
  //
835
1556
  // tooltip
@@ -837,14 +1558,61 @@ var defaultTheme = {
837
1558
  "& .cm-tooltip": {
838
1559
  border: "none"
839
1560
  },
840
- // '& .cm-tooltip-below': {},
841
- // '& .cm-tooltip-autocomplete': {},
1561
+ "& .cm-tooltip-below": {},
1562
+ //
1563
+ // autocomplete
1564
+ //
1565
+ "& .cm-tooltip-autocomplete": {
1566
+ marginTop: "4px",
1567
+ marginLeft: "-3px"
1568
+ },
1569
+ "& .cm-tooltip-autocomplete ul li": {},
1570
+ "& .cm-tooltip-autocomplete ul li[aria-selected]": {},
1571
+ "& .cm-completionIcon": {
1572
+ display: "none"
1573
+ },
1574
+ "& .cm-completionLabel": {
1575
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1576
+ },
1577
+ "& .cm-completionMatchedText": {
1578
+ textDecoration: "none"
1579
+ },
1580
+ //
1581
+ // widgets
1582
+ //
1583
+ "& .cm-widgetBuffer": {
1584
+ display: "none",
1585
+ height: 0
1586
+ },
1587
+ //
1588
+ // table
1589
+ //
1590
+ "& .cm-table *": {
1591
+ fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
1592
+ textDecoration: "none !important"
1593
+ },
1594
+ "& .cm-table-head": {
1595
+ padding: "2px 16px 2px 0px",
1596
+ borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
1597
+ fontWeight: 100,
1598
+ textAlign: "left",
1599
+ color: get3(tokens, "extend.colors.neutral.500")
1600
+ },
1601
+ "& .cm-table-cell": {
1602
+ padding: "2px 16px 2px 0px"
1603
+ },
1604
+ //
1605
+ // image
1606
+ //
1607
+ "& .cm-image": {
1608
+ margin: "0.5rem 0"
1609
+ },
842
1610
  //
843
1611
  // font size
844
1612
  // 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.
845
1613
  //
846
- ...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
847
- const height = get2(tokens, [
1614
+ ...Object.keys(get3(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
1615
+ const height = get3(tokens, [
848
1616
  "extend",
849
1617
  "fontSize",
850
1618
  fontSize,
@@ -861,46 +1629,81 @@ var defaultTheme = {
861
1629
  height
862
1630
  };
863
1631
  return acc;
864
- }, {})
1632
+ }, {}),
1633
+ /**
1634
+ * Panels
1635
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
1636
+ *
1637
+ * Find/replace panel.
1638
+ * <div class="cm-announced">...</div>
1639
+ * <div class="cm-scroller">...</div>
1640
+ * <div class="cm-panels cm-panels-bottom">
1641
+ * <div class="cm-search cm-panel">
1642
+ * <input class="cm-textfield" />
1643
+ * <button class="cm-button">...</button>
1644
+ * <label><input type="checkbox" />...</label>
1645
+ * </div>
1646
+ * </div
1647
+ */
1648
+ "& .cm-panels": {
1649
+ border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`,
1650
+ borderBottom: "none"
1651
+ },
1652
+ "& .cm-panel": {
1653
+ background: get3(tokens, "extend.colors.neutral.50"),
1654
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1655
+ },
1656
+ "& .cm-button": {
1657
+ margin: "4px",
1658
+ fontFamily: get3(tokens, "fontFamily.body", []).join(","),
1659
+ background: get3(tokens, "extend.colors.neutral.100"),
1660
+ border: "none"
1661
+ },
1662
+ "& .cm-searchMatch": {
1663
+ backgroundColor: get3(tokens, "extend.colors.yellow.100")
1664
+ },
1665
+ "& .cm-searchMatch.cm-searchMatch-selected": {
1666
+ backgroundColor: get3(tokens, "extend.colors.primary.100")
1667
+ }
865
1668
  };
866
1669
  var textTheme = {
867
1670
  "& .cm-scroller": {
868
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1671
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
869
1672
  },
870
1673
  "& .cm-placeholder": {
871
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1674
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
872
1675
  }
873
1676
  };
874
1677
  var markdownTheme = {
875
1678
  // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
876
1679
  "& .cm-placeholder": {
877
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1680
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
878
1681
  }
879
1682
  };
880
1683
  var codeTheme = {
881
1684
  "& .cm-scroller": {
882
- fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1685
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
883
1686
  },
884
1687
  "& .cm-placeholder": {
885
- fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1688
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
886
1689
  }
887
1690
  };
888
1691
 
889
1692
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
890
1693
  import { EditorState as EditorState2 } from "@codemirror/state";
891
- import { EditorView as EditorView6 } from "@codemirror/view";
1694
+ import { EditorView as EditorView11 } from "@codemirror/view";
892
1695
  import { useFocusableGroup } from "@fluentui/react-tabster";
893
1696
  import { vim } from "@replit/codemirror-vim";
894
- import defaultsDeep from "lodash.defaultsdeep";
895
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
1697
+ import defaultsDeep2 from "lodash.defaultsdeep";
1698
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
896
1699
  import { generateName } from "@dxos/display-name";
897
1700
  import { useThemeContext } from "@dxos/react-ui";
898
- import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
1701
+ import { getColorForValue, inputSurface, mx as mx4 } from "@dxos/react-ui-theme";
899
1702
  var EditorModes = [
900
1703
  "default",
901
1704
  "vim"
902
1705
  ];
903
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
1706
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
904
1707
  const { themeMode } = useThemeContext();
905
1708
  const tabsterDOMAttribute = useFocusableGroup({
906
1709
  tabBehavior: "limited"
@@ -918,7 +1721,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
918
1721
  root
919
1722
  ]);
920
1723
  const { awareness, peer } = model;
921
- useEffect(() => {
1724
+ useEffect2(() => {
922
1725
  if (awareness && peer) {
923
1726
  awareness.setLocalStateField("user", {
924
1727
  name: peer.name ?? generateName(peer.id),
@@ -938,20 +1741,22 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
938
1741
  peer,
939
1742
  themeMode
940
1743
  ]);
941
- useEffect(() => {
1744
+ useEffect2(() => {
942
1745
  if (!root) {
943
1746
  return;
944
1747
  }
945
1748
  const state2 = EditorState2.create({
946
1749
  doc: model.text(),
947
1750
  extensions: [
1751
+ readonly && EditorState2.readOnly.of(readonly),
948
1752
  // TODO(burdon): Factor out VIM mode?
949
1753
  editorMode === "vim" && vim(),
950
1754
  // Theme.
951
- EditorView6.baseTheme(defaultTheme),
952
- EditorView6.theme(slots?.editor?.theme ?? {}),
1755
+ // TODO(burdon): Make optional.
1756
+ EditorView11.baseTheme(defaultTheme),
1757
+ EditorView11.theme(slots?.editor?.theme ?? {}),
953
1758
  // TODO(burdon): themeMode doesn't change in storybooks.
954
- EditorView6.darkTheme.of(themeMode === "dark"),
1759
+ EditorView11.darkTheme.of(themeMode === "dark"),
955
1760
  // Storage and replication.
956
1761
  model.extension,
957
1762
  // Custom.
@@ -959,7 +1764,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
959
1764
  ].filter(Boolean)
960
1765
  });
961
1766
  view?.destroy();
962
- setView(new EditorView6({
1767
+ setView(new EditorView11({
963
1768
  state: state2,
964
1769
  parent: root
965
1770
  }));
@@ -973,6 +1778,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
973
1778
  root,
974
1779
  model,
975
1780
  themeMode,
1781
+ readonly,
976
1782
  editorMode
977
1783
  ]);
978
1784
  const handleKeyUp = useCallback((event) => {
@@ -1000,43 +1806,36 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
1000
1806
  onKeyUp: handleKeyUp
1001
1807
  });
1002
1808
  });
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) => {
1809
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1013
1810
  const { themeMode } = useThemeContext();
1014
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
1811
+ const slots = defaultsDeep2({}, _slots, defaultTextSlots);
1015
1812
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1016
1813
  ref: forwardedRef,
1814
+ readonly,
1017
1815
  extensions: [
1018
1816
  basicBundle({
1817
+ readonly,
1019
1818
  themeMode,
1020
1819
  placeholder: slots?.editor?.placeholder
1021
1820
  }),
1022
- maybeDebug(),
1023
1821
  ...extensions
1024
1822
  ],
1025
1823
  slots,
1026
1824
  ...props
1027
1825
  });
1028
1826
  });
1029
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1827
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
1030
1828
  const { themeMode } = useThemeContext();
1031
- const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
1829
+ const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
1032
1830
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1033
1831
  ref: forwardedRef,
1832
+ readonly,
1034
1833
  extensions: [
1035
1834
  markdownBundle({
1835
+ readonly,
1036
1836
  themeMode,
1037
1837
  placeholder: slots?.editor?.placeholder
1038
1838
  }),
1039
- maybeDebug(),
1040
1839
  ...extensions
1041
1840
  ],
1042
1841
  slots,
@@ -1045,7 +1844,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
1045
1844
  });
1046
1845
  var defaultSlots = {
1047
1846
  root: {
1048
- className: mx2("p-2", inputSurface)
1847
+ className: mx4("p-2", inputSurface)
1049
1848
  }
1050
1849
  };
1051
1850
  var defaultTextSlots = {
@@ -1062,23 +1861,23 @@ var defaultMarkdownSlots = {
1062
1861
  };
1063
1862
 
1064
1863
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1065
- import get3 from "lodash.get";
1066
- import { useEffect as useEffect2, useState as useState2 } from "react";
1864
+ import get4 from "lodash.get";
1865
+ import { useEffect as useEffect3, useState as useState2 } from "react";
1067
1866
  import { yCollab } from "y-codemirror.next";
1068
- import { invariant as invariant2 } from "@dxos/invariant";
1867
+ import { invariant as invariant3 } from "@dxos/invariant";
1069
1868
  import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
1070
1869
 
1071
1870
  // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1072
- import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
1073
- import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1871
+ import { Annotation, Facet, StateEffect as StateEffect2, StateField as StateField6 } from "@codemirror/state";
1872
+ import { ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1074
1873
  import * as automerge2 from "@dxos/automerge/automerge";
1075
1874
 
1076
1875
  // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
1077
1876
  import { next as automerge } from "@dxos/automerge/automerge";
1078
1877
 
1079
- // packages/ui/react-ui-editor/src/hooks/automerge/amToCodemirror.ts
1878
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/automerge-to-codemirror.ts
1080
1879
  import { ChangeSet } from "@codemirror/state";
1081
- var amToCodemirror_default = (view, selection, target, patches) => {
1880
+ var automergeToCodemirror = (view, selection, target, patches) => {
1082
1881
  for (const patch of patches) {
1083
1882
  const changeSpec = handlePatch(patch, target, view.state);
1084
1883
  if (changeSpec != null) {
@@ -1183,9 +1982,9 @@ var charPath = (textPath, candidatePath) => {
1183
1982
  return null;
1184
1983
  };
1185
1984
 
1186
- // packages/ui/react-ui-editor/src/hooks/automerge/codeMirrorToAm.ts
1985
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/codemirror-to-automerge.ts
1187
1986
  import { next as am } from "@dxos/automerge/automerge";
1188
- var codeMirrorToAm_default = (field, handle, transactions, state) => {
1987
+ var codemirrorToAutomerge = (field, handle, transactions, state) => {
1189
1988
  const { lastHeads, path } = state.field(field);
1190
1989
  let hasChanges = false;
1191
1990
  for (const tr of transactions) {
@@ -1229,12 +2028,12 @@ var PatchSemaphore = class {
1229
2028
  annotations: reconcileAnnotationType.of(true)
1230
2029
  });
1231
2030
  }
1232
- let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
2031
+ let newHeads = codemirrorToAutomerge(this._field, handle, transactions, view.state);
1233
2032
  if (newHeads === null || newHeads === void 0) {
1234
2033
  newHeads = automerge.getHeads(handle.docSync());
1235
2034
  }
1236
2035
  const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
1237
- amToCodemirror_default(view, selection, path, diff);
2036
+ automergeToCodemirror(view, selection, path, diff);
1238
2037
  view.dispatch({
1239
2038
  effects: updateHeads(newHeads),
1240
2039
  annotations: reconcileAnnotationType.of({})
@@ -1248,7 +2047,7 @@ var PatchSemaphore = class {
1248
2047
  };
1249
2048
 
1250
2049
  // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1251
- var effectType = StateEffect.define({});
2050
+ var effectType = StateEffect2.define({});
1252
2051
  var updateHeads = (newHeads) => effectType.of({
1253
2052
  newHeads
1254
2053
  });
@@ -1258,7 +2057,7 @@ var semaphoreFacet = Facet.define({
1258
2057
  combine: (values) => values.at(-1)
1259
2058
  });
1260
2059
  var automergePlugin = (handle, path) => {
1261
- const stateField = StateField3.define({
2060
+ const stateField = StateField6.define({
1262
2061
  create: () => ({
1263
2062
  lastHeads: automerge2.getHeads(handle.docSync()),
1264
2063
  unreconciledTransactions: [],
@@ -1288,7 +2087,7 @@ var automergePlugin = (handle, path) => {
1288
2087
  }
1289
2088
  });
1290
2089
  const semaphore = new PatchSemaphore(stateField);
1291
- const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
2090
+ const viewPlugin = ViewPlugin4.fromClass(class AutomergeCodemirrorViewPlugin {
1292
2091
  constructor(view) {
1293
2092
  this._handleChange = () => {
1294
2093
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
@@ -1296,8 +2095,8 @@ var automergePlugin = (handle, path) => {
1296
2095
  this._view = view;
1297
2096
  handle.addListener("change", this._handleChange);
1298
2097
  }
1299
- update(update2) {
1300
- if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
2098
+ update(update4) {
2099
+ if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
1301
2100
  queueMicrotask(() => {
1302
2101
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
1303
2102
  });
@@ -1319,7 +2118,7 @@ var reconcileAnnotationType = Annotation.define();
1319
2118
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1320
2119
 
1321
2120
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1322
- import * as random from "lib0/random";
2121
+ import * as random2 from "lib0/random";
1323
2122
  var cursorColors = [
1324
2123
  {
1325
2124
  color: "#30bced",
@@ -1354,7 +2153,7 @@ var cursorColors = [
1354
2153
  light: "#1be7ff33"
1355
2154
  }
1356
2155
  ];
1357
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
2156
+ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1358
2157
 
1359
2158
  // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1360
2159
  import * as decoding from "lib0/decoding";
@@ -1362,7 +2161,7 @@ import * as encoding from "lib0/encoding";
1362
2161
  import { Observable } from "lib0/observable";
1363
2162
  import * as awarenessProtocol from "y-protocols/awareness";
1364
2163
  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";
2164
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1366
2165
  var messageAwareness = 1;
1367
2166
  var messageQueryAwareness = 3;
1368
2167
  var SpaceAwarenessProvider = class extends Observable {
@@ -1399,7 +2198,7 @@ var SpaceAwarenessProvider = class extends Observable {
1399
2198
  removed,
1400
2199
  origin
1401
2200
  }, {
1402
- F: __dxlog_file2,
2201
+ F: __dxlog_file3,
1403
2202
  L: 67,
1404
2203
  S: this,
1405
2204
  C: (f, a) => f(...a)
@@ -1412,7 +2211,7 @@ var SpaceAwarenessProvider = class extends Observable {
1412
2211
  }
1413
2212
  _handleSpaceMessage({ payload }) {
1414
2213
  log("space message", payload, {
1415
- F: __dxlog_file2,
2214
+ F: __dxlog_file3,
1416
2215
  L: 79,
1417
2216
  S: this,
1418
2217
  C: (f, a) => f(...a)
@@ -1457,14 +2256,14 @@ var SpaceAwarenessProvider = class extends Observable {
1457
2256
  };
1458
2257
 
1459
2258
  // 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";
2259
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
1461
2260
  var useTextModel = ({ identity, space, text }) => {
1462
2261
  const [model, setModel] = useState2(() => createModel({
1463
2262
  identity,
1464
2263
  space,
1465
2264
  text
1466
2265
  }));
1467
- useEffect2(() => {
2266
+ useEffect3(() => {
1468
2267
  setModel(createModel({
1469
2268
  identity,
1470
2269
  space,
@@ -1489,9 +2288,9 @@ var createModel = (options) => {
1489
2288
  }
1490
2289
  };
1491
2290
  var createYjsModel = ({ identity, space, text }) => {
1492
- invariant2(text?.doc && text?.content, void 0, {
1493
- F: __dxlog_file3,
1494
- L: 74,
2291
+ invariant3(text?.doc && text?.content, void 0, {
2292
+ F: __dxlog_file4,
2293
+ L: 81,
1495
2294
  S: void 0,
1496
2295
  A: [
1497
2296
  "text?.doc && text?.content",
@@ -1507,6 +2306,7 @@ var createYjsModel = ({ identity, space, text }) => {
1507
2306
  id: text.doc.guid,
1508
2307
  content: text.content,
1509
2308
  text: () => text.content.toString(),
2309
+ ranges: [],
1510
2310
  extension: yCollab(text.content, provider?.awareness),
1511
2311
  awareness: provider?.awareness,
1512
2312
  peer: identity ? {
@@ -1523,7 +2323,8 @@ var createAutomergeModel = ({ identity, text }) => {
1523
2323
  return {
1524
2324
  id: obj.id,
1525
2325
  content: doc,
1526
- text: () => get3(doc.handle.docSync(), doc.path),
2326
+ text: () => get4(doc.handle.docSync(), doc.path),
2327
+ ranges: [],
1527
2328
  extension: automergePlugin(doc.handle, doc.path),
1528
2329
  peer: identity ? {
1529
2330
  id: identity.identityKey.toHex(),
@@ -1543,25 +2344,35 @@ export {
1543
2344
  YXmlFragment,
1544
2345
  autocomplete,
1545
2346
  basicBundle,
2347
+ blast,
2348
+ code2 as code,
1546
2349
  codeTheme,
1547
2350
  comments,
1548
2351
  cursorColor,
1549
2352
  defaultMarkdownSlots,
2353
+ defaultOptions,
1550
2354
  defaultSlots,
1551
2355
  defaultTextSlots,
1552
2356
  defaultTheme,
1553
2357
  demo,
2358
+ highlight,
2359
+ highlightDecorations,
2360
+ highlightStateField,
2361
+ image,
1554
2362
  link,
1555
- listener,
2363
+ listener2 as listener,
1556
2364
  markdownBundle,
1557
2365
  markdownHighlightStyle,
1558
2366
  markdownTags,
1559
- markdownTagsExtension,
2367
+ markdownTagsExtensions,
1560
2368
  markdownTheme,
2369
+ mention,
2370
+ table,
1561
2371
  tags2 as tags,
1562
2372
  tasklist,
1563
2373
  textTheme,
1564
2374
  tooltip,
2375
+ useHighlights,
1565
2376
  useTextModel
1566
2377
  };
1567
2378
  //# sourceMappingURL=index.mjs.map