@dxos/react-ui-editor 0.3.11-main.c86e105 → 0.3.11-main.cc2fe2c

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 (144) hide show
  1. package/dist/lib/browser/index.mjs +1036 -1056
  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 +13 -14
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +14 -6
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  9. package/dist/types/src/components/TextEditor/comments.stories.d.ts +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -1
  11. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/defs.d.ts +7 -9
  14. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/semaphore.d.ts +7 -12
  16. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
  18. package/dist/types/src/extensions/awareness.d.ts +1 -1
  19. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  20. package/dist/types/src/extensions/basic.d.ts +4 -5
  21. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  22. package/dist/types/src/extensions/comments.d.ts +27 -18
  23. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  24. package/dist/types/src/extensions/cursor.d.ts +3 -3
  25. package/dist/types/src/extensions/cursor.d.ts.map +1 -1
  26. package/dist/types/src/extensions/index.d.ts +1 -7
  27. package/dist/types/src/extensions/index.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  29. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  30. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  31. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  32. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  33. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  35. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  36. package/dist/types/src/extensions/markdown/index.d.ts +7 -0
  37. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  40. package/dist/types/src/extensions/markdown/tasklist.d.ts +3 -0
  41. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  42. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  43. package/dist/types/src/extensions/outliner.d.ts +4 -0
  44. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  45. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  46. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  47. package/dist/types/src/hooks/defs.d.ts +25 -0
  48. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  49. package/dist/types/src/hooks/index.d.ts +2 -2
  50. package/dist/types/src/hooks/index.d.ts.map +1 -1
  51. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  52. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  53. package/dist/types/src/hooks/useTextModel.d.ts +2 -27
  54. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  55. package/dist/types/src/styles/markdown.d.ts +2 -2
  56. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  57. package/dist/types/src/themes/default.d.ts.map +1 -1
  58. package/dist/types/src/util.d.ts +2 -0
  59. package/dist/types/src/util.d.ts.map +1 -1
  60. package/package.json +31 -34
  61. package/src/components/TextEditor/MarkdownEditor.stories.tsx +69 -72
  62. package/src/components/TextEditor/TextEditor.tsx +95 -70
  63. package/src/components/TextEditor/comments.stories.tsx +104 -76
  64. package/src/extensions/autocomplete.ts +6 -3
  65. package/src/extensions/automerge/automerge.ts +39 -45
  66. package/src/extensions/automerge/defs.ts +10 -31
  67. package/src/extensions/automerge/semaphore.ts +23 -20
  68. package/src/extensions/automerge/update-automerge.ts +2 -2
  69. package/src/extensions/awareness.ts +15 -14
  70. package/src/extensions/basic.ts +22 -17
  71. package/src/extensions/comments.ts +199 -161
  72. package/src/extensions/cursor.ts +7 -3
  73. package/src/extensions/index.ts +1 -7
  74. package/src/extensions/markdown/bundle.ts +20 -30
  75. package/src/extensions/markdown/code.ts +127 -0
  76. package/src/extensions/markdown/heading.ts +59 -0
  77. package/src/extensions/markdown/highlight.ts +9 -15
  78. package/src/extensions/markdown/hr.ts +71 -0
  79. package/src/extensions/markdown/index.ts +7 -0
  80. package/src/extensions/{link.ts → markdown/link.ts} +4 -5
  81. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +48 -33
  82. package/src/extensions/mention.ts +9 -2
  83. package/src/extensions/outliner.ts +12 -0
  84. package/src/hooks/awareness-provider.ts +24 -20
  85. package/src/hooks/defs.ts +42 -0
  86. package/src/hooks/index.ts +3 -2
  87. package/src/hooks/useTextEditor.ts +29 -0
  88. package/src/hooks/useTextModel.ts +47 -56
  89. package/src/styles/markdown.ts +9 -10
  90. package/src/themes/default.ts +0 -2
  91. package/src/util.ts +26 -3
  92. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  93. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  94. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
  95. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  96. package/dist/types/src/extensions/code.d.ts.map +0 -1
  97. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  98. package/dist/types/src/extensions/image.d.ts.map +0 -1
  99. package/dist/types/src/extensions/link.d.ts.map +0 -1
  100. package/dist/types/src/extensions/table.d.ts.map +0 -1
  101. package/dist/types/src/extensions/tasklist.d.ts +0 -7
  102. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  103. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  104. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  105. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  106. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  107. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  108. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  109. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  110. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  111. package/dist/types/src/hooks/automerge.d.ts +0 -3
  112. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  113. package/dist/types/src/hooks/yjs.d.ts +0 -27
  114. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  115. package/dist/types/src/testing/index.d.ts +0 -3
  116. package/dist/types/src/testing/index.d.ts.map +0 -1
  117. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  118. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  119. package/dist/types/src/testing/proto/index.d.ts +0 -2
  120. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  121. package/dist/types/src/testing/replicator.d.ts +0 -45
  122. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  123. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  124. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  125. package/src/extensions/code.ts +0 -106
  126. package/src/extensions/hr.ts +0 -73
  127. package/src/extensions/yjs/cursor.ts +0 -22
  128. package/src/extensions/yjs/index.ts +0 -5
  129. package/src/extensions/yjs/yjs.test.ts +0 -35
  130. package/src/extensions/yjs/yjs.ts +0 -16
  131. package/src/hooks/automerge.ts +0 -52
  132. package/src/hooks/yjs.ts +0 -156
  133. package/src/testing/index.ts +0 -6
  134. package/src/testing/proto/gen/schema.ts +0 -49
  135. package/src/testing/proto/index.ts +0 -5
  136. package/src/testing/proto/schema.proto +0 -15
  137. package/src/testing/replicator.ts +0 -184
  138. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  139. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  140. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  141. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  142. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  143. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  144. /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
@@ -7,14 +7,14 @@ import { tags as tags2 } from "@lezer/highlight";
7
7
 
8
8
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
9
  import { EditorState as EditorState2 } from "@codemirror/state";
10
- import { EditorView as EditorView12 } from "@codemirror/view";
10
+ import { EditorView as EditorView13 } from "@codemirror/view";
11
11
  import { useFocusableGroup } from "@fluentui/react-tabster";
12
12
  import { vim } from "@replit/codemirror-vim";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState, useRef } from "react";
15
- import { generateName } from "@dxos/display-name";
14
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
15
+ import { log as log2 } from "@dxos/log";
16
16
  import { useThemeContext } from "@dxos/react-ui";
17
- import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
18
18
 
