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

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 (118) hide show
  1. package/dist/lib/browser/index.mjs +1497 -490
  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 +26 -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 +3 -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 +3 -2
  11. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
  12. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +2 -1
  13. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
  14. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  15. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/extensions/code.d.ts +2 -0
  17. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +18 -0
  19. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
  20. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +11 -0
  21. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -0
  22. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +1 -1
  23. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
  24. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +24 -0
  25. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +1 -0
  26. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  27. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  28. package/dist/types/src/components/TextEditor/extensions/index.d.ts +8 -0
  29. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  30. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  31. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +4 -2
  32. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  33. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +10 -1
  34. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  35. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +9 -3
  36. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  37. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  38. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  39. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  40. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  41. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -4
  42. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  43. package/dist/types/src/components/TextEditor/themes/default.d.ts +8 -0
  44. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  45. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.d.ts +3 -0
  46. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -0
  47. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +16 -0
  48. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
  49. package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
  50. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +1 -1
  51. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
  52. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
  53. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +5 -0
  54. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +1 -0
  55. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +6 -0
  56. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +1 -0
  57. package/dist/types/src/hooks/automerge/translation/index.d.ts +3 -0
  58. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +1 -0
  59. package/dist/types/src/hooks/index.d.ts +0 -1
  60. package/dist/types/src/hooks/index.d.ts.map +1 -1
  61. package/dist/types/src/hooks/useTextModel.d.ts +8 -3
  62. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  63. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -2
  64. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  65. package/dist/types/src/styles/markdown.d.ts +3 -2
  66. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  67. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  68. package/package.json +23 -20
  69. package/src/components/TextEditor/MarkdownEditor.stories.tsx +161 -36
  70. package/src/components/TextEditor/TextEditor.stories.tsx +12 -1
  71. package/src/components/TextEditor/TextEditor.tsx +49 -24
  72. package/src/components/TextEditor/extensions/autocomplete.ts +9 -22
  73. package/src/components/TextEditor/extensions/basic.ts +6 -4
  74. package/src/components/TextEditor/extensions/blast.ts +373 -0
  75. package/src/components/TextEditor/extensions/code.ts +99 -0
  76. package/src/components/TextEditor/extensions/comments.ts +173 -0
  77. package/src/components/TextEditor/extensions/demo.ts +69 -0
  78. package/src/components/TextEditor/extensions/experimental.tsx +15 -35
  79. package/src/components/TextEditor/extensions/highlight.ts +128 -0
  80. package/src/components/TextEditor/extensions/image.ts +67 -0
  81. package/src/components/TextEditor/extensions/index.ts +8 -0
  82. package/src/components/TextEditor/extensions/link.ts +27 -27
  83. package/src/components/TextEditor/extensions/listener.ts +4 -3
  84. package/src/components/TextEditor/extensions/markdown/bundle.ts +46 -43
  85. package/src/components/TextEditor/extensions/markdown/highlight.ts +148 -107
  86. package/src/components/TextEditor/extensions/mention.ts +31 -0
  87. package/src/components/TextEditor/extensions/table.ts +119 -0
  88. package/src/components/TextEditor/extensions/tasklist.ts +89 -81
  89. package/src/components/TextEditor/themes/default.ts +113 -4
  90. package/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.ts +12 -9
  91. package/src/{automerge → hooks/automerge}/automerge.stories.tsx +6 -3
  92. package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +1 -0
  93. package/src/{automerge/automerge-plugin/amToCodemirror.ts → hooks/automerge/translation/automerge-to-codemirror.ts} +10 -2
  94. package/src/{automerge/automerge-plugin/codeMirrorToAm.ts → hooks/automerge/translation/codemirror-to-automerge.ts} +7 -4
  95. package/src/hooks/automerge/translation/index.ts +6 -0
  96. package/src/hooks/index.ts +0 -1
  97. package/src/hooks/useTextModel.ts +58 -41
  98. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -2
  99. package/src/styles/markdown.ts +15 -8
  100. package/src/testing/replicator.ts +7 -7
  101. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
  102. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts +0 -6
  103. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
  104. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +0 -7
  105. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
  106. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
  107. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
  108. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
  109. package/dist/types/src/automerge/automerge.stories.d.ts +0 -9
  110. package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
  111. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  112. package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
  113. package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
  114. package/src/hooks/useCollaboration.ts +0 -45
  115. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
  116. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
  117. package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
  118. package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
- import { Doc, YText as YText2, YXmlFragment } from "@dxos/text-model";
3
+ import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
4
 
5
5
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
6
  import { tags as tags2 } from "@lezer/highlight";
@@ -8,7 +8,7 @@ import { tags as tags2 } from "@lezer/highlight";
8
8
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/autocomplete.ts
9
9
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
10
10
  import { keymap } from "@codemirror/view";