19
19
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
20
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -29,9 +29,11 @@ var autocomplete = ({ onSearch }) => {
29
29
  // https://codemirror.net/examples/autocompletion
30
30
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
31
31
  autocompletion({
32
- defaultKeymap: false,
33
- // Don't start unless key press.
34
- activateOnTyping: false
32
+ activateOnTyping: true,
33
+ // closeOnBlur: false,
34
+ // defaultKeymap: false,
35
+ // TODO(burdon): Styles/fragments.
36
+ tooltipClass: () => "shadow rounded"
35
37
  }),
36
38
  // TODO(burdon): Option to create new page?
37
39
  // TODO(burdon): Optional decoration via addToOptions
@@ -51,9 +53,10 @@ var autocomplete = ({ onSearch }) => {
51
53
  };
52
54
 
53
55
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
54
- import { Facet as Facet2, StateField } from "@codemirror/state";
55
- import { ViewPlugin } from "@codemirror/view";
56
+ import { StateField } from "@codemirror/state";
57
+ import { EditorView, ViewPlugin } from "@codemirror/view";
56
58
  import { next as A } from "@dxos/automerge/automerge";
59
+ import { invariant } from "@dxos/invariant";
57
60
 
58
61
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
59
62
  import get from "lodash.get";
@@ -94,12 +97,12 @@ var cursorConverter = (handle, path) => ({
94
97
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
95
98
  import { Annotation, StateEffect } from "@codemirror/state";
96
99
  var effectType = StateEffect.define({});
100
+ var reconcileAnnotationType = Annotation.define();
101
+ var getPath = (state, field) => state.field(field).path;
102
+ var getLastHeads = (state, field) => state.field(field).lastHeads;
97
103
  var updateHeads = (newHeads) => effectType.of({
98
104
  newHeads
99
105
  });
100
- var getLastHeads = (state, field) => state.field(field).lastHeads;
101
- var getPath = (state, field) => state.field(field).path;
102
- var reconcileAnnotationType = Annotation.define();
103
106
  var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
104
107
 
105
108
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
@@ -237,40 +240,42 @@ var charPath = (textPath, candidatePath) => {
237
240
 
238
241
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
239
242
  var PatchSemaphore = class {
240
- constructor(_field) {
241
- this._field = _field;
243
+ // prettier-ignore
244
+ constructor(_handle, _state) {
245
+ this._handle = _handle;
246
+ this._state = _state;
242
247
  this._inReconcile = false;
243
- this._queue = [];
244
- this.reconcile = (handle, view) => {
245
- if (this._inReconcile) {
246
- return;
247
- }
248
- this._inReconcile = true;
249
- const path = getPath(view.state, this._field);
250
- const oldHeads = getLastHeads(view.state, this._field);
251
- let selection = view.state.selection;
252
- const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
253
- const toInvert = transactions.slice().reverse();
254
- for (const tx of toInvert) {
255
- const inverted = tx.changes.invert(tx.startState.doc);
256
- selection = selection.map(inverted);
257
- view.dispatch({
258
- changes: inverted,
259
- annotations: reconcileAnnotationType.of(true)
260
- });
261
- }
262
- let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
263
- if (newHeads === null || newHeads === void 0) {
264
- newHeads = automerge2.getHeads(handle.docSync());
265
- }
266
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
267
- updateCodeMirror(view, selection, path, diff);
248
+ }
249
+ // NOTE: Cannot destruct view.state.
250
+ reconcile(view) {
251
+ if (this._inReconcile) {
252
+ return;
253
+ }
254
+ this._inReconcile = true;
255
+ const path = getPath(view.state, this._state);
256
+ const oldHeads = getLastHeads(view.state, this._state);
257
+ let selection = view.state.selection;
258
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
259
+ const toInvert = transactions.slice().reverse();
260
+ for (const tx of toInvert) {
261
+ const inverted = tx.changes.invert(tx.startState.doc);
262
+ selection = selection.map(inverted);
268
263
  view.dispatch({
269
- effects: updateHeads(newHeads),
270
- annotations: reconcileAnnotationType.of({})
264
+ changes: inverted,
265
+ annotations: reconcileAnnotationType.of(true)
271
266
  });
272
- this._inReconcile = false;
273
- };
267
+ }
268
+ let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
+ if (newHeads === null || newHeads === void 0) {
270
+ newHeads = automerge2.getHeads(this._handle.docSync());
271
+ }
272
+ const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
273
+ updateCodeMirror(view, selection, path, diff);
274
+ view.dispatch({
275
+ effects: updateHeads(newHeads),
276
+ annotations: reconcileAnnotationType.of({})
277
+ });
278
+ this._inReconcile = false;
274
279
  }
275
280
  };
276
281
 
@@ -280,16 +285,17 @@ var defaultCursorConverter = {
280
285
  toCursor: (position) => position.toString(),
281
286
  fromCursor: (cursor) => parseInt(cursor)
282
287
  };
283
- var Cursor = class {
288
+ var Cursor = class _Cursor {
284
289
  static {
285
290
  this.converter = Facet.define({
286
291
  combine: (providers) => providers[0] ?? defaultCursorConverter
287
292
  });
288
293
  }
289
294
  static {
290
- this.getCursorFromRange = (cursorConverter3, range) => {
291
- const from = cursorConverter3.toCursor(range.from);
292
- const to = cursorConverter3.toCursor(range.to, -1);
295
+ this.getCursorFromRange = (state, range) => {
296
+ const cursorConverter2 = state.facet(_Cursor.converter);
297
+ const from = cursorConverter2.toCursor(range.from);
298
+ const to = cursorConverter2.toCursor(range.to, -1);
293
299
  return [
294
300
  from,
295
301
  to
@@ -297,10 +303,11 @@ var Cursor = class {
297
303
  };
298
304
  }
299
305
  static {
300
- this.getRangeFromCursor = (cursorConverter3, cursor) => {
306
+ this.getRangeFromCursor = (state, cursor) => {
307
+ const cursorConverter2 = state.facet(_Cursor.converter);
301
308
  const parts = cursor.split(":");
302
- const from = cursorConverter3.fromCursor(parts[0]);
303
- const to = cursorConverter3.fromCursor(parts[1]);
309
+ const from = cursorConverter2.fromCursor(parts[0]);
310
+ const to = cursorConverter2.fromCursor(parts[1]);
304
311
  return from !== void 0 && to !== void 0 ? {
305
312
  from,
306
313
  to
@@ -310,21 +317,19 @@ var Cursor = class {
310
317
  };
311
318
 
312
319
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
313
- var semaphoreFacet = Facet2.define({
314
- combine: (values) => values.at(-1)
315
- });
320
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
316
321
  var automerge3 = ({ handle, path }) => {
317
- const stateField = StateField.define({
322
+ const state = StateField.define({
318
323
  create: () => ({
324
+ path: path.slice(),
319
325
  lastHeads: A.getHeads(handle.docSync()),
320
- unreconciledTransactions: [],
321
- path: path.slice()
326
+ unreconciledTransactions: []
322
327
  }),
323
328
  update: (value, tr) => {
324
329
  const result = {
330
+ path: path.slice(),
325
331
  lastHeads: value.lastHeads,
326
- unreconciledTransactions: value.unreconciledTransactions.slice(),
327
- path: path.slice()
332
+ unreconciledTransactions: value.unreconciledTransactions.slice()
328
333
  };
329
334
  let clearUnreconciled = false;
330
335
  for (const effect of tr.effects) {
@@ -343,39 +348,44 @@ var automerge3 = ({ handle, path }) => {
343
348
  return result;
344
349
  }
345
350
  });
346
- const semaphore = new PatchSemaphore(stateField);
347
- const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
348
- constructor(_view) {
349
- this._view = _view;
350
- this._handleChange = () => {
351
- this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
352
- };
353
- handle.addListener("change", this._handleChange);
354
- }
355
- update(update2) {
356
- if (update2.transactions.length > 0 && update2.transactions.some((tr) => !isReconcileTx(tr))) {
357
- queueMicrotask(() => {
358
- this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
351
+ const semaphore = new PatchSemaphore(handle, state);
352
+ return [
353
+ Cursor.converter.of(cursorConverter(handle, path)),
354
+ // Reconcile external updates.
355
+ ViewPlugin.fromClass(class {
356
+ constructor(_view) {
357
+ this._view = _view;
358
+ invariant(this._view, void 0, {
359
+ F: __dxlog_file,
360
+ L: 66,
361
+ S: this,
362
+ A: [
363
+ "this._view",
364
+ ""
365
+ ]
359
366
  });
367
+ handle.addListener("change", this._handleChange.bind(this));
360
368
  }
361
- }
362
- destroy() {
363
- handle.addListener("change", this._handleChange);
364
- }
365
- });
366
- return {
367
- extension: [
368
- Cursor.converter.of(cursorConverter(handle, path)),
369
- semaphoreFacet.of(semaphore),
370
- stateField,
371
- viewPlugin
372
- ]
373
- };
369
+ destroy() {
370
+ handle.removeListener("change", this._handleChange.bind(this));
371
+ }
372
+ _handleChange() {
373
+ semaphore.reconcile(this._view);
374
+ }
375
+ }),
376
+ // Reconcile local updates.
377
+ EditorView.updateListener.of(({ view, changes }) => {
378
+ if (!changes.empty) {
379
+ semaphore.reconcile(view);
380
+ }
381
+ }),
382
+ state
383
+ ];
374
384
  };
375
385
 
376
386
  // packages/ui/react-ui-editor/src/extensions/awareness.ts
377
- import { Annotation as Annotation2, Facet as Facet3, RangeSet } from "@codemirror/state";
378
- import { Decoration, EditorView, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
387
+ import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
388
+ import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
379
389
  import { Event } from "@dxos/async";
380
390
  import { Context } from "@dxos/context";
381
391
  var dummyProvider = {
@@ -384,11 +394,11 @@ var dummyProvider = {
384
394
  },
385
395
  close: () => {
386
396
  },
397
+ getRemoteStates: () => [],
387
398
  update: () => {
388
- },
389
- getRemoteStates: () => []
399
+ }
390
400
  };
391
- var AwarenessProvider = Facet3.define({
401
+ var AwarenessProvider = Facet2.define({
392
402
  combine: (providers) => providers[0] ?? dummyProvider
393
403
  });
394
404
  var RemoteSelectionChangedAnnotation = Annotation2.define();
@@ -549,7 +559,7 @@ var RemoteCaretWidget = class extends WidgetType {
549
559
  return true;
550
560
  }
551
561
  };
552
- var styles = EditorView.baseTheme({
562
+ var styles = EditorView2.baseTheme({
553
563
  ".cm-collab-selection": {},
554
564
  ".cm-collab-selectionLine": {
555
565
  padding: 0,
@@ -562,40 +572,42 @@ var styles = EditorView.baseTheme({
562
572
  marginLeft: "-1px",
563
573
  marginRight: "-1px",
564
574
  boxSizing: "border-box",
565
- display: "inline"
575
+ display: "inline",
576
+ cursor: "pointer"
566
577
  },
567
578
  ".cm-collab-selectionCaretDot": {
568
579
  borderRadius: "50%",
569
580
  position: "absolute",
570
- width: ".4em",
571
- height: ".4em",
572
- top: "-.2em",
573
- left: "-.2em",
581
+ width: ".5em",
582
+ height: ".5em",
583
+ top: "-.25em",
584
+ left: "-.25em",
574
585
  backgroundColor: "inherit",
575
586
  transition: "transform .3s ease-in-out",
576
587
  boxSizing: "border-box"
577
588
  },
578
589
  ".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
579
- transformOrigin: "bottom center",
580
- transform: "scale(0)"
590
+ transform: "scale(0)",
591
+ transformOrigin: "center"
581
592
  },
582
593
  ".cm-collab-selectionInfo": {
583
594
  position: "absolute",
584
- top: "-1.05em",
585
- left: "-1px",
595
+ transform: "translate(-50%, 0)",
596
+ top: "-20px",
597
+ left: 0,
586
598
  fontSize: ".75em",
587
- fontFamily: "serif",
599
+ fontFamily: "sans-serif",
588
600
  fontStyle: "normal",
589
601
  fontWeight: "normal",
590
602
  lineHeight: "normal",
591
603
  userSelect: "none",
592
604
  color: "white",
593
- paddingLeft: "2px",
594
- paddingRight: "2px",
605
+ padding: "2px",
595
606
  zIndex: 101,
596
607
  transition: "opacity .3s ease-in-out",
597
608
  backgroundColor: "inherit",
598
- // these should be separate
609
+ borderRadius: "2px",
610
+ // These should be separate.
599
611
  opacity: 0,
600
612
  transitionDelay: "0s",
601
613
  whiteSpace: "nowrap"
@@ -608,26 +620,28 @@ var styles = EditorView.baseTheme({
608
620
 
609
621
  // packages/ui/react-ui-editor/src/extensions/basic.ts
610
622
  import { closeBrackets } from "@codemirror/autocomplete";
623
+ import { history } from "@codemirror/commands";
611
624
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
612
625
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
613
- import { drawSelection, EditorView as EditorView2, placeholder } from "@codemirror/view";
614
- var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
615
- EditorView2.lineWrapping,
616
- // TODO(burdon): Compare with minimalSetup.
626
+ import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
627
+ var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
628
+ lineWrapping ? EditorView3.lineWrapping : [],
617
629
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
618
630
  bracketMatching(),
619
631
  closeBrackets(),
620
632
  drawSelection(),
621
- _placeholder && placeholder(_placeholder),
622
- // TODO(burdon): Is this required?
633
+ highlightActiveLine(),
634
+ history(),
635
+ _placeholder ? placeholder(_placeholder) : [],
636
+ // https://github.com/codemirror/theme-one-dark
623
637
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
624
- ].filter(Boolean);
638
+ ];
625
639
 
626
640
  // packages/ui/react-ui-editor/src/extensions/blast.ts
627
- import { EditorView as EditorView3, keymap as keymap2 } from "@codemirror/view";
641
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
628
642
  import defaultsDeep from "lodash.defaultsdeep";
629
- import { invariant } from "@dxos/invariant";
630
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
643
+ import { invariant as invariant2 } from "@dxos/invariant";
644
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
631
645
  var defaultOptions = {
632
646
  effect: 2,
633
647
  maxParticles: 200,
@@ -660,10 +674,10 @@ var blast = (options = defaultOptions) => {
660
674
  const getPoint = (view, pos) => {
661
675
  const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
662
676
  const element = document.elementFromPoint(x, y);
663
- const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
677
+ const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
664
678
  const point = {
665
679
  x: x - left,
666
- y: y - top
680
+ y: y - top2
667
681
  };
668
682
  return {
669
683
  element,
@@ -672,7 +686,7 @@ var blast = (options = defaultOptions) => {
672
686
  };
673
687
  return [
674
688
  // Cursor moved.
675
- EditorView3.updateListener.of((update2) => {
689
+ EditorView4.updateListener.of((update2) => {
676
690
  if (blaster?.node !== update2.view.scrollDOM) {
677
691
  if (blaster) {
678
692
  blaster.destroy();
@@ -753,8 +767,8 @@ var Blaster = class {
753
767
  return this._node;
754
768
  }
755
769
  initialize() {
756
- invariant(!this._canvas && !this._ctx, void 0, {
757
- F: __dxlog_file,
770
+ invariant2(!this._canvas && !this._ctx, void 0, {
771
+ F: __dxlog_file2,
758
772
  L: 138,
759
773
  S: this,
760
774
  A: [
@@ -790,8 +804,8 @@ var Blaster = class {
790
804
  }
791
805
  }
792
806
  start() {
793
- invariant(this._canvas && this._ctx, void 0, {
794
- F: __dxlog_file,
807
+ invariant2(this._canvas && this._ctx, void 0, {
808
+ F: __dxlog_file2,
795
809
  L: 177,
796
810
  S: this,
797
811
  A: [
@@ -933,9 +947,14 @@ var random = (min, max) => {
933
947
  return min + ~~(Math.random() * (max - min + 1));
934
948
  };
935
949
 
936
- // packages/ui/react-ui-editor/src/extensions/code.ts
937
- import { ViewPlugin as ViewPlugin3, EditorView as EditorView4, Decoration as Decoration2 } from "@codemirror/view";
938
- import { mx as mx2 } from "@dxos/react-ui-theme";
950
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
951
+ import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
952
+ import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
953
+ import sortBy from "lodash.sortby";
954
+ import { useEffect } from "react";
955
+ import { debounce } from "@dxos/async";
956
+ import { invariant as invariant3 } from "@dxos/invariant";
957
+ import { nonNullable } from "@dxos/util";
939
958
 
940
959
  // packages/ui/react-ui-editor/src/styles/cursors.ts
941
960
  import * as random2 from "lib0/random";
@@ -978,34 +997,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
978
997
  // packages/ui/react-ui-editor/src/styles/markdown.ts
979
998
  import { mx } from "@dxos/react-ui-theme";
980
999
  var headings = {
981
- 1: [
982
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
983
- "-ml-[10px]"
984
- ],
985
- 2: [
986
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
987
- "-ml-[8px]"
988
- ],
989
- 3: [
990
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
991
- "-ml-[7px]"
992
- ],
993
- 4: [
994
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
995
- "-ml-[6px]"
996
- ],
997
- 5: [
998
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
999
- "-ml-[5px]"
1000
- ],
1001
- 6: [
1002
- "mbs-4 mbe-2 font-medium text-inherit no-underline",
1003
- "-ml-[4px]"
1004
- ]
1005
- };
1006
- var heading = (level, readonly) => {
1007
- const [style, offset] = headings[level];
1008
- return mx(style, readonly && offset);
1000
+ 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
1001
+ 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1002
+ 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1003
+ 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1004
+ 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1005
+ 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
1006
+ };
1007
+ var heading = (level) => {
1008
+ return headings[level];
1009
1009
  };
1010
1010
  var light = "text-neutral-200 dark:text-neutral-800";
1011
1011
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -1023,168 +1023,112 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
1023
1023
  var tokens = tailwindConfig({}).theme;
1024
1024
  var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
1025
1025
 
1026
- // packages/ui/react-ui-editor/src/extensions/code.ts
1027
- var CODE_REGEX = /```[\s\S]*?```/gs;
1028
- var styles2 = EditorView4.baseTheme({
1029
- "& .cm-codeblock": {
1030
- fontFamily: getToken("fontFamily.mono", []).join(",")
1031
- },
1032
- '&light [aria-readonly="true"] .cm-codeblock': {
1033
- background: getToken("extend.colors.neutral.50")
1034
- },
1035
- '&dark [aria-readonly="true"] .cm-codeblock': {
1036
- background: getToken("extend.colors.neutral.850")
1037
- },
1038
- '& [aria-readonly="true"] .cm-codeblock': {
1039
- display: "block",
1040
- paddingInline: "4px !important"
1041
- },
1042
- '& [aria-readonly="true"] .cm-codeblock-first': {
1043
- paddingTop: "4px !important",
1044
- borderTopLeftRadius: "4px",
1045
- borderTopRightRadius: "4px"
1046
- },
1047
- '& [aria-readonly="true"] .cm-codeblock-last': {
1048
- paddingBottom: "4px !important",
1049
- borderBottomLeftRadius: "4px",
1050
- borderBottomRightRadius: "4px"
1051
- }
1052
- });
1053
- var getLineRange = (lines, from, to) => {
1054
- const start = lines.findIndex((line) => line.from >= from);
1055
- const end = lines.findIndex((line) => line.to >= to);
1056
- return [
1057
- start,
1058
- end
1059
- ];
1060
- };
1061
- var code2 = () => {
1062
- const getDecorations = (view) => {
1063
- const decorations = [];
1064
- if (!view.hasFocus || view.state.readOnly) {
1065
- const text = view.state.doc.sliceString(0);
1066
- const matches = text.matchAll(CODE_REGEX);
1067
- const blocks = view.viewportLineBlocks;
1068
- for (const match of matches) {
1069
- const range = getLineRange(blocks, match.index, match.index + match[0].length);
1070
- for (let i = range[0]; i <= range[1]; i++) {
1071
- const block = blocks[i];
1072
- decorations.push(Decoration2.line({
1073
- class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1074
- }).range(block.from));
1075
- }
1076
- }
1077
- }
1078
- return Decoration2.set(decorations);
1079
- };
1080
- return [
1081
- ViewPlugin3.fromClass(class {
1082
- constructor(view) {
1083
- this.hasFocus = false;
1084
- this.decorations = getDecorations(view);
1085
- }
1086
- update(update2) {
1087
- if (
1088
- // TODO(burdon): Generalize for other extensions.
1089
- this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
1090
- ) {
1091
- this.hasFocus = update2.view.hasFocus;
1092
- this.decorations = getDecorations(update2.view);
1093
- }
1094
- }
1095
- }, {
1096
- decorations: (value) => value.decorations
1097
- }),
1098
- styles2
1099
- ];
1100
- };
1101
-
1102
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1103
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1104
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
1105
- import sortBy from "lodash.sortby";
1106
- import { debounce } from "@dxos/async";
1107
- import { invariant as invariant2 } from "@dxos/invariant";
1108
- import { log as log2 } from "@dxos/log";
1109
- import { nonNullable } from "@dxos/util";
1110
-
1111
1026
  // packages/ui/react-ui-editor/src/util.ts
1112
1027
  import { log } from "@dxos/log";
1113
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1028
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1114
1029
  var callbackWrapper = (fn) => (...args) => {
1115
1030
  try {
1116
1031
  return fn(...args);
1117
- } catch (error) {
1118
- log.catch(error, void 0, {
1119
- F: __dxlog_file2,
1120
- L: 16,
1032
+ } catch (err) {
1033
+ log.catch(err, void 0, {
1034
+ F: __dxlog_file3,
1035
+ L: 18,
1121
1036
  S: void 0,
1122
1037
  C: (f, a) => f(...a)
1123
1038
  });
1124
1039
  }
1125
1040
  };
1041
+ var logChanges = (trs) => {
1042
+ const changes = trs.flatMap((tr) => {
1043
+ if (tr.changes.empty) {
1044
+ return void 0;
1045
+ }
1046
+ const changes2 = [];
1047
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
1048
+ fromA,
1049
+ toA,
1050
+ fromB,
1051
+ toB,
1052
+ inserted: inserted.toString()
1053
+ })));
1054
+ return changes2;
1055
+ }).filter(Boolean);
1056
+ if (changes.length) {
1057
+ console.log("changes", changes);
1058
+ }
1059
+ };
1126
1060
 
1127
1061
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1128
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1129
- var styles3 = EditorView5.baseTheme({
1062
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1063
+ var styles2 = EditorView5.baseTheme({
1130
1064
  "& .cm-comment": {
1131
1065
  backgroundColor: getToken("extend.colors.yellow.50")
1132
1066
  },
1133
- "& .cm-comment-active": {
1067
+ "& .cm-comment-current": {
1134
1068
  backgroundColor: getToken("extend.colors.yellow.100")
1135
1069
  }
1136
1070
  });
1137
1071
  var marks = {
1138
- highlight: Decoration3.mark({
1072
+ highlight: Decoration2.mark({
1139
1073
  class: "cm-comment"
1140
1074
  }),
1141
- highlightActive: Decoration3.mark({
1142
- class: "cm-comment-active"
1075
+ highlightActive: Decoration2.mark({
1076
+ class: "cm-comment-current"
1143
1077
  })
1144
1078
  };
1145
- var setFocus = (view, thread) => {
1146
- const range = view.state.field(commentsStateField).ranges.find((range2) => range2.id === thread);
1147
- if (!range) {
1079
+ var setFocus = (view, id, center = true) => {
1080
+ const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1081
+ if (!comment?.comment.cursor) {
1148
1082
  return;
1149
1083
  }
1150
- view.dispatch({
1151
- effects: setSelection.of({
1152
- active: thread
1153
- }),
1154
- selection: {
1155
- anchor: range.from
1156
- },
1157
- scrollIntoView: true
1158
- });
1084
+ const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
1085
+ if (range) {
1086
+ view.dispatch({
1087
+ selection: {
1088
+ anchor: range.from
1089
+ },
1090
+ effects: [
1091
+ //
1092
+ EditorView5.scrollIntoView(range.from, center ? {
1093
+ y: "center"
1094
+ } : void 0),
1095
+ setSelection.of({
1096
+ current: id
1097
+ })
1098
+ ]
1099
+ });
1100
+ }
1159
1101
  };
1160
- var setCommentRange = StateEffect2.define();
1102
+ var setComments = StateEffect2.define();
1161
1103
  var setSelection = StateEffect2.define();
1162
1104
  var setCommentState = StateEffect2.define();
1163
- var commentsStateField = StateField2.define({
1105
+ var commentsState = StateField2.define({
1164
1106
  create: () => ({
1165
- ranges: []
1107
+ comments: [],
1108
+ selection: {}
1166
1109
  }),
1167
1110
  update: (value, tr) => {
1168
- const cursorConverter3 = tr.state.facet(Cursor.converter);
1169
1111
  for (const effect of tr.effects) {
1170
1112
  if (effect.is(setSelection)) {
1171
1113
  return {
1172
1114
  ...value,
1173
- ...effect.value
1115
+ selection: effect.value
1174
1116
  };
1175
1117
  }
1176
- if (effect.is(setCommentRange)) {
1177
- const { comments: comments2 } = effect.value;
1178
- const ranges = comments2.map((comment) => {
1179
- const range = Cursor.getRangeFromCursor(cursorConverter3, comment.cursor);
1118
+ if (effect.is(setComments)) {
1119
+ const comments2 = effect.value.map((comment) => {
1120
+ if (!comment.cursor) {
1121
+ return void 0;
1122
+ }
1123
+ const range = Cursor.getRangeFromCursor(tr.state, comment.cursor);
1180
1124
  return range && {
1181
- ...comment,
1182
- ...range
1125
+ comment,
1126
+ range
1183
1127
  };
1184
1128
  }).filter(nonNullable);
1185
1129
  return {
1186
1130
  ...value,
1187
- ranges
1131
+ comments: comments2
1188
1132
  };
1189
1133
  }
1190
1134
  if (effect.is(setCommentState)) {
@@ -1195,71 +1139,69 @@ var commentsStateField = StateField2.define({
1195
1139
  }
1196
1140
  });
1197
1141
  var highlightDecorations = EditorView5.decorations.compute([
1198
- commentsStateField
1142
+ commentsState
1199
1143
  ], (state) => {
1200
- const { active, ranges } = state.field(commentsStateField);
1201
- const decorations = sortBy(ranges ?? [], (range) => range.from)?.flatMap((selection) => {
1202
- if (selection.from === selection.to) {
1144
+ const { selection: { current }, comments: comments2 } = state.field(commentsState);
1145
+ const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1146
+ const range = comment.range;
1147
+ if (!range || range.from === range.to) {
1148
+ console.warn("Invalid range:", range);
1203
1149
  return void 0;
1204
1150
  }
1205
- const range = {
1206
- from: selection.from,
1207
- to: selection.to
1208
- };
1209
- if (selection.id === active) {
1151
+ if (comment.comment.id === current) {
1210
1152
  return marks.highlightActive.range(range.from, range.to);
1211
1153
  } else {
1212
1154
  return marks.highlight.range(range.from, range.to);
1213
1155
  }
1214
- }).filter(nonNullable) ?? [];
1215
- return Decoration3.set(decorations);
1156
+ }).filter(nonNullable);
1157
+ return Decoration2.set(decorations);
1216
1158
  });
1217
- var trackPastedComments = (onMove) => {
1159
+ var trackPastedComments = (onUpdate) => {
1218
1160
  let tracked = null;
1219
- const registerCopy = (event, view) => {
1220
- const comments2 = view.state.field(commentsStateField);
1161
+ const handleTrack = (event, view) => {
1162
+ const comments2 = view.state.field(commentsState);
1221
1163
  const { main } = view.state.selection;
1222
- const inSel = comments2.ranges.filter((range) => range.from >= main.from && range.to <= main.to && range.from < range.to);
1223
- if (!inSel.length) {
1164
+ const selectedRanges = comments2.comments.filter(({ range }) => range.from >= main.from && range.to <= main.to && range.from < range.to);
1165
+ if (!selectedRanges.length) {
1224
1166
  tracked = null;
1225
1167
  } else {
1226
1168
  tracked = {
1227
1169
  text: view.state.doc.slice(main.from, main.to),
1228
- comments: inSel.map((range) => ({
1170
+ comments: selectedRanges.map(({ comment, range }) => ({
1171
+ id: comment.id,
1229
1172
  from: range.from - main.from,
1230
- to: range.to - main.from,
1231
- id: range.id
1173
+ to: range.to - main.from
1232
1174
  }))
1233
1175
  };
1234
1176
  }
1235
1177
  };
1236
1178
  return [
1237
1179
  EditorView5.domEventHandlers({
1238
- cut: registerCopy,
1239
- copy: registerCopy
1180
+ cut: handleTrack,
1181
+ copy: handleTrack
1240
1182
  }),
1241
- EditorView5.updateListener.of((update2) => {
1183
+ // Handle paste.
1184
+ EditorView5.updateListener.of(({ view, state, transactions }) => {
1242
1185
  if (tracked) {
1243
- const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1186
+ const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1244
1187
  if (paste) {
1245
1188
  let found = -1;
1246
1189
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1247
1190
  if (text.eq(tracked.text)) {
1248
- for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1249
- fromB = update2.transactions[i].changes.mapPos(fromB);
1191
+ for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1192
+ fromB = transactions[i].changes.mapPos(fromB);
1250
1193
  }
1251
1194
  found = fromB;
1252
1195
  }
1253
1196
  });
1254
1197
  if (found > -1) {
1255
- const active = update2.view.state.field(commentsStateField).ranges;
1256
1198
  for (const moved of tracked.comments) {
1257
- if (active.some((range) => range.id === moved.id && range.from === range.to)) {
1258
- onMove(moved.id, Cursor.getCursorFromRange(update2.view.state.facet(Cursor.converter), {
1259
- from: found + moved.from,
1260
- to: found + moved.to
1261
- }));
1262
- }
1199
+ const range = {
1200
+ from: found + moved.from,
1201
+ to: found + moved.to
1202
+ };
1203
+ const cursor = Cursor.getCursorFromRange(state, range);
1204
+ onUpdate(moved.id, cursor);
1263
1205
  }
1264
1206
  }
1265
1207
  tracked = null;
@@ -1272,56 +1214,51 @@ var comments = (options = {}) => {
1272
1214
  const { key: shortcut = "meta-'" } = options;
1273
1215
  const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1274
1216
  const createCommentThread = (view) => {
1275
- const cursorConverter3 = view.state.facet(Cursor.converter);
1276
- invariant2(options.onCreate, void 0, {
1277
- F: __dxlog_file3,
1278
- L: 240,
1217
+ invariant3(options.onCreate, void 0, {
1218
+ F: __dxlog_file4,
1219
+ L: 259,
1279
1220
  S: void 0,
1280
1221
  A: [
1281
1222
  "options.onCreate",
1282
1223
  ""
1283
1224
  ]
1284
1225
  });
1285
- const { head, from, to } = view.state.selection.main;
1226
+ const { from, to } = view.state.selection.main;
1286
1227
  if (from === to) {
1287
1228
  return false;
1288
1229
  }
1289
- const cursor = Cursor.getCursorFromRange(cursorConverter3, {
1230
+ if (to === view.state.doc.length) {
1231
+ view.dispatch({
1232
+ changes: {
1233
+ from: to,
1234
+ insert: "\n"
1235
+ }
1236
+ });
1237
+ }
1238
+ const cursor = Cursor.getCursorFromRange(view.state, {
1290
1239
  from,
1291
1240
  to
1292
1241
  });
1293
1242
  if (cursor) {
1294
- const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
1243
+ const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1295
1244
  if (id) {
1296
1245
  view.dispatch({
1297
1246
  effects: setSelection.of({
1298
- active: id
1247
+ current: id
1299
1248
  }),
1300
1249
  selection: {
1301
1250
  anchor: from
1302
1251
  }
1303
1252
  });
1304
- if (options.footnote) {
1305
- const tag = `[^${id}]`;
1306
- view.dispatch({
1307
- changes: {
1308
- from: head,
1309
- insert: tag
1310
- },
1311
- selection: {
1312
- anchor: head + tag.length
1313
- }
1314
- });
1315
- }
1316
1253
  return true;
1317
1254
  }
1318
1255
  }
1319
1256
  return false;
1320
1257
  };
1321
1258
  return [
1322
- commentsStateField,
1259
+ commentsState,
1323
1260
  highlightDecorations,
1324
- styles3,
1261
+ styles2,
1325
1262
  //
1326
1263
  // Keymap.
1327
1264
  //
@@ -1344,7 +1281,7 @@ var comments = (options = {}) => {
1344
1281
  above: true,
1345
1282
  create: () => {
1346
1283
  const el = document.createElement("div");
1347
- options.onHover?.(el, shortcut);
1284
+ options.onHover(el, shortcut);
1348
1285
  return {
1349
1286
  dom: el,
1350
1287
  offset: {
@@ -1357,400 +1294,150 @@ var comments = (options = {}) => {
1357
1294
  }
1358
1295
  return null;
1359
1296
  }, {
1360
- hideOnChange: true,
1297
+ // TODO(burdon): Hide on change triggered immediately?
1298
+ // hideOnChange: true,
1361
1299
  hoverTime: 1e3
1362
1300
  }) : [],
1363
1301
  //
1364
- // Monitor cursor movement and text updates.
1365
- // TODO(burdon): Is there a better (finer grained) way to do this?
1302
+ // Track deleted ranges and update ranges for decorations.
1366
1303
  //
1367
1304
  EditorView5.updateListener.of(({ view, state, changes }) => {
1368
- const cursorConverter3 = view.state.facet(Cursor.converter);
1369
- {
1370
- let mod = false;
1371
- const { active, ranges } = state.field(commentsStateField);
1372
- changes.iterChanges((from, to, from2, to2) => {
1373
- ranges.forEach((range) => {
1374
- if (from2 === to2) {
1375
- const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
1376
- if (!newRange || newRange.to - newRange.from === 0) {
1377
- log2.info("deleted comment", {
1378
- thread: range.id
1379
- }, {
1380
- F: __dxlog_file3,
1381
- L: 337,
1382
- S: void 0,
1383
- C: (f, a) => f(...a)
1384
- });
1385
- }
1305
+ let mod = false;
1306
+ const { comments: comments2, ...value } = state.field(commentsState);
1307
+ changes.iterChanges((from, to, from2, to2) => {
1308
+ comments2.forEach(({ comment, range }) => {
1309
+ if (from2 === to2) {
1310
+ const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
1311
+ if (!newRange || newRange.to - newRange.from === 0) {
1312
+ options.onDelete?.(comment.id);
1386
1313
  }
1387
- if (from <= range.to) {
1388
- const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
1389
- Object.assign(range, newRange);
1390
- mod = true;
1391
- }
1392
- });
1314
+ }
1315
+ if (from <= range.to) {
1316
+ const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
1317
+ Object.assign(range, newRange);
1318
+ mod = true;
1319
+ }
1320
+ });
1321
+ });
1322
+ if (mod) {
1323
+ view.dispatch({
1324
+ effects: setCommentState.of({
1325
+ comments: comments2,
1326
+ ...value
1327
+ })
1393
1328
  });
1394
- if (mod) {
1395
- view.dispatch({
1396
- effects: setCommentState.of({
1397
- active,
1398
- ranges
1399
- })
1400
- });
1401
- }
1402
1329
  }
1403
- {
1404
- const { head } = state.selection.main;
1405
- let min = Infinity;
1406
- const { active, closest, ranges } = state.field(commentsStateField);
1407
- const selected = {
1408
- active: void 0,
1409
- closest: void 0
1410
- };
1411
- ranges.forEach((comment) => {
1412
- const d = Math.min(Math.abs(head - comment.from), Math.abs(head - comment.to));
1413
- if (head >= comment.from && head <= comment.to) {
1414
- selected.active = comment.id;
1415
- }
1330
+ }),
1331
+ //
1332
+ // Track selection/proximity.
1333
+ //
1334
+ EditorView5.updateListener.of(({ view, state }) => {
1335
+ let min = Infinity;
1336
+ const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1337
+ const { head } = state.selection.main;
1338
+ const selection = {};
1339
+ comments2.forEach(({ comment, range }) => {
1340
+ if (head >= range.from && head <= range.to) {
1341
+ selection.current = comment.id;
1342
+ selection.closest = void 0;
1343
+ }
1344
+ if (!selection.current) {
1345
+ const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
1416
1346
  if (d < min) {
1417
- selected.closest = comment.id;
1347
+ selection.closest = comment.id;
1418
1348
  min = d;
1419
1349
  }
1420
- });
1421
- if (selected.active !== active || selected.closest !== closest) {
1422
- view.dispatch({
1423
- effects: setSelection.of(selected)
1424
- });
1425
- handleSelect({
1426
- ...selected,
1427
- ranges: ranges.map((range) => ({
1428
- ...range,
1429
- location: view.coordsAtPos(range.from)
1430
- }))
1431
- });
1432
1350
  }
1351
+ });
1352
+ if (selection.current !== current || selection.closest !== closest) {
1353
+ view.dispatch({
1354
+ effects: setSelection.of(selection)
1355
+ });
1356
+ handleSelect({
1357
+ selection,
1358
+ comments: comments2.map(({ comment, range }) => ({
1359
+ comment,
1360
+ range,
1361
+ location: view.coordsAtPos(range.from)
1362
+ }))
1363
+ });
1433
1364
  }
1434
1365
  }),
1435
- options.onMove ? trackPastedComments(options.onMove) : []
1366
+ options.onUpdate ? trackPastedComments(options.onUpdate) : []
1436
1367
  ];
1437
1368
  };
1369
+ var useComments = (view, comments2 = []) => {
1370
+ useEffect(() => {
1371
+ if (view) {
1372
+ view.dispatch({
1373
+ effects: setComments.of(comments2)
1374
+ });
1375
+ }
1376
+ }, [
1377
+ view,
1378
+ comments2
1379
+ ]);
1380
+ };
1438
1381
 
1439
- // packages/ui/react-ui-editor/src/extensions/hr.ts
1440
- import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
1441
- var styles4 = EditorView6.baseTheme({
1442
- "& .cm-hr": {
1443
- // TODO(burdon): ???
1444
- // Note that block-level decorations should not have vertical margins,
1445
- borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
1382
+ // packages/ui/react-ui-editor/src/extensions/listener.ts
1383
+ import { EditorView as EditorView6 } from "@codemirror/view";
1384
+ var listener = ({ onFocus, onChange }) => {
1385
+ const extensions = [];
1386
+ onFocus && extensions.push(EditorView6.focusChangeEffect.of((_, focused) => {
1387
+ onFocus(focused);
1388
+ return null;
1389
+ }));
1390
+ onChange && extensions.push(EditorView6.updateListener.of((update2) => {
1391
+ onChange(update2.state.doc.toString());
1392
+ }));
1393
+ return extensions;
1394
+ };
1395
+
1396
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1397
+ import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1398
+ import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1399
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1400
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1401
+ import { languages } from "@codemirror/language-data";
1402
+ import { searchKeymap } from "@codemirror/search";
1403
+ import { EditorState } from "@codemirror/state";
1404
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1405
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1406
+
1407
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1408
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1409
+ import { HighlightStyle } from "@codemirror/language";
1410
+ import { tags, styleTags, Tag } from "@lezer/highlight";
1411
+ import { Table } from "@lezer/markdown";
1412
+ var markdownTags = {
1413
+ Blockquote: Tag.define(),
1414
+ CodeMark: Tag.define(),
1415
+ CodeText: Tag.define(),
1416
+ EmphasisMark: Tag.define(),
1417
+ HeaderMark: Tag.define(),
1418
+ InlineCode: Tag.define(),
1419
+ LinkLabel: Tag.define(),
1420
+ LinkReference: Tag.define(),
1421
+ ListMark: Tag.define(),
1422
+ QuoteMark: Tag.define(),
1423
+ URL: Tag.define(),
1424
+ // Custom.
1425
+ TableCell: Tag.define()
1426
+ };
1427
+ Table.defineNodes?.forEach((node) => {
1428
+ switch (node?.name) {
1429
+ case "TableCell": {
1430
+ node.style = markdownTags.TableCell;
1431
+ break;
1432
+ }
1446
1433
  }
1447
1434
  });
1448
- var HorizontalRuleWidget = class extends WidgetType2 {
1449
- constructor(_pos) {
1450
- super();
1451
- this._pos = _pos;
1452
- }
1453
- eq(other) {
1454
- return this._pos === other._pos;
1455
- }
1456
- toDOM(view) {
1457
- const el = document.createElement("div");
1458
- el.className = "cm-hr";
1459
- return el;
1460
- }
1461
- };
1462
- var placeholderMatcher = new MatchDecorator({
1463
- regexp: /^---$/g,
1464
- decoration: (match, view, pos) => Decoration4.replace({
1465
- widget: new HorizontalRuleWidget(pos)
1466
- })
1467
- });
1468
- var hr = () => [
1469
- styles4,
1470
- ViewPlugin4.fromClass(class {
1471
- constructor(view) {
1472
- this.rules = placeholderMatcher.createDeco(view);
1473
- }
1474
- update(update2) {
1475
- this.rules = placeholderMatcher.updateDeco(update2, this.rules);
1476
- }
1477
- }, {
1478
- decorations: (instance) => instance.rules,
1479
- // TODO(burdon): Is this really atomic (cursor can move into ---).
1480
- provide: (plugin) => EditorView6.atomicRanges.of((view) => {
1481
- return view.plugin(plugin)?.rules || Decoration4.none;
1482
- })
1483
- })
1484
- ];
1485
-
1486
- // packages/ui/react-ui-editor/src/extensions/image.ts
1487
- import { syntaxTree } from "@codemirror/language";
1488
- import { StateField as StateField3 } from "@codemirror/state";
1489
- import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
1490
- var image = (options = {}) => {
1491
- return StateField3.define({
1492
- create: (state) => {
1493
- return Decoration5.set(buildDecorations(0, state.doc.length, state));
1494
- },
1495
- update: (value, tr) => {
1496
- if (!tr.docChanged && !tr.selection) {
1497
- return value;
1498
- }
1499
- const cursor = tr.state.selection.main.head;
1500
- const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
1501
- let from = Math.min(cursor, oldCursor);
1502
- let to = Math.max(cursor, oldCursor);
1503
- tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
1504
- from = Math.min(from, fromB);
1505
- to = Math.max(to, toB);
1506
- });
1507
- from = tr.state.doc.lineAt(from).from;
1508
- to = tr.state.doc.lineAt(to).to;
1509
- return value.map(tr.changes).update({
1510
- filterFrom: from,
1511
- filterTo: to,
1512
- filter: () => false,
1513
- add: buildDecorations(from, to, tr.state)
1514
- });
1515
- },
1516
- provide: (field) => EditorView7.decorations.from(field)
1517
- });
1518
- };
1519
- var buildDecorations = (from, to, state) => {
1520
- const decorations = [];
1521
- const cursor = state.selection.main.head;
1522
- syntaxTree(state).iterate({
1523
- enter: (node) => {
1524
- if (node.name === "Image") {
1525
- const urlNode = node.node.getChild("URL");
1526
- if (urlNode) {
1527
- const hide = state.readOnly || cursor < node.from || cursor > node.to;
1528
- const url = state.sliceDoc(urlNode.from, urlNode.to);
1529
- decorations.push(Decoration5.replace({
1530
- block: true,
1531
- widget: new ImageWidget(url)
1532
- }).range(hide ? node.from : node.to, node.to));
1533
- }
1534
- }
1535
- },
1536
- from,
1537
- to
1538
- });
1539
- return decorations;
1540
- };
1541
- var ImageWidget = class extends WidgetType3 {
1542
- constructor(_url) {
1543
- super();
1544
- this._url = _url;
1545
- }
1546
- eq(other) {
1547
- return this._url === other._url;
1548
- }
1549
- toDOM(view) {
1550
- const img = document.createElement("img");
1551
- img.setAttribute("src", this._url);
1552
- img.setAttribute("class", "cm-image");
1553
- img.onload = () => img.classList.add("cm-loaded-image");
1554
- return img;
1555
- }
1556
- };
1557
-
1558
- // packages/ui/react-ui-editor/src/extensions/link.ts
1559
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1560
- import { RangeSetBuilder } from "@codemirror/state";
1561
- import { Decoration as Decoration6, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1562
- import { tooltipContent } from "@dxos/react-ui-theme";
1563
- var link = (options = {}) => {
1564
- const extensions = [
1565
- ViewPlugin5.fromClass(class {
1566
- constructor(view) {
1567
- this.decorations = buildDecorations2(view, options);
1568
- }
1569
- update(update2) {
1570
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
1571
- this.decorations = buildDecorations2(update2.view, options);
1572
- }
1573
- }
1574
- }, {
1575
- decorations: (v) => v.decorations
1576
- })
1577
- ];
1578
- if (options.onHover) {
1579
- extensions.push(
1580
- // https://codemirror.net/examples/tooltip
1581
- // https://codemirror.net/docs/ref/#view.hoverTooltip
1582
- // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1583
- hoverTooltip2((view, pos, side) => {
1584
- const syntax = syntaxTree2(view.state).resolveInner(pos, side);
1585
- let link2 = null;
1586
- for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1587
- link2 = node.name === "Link" ? node : null;
1588
- }
1589
- const url = link2 && link2.getChild("URL");
1590
- if (!url || !link2) {
1591
- return null;
1592
- }
1593
- const urlText = view.state.sliceDoc(url.from, url.to);
1594
- return {
1595
- pos: link2.from,
1596
- end: link2.to,
1597
- above: true,
1598
- create: () => {
1599
- const el = document.createElement("div");
1600
- el.className = tooltipContent({}, "pli-2 plb-1");
1601
- options.onHover(el, urlText);
1602
- return {
1603
- dom: el,
1604
- offset: {
1605
- x: 0,
1606
- y: 4
1607
- }
1608
- };
1609
- }
1610
- };
1611
- })
1612
- );
1613
- }
1614
- return extensions;
1615
- };
1616
- var LinkButton = class extends WidgetType4 {
1617
- constructor(_url, _onAttach) {
1618
- super();
1619
- this._url = _url;
1620
- this._onAttach = _onAttach;
1621
- }
1622
- eq(other) {
1623
- return this._url === other._url;
1624
- }
1625
- toDOM(view) {
1626
- const el = document.createElement("span");
1627
- this._onAttach(el, this._url);
1628
- return el;
1629
- }
1630
- };
1631
- var LinkText = class extends WidgetType4 {
1632
- constructor(_text, _url) {
1633
- super();
1634
- this._text = _text;
1635
- this._url = _url;
1636
- }
1637
- eq(other) {
1638
- return this._url === other._url;
1639
- }
1640
- toDOM(view) {
1641
- const link2 = document.createElement("a");
1642
- link2.setAttribute("class", "cm-link");
1643
- link2.textContent = this._text;
1644
- if (this._url) {
1645
- link2.setAttribute("rel", "noreferrer");
1646
- link2.setAttribute("target", "_blank");
1647
- link2.href = this._url;
1648
- } else {
1649
- link2.onclick = () => {
1650
- view.dispatch({
1651
- selection: {
1652
- anchor: view.posAtDOM(link2)
1653
- }
1654
- });
1655
- };
1656
- }
1657
- return link2;
1658
- }
1659
- };
1660
- var buildDecorations2 = (view, options) => {
1661
- const builder = new RangeSetBuilder();
1662
- const { state } = view;
1663
- const cursor = state.selection.main.head;
1664
- for (const { from, to } of view.visibleRanges) {
1665
- syntaxTree2(state).iterate({
1666
- enter: (node) => {
1667
- if (node.name === "Link") {
1668
- const marks2 = node.node.getChildren("LinkMark");
1669
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
1670
- const urlNode = node.node.getChild("URL");
1671
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
1672
- if (!url) {
1673
- return false;
1674
- }
1675
- if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
1676
- builder.add(node.from, node.to, Decoration6.replace({
1677
- widget: new LinkText(text, options.link ? url : void 0)
1678
- }));
1679
- }
1680
- if (options.onRender) {
1681
- builder.add(node.to, node.to, Decoration6.replace({
1682
- widget: new LinkButton(url, options.onRender)
1683
- }));
1684
- }
1685
- return false;
1686
- }
1687
- },
1688
- from,
1689
- to
1690
- });
1691
- }
1692
- return builder.finish();
1693
- };
1694
-
1695
- // packages/ui/react-ui-editor/src/extensions/listener.ts
1696
- import { EditorView as EditorView8 } from "@codemirror/view";
1697
- var listener = ({ onFocus, onChange }) => {
1698
- const extensions = [];
1699
- onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
1700
- onFocus(focused);
1701
- return null;
1702
- }));
1703
- onChange && extensions.push(EditorView8.updateListener.of((update2) => {
1704
- onChange(update2.state.doc.toString());
1705
- }));
1706
- return extensions;
1707
- };
1708
-
1709
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1710
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1711
- import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1712
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1713
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1714
- import { languages } from "@codemirror/language-data";
1715
- import { searchKeymap } from "@codemirror/search";
1716
- import { EditorState } from "@codemirror/state";
1717
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1718
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1719
-
1720
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1721
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1722
- import { HighlightStyle } from "@codemirror/language";
1723
- import { tags, styleTags, Tag } from "@lezer/highlight";
1724
- import { Table } from "@lezer/markdown";
1725
- var markdownTags = {
1726
- Blockquote: Tag.define(),
1727
- CodeMark: Tag.define(),
1728
- CodeText: Tag.define(),
1729
- EmphasisMark: Tag.define(),
1730
- HeaderMark: Tag.define(),
1731
- InlineCode: Tag.define(),
1732
- LinkLabel: Tag.define(),
1733
- LinkReference: Tag.define(),
1734
- ListMark: Tag.define(),
1735
- QuoteMark: Tag.define(),
1736
- URL: Tag.define(),
1737
- // Custom.
1738
- TableCell: Tag.define()
1739
- };
1740
- Table.defineNodes?.forEach((node) => {
1741
- switch (node?.name) {
1742
- case "TableCell": {
1743
- node.style = markdownTags.TableCell;
1744
- break;
1745
- }
1746
- }
1747
- });
1748
- var markdownTagsExtensions = [
1749
- Table,
1750
- {
1751
- props: [
1752
- styleTags(markdownTags)
1753
- ]
1435
+ var markdownTagsExtensions = [
1436
+ Table,
1437
+ {
1438
+ props: [
1439
+ styleTags(markdownTags)
1440
+ ]
1754
1441
  }
1755
1442
  ];
1756
1443
  var markdownHighlightStyle = (readonly) => {
@@ -1812,7 +1499,7 @@ var markdownHighlightStyle = (readonly) => {
1812
1499
  markdownTags.QuoteMark,
1813
1500
  markdownTags.EmphasisMark
1814
1501
  ],
1815
- class: readonly ? "hidden" : mark
1502
+ class: mark
1816
1503
  },
1817
1504
  // E.g., code block language (after ```).
1818
1505
  {
@@ -1820,7 +1507,7 @@ var markdownHighlightStyle = (readonly) => {
1820
1507
  tags.function(tags.variableName),
1821
1508
  tags.labelName
1822
1509
  ],
1823
- class: readonly ? "hidden" : codeMark
1510
+ class: codeMark
1824
1511
  },
1825
1512
  {
1826
1513
  tag: [
@@ -1831,27 +1518,27 @@ var markdownHighlightStyle = (readonly) => {
1831
1518
  // Headings.
1832
1519
  {
1833
1520
  tag: tags.heading1,
1834
- class: heading(1, readonly)
1521
+ class: heading(1)
1835
1522
  },
1836
1523
  {
1837
1524
  tag: tags.heading2,
1838
- class: heading(2, readonly)
1525
+ class: heading(2)
1839
1526
  },
1840
1527
  {
1841
1528
  tag: tags.heading3,
1842
- class: heading(3, readonly)
1529
+ class: heading(3)
1843
1530
  },
1844
1531
  {
1845
1532
  tag: tags.heading4,
1846
- class: heading(4, readonly)
1533
+ class: heading(4)
1847
1534
  },
1848
1535
  {
1849
1536
  tag: tags.heading5,
1850
- class: heading(5, readonly)
1537
+ class: heading(5)
1851
1538
  },
1852
1539
  {
1853
1540
  tag: tags.heading6,
1854
- class: heading(6, readonly)
1541
+ class: heading(6)
1855
1542
  },
1856
1543
  // Emphasis.
1857
1544
  {
@@ -1877,7 +1564,7 @@ var markdownHighlightStyle = (readonly) => {
1877
1564
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
1878
1565
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
1879
1566
  // the code, but all other CSS properties will be inherited.
1880
- // IMPORTANT: Therefore, the fenced code block will use the base editor font.
1567
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
1881
1568
  {
1882
1569
  tag: [
1883
1570
  markdownTags.CodeText,
@@ -1906,21 +1593,21 @@ var markdownHighlightStyle = (readonly) => {
1906
1593
  };
1907
1594
 
1908
1595
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1909
- var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1596
+ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1910
1597
  return [
1911
1598
  EditorState.allowMultipleSelections.of(true),
1912
1599
  EditorState.tabSize.of(2),
1913
- EditorView9.lineWrapping,
1914
- customKeymap(),
1600
+ EditorView7.lineWrapping,
1601
+ // https://github.com/codemirror/basic-setup
1915
1602
  bracketMatching2(),
1916
1603
  closeBrackets2(),
1917
1604
  crosshairCursor(),
1918
1605
  dropCursor(),
1919
1606
  drawSelection2(),
1920
- highlightActiveLine(),
1921
- history(),
1607
+ highlightActiveLine2(),
1608
+ history2(),
1922
1609
  indentOnInput(),
1923
- _placeholder && placeholder2(_placeholder),
1610
+ _placeholder ? placeholder2(_placeholder) : [],
1924
1611
  // Main extension.
1925
1612
  // https://github.com/codemirror/lang-markdown
1926
1613
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1942,58 +1629,445 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1942
1629
  // https://github.com/codemirror/theme-one-dark
1943
1630
  themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
1944
1631
  // Custom styles.
1945
- syntaxHighlighting2(markdownHighlightStyle(readonly))
1946
- ].filter(Boolean);
1947
- };
1948
- var customKeymap = () => keymap4.of([
1949
- // https://codemirror.net/docs/ref/#commands.indentWithTab
1950
- indentWithTab,
1951
- // https://codemirror.net/docs/ref/#commands.standardKeymap
1952
- ...standardKeymap,
1953
- // https://codemirror.net/docs/ref/#commands.historyKeymap
1954
- ...historyKeymap,
1955
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1956
- ...closeBracketsKeymap,
1957
- // https://codemirror.net/docs/ref/#search.searchKeymap
1958
- ...searchKeymap
1959
- ]);
1632
+ syntaxHighlighting2(markdownHighlightStyle()),
1633
+ keymap4.of([
1634
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
1635
+ indentWithTab,
1636
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1637
+ ...closeBracketsKeymap,
1638
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
1639
+ ...historyKeymap,
1640
+ // https://codemirror.net/docs/ref/#search.searchKeymap
1641
+ ...searchKeymap,
1642
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
1643
+ ...standardKeymap
1644
+ ])
1645
+ ];
1646
+ };
1960
1647
 
1961
- // packages/ui/react-ui-editor/src/extensions/mention.ts
1962
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
1963
- var mention = ({ onSearch }) => {
1964
- return autocompletion2({
1965
- closeOnBlur: false,
1966
- override: [
1967
- (context) => {
1968
- const match = context.matchBefore(/@(\w+)?/);
1969
- if (!match || match.from === match.to && !context.explicit) {
1648
+ // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1649
+ import { syntaxTree } from "@codemirror/language";
1650
+ import { RangeSetBuilder } from "@codemirror/state";
1651
+ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Decoration3, WidgetType as WidgetType2 } from "@codemirror/view";
1652
+ import { mx as mx2 } from "@dxos/react-ui-theme";
1653
+ var styles3 = EditorView8.baseTheme({
1654
+ "& .cm-code": {
1655
+ paddingInline: "1rem !important",
1656
+ fontFamily: getToken("fontFamily.mono", []).join(",")
1657
+ },
1658
+ "& .cm-codeblock": {
1659
+ display: "inline-block",
1660
+ width: "100%"
1661
+ },
1662
+ "&light .cm-codeblock, &light .cm-codeblock.cm-activeLine": {
1663
+ background: getToken("extend.colors.neutral.25")
1664
+ },
1665
+ "&dark .cm-codeblock, &dark .cm-codeblock.cm-activeLine": {
1666
+ background: getToken("extend.colors.neutral.850")
1667
+ },
1668
+ "& .cm-codeblock-first": {
1669
+ borderTopLeftRadius: ".5rem",
1670
+ borderTopRightRadius: ".5rem"
1671
+ },
1672
+ "& .cm-codeblock-last": {
1673
+ borderBottomLeftRadius: ".5rem",
1674
+ borderBottomRightRadius: ".5rem"
1675
+ }
1676
+ });
1677
+ var getLineRange = (lines, from, to) => {
1678
+ const start = lines.findIndex((line) => line.from >= from);
1679
+ const end = lines.findIndex((line) => line.to >= to);
1680
+ return [
1681
+ start,
1682
+ end
1683
+ ];
1684
+ };
1685
+ var LineWidget = class extends WidgetType2 {
1686
+ constructor(_className) {
1687
+ super();
1688
+ this._className = _className;
1689
+ }
1690
+ toDOM() {
1691
+ const el = document.createElement("span");
1692
+ el.innerHTML = "&nbsp;";
1693
+ el.className = mx2("cm-code cm-codeblock", this._className);
1694
+ return el;
1695
+ }
1696
+ };
1697
+ var top = new LineWidget("cm-codeblock-first");
1698
+ var bottom = new LineWidget("cm-codeblock-last");
1699
+ var buildDecorations = (view) => {
1700
+ const builder = new RangeSetBuilder();
1701
+ const { state } = view;
1702
+ const blocks = view.viewportLineBlocks;
1703
+ const cursor = state.selection.main.head;
1704
+ for (const { from, to } of view.visibleRanges) {
1705
+ syntaxTree(state).iterate({
1706
+ enter: (node) => {
1707
+ if (node.name === "FencedCode") {
1708
+ const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
1709
+ const range = getLineRange(blocks, node.from, node.to);
1710
+ for (let i = range[0]; i <= range[1]; i++) {
1711
+ const block = blocks[i];
1712
+ if (!edit && (i === range[0] || i === range[1])) {
1713
+ builder.add(block.from, block.to, Decoration3.replace({
1714
+ widget: i === range[0] ? top : bottom
1715
+ }));
1716
+ } else {
1717
+ builder.add(block.from, block.from, Decoration3.line({
1718
+ class: mx2("cm-code cm-codeblock")
1719
+ }));
1720
+ }
1721
+ }
1722
+ }
1723
+ },
1724
+ from,
1725
+ to
1726
+ });
1727
+ }
1728
+ return builder.finish();
1729
+ };
1730
+ var code2 = () => {
1731
+ return [
1732
+ ViewPlugin3.fromClass(class {
1733
+ constructor(view) {
1734
+ this.decorations = buildDecorations(view);
1735
+ }
1736
+ update(update2) {
1737
+ this.decorations = buildDecorations(update2.view);
1738
+ }
1739
+ }, {
1740
+ decorations: (value) => value.decorations
1741
+ }),
1742
+ styles3
1743
+ ];
1744
+ };
1745
+
1746
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1747
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
+ import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
+ import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1750
+ var buildDecorations2 = (view) => {
1751
+ const builder = new RangeSetBuilder2();
1752
+ const { state } = view;
1753
+ const cursor = state.selection.main.head;
1754
+ for (const { from, to } of view.visibleRanges) {
1755
+ syntaxTree2(state).iterate({
1756
+ enter: (node) => {
1757
+ switch (node.name) {
1758
+ case "ATXHeading1":
1759
+ case "ATXHeading2":
1760
+ case "ATXHeading3":
1761
+ case "ATXHeading4":
1762
+ case "ATXHeading5":
1763
+ case "ATXHeading6": {
1764
+ const mark2 = node.node.getChild("HeaderMark");
1765
+ if (mark2) {
1766
+ if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
+ builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1768
+ }
1769
+ }
1770
+ }
1771
+ }
1772
+ },
1773
+ from,
1774
+ to
1775
+ });
1776
+ }
1777
+ return builder.finish();
1778
+ };
1779
+ var heading2 = () => {
1780
+ return [
1781
+ ViewPlugin4.fromClass(class {
1782
+ constructor(view) {
1783
+ this.decorations = buildDecorations2(view);
1784
+ }
1785
+ update(update2) {
1786
+ this.decorations = buildDecorations2(update2.view);
1787
+ }
1788
+ }, {
1789
+ decorations: (value) => value.decorations
1790
+ })
1791
+ ];
1792
+ };
1793
+
1794
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
1795
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1796
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1797
+ import { Decoration as Decoration5, EditorView as EditorView9, ViewPlugin as ViewPlugin5, WidgetType as WidgetType3 } from "@codemirror/view";
1798
+ var styles4 = EditorView9.baseTheme({
1799
+ "& .cm-hr": {
1800
+ // Note that block-level decorations should not have vertical margins,
1801
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
1802
+ }
1803
+ });
1804
+ var HorizontalRuleWidget = class extends WidgetType3 {
1805
+ toDOM() {
1806
+ const el = document.createElement("div");
1807
+ el.className = "cm-hr";
1808
+ return el;
1809
+ }
1810
+ };
1811
+ var decoration = Decoration5.replace({
1812
+ widget: new HorizontalRuleWidget()
1813
+ });
1814
+ var buildDecorations3 = (view) => {
1815
+ const builder = new RangeSetBuilder3();
1816
+ const { state } = view;
1817
+ const cursor = state.selection.main.head;
1818
+ for (const { from, to } of view.visibleRanges) {
1819
+ syntaxTree3(state).iterate({
1820
+ enter: (node) => {
1821
+ if (node.name === "HorizontalRule") {
1822
+ if (cursor <= node.from || cursor >= node.to) {
1823
+ builder.add(node.from, node.to, decoration);
1824
+ }
1825
+ }
1826
+ },
1827
+ from,
1828
+ to
1829
+ });
1830
+ }
1831
+ return builder.finish();
1832
+ };
1833
+ var hr = () => {
1834
+ return [
1835
+ ViewPlugin5.fromClass(class {
1836
+ constructor(view) {
1837
+ this.decorations = buildDecorations3(view);
1838
+ }
1839
+ update(update2) {
1840
+ this.decorations = buildDecorations3(update2.view);
1841
+ }
1842
+ }, {
1843
+ decorations: (value) => value.decorations
1844
+ }),
1845
+ styles4
1846
+ ];
1847
+ };
1848
+
1849
+ // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
1851
+ import { StateField as StateField3 } from "@codemirror/state";
1852
+ import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
1853
+ var image = (options = {}) => {
1854
+ return StateField3.define({
1855
+ create: (state) => {
1856
+ return Decoration6.set(buildDecorations4(0, state.doc.length, state));
1857
+ },
1858
+ update: (value, tr) => {
1859
+ if (!tr.docChanged && !tr.selection) {
1860
+ return value;
1861
+ }
1862
+ const cursor = tr.state.selection.main.head;
1863
+ const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
1864
+ let from = Math.min(cursor, oldCursor);
1865
+ let to = Math.max(cursor, oldCursor);
1866
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
1867
+ from = Math.min(from, fromB);
1868
+ to = Math.max(to, toB);
1869
+ });
1870
+ from = tr.state.doc.lineAt(from).from;
1871
+ to = tr.state.doc.lineAt(to).to;
1872
+ return value.map(tr.changes).update({
1873
+ filterFrom: from,
1874
+ filterTo: to,
1875
+ filter: () => false,
1876
+ add: buildDecorations4(from, to, tr.state)
1877
+ });
1878
+ },
1879
+ provide: (field) => EditorView10.decorations.from(field)
1880
+ });
1881
+ };
1882
+ var buildDecorations4 = (from, to, state) => {
1883
+ const decorations = [];
1884
+ const cursor = state.selection.main.head;
1885
+ syntaxTree4(state).iterate({
1886
+ enter: (node) => {
1887
+ if (node.name === "Image") {
1888
+ const urlNode = node.node.getChild("URL");
1889
+ if (urlNode) {
1890
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
+ decorations.push(Decoration6.replace({
1893
+ block: true,
1894
+ widget: new ImageWidget(url)
1895
+ }).range(hide ? node.from : node.to, node.to));
1896
+ }
1897
+ }
1898
+ },
1899
+ from,
1900
+ to
1901
+ });
1902
+ return decorations;
1903
+ };
1904
+ var ImageWidget = class extends WidgetType4 {
1905
+ constructor(_url) {
1906
+ super();
1907
+ this._url = _url;
1908
+ }
1909
+ eq(other) {
1910
+ return this._url === other._url;
1911
+ }
1912
+ toDOM(view) {
1913
+ const img = document.createElement("img");
1914
+ img.setAttribute("src", this._url);
1915
+ img.setAttribute("class", "cm-image");
1916
+ img.onload = () => img.classList.add("cm-loaded-image");
1917
+ return img;
1918
+ }
1919
+ };
1920
+
1921
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
1922
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1923
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
1924
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration7, ViewPlugin as ViewPlugin6, WidgetType as WidgetType5 } from "@codemirror/view";
1925
+ import { tooltipContent } from "@dxos/react-ui-theme";
1926
+ var link = (options = {}) => {
1927
+ const extensions = [
1928
+ ViewPlugin6.fromClass(class {
1929
+ constructor(view) {
1930
+ this.decorations = buildDecorations5(view, options);
1931
+ }
1932
+ update(update2) {
1933
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
1934
+ this.decorations = buildDecorations5(update2.view, options);
1935
+ }
1936
+ }
1937
+ }, {
1938
+ decorations: (value) => value.decorations
1939
+ })
1940
+ ];
1941
+ if (options.onHover) {
1942
+ extensions.push(
1943
+ // https://codemirror.net/examples/tooltip
1944
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
+ hoverTooltip2((view, pos, side) => {
1947
+ const syntax = syntaxTree5(view.state).resolveInner(pos, side);
1948
+ let link2 = null;
1949
+ for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
+ link2 = node.name === "Link" ? node : null;
1951
+ }
1952
+ const url = link2 && link2.getChild("URL");
1953
+ if (!url || !link2) {
1970
1954
  return null;
1971
1955
  }
1972
- console.log(match);
1973
- return {
1974
- from: match.from,
1975
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
1976
- label: `@${value}`
1977
- }))
1978
- };
1979
- }
1980
- ]
1981
- });
1956
+ const urlText = view.state.sliceDoc(url.from, url.to);
1957
+ return {
1958
+ pos: link2.from,
1959
+ end: link2.to,
1960
+ above: true,
1961
+ create: () => {
1962
+ const el = document.createElement("div");
1963
+ el.className = tooltipContent({}, "pli-2 plb-1");
1964
+ options.onHover(el, urlText);
1965
+ return {
1966
+ dom: el,
1967
+ offset: {
1968
+ x: 0,
1969
+ y: 4
1970
+ }
1971
+ };
1972
+ }
1973
+ };
1974
+ })
1975
+ );
1976
+ }
1977
+ return extensions;
1978
+ };
1979
+ var LinkButton = class extends WidgetType5 {
1980
+ constructor(_url, _onAttach) {
1981
+ super();
1982
+ this._url = _url;
1983
+ this._onAttach = _onAttach;
1984
+ }
1985
+ eq(other) {
1986
+ return this._url === other._url;
1987
+ }
1988
+ toDOM(view) {
1989
+ const el = document.createElement("span");
1990
+ this._onAttach(el, this._url);
1991
+ return el;
1992
+ }
1993
+ };
1994
+ var LinkText = class extends WidgetType5 {
1995
+ constructor(_text, _url) {
1996
+ super();
1997
+ this._text = _text;
1998
+ this._url = _url;
1999
+ }
2000
+ eq(other) {
2001
+ return this._url === other._url;
2002
+ }
2003
+ toDOM(view) {
2004
+ const link2 = document.createElement("a");
2005
+ link2.setAttribute("class", "cm-link");
2006
+ link2.textContent = this._text;
2007
+ if (this._url) {
2008
+ link2.setAttribute("rel", "noreferrer");
2009
+ link2.setAttribute("target", "_blank");
2010
+ link2.href = this._url;
2011
+ } else {
2012
+ link2.onclick = () => {
2013
+ view.dispatch({
2014
+ selection: {
2015
+ anchor: view.posAtDOM(link2)
2016
+ }
2017
+ });
2018
+ };
2019
+ }
2020
+ return link2;
2021
+ }
2022
+ };
2023
+ var buildDecorations5 = (view, options) => {
2024
+ const builder = new RangeSetBuilder4();
2025
+ const { state } = view;
2026
+ const cursor = state.selection.main.head;
2027
+ for (const { from, to } of view.visibleRanges) {
2028
+ syntaxTree5(state).iterate({
2029
+ enter: (node) => {
2030
+ if (node.name === "Link") {
2031
+ const marks2 = node.node.getChildren("LinkMark");
2032
+ const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
2033
+ const urlNode = node.node.getChild("URL");
2034
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
+ if (!url) {
2036
+ return false;
2037
+ }
2038
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
+ builder.add(node.from, node.to, Decoration7.replace({
2040
+ widget: new LinkText(text, options.link ? url : void 0)
2041
+ }));
2042
+ }
2043
+ if (options.onRender) {
2044
+ builder.add(node.to, node.to, Decoration7.replace({
2045
+ widget: new LinkButton(url, options.onRender)
2046
+ }));
2047
+ }
2048
+ return false;
2049
+ }
2050
+ },
2051
+ from,
2052
+ to
2053
+ });
2054
+ }
2055
+ return builder.finish();
1982
2056
  };
1983
2057
 
1984
- // packages/ui/react-ui-editor/src/extensions/table.ts
1985
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1986
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
1987
- import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
2058
+ // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2059
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2060
+ import { RangeSetBuilder as RangeSetBuilder5, StateField as StateField4 } from "@codemirror/state";
2061
+ import { Decoration as Decoration8, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
1988
2062
  var table = (options = {}) => {
1989
2063
  return StateField4.define({
1990
2064
  create: (state) => update(state, options),
1991
2065
  update: (_, tr) => update(tr.state, options),
1992
- provide: (field) => EditorView10.decorations.from(field)
2066
+ provide: (field) => EditorView11.decorations.from(field)
1993
2067
  });
1994
2068
  };
1995
2069
  var update = (state, options) => {
1996
- const builder = new RangeSetBuilder2();
2070
+ const builder = new RangeSetBuilder5();
1997
2071
  const cursor = state.selection.main.head;
1998
2072
  const tables = [];
1999
2073
  const getTable = () => tables[tables.length - 1];
@@ -2001,7 +2075,7 @@ var update = (state, options) => {
2001
2075
  const table2 = getTable();
2002
2076
  return table2.rows?.[table2.rows.length - 1];
2003
2077
  };
2004
- syntaxTree3(state).iterate({
2078
+ syntaxTree6(state).iterate({
2005
2079
  enter: (node) => {
2006
2080
  switch (node.name) {
2007
2081
  case "Table": {
@@ -2033,16 +2107,16 @@ var update = (state, options) => {
2033
2107
  });
2034
2108
  tables.forEach((table2) => {
2035
2109
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2036
- hide && builder.add(table2.from, table2.to, Decoration7.replace({
2110
+ hide && builder.add(table2.from, table2.to, Decoration8.replace({
2037
2111
  widget: new TableWidget(table2)
2038
2112
  }));
2039
- builder.add(table2.from, table2.to, Decoration7.mark({
2113
+ builder.add(table2.from, table2.to, Decoration8.mark({
2040
2114
  class: "cm-table"
2041
2115
  }));
2042
2116
  });
2043
2117
  return builder.finish();
2044
2118
  };
2045
- var TableWidget = class extends WidgetType5 {
2119
+ var TableWidget = class extends WidgetType6 {
2046
2120
  constructor(_table) {
2047
2121
  super();
2048
2122
  this._table = _table;
@@ -2079,11 +2153,20 @@ var TableWidget = class extends WidgetType5 {
2079
2153
  }
2080
2154
  };
2081
2155
 
2082
- // packages/ui/react-ui-editor/src/extensions/tasklist.ts
2083
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2084
- import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
2085
- import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
2086
- var CheckboxWidget = class extends WidgetType6 {
2156
+ // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2157
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2158
+ import { RangeSetBuilder as RangeSetBuilder6 } from "@codemirror/state";
2159
+ import { EditorView as EditorView12, Decoration as Decoration9, WidgetType as WidgetType7, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
2160
+ var styles5 = EditorView12.baseTheme({
2161
+ "& .cm-task": {
2162
+ color: getToken("extend.colors.blue.500")
2163
+ },
2164
+ "& .cm-task-checkbox": {
2165
+ marginLeft: "4px",
2166
+ marginRight: "4px"
2167
+ }
2168
+ });
2169
+ var CheckboxWidget = class extends WidgetType7 {
2087
2170
  constructor(_checked) {
2088
2171
  super();
2089
2172
  this._checked = _checked;
@@ -2093,6 +2176,7 @@ var CheckboxWidget = class extends WidgetType6 {
2093
2176
  }
2094
2177
  toDOM(view) {
2095
2178
  const input = document.createElement("input");
2179
+ input.className = "cm-task-checkbox";
2096
2180
  input.type = "checkbox";
2097
2181
  input.checked = this._checked;
2098
2182
  if (view.state.readOnly) {
@@ -2119,39 +2203,27 @@ var CheckboxWidget = class extends WidgetType6 {
2119
2203
  return false;
2120
2204
  }
2121
2205
  };
2122
- var checkedDeco = Decoration8.replace({
2206
+ var checkedDecoration = Decoration9.replace({
2123
2207
  widget: new CheckboxWidget(true)
2124
2208
  });
2125
- var uncheckedDeco = Decoration8.replace({
2209
+ var uncheckedDecoration = Decoration9.replace({
2126
2210
  widget: new CheckboxWidget(false)
2127
2211
  });
2128
- var tasklist = (options = {}) => {
2129
- return ViewPlugin6.fromClass(class {
2130
- constructor(view) {
2131
- this.decorations = buildDecorations3(view);
2132
- }
2133
- update(update2) {
2134
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2135
- this.decorations = buildDecorations3(update2.view);
2136
- }
2137
- }
2138
- }, {
2139
- decorations: (v) => v.decorations,
2140
- provide: (plugin) => EditorView11.atomicRanges.of((view) => {
2141
- return view.plugin(plugin)?.decorations || Decoration8.none;
2142
- })
2143
- });
2144
- };
2145
- var buildDecorations3 = (view) => {
2146
- const builder = new RangeSetBuilder3();
2212
+ var buildDecorations6 = (view) => {
2213
+ const builder = new RangeSetBuilder6();
2147
2214
  const { state } = view;
2148
2215
  const cursor = state.selection.main.head;
2149
2216
  for (const { from, to } of view.visibleRanges) {
2150
- syntaxTree4(state).iterate({
2217
+ syntaxTree7(state).iterate({
2151
2218
  enter: (node) => {
2152
- if (node.name === "TaskMarker" && (cursor < node.from || cursor > node.to)) {
2153
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2154
- builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
2219
+ if (node.name === "TaskMarker") {
2220
+ if (cursor < node.from || cursor > node.to) {
2221
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
+ builder.add(node.from - 2, node.from - 1, Decoration9.mark({
2223
+ class: "cm-task"
2224
+ }));
2225
+ builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
2226
+ }
2155
2227
  }
2156
2228
  },
2157
2229
  from,
@@ -2160,6 +2232,57 @@ var buildDecorations3 = (view) => {
2160
2232
  }
2161
2233
  return builder.finish();
2162
2234
  };
2235
+ var tasklist = (options = {}) => {
2236
+ return [
2237
+ ViewPlugin7.fromClass(class {
2238
+ constructor(view) {
2239
+ this.decorations = buildDecorations6(view);
2240
+ }
2241
+ update(update2) {
2242
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2243
+ this.decorations = buildDecorations6(update2.view);
2244
+ }
2245
+ }
2246
+ }, {
2247
+ decorations: (value) => value.decorations
2248
+ }),
2249
+ styles5
2250
+ ];
2251
+ };
2252
+
2253
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
2254
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
2255
+ var mention = ({ onSearch }) => {
2256
+ return autocompletion2({
2257
+ // TODO(burdon): Not working.
2258
+ activateOnTyping: true,
2259
+ // activateOnTypingDelay: 100,
2260
+ // selectOnOpen: true,
2261
+ closeOnBlur: false,
2262
+ // defaultKeymap: false,
2263
+ icons: false,
2264
+ override: [
2265
+ (context) => {
2266
+ console.log(context);
2267
+ const match = context.matchBefore(/@(\w+)?/);
2268
+ if (!match || match.from === match.to && !context.explicit) {
2269
+ return null;
2270
+ }
2271
+ return {
2272
+ from: match.from,
2273
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
2274
+ label: `@${value}`
2275
+ }))
2276
+ };
2277
+ }
2278
+ ]
2279
+ });
2280
+ };
2281
+
2282
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
2283
+ var outliner = (options = {}) => {
2284
+ return [];
2285
+ };
2163
2286
 
2164
2287
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2165
2288
  import { keymap as keymap5 } from "@codemirror/view";
@@ -2218,27 +2341,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2218
2341
  ];
2219
2342
  };
2220
2343
 
2221
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2222
- import { yCollab } from "y-codemirror.next";
2223
-
2224
- // packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
2225
- import * as Y from "yjs";
2226
- import { arrayToString, stringToArray } from "@dxos/util";
2227
- var cursorConverter2 = (text) => ({
2228
- toCursor: (index, assoc) => {
2229
- return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
2230
- },
2231
- fromCursor: (cursor) => {
2232
- return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
2233
- }
2234
- });
2235
-
2236
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2237
- var yjs = (content, awareness2) => [
2238
- Cursor.converter.of(cursorConverter2(content)),
2239
- yCollab(content, awareness2)
2240
- ];
2241
-
2242
2344
  // packages/ui/react-ui-editor/src/themes/default.ts
2243
2345
  import get3 from "lodash.get";
2244
2346
  var defaultTheme = {
@@ -2476,11 +2578,12 @@ var codeTheme = {
2476
2578
  };
2477
2579
 
2478
2580
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2581
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2479
2582
  var EditorModes = [
2480
2583
  "default",
2481
2584
  "vim"
2482
2585
  ];
2483
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, readonly, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
2586
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2484
2587
  const tabsterDOMAttribute = useFocusableGroup({
2485
2588
  tabBehavior: "limited"
2486
2589
  });
@@ -2490,76 +2593,67 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2490
2593
  useImperativeHandle(forwardedRef, () => view, [
2491
2594
  view
2492
2595
  ]);
2493
- useEffect(() => {
2494
- if (view && focus) {
2495
- view.focus();
2496
- }
2497
- }, [
2498
- view,
2499
- focus
2500
- ]);
2501
- const { awareness: awareness2, peer } = model;
2502
- useEffect(() => {
2503
- if (awareness2 && peer) {
2504
- awareness2.setLocalStateField("user", {
2505
- name: peer.name ?? generateName(peer.id),
2506
- color: getColorForValue({
2507
- value: peer.id,
2508
- type: "color"
2509
- }),
2510
- colorLight: getColorForValue({
2511
- value: peer.id,
2512
- themeMode,
2513
- type: "highlight"
2514
- })
2515
- });
2516
- }
2517
- }, [
2518
- awareness2,
2519
- peer,
2520
- themeMode
2521
- ]);
2522
- useEffect(() => {
2523
- if (view && comments2 !== void 0) {
2524
- view.dispatch({
2525
- effects: setCommentRange.of({
2526
- model,
2527
- comments: comments2
2528
- })
2529
- });
2596
+ useEffect2(() => {
2597
+ if (autoFocus) {
2598
+ view?.focus();
2530
2599
  }
2531
2600
  }, [
2532
2601
  view,
2533
- comments2
2602
+ autoFocus
2534
2603
  ]);
2535
- useEffect(() => {
2604
+ useEffect2(() => {
2536
2605
  if (!model || !rootRef.current) {
2537
2606
  return;
2538
2607
  }
2539
2608
  const state = EditorState2.create({
2540
2609
  doc: model.text(),
2541
- // TODO(burdon): Composer should store and set selection when switching documents.
2542
2610
  selection,
2543
2611
  extensions: [
2544
- readonly && EditorState2.readOnly.of(readonly),
2545
- // TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
2546
- editorMode === "vim" && vim(),
2612
+ // TODO(burdon): Doesn't catch errors in keymap functions.
2613
+ EditorView13.exceptionSink.of((err) => {
2614
+ log2.catch(err, void 0, {
2615
+ F: __dxlog_file5,
2616
+ L: 123,
2617
+ S: void 0,
2618
+ C: (f, a) => f(...a)
2619
+ });
2620
+ }),
2547
2621
  // Theme.
2548
- // TODO(burdon): Make theme configurable.
2549
- EditorView12.baseTheme(defaultTheme),
2550
- EditorView12.theme(theme ?? {}),
2551
- EditorView12.darkTheme.of(themeMode === "dark"),
2622
+ // TODO(burdon): Make configurable.
2623
+ EditorView13.baseTheme(defaultTheme),
2624
+ EditorView13.theme(theme ?? {}),
2625
+ EditorView13.darkTheme.of(themeMode === "dark"),
2626
+ EditorView13.editorAttributes.of({
2627
+ class: slots.editor?.className ?? ""
2628
+ }),
2629
+ EditorView13.contentAttributes.of({
2630
+ class: slots.content?.className ?? ""
2631
+ }),
2632
+ // State.
2633
+ EditorState2.readOnly.of(!!readonly),
2552
2634
  // Storage and replication.
2635
+ // NOTE: This must come before user extensions.
2553
2636
  model.extension,
2637
+ // TODO(burdon): Factor out (requires special handling for Escape/focus).
2638
+ editorMode === "vim" && vim(),
2554
2639
  // Custom.
2555
2640
  ...extensions
2556
2641
  ].filter(Boolean)
2557
2642
  });
2558
- view?.destroy();
2559
- const newView = new EditorView12({
2643
+ const newView = new EditorView13({
2644
+ state,
2560
2645
  parent: rootRef.current,
2561
- state
2646
+ scrollTo,
2647
+ // NOTE: Uncomment to debug/monitor all transactions.
2648
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
2649
+ dispatchTransactions: (trs, view2) => {
2650
+ if (debug) {
2651
+ logChanges(trs);
2652
+ }
2653
+ view2.update(trs);
2654
+ }
2562
2655
  });
2656
+ view?.destroy();
2563
2657
  setView(newView);
2564
2658
  return () => {
2565
2659
  newView?.destroy();
@@ -2575,8 +2669,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2575
2669
  const handleKeyUp = useCallback((event) => {
2576
2670
  const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
2577
2671
  switch (key) {
2672
+ case "Enter": {
2673
+ view?.focus();
2674
+ break;
2675
+ }
2578
2676
  case "Escape": {
2579
- editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && rootRef.current?.focus();
2677
+ if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2678
+ rootRef.current?.focus();
2679
+ }
2580
2680
  break;
2581
2681
  }
2582
2682
  }
@@ -2587,14 +2687,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
2587
2687
  return /* @__PURE__ */ React.createElement("div", {
2588
2688
  key: model.id,
2589
2689
  role: "none",
2590
- ref: rootRef,
2591
2690
  tabIndex: 0,
2592
2691
  onKeyUp: handleKeyUp,
2593
2692
  ...slots.root,
2594
- ...editorMode !== "vim" && tabsterDOMAttribute
2693
+ ...editorMode !== "vim" && tabsterDOMAttribute,
2694
+ ref: rootRef
2595
2695
  });
2596
2696
  });
2597
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
2697
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
2598
2698
  const { themeMode } = useThemeContext();
2599
2699
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
2600
2700
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2602,9 +2702,9 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2602
2702
  readonly,
2603
2703
  extensions: [
2604
2704
  basicBundle({
2605
- readonly,
2606
2705
  themeMode,
2607
- placeholder: placeholder3
2706
+ placeholder: placeholder3,
2707
+ lineWrapping
2608
2708
  }),
2609
2709
  ...extensions
2610
2710
  ],
@@ -2613,7 +2713,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2613
2713
  ...props
2614
2714
  });
2615
2715
  });
2616
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
2716
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
2617
2717
  const { themeMode } = useThemeContext();
2618
2718
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
2619
2719
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2621,7 +2721,6 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2621
2721
  readonly,
2622
2722
  extensions: [
2623
2723
  markdownBundle({
2624
- readonly,
2625
2724
  themeMode,
2626
2725
  placeholder: placeholder3
2627
2726
  }),
@@ -2634,7 +2733,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2634
2733
  });
2635
2734
  var defaultSlots = {
2636
2735
  root: {
2637
- className: mx3("p-2 overflow-y-auto", inputSurface)
2736
+ // TODO(burdon): Add focusRing by default/as property?
2737
+ className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
2738
+ },
2739
+ editor: {
2740
+ className: "h-full p-2"
2638
2741
  }
2639
2742
  };
2640
2743
  var defaultTextSlots = {
@@ -2644,22 +2747,17 @@ var defaultMarkdownSlots = {
2644
2747
  ...defaultSlots
2645
2748
  };
2646
2749
 
2647
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2648
- import get4 from "lodash.get";
2649
- import { getRawDoc } from "@dxos/echo-schema";
2650
- import { isNotNullOrUndefined } from "@dxos/util";
2651
-
2652
2750
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2653
2751
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2654
2752
  import { Context as Context2 } from "@dxos/context";
2655
- import { invariant as invariant3 } from "@dxos/invariant";
2753
+ import { invariant as invariant4 } from "@dxos/invariant";
2656
2754
  import { log as log3 } from "@dxos/log";
2657
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2755
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2658
2756
  var DEBOUNCE_INTERVAL = 100;
2659
2757
  var SpaceAwarenessProvider = class {
2660
2758
  constructor(params) {
2661
- this.remoteStateChange = new Event2();
2662
2759
  this._remoteStates = /* @__PURE__ */ new Map();
2760
+ this.remoteStateChange = new Event2();
2663
2761
  this._space = params.space;
2664
2762
  this._channel = params.channel;
2665
2763
  this._peerId = params.peerId;
@@ -2676,7 +2774,7 @@ var SpaceAwarenessProvider = class {
2676
2774
  await sleep(DEBOUNCE_INTERVAL);
2677
2775
  }
2678
2776
  });
2679
- const unsubscribe = this._space.listen(this._channel, (message) => {
2777
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
2680
2778
  switch (message.payload.kind) {
2681
2779
  case "query": {
2682
2780
  this._handleQueryMessage();
@@ -2687,16 +2785,15 @@ var SpaceAwarenessProvider = class {
2687
2785
  break;
2688
2786
  }
2689
2787
  }
2690
- });
2691
- this._ctx.onDispose(unsubscribe);
2788
+ }));
2692
2789
  void this._space.postMessage(this._channel, {
2693
2790
  kind: "query"
2694
2791
  }).catch((err) => {
2695
2792
  log3.debug("failed to query awareness", {
2696
2793
  err
2697
2794
  }, {
2698
- F: __dxlog_file4,
2699
- L: 85,
2795
+ F: __dxlog_file6,
2796
+ L: 89,
2700
2797
  S: this,
2701
2798
  C: (f, a) => f(...a)
2702
2799
  });
@@ -2707,10 +2804,13 @@ var SpaceAwarenessProvider = class {
2707
2804
  this._ctx = void 0;
2708
2805
  this._postTask = void 0;
2709
2806
  }
2807
+ getRemoteStates() {
2808
+ return Array.from(this._remoteStates.values());
2809
+ }
2710
2810
  update(position) {
2711
- invariant3(this._postTask, void 0, {
2712
- F: __dxlog_file4,
2713
- L: 96,
2811
+ invariant4(this._postTask, void 0, {
2812
+ F: __dxlog_file6,
2813
+ L: 104,
2714
2814
  S: this,
2715
2815
  A: [
2716
2816
  "this._postTask",
@@ -2724,13 +2824,10 @@ var SpaceAwarenessProvider = class {
2724
2824
  };
2725
2825
  this._postTask.schedule();
2726
2826
  }
2727
- getRemoteStates() {
2728
- return Array.from(this._remoteStates.values());
2729
- }
2730
2827
  _handleQueryMessage() {
2731
- invariant3(this._postTask, void 0, {
2732
- F: __dxlog_file4,
2733
- L: 111,
2828
+ invariant4(this._postTask, void 0, {
2829
+ F: __dxlog_file6,
2830
+ L: 115,
2734
2831
  S: this,
2735
2832
  A: [
2736
2833
  "this._postTask",
@@ -2740,9 +2837,9 @@ var SpaceAwarenessProvider = class {
2740
2837
  this._postTask.schedule();
2741
2838
  }
2742
2839
  _handlePostMessage(message) {
2743
- invariant3(message.kind === "post", void 0, {
2744
- F: __dxlog_file4,
2745
- L: 116,
2840
+ invariant4(message.kind === "post", void 0, {
2841
+ F: __dxlog_file6,
2842
+ L: 120,
2746
2843
  S: this,
2747
2844
  A: [
2748
2845
  "message.kind === 'post'",
@@ -2754,171 +2851,54 @@ var SpaceAwarenessProvider = class {
2754
2851
  }
2755
2852
  };
2756
2853
 
2757
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2854
+ // packages/ui/react-ui-editor/src/hooks/defs.ts
2758
2855
  import { StateField as StateField5 } from "@codemirror/state";
2759
- import { useEffect as useEffect2, useState as useState2 } from "react";
2760
- import { isAutomergeObject } from "@dxos/react-client/echo";
2761
-
2762
- // packages/ui/react-ui-editor/src/hooks/yjs.ts
2763
- import * as decoding from "lib0/decoding";
2764
- import * as encoding from "lib0/encoding";
2765
- import { Observable } from "lib0/observable";
2766
- import * as YP from "y-protocols/awareness";
2767
- import { invariant as invariant4 } from "@dxos/invariant";
2768
- import { log as log4 } from "@dxos/log";
2769
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
2770
- var createYjsModel = ({ identity, space, text }) => {
2771
- invariant4(text?.doc && text?.content, void 0, {
2772
- F: __dxlog_file5,
2773
- L: 21,
2774
- S: void 0,
2775
- A: [
2776
- "text?.doc && text?.content",
2777
- ""
2778
- ]
2779
- });
2780
- const provider = space ? new YAwarenessProvider({
2781
- space,
2782
- doc: text.doc,
2783
- channel: `yjs.awareness.${text.id}`
2784
- }) : void 0;
2785
- const model = {
2786
- id: text.doc.guid,
2787
- content: text.content,
2788
- text: () => text.content.toString(),
2789
- extension: [
2790
- modelState.init(() => model),
2791
- yjs(text.content, provider?.awareness)
2792
- ],
2793
- awareness: provider?.awareness,
2794
- peer: identity ? {
2795
- id: identity.identityKey.toHex(),
2796
- name: identity.profile?.displayName
2797
- } : void 0
2798
- };
2799
- return model;
2800
- };
2801
- var messageAwareness = 1;
2802
- var messageQueryAwareness = 3;
2803
- var YAwarenessProvider = class extends Observable {
2804
- constructor({ space, doc, channel, awareness: awareness2 }) {
2805
- super();
2806
- this._space = space;
2807
- this._awareness = awareness2 ?? new YP.Awareness(doc);
2808
- this._channel = channel;
2809
- this._clientId = doc.clientID;
2810
- this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
2811
- this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
2812
- if (typeof window !== "undefined") {
2813
- window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
2814
- } else if (typeof process !== "undefined") {
2815
- process.on("exit", this._handleBeforeUnload.bind(this));
2816
- }
2817
- const encoderAwarenessQuery = encoding.createEncoder();
2818
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
2819
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
2820
- const encoderAwarenessState = encoding.createEncoder();
2821
- encoding.writeVarUint(encoderAwarenessState, messageAwareness);
2822
- encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
2823
- this._clientId
2824
- ]));
2825
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
2826
- }
2827
- get awareness() {
2828
- return this._awareness;
2829
- }
2830
- _handleAwarenessUpdate({ added, updated, removed }, origin) {
2831
- log4("awareness update", {
2832
- added,
2833
- updated,
2834
- removed,
2835
- origin
2836
- }, {
2837
- F: __dxlog_file5,
2838
- L: 100,
2839
- S: this,
2840
- C: (f, a) => f(...a)
2841
- });
2842
- const changedClients = added.concat(updated).concat(removed);
2843
- const encoderAwareness = encoding.createEncoder();
2844
- encoding.writeVarUint(encoderAwareness, messageAwareness);
2845
- encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
2846
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
2847
- }
2848
- _handleSpaceMessage({ payload }) {
2849
- log4("space message", payload, {
2850
- F: __dxlog_file5,
2851
- L: 109,
2852
- S: this,
2853
- C: (f, a) => f(...a)
2854
- });
2855
- const data = new Uint8Array(Array.from(Object.values(payload)));
2856
- const encoder = this._readMessage(data);
2857
- if (encoder) {
2858
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
2859
- }
2860
- }
2861
- _readMessage(message) {
2862
- const decoder = decoding.createDecoder(message);
2863
- const encoder = encoding.createEncoder();
2864
- const messageType = decoding.readVarUint(decoder);
2865
- let sendReply = false;
2866
- switch (messageType) {
2867
- case messageAwareness: {
2868
- YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
2869
- break;
2870
- }
2871
- case messageQueryAwareness: {
2872
- encoding.writeVarUint(encoder, messageAwareness);
2873
- encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
2874
- sendReply = true;
2875
- break;
2876
- }
2877
- default: {
2878
- console.error("Invalid message:", messageType);
2879
- return encoder;
2880
- }
2881
- }
2882
- if (!sendReply) {
2883
- return null;
2884
- }
2885
- return encoder;
2886
- }
2887
- _handleBeforeUnload() {
2888
- YP.removeAwarenessStates(this._awareness, [
2889
- this._clientId
2890
- ], "window unload");
2891
- }
2892
- };
2893
-
2894
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2895
2856
  var modelState = StateField5.define({
2896
2857
  create: () => void 0,
2897
2858
  update: (model) => model
2898
2859
  });
2860
+
2861
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
2862
+ import { useState as useState2 } from "react";
2863
+ var useTextEditor = () => {
2864
+ const [view, setView] = useState2(null);
2865
+ return [
2866
+ (ref) => {
2867
+ setView(ref);
2868
+ },
2869
+ view
2870
+ ];
2871
+ };
2872
+
2873
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2874
+ import get4 from "lodash.get";
2875
+ import { useEffect as useEffect3, useState as useState3 } from "react";
2876
+ import { generateName } from "@dxos/display-name";
2877
+ import { getRawDoc } from "@dxos/echo-schema";
2878
+ import { invariant as invariant5 } from "@dxos/invariant";
2879
+ import { isAutomergeObject } from "@dxos/react-client/echo";
2880
+ import { isNotNullOrUndefined } from "@dxos/util";
2881
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2899
2882
  var useTextModel = (props) => {
2900
2883
  const { identity, space, text } = props;
2901
- const [model, setModel] = useState2();
2902
- useEffect2(() => setModel(createModel(props)), [
2884
+ const [model, setModel] = useState3();
2885
+ useEffect3(() => setModel(createModel(props)), [
2903
2886
  identity,
2904
2887
  space,
2905
2888
  text
2906
2889
  ]);
2907
2890
  return model;
2908
2891
  };
2909
- var createModel = (props) => {
2910
- const { text } = props;
2911
- if (isAutomergeObject(text)) {
2912
- return createAutomergeModel(props);
2913
- } else if (text?.doc) {
2914
- return createYjsModel(props);
2915
- } else {
2916
- return void 0;
2917
- }
2918
- };
2919
-
2920
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2921
- var createAutomergeModel = ({ space, identity, text }) => {
2892
+ var createModel = ({ space, identity, text }) => {
2893
+ invariant5(isAutomergeObject(text), void 0, {
2894
+ F: __dxlog_file7,
2895
+ L: 35,
2896
+ S: void 0,
2897
+ A: [
2898
+ "isAutomergeObject(text)",
2899
+ ""
2900
+ ]
2901
+ });
2922
2902
  const obj = text;
2923
2903
  const doc = getRawDoc(obj, [
2924
2904
  obj.field
@@ -2927,7 +2907,7 @@ var createAutomergeModel = ({ space, identity, text }) => {
2927
2907
  space,
2928
2908
  channel: `automerge.awareness.${obj.id}`,
2929
2909
  info: {
2930
- displayName: identity?.profile?.displayName ?? "",
2910
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
2931
2911
  color: cursorColor.color,
2932
2912
  lightColor: cursorColor.light
2933
2913
  },
@@ -2937,7 +2917,6 @@ var createAutomergeModel = ({ space, identity, text }) => {
2937
2917
  id: obj.id,
2938
2918
  content: doc,
2939
2919
  text: () => get4(doc.handle.docSync(), doc.path),
2940
- // TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
2941
2920
  extension: [
2942
2921
  modelState.init(() => model),
2943
2922
  automerge3({
@@ -2965,7 +2944,6 @@ export {
2965
2944
  SpaceAwarenessProvider,
2966
2945
  TextEditor,
2967
2946
  TextKind,
2968
- YAwarenessProvider,
2969
2947
  YText,
2970
2948
  YXmlFragment,
2971
2949
  autocomplete,
@@ -2977,18 +2955,18 @@ export {
2977
2955
  code2 as code,
2978
2956
  codeTheme,
2979
2957
  comments,
2980
- createAutomergeModel,
2981
- createYjsModel,
2982
2958
  defaultMarkdownSlots,
2983
2959
  defaultOptions,
2984
2960
  defaultSlots,
2985
2961
  defaultTextSlots,
2986
2962
  defaultTheme,
2987
2963
  getToken,
2964
+ heading2 as heading,
2988
2965
  hr,
2989
2966
  image,
2990
2967
  link,
2991
2968
  listener,
2969
+ logChanges,
2992
2970
  markdownBundle,
2993
2971
  markdownHighlightStyle,
2994
2972
  markdownTags,
@@ -2996,7 +2974,8 @@ export {
2996
2974
  markdownTheme,
2997
2975
  mention,
2998
2976
  modelState,
2999
- setCommentRange,
2977
+ outliner,
2978
+ setComments,
3000
2979
  setFocus,
3001
2980
  setSelection,
3002
2981
  table,
@@ -3004,7 +2983,8 @@ export {
3004
2983
  tasklist,
3005
2984
  textTheme,
3006
2985
  typewriter,
3007
- useTextModel,
3008
- yjs
2986
+ useComments,
2987
+ useTextEditor,
2988
+ useTextModel
3009
2989
  };
3010
2990
  //# sourceMappingURL=index.mjs.map