11
- var autocomplete = ({ getOptions }) => {
11
+ var autocomplete = ({ onSearch }) => {
12
12
  return [
13
13
  // https://codemirror.net/docs/ref/#view.keymap
14
14
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -24,13 +24,13 @@ var autocomplete = ({ getOptions }) => {
24
24
  // TODO(burdon): Optional decoration via addToOptions
25
25
  override: [
26
26
  (context) => {
27
- const word = context.matchBefore(/\w*/);
28
- if (!word || word.from === word.to && !context.explicit) {
27
+ const match = context.matchBefore(/\w*/);
28
+ if (!match || match.from === match.to && !context.explicit) {
29
29
  return null;
30
30
  }
31
31
  return {
32
- from: word.from,
33
- options: getOptions(word.text.toLowerCase())
32
+ from: match.from,
33
+ options: onSearch(match.text.toLowerCase())
34
34
  };
35
35
  }
36
36
  ]
@@ -42,23 +42,809 @@ var autocomplete = ({ getOptions }) => {
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
 
57
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
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
+
488
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
489
+ import { keymap as keymap3 } from "@codemirror/view";
490
+ var defaultItems = [
491
+ "hello world!",
492
+ "this is a test.",
493
+ "this is [DXOS](https://dxos.org)"
494
+ ];
495
+ var demo = ({ delay = 75, items = defaultItems } = {}) => {
496
+ let t;
497
+ let idx = 0;
498
+ return [
499
+ keymap3.of([
500
+ {
501
+ // Reset.
502
+ key: "alt-meta-'",
503
+ run: (view) => {
504
+ clearTimeout(t);
505
+ idx = 0;
506
+ return true;
507
+ }
508
+ },
509
+ {
510
+ // Next prompt.
511
+ // TODO(burdon): Press 1-9 to select prompt?
512
+ key: "shift-meta-'",
513
+ run: (view) => {
514
+ clearTimeout(t);
515
+ const text = items[idx++];
516
+ if (idx === items?.length) {
517
+ idx = 0;
518
+ }
519
+ let i = 0;
520
+ const insert = (d = 0) => {
521
+ t = setTimeout(() => {
522
+ const pos = view.state.selection.main.head;
523
+ view.dispatch({
524
+ changes: {
525
+ from: pos,
526
+ insert: text[i++]
527
+ },
528
+ selection: {
529
+ anchor: pos + 1
530
+ }
531
+ });
532
+ if (i < text.length) {
533
+ insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
534
+ }
535
+ }, d);
536
+ };
537
+ insert();
538
+ return true;
539
+ }
540
+ }
541
+ ])
542
+ ];
543
+ };
544
+
545
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
546
+ import { keymap as keymap4, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
547
+ import { debounce } from "@dxos/async";
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
+ });
561
+ var BookmarkWidget = class extends WidgetType {
562
+ constructor(_pos, _id, _handleClick) {
563
+ super();
564
+ this._pos = _pos;
565
+ this._id = _id;
566
+ this._handleClick = _handleClick;
567
+ invariant2(this._id, void 0, {
568
+ F: __dxlog_file2,
569
+ L: 37,
570
+ S: this,
571
+ A: [
572
+ "this._id",
573
+ ""
574
+ ]
575
+ });
576
+ }
577
+ eq(other) {
578
+ return this._id === other._id;
579
+ }
580
+ toDOM() {
581
+ const span = document.createElement("span");
582
+ span.className = "cm-bookmark";
583
+ span.textContent = "\xA7";
584
+ span.onclick = () => this._handleClick();
585
+ return span;
586
+ }
587
+ };
588
+ var comments = (options = {}) => {
589
+ let active;
590
+ const bookmarkMatcher = new MatchDecorator({
591
+ regexp: /\[\^(\w+)\]/g,
592
+ decoration: (match, view, pos) => {
593
+ const id = match[1];
594
+ return Decoration2.replace({
595
+ id,
596
+ widget: new BookmarkWidget(pos, id, () => handleUpdate({
597
+ active: id
598
+ }))
599
+ });
600
+ }
601
+ });
602
+ const bookmarks = ViewPlugin2.fromClass(class {
603
+ constructor(view) {
604
+ this.bookmarks = bookmarkMatcher.createDeco(view);
605
+ }
606
+ update(update4) {
607
+ this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
608
+ }
609
+ }, {
610
+ decorations: (instance) => instance.bookmarks,
611
+ provide: (plugin) => EditorView4.atomicRanges.of((view) => {
612
+ return view.plugin(plugin)?.bookmarks || Decoration2.none;
613
+ })
614
+ });
615
+ const handleUpdate = debounce((data) => {
616
+ options.onUpdate?.(data);
617
+ }, 200);
618
+ return [
619
+ keymap4.of([
620
+ {
621
+ key: options?.key ?? "shift-meta-c",
622
+ run: (view) => {
623
+ const { from, to, head } = view.state.selection.main;
624
+ const id = options.onCreate?.(from, to);
625
+ if (id) {
626
+ const tag = `[^${id}]`;
627
+ view.dispatch({
628
+ changes: {
629
+ from: head,
630
+ insert: tag
631
+ },
632
+ selection: {
633
+ anchor: head + tag.length
634
+ }
635
+ });
636
+ return true;
637
+ }
638
+ return false;
639
+ }
640
+ }
641
+ ]),
642
+ bookmarks,
643
+ // Monitor cursor movement.
644
+ EditorView4.updateListener.of((update4) => {
645
+ active = void 0;
646
+ if (!options.onUpdate) {
647
+ return;
648
+ }
649
+ const { view, state } = update4;
650
+ const pos = state.selection.main.head;
651
+ const decorations = [];
652
+ const rangeSet = view.plugin(bookmarks)?.bookmarks;
653
+ let closest = Infinity;
654
+ const { from, to } = (
655
+ /* view.visibleRanges?.[0] ?? */
656
+ {
657
+ from: 0,
658
+ to: state.doc.length
659
+ }
660
+ );
661
+ rangeSet?.between(from, to, (from2, to2, value) => {
662
+ decorations.push({
663
+ from: from2,
664
+ to: to2,
665
+ value
666
+ });
667
+ const d = Math.min(Math.abs(pos - from2), Math.abs(pos - to2));
668
+ if (d < closest) {
669
+ active = value.spec.id;
670
+ closest = d;
671
+ }
672
+ });
673
+ if (decorations.length) {
674
+ handleUpdate({
675
+ active,
676
+ items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
677
+ id,
678
+ pos: from2,
679
+ location: view.coordsAtPos(from2)
680
+ }))
681
+ });
682
+ }
683
+ }),
684
+ styles2
685
+ ];
686
+ };
687
+
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
58
791
  import { syntaxTree } from "@codemirror/language";
59
- import { RangeSetBuilder, StateField } from "@codemirror/state";
60
- import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
61
- var LinkButton = class extends WidgetType {
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 {
62
848
  constructor(_pos, _url, _onAttach) {
63
849
  super();
64
850
  this._pos = _pos;
@@ -74,7 +860,7 @@ var LinkButton = class extends WidgetType {
74
860
  return el;
75
861
  }
76
862
  };
77
- var LinkText = class extends WidgetType {
863
+ var LinkText = class extends WidgetType3 {
78
864
  constructor(_pos, _text, _url) {
79
865
  super();
80
866
  this._pos = _pos;
@@ -104,281 +890,276 @@ var LinkText = class extends WidgetType {
104
890
  return link2;
105
891
  }
106
892
  };
107
- var update = (state, options) => {
108
- const builder = new RangeSetBuilder();
893
+ var update2 = (state, options) => {
894
+ const builder = new RangeSetBuilder2();
109
895
  const cursor = state.selection.main.head;
110
- syntaxTree(state).iterate({
896
+ syntaxTree2(state).iterate({
111
897
  enter: (node) => {
112
898
  if (node.name === "Link") {
113
- const marks = node.node.getChildren("LinkMark");
114
- 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) : "";
115
901
  const urlNode = node.node.getChild("URL");
116
902
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
117
- if (cursor < node.from || cursor > node.to) {
118
- builder.add(node.from, node.to, Decoration.replace({
903
+ if (!url) {
904
+ return false;
905
+ }
906
+ if (state.readOnly || cursor < node.from || cursor > node.to) {
907
+ builder.add(node.from, node.to, Decoration5.replace({
119
908
  widget: new LinkText(node.from, text, options.link ? url : void 0)
120
909
  }));
121
910
  }
122
911
  if (options.onRender) {
123
- builder.add(node.to, node.to, Decoration.replace({
912
+ builder.add(node.to, node.to, Decoration5.replace({
124
913
  widget: new LinkButton(node.from, url, options.onRender)
125
914
  }));
126
915
  }
916
+ return false;
127
917
  }
128
- return true;
129
918
  }
130
919
  });
131
920
  return builder.finish();
132
921
  };
133
- var link = (options = {}) => {
134
- return StateField.define({
135
- create: (state) => update(state, options),
136
- update: (_, tr) => update(tr.state, options),
137
- provide: (field) => EditorView2.decorations.from(field)
138
- });
139
- };
140
922
 
141
923
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/listener.ts
142
- import { StateField as StateField2 } from "@codemirror/state";
143
- var listener = (onChange) => StateField2.define({
144
- create: () => null,
145
- update: (_value, transaction) => {
146
- if (transaction.docChanged && onChange) {
147
- onChange(transaction.newDoc.toString());
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;
148
933
  }
149
- return null;
150
- }
151
- });
934
+ });
935
+ };
152
936
 
153
937
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
154
938
  import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
155
- import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
156
- import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
157
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, foldKeymap, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
158
- import { languages } from "@codemirror/language-data";
159
- import { lintKeymap } from "@codemirror/lint";
160
- import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
161
- import { EditorState } from "@codemirror/state";
162
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
163
- import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView3, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap2, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
164
-
165
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
166
- import { markdownLanguage } from "@codemirror/lang-markdown";
167
- import { HighlightStyle } from "@codemirror/language";
168
- import { tags, styleTags, Tag } from "@lezer/highlight";
169
- import get from "lodash.get";
170
-
171
- // packages/ui/react-ui-editor/src/styles/markdown.ts
172
- import { mx } from "@dxos/react-ui-theme";
173
- var heading = {
174
- 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
175
- 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
176
- 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
177
- 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
178
- 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
179
- 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
180
- };
181
- var light = "text-neutral-200 dark:text-neutral-800";
182
- var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
183
- var bold = "font-bold";
184
- var italic = "italic";
185
- var strikethrough = "line-through";
186
- var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
187
- var codeMark = "font-mono text-primary-500";
188
- var inlineUrl = mx(code, "px-1");
189
- var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
190
- var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-500/50";
191
-
192
- // packages/ui/react-ui-editor/src/styles/tokens.ts
193
- import { tailwindConfig } from "@dxos/react-ui-theme";
194
- var tokens = tailwindConfig({}).theme;
195
-
196
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
197
- var markdownTags = {
198
- Blockquote: Tag.define(),
199
- CodeMark: Tag.define(),
200
- CodeText: Tag.define(),
201
- EmphasisMark: Tag.define(),
202
- HeaderMark: Tag.define(),
203
- HorizontalRule: Tag.define(),
204
- InlineCode: Tag.define(),
205
- LinkLabel: Tag.define(),
206
- LinkReference: Tag.define(),
207
- ListMark: Tag.define(),
208
- QuoteMark: Tag.define(),
209
- URL: Tag.define()
210
- };
211
- var markdownTagsExtension = {
212
- props: [
213
- styleTags(markdownTags)
214
- ]
215
- };
216
- var markdownHighlightStyle = HighlightStyle.define([
217
- {
218
- tag: [
219
- tags.keyword,
220
- tags.name,
221
- tags.deleted,
222
- tags.character,
223
- tags.propertyName,
224
- tags.macroName,
225
- tags.color,
226
- tags.constant(tags.name),
227
- tags.standard(tags.name),
228
- tags.definition(tags.name),
229
- tags.separator,
230
- tags.typeName,
231
- tags.className,
232
- tags.number,
233
- tags.changed,
234
- tags.annotation,
235
- tags.modifier,
236
- tags.self,
237
- tags.namespace,
238
- tags.operator,
239
- tags.operatorKeyword,
240
- tags.escape,
241
- tags.regexp,
242
- tags.special(tags.string),
243
- tags.meta,
244
- tags.comment,
245
- tags.atom,
246
- tags.bool,
247
- tags.special(tags.variableName),
248
- tags.processingInstruction,
249
- tags.string,
250
- tags.inserted,
251
- tags.invalid
252
- ],
253
- color: "inherit !important"
254
- },
255
- // Markdown marks.
256
- {
257
- tag: [
258
- tags.meta,
259
- tags.processingInstruction,
260
- markdownTags.CodeMark,
261
- markdownTags.EmphasisMark,
262
- markdownTags.HeaderMark,
263
- markdownTags.LinkLabel,
264
- markdownTags.LinkReference,
265
- markdownTags.ListMark,
266
- markdownTags.QuoteMark
267
- ],
268
- class: mark
269
- },
270
- // Headings.
271
- {
272
- tag: tags.heading1,
273
- class: heading[1]
274
- },
275
- {
276
- tag: tags.heading2,
277
- class: heading[2]
278
- },
279
- {
280
- tag: tags.heading3,
281
- class: heading[3]
282
- },
283
- {
284
- tag: tags.heading4,
285
- class: heading[4]
286
- },
287
- {
288
- tag: tags.heading5,
289
- class: heading[5]
290
- },
291
- {
292
- tag: tags.heading6,
293
- class: heading[6]
294
- },
295
- // Emphasis.
296
- {
297
- tag: tags.emphasis,
298
- class: italic
299
- },
300
- {
301
- tag: tags.strong,
302
- class: bold
303
- },
304
- {
305
- tag: tags.strikethrough,
306
- class: strikethrough
307
- },
308
- // Naked URLs.
309
- {
310
- tag: [
311
- markdownTags.URL
312
- ],
313
- class: inlineUrl
314
- },
315
- // E.g., code block language (after ```).
316
- {
317
- tag: [
318
- tags.function(tags.variableName),
319
- tags.labelName
320
- ],
321
- class: codeMark
322
- },
323
- // The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
324
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
325
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
326
- // the code, but all other CSS properties will be inherited.
327
- {
328
- tag: [
329
- markdownTags.CodeText,
330
- markdownTags.InlineCode
331
- ],
332
- class: code
333
- },
334
- {
335
- tag: [
336
- markdownTags.QuoteMark
337
- ],
338
- class: blockquote
339
- },
340
- {
341
- tag: [
342
- markdownTags.HorizontalRule
343
- ],
344
- class: horizontalRule
345
- }
346
- ], {
347
- scope: markdownLanguage,
348
- all: {
349
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
939
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
940
+ import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
941
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
942
+ import { languages } from "@codemirror/language-data";
943
+ import { searchKeymap } from "@codemirror/search";
944
+ import { EditorState } from "@codemirror/state";
945
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
946
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView8, highlightActiveLine, keymap as keymap5, placeholder as placeholder2 } from "@codemirror/view";
947
+
948
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
949
+ import { markdownLanguage } from "@codemirror/lang-markdown";
950
+ import { HighlightStyle } from "@codemirror/language";
951
+ import { tags, styleTags, Tag } from "@lezer/highlight";
952
+ import { Table } from "@lezer/markdown";
953
+ import get2 from "lodash.get";
954
+ import { mx as mx3 } from "@dxos/react-ui-theme";
955
+ var markdownTags = {
956
+ Blockquote: Tag.define(),
957
+ CodeMark: Tag.define(),
958
+ CodeText: Tag.define(),
959
+ EmphasisMark: Tag.define(),
960
+ HeaderMark: Tag.define(),
961
+ HorizontalRule: Tag.define(),
962
+ InlineCode: Tag.define(),
963
+ LinkLabel: Tag.define(),
964
+ LinkReference: Tag.define(),
965
+ ListMark: Tag.define(),
966
+ QuoteMark: Tag.define(),
967
+ URL: Tag.define(),
968
+ // Custom.
969
+ TableCell: Tag.define()
970
+ };
971
+ Table.defineNodes?.forEach((node) => {
972
+ switch (node?.name) {
973
+ case "TableCell": {
974
+ node.style = markdownTags.TableCell;
975
+ break;
976
+ }
350
977
  }
351
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
+ };
352
1146
 
353
1147
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
354
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1148
+ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
355
1149
  return [
1150
+ EditorState.allowMultipleSelections.of(true),
1151
+ EditorState.tabSize.of(2),
1152
+ EditorView8.lineWrapping,
1153
+ customKeymap(),
356
1154
  bracketMatching2(),
357
1155
  closeBrackets2(),
358
- _placeholder && placeholder2(_placeholder),
359
- EditorState.allowMultipleSelections.of(true),
360
- EditorView3.lineWrapping,
361
- // autocompletion(),
362
1156
  crosshairCursor(),
363
1157
  dropCursor(),
364
- drawSelection(),
1158
+ drawSelection2(),
365
1159
  highlightActiveLine(),
366
- highlightActiveLineGutter(),
367
- highlightSelectionMatches(),
368
- highlightSpecialChars(),
369
1160
  history(),
370
1161
  indentOnInput(),
371
- rectangularSelection(),
372
- keymap2.of([
373
- ...closeBracketsKeymap,
374
- // ...completionKeymap,
375
- ...defaultKeymap,
376
- ...foldKeymap,
377
- ...historyKeymap,
378
- ...lintKeymap,
379
- ...searchKeymap,
380
- indentWithTab
381
- ]),
1162
+ _placeholder && placeholder2(_placeholder),
382
1163
  // Main extension.
383
1164
  // https://github.com/codemirror/lang-markdown
384
1165
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -393,102 +1174,231 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
393
1174
  codeLanguages: languages,
394
1175
  // Parser extensions.
395
1176
  extensions: [
396
- // GFM, // TODO(burdon): Provided by default?
397
- markdownTagsExtension
1177
+ // GFM provided by default.
1178
+ markdownTagsExtensions
398
1179
  ]
399
1180
  }),
1181
+ // https://github.com/codemirror/theme-one-dark
1182
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
400
1183
  // Custom styles.
401
- syntaxHighlighting2(markdownHighlightStyle),
402
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
403
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
1184
+ syntaxHighlighting2(markdownHighlightStyle(readonly))
404
1185
  ].filter(Boolean);
405
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
+ };
406
1317
 
407
1318
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
408
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
409
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
410
- import { EditorView as EditorView4, Decoration as Decoration2, WidgetType as WidgetType2 } from "@codemirror/view";
411
- var CheckboxWidget = class extends WidgetType2 {
412
- constructor(_pos, _getCursor, _checked) {
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 {
1327
+ constructor(_pos, _checked, _indent, _onCheck) {
413
1328
  super();
414
1329
  this._pos = _pos;
415
- this._getCursor = _getCursor;
416
1330
  this._checked = _checked;
1331
+ this._indent = _indent;
1332
+ this._onCheck = _onCheck;
417
1333
  }
418
1334
  eq(other) {
419
- return this._pos === other._pos && this._checked === other._checked;
1335
+ return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
420
1336
  }
421
1337
  toDOM(view) {
422
1338
  const wrap = document.createElement("span");
423
- wrap.setAttribute("aria-hidden", "true");
424
1339
  wrap.className = "cm-task-item";
425
- const box = wrap.appendChild(document.createElement("input"));
426
- box.type = "checkbox";
427
- box.checked = this._checked;
428
- box.onclick = (event) => {
429
- this._checked = !isChecked(view.state, this._pos);
430
- view.dispatch({
431
- changes: {
432
- from: this._pos + 1,
433
- to: this._pos + 2,
434
- insert: this._checked ? "x" : " "
435
- },
436
- // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
437
- selection: {
438
- anchor: this._pos + 4
439
- }
440
- });
441
- return true;
442
- };
1340
+ wrap.setAttribute("aria-hidden", "true");
1341
+ wrap.style.setProperty("margin-left", this._indent * 24 + "px");
1342
+ const input = wrap.appendChild(document.createElement("input"));
1343
+ input.type = "checkbox";
1344
+ input.checked = this._checked;
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
+ }
443
1354
  return wrap;
444
1355
  }
445
- // TODO(burdon): Remove listener?
446
- // override destroy() {
447
- // console.log('destroy', this._pos);
448
- // }
449
1356
  ignoreEvent() {
450
1357
  return false;
451
1358
  }
452
1359
  };
453
- var isChecked = (state, pos) => {
454
- return state.sliceDoc(pos, pos + 3).toLowerCase() === "[x]";
455
- };
456
- var statefield = () => {
457
- let lastPosition = 0;
458
- const listener2 = EditorView4.updateListener.of((update2) => {
459
- lastPosition = update2.startState.selection.main.head;
1360
+ var tasklist = (options = {}) => {
1361
+ const taskMatcher = new MatchDecorator2({
1362
+ regexp: /^(\s*)- \[([ xX])\]\s/g,
1363
+ decoration: (match, view, pos) => {
1364
+ const indent = Math.floor(match[1].length / 2);
1365
+ const checked = match[2] === "x" || match[2] === "X";
1366
+ return Decoration7.replace({
1367
+ widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
1368
+ const idx = pos + match[0].indexOf("[") + 1;
1369
+ view.dispatch({
1370
+ changes: {
1371
+ from: idx,
1372
+ to: idx + 1,
1373
+ insert: checked2 ? "x" : " "
1374
+ },
1375
+ // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
1376
+ selection: {
1377
+ anchor: idx + 3
1378
+ }
1379
+ });
1380
+ })
1381
+ });
1382
+ }
460
1383
  });
461
- const checkbox = (state) => {
462
- const builder = new RangeSetBuilder2();
463
- syntaxTree2(state).iterate({
464
- enter: (node) => {
465
- if (node.name === "TaskMarker") {
466
- builder.add(node.from, node.to, Decoration2.replace({
467
- widget: new CheckboxWidget(node.from, () => lastPosition, isChecked(state, node.from))
468
- }));
469
- }
470
- }
471
- });
472
- return builder.finish();
473
- };
474
- return [
475
- listener2,
476
- StateField3.define({
477
- create: (state) => checkbox(state),
478
- update: (_, tr) => checkbox(tr.state),
479
- provide: (field) => EditorView4.decorations.from(field)
1384
+ const tasks = ViewPlugin3.fromClass(class {
1385
+ constructor(view) {
1386
+ this.tasks = taskMatcher.createDeco(view);
1387
+ }
1388
+ update(update4) {
1389
+ this.tasks = taskMatcher.updateDeco(update4, this.tasks);
1390
+ }
1391
+ }, {
1392
+ decorations: (value) => value.tasks,
1393
+ provide: (plugin) => EditorView10.atomicRanges.of((view) => {
1394
+ return view.plugin(plugin)?.tasks || Decoration7.none;
480
1395
  })
1396
+ });
1397
+ return [
1398
+ tasks,
1399
+ styles4
481
1400
  ];
482
1401
  };
483
- var styles = EditorView4.baseTheme({
484
- "& .cm-task-item": {
485
- padding: "0 4px"
486
- }
487
- });
488
- var tasklist = () => [
489
- statefield(),
490
- styles
491
- ];
492
1402
 
493
1403
  // packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
494
1404
  import { hoverTooltip } from "@codemirror/view";
@@ -535,7 +1445,7 @@ var tooltip = ({ onHover, regexp = markdownLinkRegexp }) => hoverTooltip((view,
535
1445
  });
536
1446
 
537
1447
  // packages/ui/react-ui-editor/src/components/TextEditor/themes/default.ts
538
- import get2 from "lodash.get";
1448
+ import get3 from "lodash.get";
539
1449
  var defaultTheme = {
540
1450
  //
541
1451
  // Main layout:
@@ -552,7 +1462,8 @@ var defaultTheme = {
552
1462
  outline: "none"
553
1463
  },
554
1464
  "& .cm-scroller": {
555
- overflow: "visible"
1465
+ overflow: "visible",
1466
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
556
1467
  },
557
1468
  "& .cm-content": {
558
1469
  padding: 0,
@@ -560,20 +1471,20 @@ var defaultTheme = {
560
1471
  fontSize: "16px"
561
1472
  },
562
1473
  "&light .cm-content": {
563
- color: get2(tokens, "extend.colors.neutral.900", "black"),
1474
+ color: get3(tokens, "extend.colors.neutral.900", "black"),
564
1475
  caretColor: "black"
565
1476
  },
566
1477
  "&dark .cm-content": {
567
- color: get2(tokens, "extend.colors.neutral.100", "white"),
1478
+ color: get3(tokens, "extend.colors.neutral.100", "white"),
568
1479
  caretColor: "white"
569
1480
  },
570
1481
  //
571
1482
  // Cursor
572
1483
  //
573
- "&light .cm-cursor": {
1484
+ "&light .cm-cursor, &light .cm-dropCursor": {
574
1485
  borderLeft: "2px solid black"
575
1486
  },
576
- "&dark .cm-cursor": {
1487
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
577
1488
  borderLeft: "2px solid white"
578
1489
  },
579
1490
  "& .cm-placeholder": {
@@ -593,16 +1504,16 @@ var defaultTheme = {
593
1504
  // selection
594
1505
  //
595
1506
  "&light .cm-selectionBackground, &light.cm-focused .cm-selectionBackground": {
596
- background: get2(tokens, "extend.colors.primary.150", "#00ffff")
1507
+ background: get3(tokens, "extend.colors.primary.150", "#00ffff")
597
1508
  },
598
1509
  "&dark .cm-selectionBackground, &dark.cm-focused .cm-selectionBackground": {
599
- background: get2(tokens, "extend.colors.primary.850", "#00ffff")
1510
+ background: get3(tokens, "extend.colors.primary.850", "#00ffff")
600
1511
  },
601
1512
  "&light .cm-selectionMatch": {
602
- background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
1513
+ background: get3(tokens, "extend.colors.primary.250", "#00ffff") + "44"
603
1514
  },
604
1515
  "&dark .cm-selectionMatch": {
605
- background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
1516
+ background: get3(tokens, "extend.colors.primary.600", "#00ffff") + "44"
606
1517
  },
607
1518
  //
608
1519
  // collaboration
@@ -635,12 +1546,12 @@ var defaultTheme = {
635
1546
  // link
636
1547
  //
637
1548
  "& .cm-link": {
638
- color: get2(tokens, "extend.colors.primary.500"),
1549
+ color: get3(tokens, "extend.colors.primary.500"),
639
1550
  textDecorationLine: "underline",
640
1551
  textDecorationThickness: "1px",
641
1552
  textUnderlineOffset: "2px",
642
1553
  borderRadius: ".125rem",
643
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1554
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
644
1555
  },
645
1556
  //
646
1557
  // tooltip
@@ -648,14 +1559,61 @@ var defaultTheme = {
648
1559
  "& .cm-tooltip": {
649
1560
  border: "none"
650
1561
  },
651
- // '& .cm-tooltip-below': {},
652
- // '& .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
+ },
653
1611
  //
654
1612
  // font size
655
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.
656
1614
  //
657
- ...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
658
- const height = get2(tokens, [
1615
+ ...Object.keys(get3(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
1616
+ const height = get3(tokens, [
659
1617
  "extend",
660
1618
  "fontSize",
661
1619
  fontSize,
@@ -672,43 +1630,101 @@ var defaultTheme = {
672
1630
  height
673
1631
  };
674
1632
  return acc;
675
- }, {})
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
+ }
676
1669
  };
677
1670
  var textTheme = {
678
1671
  "& .cm-scroller": {
679
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1672
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1673
+ },
1674
+ "& .cm-placeholder": {
1675
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
1676
+ }
1677
+ };
1678
+ var markdownTheme = {
1679
+ // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
1680
+ "& .cm-placeholder": {
1681
+ fontFamily: get3(tokens, "fontFamily.body", []).join(",")
680
1682
  }
681
1683
  };
682
1684
  var codeTheme = {
683
1685
  "& .cm-scroller": {
684
- fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
1686
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
1687
+ },
1688
+ "& .cm-placeholder": {
1689
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
685
1690
  }
686
1691
  };
687
1692
 
688
1693
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
689
1694
  import { EditorState as EditorState2 } from "@codemirror/state";
690
- import { EditorView as EditorView5 } from "@codemirror/view";
1695
+ import { EditorView as EditorView11 } from "@codemirror/view";
691
1696
  import { useFocusableGroup } from "@fluentui/react-tabster";
692
1697
  import { vim } from "@replit/codemirror-vim";
693
- import defaultsDeep from "lodash.defaultsdeep";
694
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
695
- import { yCollab } from "y-codemirror.next";
1698
+ import defaultsDeep2 from "lodash.defaultsdeep";
1699
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
696
1700
  import { generateName } from "@dxos/display-name";
697
1701
  import { useThemeContext } from "@dxos/react-ui";
698
- import { getColorForValue, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
1702
+ import { getColorForValue, inputSurface, mx as mx4 } from "@dxos/react-ui-theme";
699
1703
  var EditorModes = [
700
1704
  "default",
701
1705
  "vim"
702
1706
  ];
703
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
1707
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
704
1708
  const { themeMode } = useThemeContext();
705
1709
  const tabsterDOMAttribute = useFocusableGroup({
706
1710
  tabBehavior: "limited"
707
1711
  });
708
- const { provider, peer } = model;
709
- useEffect(() => {
710
- if (provider && peer) {
711
- provider.awareness.setLocalStateField("user", {
1712
+ const [root, setRoot] = useState(null);
1713
+ const [state, setState] = useState();
1714
+ const [view, setView] = useState();
1715
+ useImperativeHandle(forwardedRef, () => ({
1716
+ root,
1717
+ state,
1718
+ view
1719
+ }), [
1720
+ view,
1721
+ state,
1722
+ root
1723
+ ]);
1724
+ const { awareness, peer } = model;
1725
+ useEffect2(() => {
1726
+ if (awareness && peer) {
1727
+ awareness.setLocalStateField("user", {
712
1728
  name: peer.name ?? generateName(peer.id),
713
1729
  color: getColorForValue({
714
1730
  value: peer.id,
@@ -722,44 +1738,34 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
722
1738
  });
723
1739
  }
724
1740
  }, [
725
- provider,
1741
+ awareness,
726
1742
  peer,
727
1743
  themeMode
728
1744
  ]);
729
- const [root, setRoot] = useState(null);
730
- const [state, setState] = useState();
731
- const [view, setView] = useState();
732
- useImperativeHandle(forwardedRef, () => ({
733
- root,
734
- state,
735
- view
736
- }), [
737
- view,
738
- state,
739
- root
740
- ]);
741
- useEffect(() => {
1745
+ useEffect2(() => {
742
1746
  if (!root) {
743
1747
  return;
744
1748
  }
745
1749
  const state2 = EditorState2.create({
746
1750
  doc: model.text(),
747
1751
  extensions: [
1752
+ readonly && EditorState2.readOnly.of(readonly),
748
1753
  // TODO(burdon): Factor out VIM mode?
749
1754
  editorMode === "vim" && vim(),
750
1755
  // Theme.
751
- EditorView5.baseTheme(defaultTheme),
752
- EditorView5.theme(slots?.editor?.theme ?? {}),
1756
+ // TODO(burdon): Make optional.
1757
+ EditorView11.baseTheme(defaultTheme),
1758
+ EditorView11.theme(slots?.editor?.theme ?? {}),
753
1759
  // TODO(burdon): themeMode doesn't change in storybooks.
754
- EditorView5.darkTheme.of(themeMode === "dark"),
1760
+ EditorView11.darkTheme.of(themeMode === "dark"),
755
1761
  // Storage and replication.
756
- yCollab(model.content, model.provider?.awareness),
1762
+ model.extension,
757
1763
  // Custom.
758
1764
  ...extensions
759
1765
  ].filter(Boolean)
760
1766
  });
761
1767
  view?.destroy();
762
- setView(new EditorView5({
1768
+ setView(new EditorView11({
763
1769
  state: state2,
764
1770
  parent: root
765
1771
  }));
@@ -771,8 +1777,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
771
1777
  };
772
1778
  }, [
773
1779
  root,
774
- model.content,
1780
+ model,
775
1781
  themeMode,
1782
+ readonly,
776
1783
  editorMode
777
1784
  ]);
778
1785
  const handleKeyUp = useCallback((event) => {
@@ -800,13 +1807,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
800
1807
  onKeyUp: handleKeyUp
801
1808
  });
802
1809
  });
803
- var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1810
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
804
1811
  const { themeMode } = useThemeContext();
805
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
1812
+ const slots = defaultsDeep2({}, _slots, defaultTextSlots);
806
1813
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
807
1814
  ref: forwardedRef,
1815
+ readonly,
808
1816
  extensions: [
809
1817
  basicBundle({
1818
+ readonly,
810
1819
  themeMode,
811
1820
  placeholder: slots?.editor?.placeholder
812
1821
  }),
@@ -816,13 +1825,15 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, .
816
1825
  ...props
817
1826
  });
818
1827
  });
819
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
1828
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
820
1829
  const { themeMode } = useThemeContext();
821
- const slots = defaultsDeep({}, _slots, defaultSlots);
1830
+ const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
822
1831
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
823
1832
  ref: forwardedRef,
1833
+ readonly,
824
1834
  extensions: [
825
1835
  markdownBundle({
1836
+ readonly,
826
1837
  themeMode,
827
1838
  placeholder: slots?.editor?.placeholder
828
1839
  }),
@@ -834,7 +1845,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
834
1845
  });
835
1846
  var defaultSlots = {
836
1847
  root: {
837
- className: mx2("p-2", inputSurface)
1848
+ className: mx4("p-2", inputSurface)
838
1849
  }
839
1850
  };
840
1851
  var defaultTextSlots = {
@@ -843,26 +1854,31 @@ var defaultTextSlots = {
843
1854
  theme: textTheme
844
1855
  }
845
1856
  };
1857
+ var defaultMarkdownSlots = {
1858
+ ...defaultSlots,
1859
+ editor: {
1860
+ theme: markdownTheme
1861
+ }
1862
+ };
846
1863
 
847
- // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
848
- import { useEffect as useEffect2, useState as useState2 } from "react";
849
- import { yCollab as yCollab2 } from "y-codemirror.next";
850
- import { generateName as generateName2 } from "@dxos/display-name";
851
- import { isDocAccessor } from "@dxos/echo-schema";
852
- import { getColorForValue as getColorForValue2 } from "@dxos/react-ui-theme";
853
- import { YText } from "@dxos/text-model";
1864
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1865
+ import get4 from "lodash.get";
1866
+ import { useEffect as useEffect3, useState as useState2 } from "react";
1867
+ import { yCollab } from "y-codemirror.next";
1868
+ import { invariant as invariant3 } from "@dxos/invariant";
1869
+ import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
854
1870
 
855
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
856
- import { Annotation, Facet, StateEffect, StateField as StateField4 } from "@codemirror/state";
857
- import { ViewPlugin } from "@codemirror/view";
1871
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
1872
+ import { Annotation, Facet, StateEffect as StateEffect2, StateField as StateField6 } from "@codemirror/state";
1873
+ import { ViewPlugin as ViewPlugin4 } from "@codemirror/view";
858
1874
  import * as automerge2 from "@dxos/automerge/automerge";
859
1875
 
860
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
1876
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
861
1877
  import { next as automerge } from "@dxos/automerge/automerge";
862
1878
 
863
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
1879
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/automerge-to-codemirror.ts
864
1880
  import { ChangeSet } from "@codemirror/state";
865
- var amToCodemirror_default = (view, selection, target, patches) => {
1881
+ var automergeToCodemirror = (view, selection, target, patches) => {
866
1882
  for (const patch of patches) {
867
1883
  const changeSpec = handlePatch(patch, target, view.state);
868
1884
  if (changeSpec != null) {
@@ -967,9 +1983,9 @@ var charPath = (textPath, candidatePath) => {
967
1983
  return null;
968
1984
  };
969
1985
 
970
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
1986
+ // packages/ui/react-ui-editor/src/hooks/automerge/translation/codemirror-to-automerge.ts
971
1987
  import { next as am } from "@dxos/automerge/automerge";
972
- var codeMirrorToAm_default = (field, handle, transactions, state) => {
1988
+ var codemirrorToAutomerge = (field, handle, transactions, state) => {
973
1989
  const { lastHeads, path } = state.field(field);
974
1990
  let hasChanges = false;
975
1991
  for (const tr of transactions) {
@@ -990,7 +2006,7 @@ var codeMirrorToAm_default = (field, handle, transactions, state) => {
990
2006
  return newHeads ?? void 0;
991
2007
  };
992
2008
 
993
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
2009
+ // packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
994
2010
  var PatchSemaphore = class {
995
2011
  constructor(field) {
996
2012
  this._inReconcile = false;
@@ -1013,12 +2029,12 @@ var PatchSemaphore = class {
1013
2029
  annotations: reconcileAnnotationType.of(true)
1014
2030
  });
1015
2031
  }
1016
- let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
2032
+ let newHeads = codemirrorToAutomerge(this._field, handle, transactions, view.state);
1017
2033
  if (newHeads === null || newHeads === void 0) {
1018
2034
  newHeads = automerge.getHeads(handle.docSync());
1019
2035
  }
1020
2036
  const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
1021
- amToCodemirror_default(view, selection, path, diff);
2037
+ automergeToCodemirror(view, selection, path, diff);
1022
2038
  view.dispatch({
1023
2039
  effects: updateHeads(newHeads),
1024
2040
  annotations: reconcileAnnotationType.of({})
@@ -1031,8 +2047,8 @@ var PatchSemaphore = class {
1031
2047
  }
1032
2048
  };
1033
2049
 
1034
- // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
1035
- var effectType = StateEffect.define({});
2050
+ // packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
2051
+ var effectType = StateEffect2.define({});
1036
2052
  var updateHeads = (newHeads) => effectType.of({
1037
2053
  newHeads
1038
2054
  });
@@ -1042,7 +2058,7 @@ var semaphoreFacet = Facet.define({
1042
2058
  combine: (values) => values.at(-1)
1043
2059
  });
1044
2060
  var automergePlugin = (handle, path) => {
1045
- const stateField = StateField4.define({
2061
+ const stateField = StateField6.define({
1046
2062
  create: () => ({
1047
2063
  lastHeads: automerge2.getHeads(handle.docSync()),
1048
2064
  unreconciledTransactions: [],
@@ -1072,7 +2088,7 @@ var automergePlugin = (handle, path) => {
1072
2088
  }
1073
2089
  });
1074
2090
  const semaphore = new PatchSemaphore(stateField);
1075
- const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
2091
+ const viewPlugin = ViewPlugin4.fromClass(class AutomergeCodemirrorViewPlugin {
1076
2092
  constructor(view) {
1077
2093
  this._handleChange = () => {
1078
2094
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
@@ -1080,8 +2096,8 @@ var automergePlugin = (handle, path) => {
1080
2096
  this._view = view;
1081
2097
  handle.addListener("change", this._handleChange);
1082
2098
  }
1083
- update(update2) {
1084
- 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))) {
1085
2101
  queueMicrotask(() => {
1086
2102
  this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
1087
2103
  });
@@ -1102,47 +2118,8 @@ var automergePlugin = (handle, path) => {
1102
2118
  var reconcileAnnotationType = Annotation.define();
1103
2119
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
1104
2120
 
1105
- // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
1106
- var useCollaboration = (model, themeMode) => {
1107
- const { content, provider, peer } = model;
1108
- const [extension] = useState2(() => {
1109
- if (content instanceof YText) {
1110
- return yCollab2(content, provider?.awareness);
1111
- } else if (isDocAccessor(content)) {
1112
- return automergePlugin(content.handle, content.path);
1113
- } else {
1114
- return void 0;
1115
- }
1116
- });
1117
- useEffect2(() => {
1118
- if (provider && peer) {
1119
- provider.awareness.setLocalStateField("user", {
1120
- name: peer.name ?? generateName2(peer.id),
1121
- color: getColorForValue2({
1122
- value: peer.id,
1123
- type: "color"
1124
- }),
1125
- colorLight: getColorForValue2({
1126
- value: peer.id,
1127
- themeMode,
1128
- type: "highlight"
1129
- })
1130
- });
1131
- }
1132
- }, [
1133
- provider,
1134
- peer,
1135
- themeMode
1136
- ]);
1137
- return extension;
1138
- };
1139
-
1140
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1141
- import { useMemo } from "react";
1142
- import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
1143
-
1144
2121
  // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
1145
- import * as random from "lib0/random";
2122
+ import * as random2 from "lib0/random";
1146
2123
  var cursorColors = [
1147
2124
  {
1148
2125
  color: "#30bced",
@@ -1177,7 +2154,7 @@ var cursorColors = [
1177
2154
  light: "#1be7ff33"
1178
2155
  }
1179
2156
  ];
1180
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
2157
+ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1181
2158
 
1182
2159
  // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1183
2160
  import * as decoding from "lib0/decoding";
@@ -1185,7 +2162,7 @@ import * as encoding from "lib0/encoding";
1185
2162
  import { Observable } from "lib0/observable";
1186
2163
  import * as awarenessProtocol from "y-protocols/awareness";
1187
2164
  import { log } from "@dxos/log";
1188
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
2165
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1189
2166
  var messageAwareness = 1;
1190
2167
  var messageQueryAwareness = 3;
1191
2168
  var SpaceAwarenessProvider = class extends Observable {
@@ -1222,7 +2199,7 @@ var SpaceAwarenessProvider = class extends Observable {
1222
2199
  removed,
1223
2200
  origin
1224
2201
  }, {
1225
- F: __dxlog_file,
2202
+ F: __dxlog_file3,
1226
2203
  L: 67,
1227
2204
  S: this,
1228
2205
  C: (f, a) => f(...a)
@@ -1235,7 +2212,7 @@ var SpaceAwarenessProvider = class extends Observable {
1235
2212
  }
1236
2213
  _handleSpaceMessage({ payload }) {
1237
2214
  log("space message", payload, {
1238
- F: __dxlog_file,
2215
+ F: __dxlog_file3,
1239
2216
  L: 79,
1240
2217
  S: this,
1241
2218
  C: (f, a) => f(...a)
@@ -1280,57 +2257,76 @@ var SpaceAwarenessProvider = class extends Observable {
1280
2257
  };
1281
2258
 
1282
2259
  // 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";
1283
2261
  var useTextModel = ({ identity, space, text }) => {
1284
- const provider = useMemo(() => {
1285
- if (isActualAutomergeObject(text)) {
1286
- return void 0;
1287
- }
1288
- if (!space || !text?.doc) {
1289
- return void 0;
1290
- }
1291
- return new SpaceAwarenessProvider({
2262
+ const [model, setModel] = useState2(() => createModel({
2263
+ identity,
2264
+ space,
2265
+ text
2266
+ }));
2267
+ useEffect3(() => {
2268
+ setModel(createModel({
2269
+ identity,
1292
2270
  space,
1293
- doc: text.doc,
1294
- channel: `yjs.awareness.${text.id}`
1295
- });
2271
+ text
2272
+ }));
1296
2273
  }, [
1297
2274
  identity,
1298
2275
  space,
1299
- text,
1300
- text?.doc
1301
- ]);
1302
- const content = useMemo(() => {
1303
- if (isActualAutomergeObject(text)) {
1304
- const obj = text;
1305
- return getRawDoc(obj, [
1306
- obj.field
1307
- ]);
1308
- } else {
1309
- return text?.content;
1310
- }
1311
- }, [
1312
2276
  text
1313
2277
  ]);
1314
- if (!text?.doc || !text?.content) {
1315
- return void 0;
1316
- }
2278
+ return model;
2279
+ };
2280
+ var createModel = (options) => {
2281
+ const { text } = options;
1317
2282
  if (isActualAutomergeObject(text)) {
1318
- const obj = text;
1319
- return {
1320
- id: obj.id,
1321
- content,
1322
- text: () => content.toString(),
1323
- provider: void 0,
1324
- peer: identity ? {
1325
- id: identity.identityKey.toHex(),
1326
- name: identity.profile?.displayName
1327
- } : void 0
1328
- };
2283
+ return createAutomergeModel(options);
2284
+ } else {
2285
+ if (!text?.doc) {
2286
+ return void 0;
2287
+ }
2288
+ return createYjsModel(options);
1329
2289
  }
2290
+ };
2291
+ var createYjsModel = ({ identity, space, text }) => {
2292
+ invariant3(text?.doc && text?.content, void 0, {
2293
+ F: __dxlog_file4,
2294
+ L: 81,
2295
+ S: void 0,
2296
+ A: [
2297
+ "text?.doc && text?.content",
2298
+ ""
2299
+ ]
2300
+ });
2301
+ const provider = space ? new SpaceAwarenessProvider({
2302
+ space,
2303
+ doc: text.doc,
2304
+ channel: `yjs.awareness.${text.id}`
2305
+ }) : void 0;
1330
2306
  return {
1331
2307
  id: text.doc.guid,
1332
2308
  content: text.content,
1333
2309
  text: () => text.content.toString(),
2310
+ ranges: [],
2311
+ extension: yCollab(text.content, provider?.awareness),
2312
+ awareness: provider?.awareness,
2313
+ peer: identity ? {
2314
+ id: identity.identityKey.toHex(),
2315
+ name: identity.profile?.displayName
2316
+ } : void 0
2317
+ };
2318
+ };
2319
+ var createAutomergeModel = ({ identity, text }) => {
2320
+ const obj = text;
2321
+ const doc = getRawDoc(obj, [
2322
+ obj.field
2323
+ ]);
2324
+ return {
2325
+ id: obj.id,
2326
+ content: doc,
2327
+ text: () => get4(doc.handle.docSync(), doc.path),
2328
+ ranges: [],
2329
+ extension: automergePlugin(doc.handle, doc.path),
1334
2330
  peer: identity ? {
1335
2331
  id: identity.identityKey.toHex(),
1336
2332
  name: identity.profile?.displayName
@@ -1345,28 +2341,39 @@ export {
1345
2341
  SpaceAwarenessProvider,
1346
2342
  TextEditor,
1347
2343
  TextKind,
1348
- YText2 as YText,
2344
+ YText,
1349
2345
  YXmlFragment,
1350
2346
  autocomplete,
1351
2347
  basicBundle,
2348
+ blast,
2349
+ code2 as code,
1352
2350
  codeTheme,
2351
+ comments,
1353
2352
  cursorColor,
2353
+ defaultMarkdownSlots,
2354
+ defaultOptions,
1354
2355
  defaultSlots,
1355
2356
  defaultTextSlots,
1356
2357
  defaultTheme,
2358
+ demo,
2359
+ highlight,
2360
+ highlightDecorations,
2361
+ highlightStateField,
2362
+ image,
1357
2363
  link,
1358
- listener,
2364
+ listener2 as listener,
1359
2365
  markdownBundle,
1360
2366
  markdownHighlightStyle,
1361
2367
  markdownTags,
1362
- markdownTagsExtension,
1363
- statefield,
1364
- styles,
2368
+ markdownTagsExtensions,
2369
+ markdownTheme,
2370
+ mention,
2371
+ table,
1365
2372
  tags2 as tags,
1366
2373
  tasklist,
1367
2374
  textTheme,
1368
2375
  tooltip,
1369
- useCollaboration,
2376
+ useHighlights,
1370
2377
  useTextModel
1371
2378
  };
1372
2379
  //# sourceMappingURL=index.mjs.map