@dxos/react-ui-editor 0.3.11-main.08776d7 → 0.3.11-main.0999ea0

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 (160) hide show
  1. package/dist/lib/browser/index.mjs +1211 -1005
  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 +12 -17
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +13 -7
  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.map +1 -1
  10. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  14. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  16. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  17. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  18. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  19. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  20. package/dist/types/src/extensions/automerge/defs.d.ts +6 -6
  21. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/semaphore.d.ts +6 -6
  23. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  26. package/dist/types/src/extensions/awareness.d.ts +2 -2
  27. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  28. package/dist/types/src/extensions/basic.d.ts +4 -5
  29. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  30. package/dist/types/src/extensions/comments.d.ts +11 -3
  31. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  32. package/dist/types/src/extensions/index.d.ts +0 -8
  33. package/dist/types/src/extensions/index.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  35. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  37. package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
  38. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  40. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  42. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  44. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  45. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  47. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  48. package/dist/types/src/extensions/markdown/tasklist.d.ts +3 -0
  49. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  50. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  51. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  52. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  53. package/dist/types/src/hooks/defs.d.ts +1 -4
  54. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  55. package/dist/types/src/hooks/index.d.ts +2 -2
  56. package/dist/types/src/hooks/index.d.ts.map +1 -1
  57. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  58. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  59. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  60. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  61. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  62. package/dist/types/src/styles/markdown.d.ts +2 -2
  63. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  64. package/dist/types/src/themes/default.d.ts.map +1 -1
  65. package/dist/types/src/util.d.ts +2 -0
  66. package/dist/types/src/util.d.ts.map +1 -1
  67. package/package.json +45 -36
  68. package/src/components/TextEditor/MarkdownEditor.stories.tsx +61 -72
  69. package/src/components/TextEditor/TextEditor.tsx +101 -77
  70. package/src/components/TextEditor/comments.stories.tsx +16 -21
  71. package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
  72. package/src/components/Toolbar/Toolbar.tsx +115 -0
  73. package/src/extensions/autocomplete.ts +6 -3
  74. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  75. package/src/extensions/automerge/automerge.test.ts +13 -0
  76. package/src/extensions/automerge/automerge.ts +53 -13
  77. package/src/extensions/automerge/cursor.ts +3 -3
  78. package/src/extensions/automerge/defs.ts +9 -29
  79. package/src/extensions/automerge/semaphore.ts +24 -28
  80. package/src/extensions/automerge/update-automerge.ts +14 -8
  81. package/src/extensions/automerge/update-codemirror.ts +5 -5
  82. package/src/extensions/awareness.ts +17 -17
  83. package/src/extensions/basic.ts +22 -17
  84. package/src/extensions/comments.ts +149 -92
  85. package/src/extensions/index.ts +0 -8
  86. package/src/extensions/markdown/bundle.ts +20 -30
  87. package/src/extensions/markdown/code.ts +142 -0
  88. package/src/extensions/markdown/formatting.ts +159 -0
  89. package/src/extensions/markdown/heading.ts +59 -0
  90. package/src/extensions/markdown/highlight.ts +9 -15
  91. package/src/extensions/markdown/hr.ts +71 -0
  92. package/src/extensions/markdown/index.ts +8 -0
  93. package/src/extensions/{link.ts → markdown/link.ts} +5 -2
  94. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +48 -34
  95. package/src/extensions/mention.ts +9 -2
  96. package/src/hooks/awareness-provider.ts +24 -20
  97. package/src/hooks/defs.ts +1 -6
  98. package/src/hooks/index.ts +2 -2
  99. package/src/hooks/useActionHandler.ts +45 -0
  100. package/src/hooks/useTextEditor.ts +29 -0
  101. package/src/hooks/useTextModel.ts +48 -11
  102. package/src/styles/markdown.ts +9 -10
  103. package/src/themes/default.ts +1 -3
  104. package/src/util.ts +26 -3
  105. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  106. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  107. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
  108. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  109. package/dist/types/src/extensions/code.d.ts.map +0 -1
  110. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  111. package/dist/types/src/extensions/image.d.ts.map +0 -1
  112. package/dist/types/src/extensions/link.d.ts.map +0 -1
  113. package/dist/types/src/extensions/sync.d.ts +0 -7
  114. package/dist/types/src/extensions/sync.d.ts.map +0 -1
  115. package/dist/types/src/extensions/table.d.ts.map +0 -1
  116. package/dist/types/src/extensions/tasklist.d.ts +0 -7
  117. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  118. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  119. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  120. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  121. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  122. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  123. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  124. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  125. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  126. package/dist/types/src/hooks/automerge.d.ts +0 -4
  127. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  128. package/dist/types/src/hooks/yjs.d.ts +0 -28
  129. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  130. package/dist/types/src/testing/index.d.ts +0 -3
  131. package/dist/types/src/testing/index.d.ts.map +0 -1
  132. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  133. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  134. package/dist/types/src/testing/proto/index.d.ts +0 -2
  135. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  136. package/dist/types/src/testing/replicator.d.ts +0 -45
  137. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  138. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  139. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  140. package/src/extensions/code.ts +0 -104
  141. package/src/extensions/hr.ts +0 -72
  142. package/src/extensions/sync.ts +0 -15
  143. package/src/extensions/yjs/cursor.ts +0 -22
  144. package/src/extensions/yjs/index.ts +0 -5
  145. package/src/extensions/yjs/yjs.test.ts +0 -35
  146. package/src/extensions/yjs/yjs.ts +0 -16
  147. package/src/hooks/automerge.ts +0 -53
  148. package/src/hooks/yjs.ts +0 -157
  149. package/src/testing/index.ts +0 -6
  150. package/src/testing/proto/gen/schema.ts +0 -49
  151. package/src/testing/proto/index.ts +0 -5
  152. package/src/testing/proto/schema.proto +0 -15
  153. package/src/testing/replicator.ts +0 -184
  154. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  155. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  156. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  157. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  158. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  159. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  160. /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";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
10
+ import { EditorView as EditorView13 } from "@codemirror/view";
12
11
  import { vim } from "@replit/codemirror-vim";
13
12
  import defaultsDeep2 from "lodash.defaultsdeep";
14
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState, useRef } from "react";
15
- import { generateName } from "@dxos/display-name";
13
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
14
+ import { log as log2 } from "@dxos/log";
16
15
  import { useThemeContext } from "@dxos/react-ui";
17
- import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
16
+ import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { isNotFalsy } from "@dxos/util";
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,12 +53,14 @@ var autocomplete = ({ onSearch }) => {
51
53
  };
52
54
 
53
55
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
56
+ import { invertedEffects } from "@codemirror/commands";
54
57
  import { StateField } from "@codemirror/state";
55
- import { next as A } from "@dxos/automerge/automerge";
58
+ import { EditorView, ViewPlugin } from "@codemirror/view";
59
+ import { next as A4 } from "@dxos/automerge/automerge";
56
60
 
57
61
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
58
62
  import get from "lodash.get";
59
- import { next as automerge } from "@dxos/automerge/automerge";
63
+ import { next as A } from "@dxos/automerge/automerge";
60
64
  var cursorConverter = (handle, path) => ({
61
65
  // TODO(burdon): Handle assoc to associate with a previous character.
62
66
  toCursor: (pos) => {
@@ -68,7 +72,7 @@ var cursorConverter = (handle, path) => ({
68
72
  if (typeof value === "string" && value.length <= pos) {
69
73
  return "end";
70
74
  }
71
- return automerge.getCursor(doc, path.slice(), pos);
75
+ return A.getCursor(doc, path.slice(), pos);
72
76
  },
73
77
  fromCursor: (cursor) => {
74
78
  if (cursor === "") {
@@ -86,26 +90,28 @@ var cursorConverter = (handle, path) => ({
86
90
  return 0;
87
91
  }
88
92
  }
89
- return automerge.getCursorPosition(doc, path.slice(), cursor);
93
+ return A.getCursorPosition(doc, path.slice(), cursor);
90
94
  }
91
95
  });
92
96
 
93
97
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
94
98
  import { Annotation, StateEffect } from "@codemirror/state";
95
- var effectType = StateEffect.define({});
96
- var reconcileAnnotationType = Annotation.define();
97
99
  var getPath = (state, field) => state.field(field).path;
98
100
  var getLastHeads = (state, field) => state.field(field).lastHeads;
99
- var updateHeads = (newHeads) => effectType.of({
101
+ var updateHeadsEffect = StateEffect.define({});
102
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
100
103
  newHeads
101
104
  });
102
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
105
+ var reconcileAnnotation = Annotation.define();
106
+ var isReconcile = (tr) => {
107
+ return !!tr.annotation(reconcileAnnotation);
108
+ };
103
109
 
104
110
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
105
- import { next as automerge2 } from "@dxos/automerge/automerge";
111
+ import { next as A3 } from "@dxos/automerge/automerge";
106
112
 
107
113
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
108
- import { next as am } from "@dxos/automerge/automerge";
114
+ import { next as A2 } from "@dxos/automerge/automerge";
109
115
  var updateAutomerge = (field, handle, transactions, state) => {
110
116
  const { lastHeads, path } = state.field(field);
111
117
  let hasChanges = false;
@@ -118,11 +124,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
118
124
  return void 0;
119
125
  }
120
126
  const newHeads = handle.changeAt(lastHeads, (doc) => {
127
+ const invertedTransactions = [];
121
128
  for (const tr of transactions) {
122
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
123
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
129
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
130
+ invertedTransactions.push({
131
+ from: fromA,
132
+ del: toA - fromA,
133
+ insert
134
+ });
124
135
  });
125
136
  }
137
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
138
+ A2.splice(doc, path.slice(), from, del, insert.toString());
139
+ });
126
140
  });
127
141
  return newHeads ?? void 0;
128
142
  };
@@ -137,13 +151,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
137
151
  selection = selection.map(changeSet, 1);
138
152
  view.dispatch({
139
153
  changes: changeSet,
140
- annotations: reconcileAnnotationType.of({})
154
+ annotations: reconcileAnnotation.of(false)
141
155
  });
142
156
  }
143
157
  }
144
158
  view.dispatch({
145
159
  selection,
146
- annotations: reconcileAnnotationType.of({})
160
+ annotations: reconcileAnnotation.of(false)
147
161
  });
148
162
  };
149
163
  var handlePatch = (patch, target, state) => {
@@ -164,7 +178,7 @@ var handleInsert = (target, patch) => {
164
178
  if (index == null) {
165
179
  return [];
166
180
  }
167
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
181
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
168
182
  return [
169
183
  {
170
184
  from: index,
@@ -237,41 +251,42 @@ var charPath = (textPath, candidatePath) => {
237
251
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
238
252
  var PatchSemaphore = class {
239
253
  // prettier-ignore
240
- constructor(_handle, _field) {
254
+ constructor(_handle, _state) {
241
255
  this._handle = _handle;
242
- this._field = _field;
256
+ this._state = _state;
243
257
  this._inReconcile = false;
244
258
  }
245
- // NOTE: Cannot destruct view.state.
246
259
  reconcile(view) {
247
- if (this._inReconcile) {
248
- return;
260
+ if (!this._inReconcile) {
261
+ this._inReconcile = true;
262
+ this.doReconcile(view);
263
+ this._inReconcile = false;
249
264
  }
250
- this._inReconcile = true;
251
- const path = getPath(view.state, this._field);
252
- const oldHeads = getLastHeads(view.state, this._field);
265
+ }
266
+ doReconcile(view) {
267
+ const path = getPath(view.state, this._state);
268
+ const oldHeads = getLastHeads(view.state, this._state);
253
269
  let selection = view.state.selection;
254
- const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
270
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
255
271
  const toInvert = transactions.slice().reverse();
256
- for (const tx of toInvert) {
257
- const inverted = tx.changes.invert(tx.startState.doc);
272
+ for (const tr of toInvert) {
273
+ const inverted = tr.changes.invert(tr.startState.doc);
258
274
  selection = selection.map(inverted);
259
275
  view.dispatch({
260
276
  changes: inverted,
261
- annotations: reconcileAnnotationType.of(true)
277
+ annotations: reconcileAnnotation.of(true)
262
278
  });
263
279
  }
264
- let newHeads = updateAutomerge(this._field, this._handle, transactions, view.state);
280
+ let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
265
281
  if (newHeads === null || newHeads === void 0) {
266
- newHeads = automerge2.getHeads(this._handle.docSync());
282
+ newHeads = A3.getHeads(this._handle.docSync());
267
283
  }
268
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
284
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
269
285
  updateCodeMirror(view, selection, path, diff);
270
286
  view.dispatch({
271
287
  effects: updateHeads(newHeads),
272
- annotations: reconcileAnnotationType.of({})
288
+ annotations: reconcileAnnotation.of(false)
273
289
  });
274
- this._inReconcile = false;
275
290
  }
276
291
  };
277
292
 
@@ -289,9 +304,9 @@ var Cursor = class _Cursor {
289
304
  }
290
305
  static {
291
306
  this.getCursorFromRange = (state, range) => {
292
- const cursorConverter3 = state.facet(_Cursor.converter);
293
- const from = cursorConverter3.toCursor(range.from);
294
- const to = cursorConverter3.toCursor(range.to, -1);
307
+ const cursorConverter2 = state.facet(_Cursor.converter);
308
+ const from = cursorConverter2.toCursor(range.from);
309
+ const to = cursorConverter2.toCursor(range.to, -1);
295
310
  return [
296
311
  from,
297
312
  to
@@ -300,10 +315,10 @@ var Cursor = class _Cursor {
300
315
  }
301
316
  static {
302
317
  this.getRangeFromCursor = (state, cursor) => {
303
- const cursorConverter3 = state.facet(_Cursor.converter);
318
+ const cursorConverter2 = state.facet(_Cursor.converter);
304
319
  const parts = cursor.split(":");
305
- const from = cursorConverter3.fromCursor(parts[0]);
306
- const to = cursorConverter3.fromCursor(parts[1]);
320
+ const from = cursorConverter2.fromCursor(parts[0]);
321
+ const to = cursorConverter2.fromCursor(parts[1]);
307
322
  return from !== void 0 && to !== void 0 ? {
308
323
  from,
309
324
  to
@@ -312,18 +327,12 @@ var Cursor = class _Cursor {
312
327
  }
313
328
  };
314
329
 
315
- // packages/ui/react-ui-editor/src/extensions/sync.ts
316
- import { Facet as Facet2 } from "@codemirror/state";
317
- var syncFacet = Facet2.define({
318
- combine: (values) => values.at(-1)
319
- });
320
-
321
330
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
322
- var automerge3 = ({ handle, path }) => {
323
- const stateField = StateField.define({
331
+ var automerge = ({ handle, path }) => {
332
+ const syncState = StateField.define({
324
333
  create: () => ({
325
334
  path: path.slice(),
326
- lastHeads: A.getHeads(handle.docSync()),
335
+ lastHeads: A4.getHeads(handle.docSync()),
327
336
  unreconciledTransactions: []
328
337
  }),
329
338
  update: (value, tr) => {
@@ -334,7 +343,7 @@ var automerge3 = ({ handle, path }) => {
334
343
  };
335
344
  let clearUnreconciled = false;
336
345
  for (const effect of tr.effects) {
337
- if (effect.is(effectType)) {
346
+ if (effect.is(updateHeadsEffect)) {
338
347
  result.lastHeads = effect.value.newHeads;
339
348
  clearUnreconciled = true;
340
349
  }
@@ -342,26 +351,53 @@ var automerge3 = ({ handle, path }) => {
342
351
  if (clearUnreconciled) {
343
352
  result.unreconciledTransactions = [];
344
353
  } else {
345
- if (!isReconcileTx(tr)) {
354
+ if (!isReconcile(tr)) {
346
355
  result.unreconciledTransactions.push(tr);
347
356
  }
348
357
  }
349
358
  return result;
350
359
  }
351
360
  });
352
- const semaphore = new PatchSemaphore(handle, stateField);
353
- return {
354
- extension: [
355
- Cursor.converter.of(cursorConverter(handle, path)),
356
- syncFacet.of(semaphore),
357
- stateField
358
- ]
359
- };
361
+ const semaphore = new PatchSemaphore(handle, syncState);
362
+ return [
363
+ Cursor.converter.of(cursorConverter(handle, path)),
364
+ syncState,
365
+ // Reconcile external updates.
366
+ ViewPlugin.fromClass(class {
367
+ constructor(_view) {
368
+ this._view = _view;
369
+ handle.addListener("change", this._handleChange.bind(this));
370
+ }
371
+ destroy() {
372
+ handle.removeListener("change", this._handleChange.bind(this));
373
+ }
374
+ _handleChange() {
375
+ semaphore.reconcile(this._view);
376
+ }
377
+ }),
378
+ // Reconcile local updates.
379
+ EditorView.updateListener.of(({ view, changes }) => {
380
+ if (!changes.empty) {
381
+ semaphore.reconcile(view);
382
+ }
383
+ }),
384
+ // TODO(burdon): Record undo transactions.
385
+ // See https://github.com/yjs/y-codemirror.next/tree/main
386
+ // https://codemirror.net/examples/inverted-effect
387
+ invertedEffects.of((tr) => {
388
+ const effects = [];
389
+ if (!tr.changes.empty) {
390
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
391
+ });
392
+ }
393
+ return effects;
394
+ })
395
+ ];
360
396
  };
361
397
 
362
398
  // packages/ui/react-ui-editor/src/extensions/awareness.ts
363
- import { Annotation as Annotation2, Facet as Facet3, RangeSet } from "@codemirror/state";
364
- import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view";
399
+ import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
400
+ import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
365
401
  import { Event } from "@dxos/async";
366
402
  import { Context } from "@dxos/context";
367
403
  var dummyProvider = {
@@ -370,18 +406,18 @@ var dummyProvider = {
370
406
  },
371
407
  close: () => {
372
408
  },
409
+ getRemoteStates: () => [],
373
410
  update: () => {
374
- },
375
- getRemoteStates: () => []
411
+ }
376
412
  };
377
- var AwarenessProvider = Facet3.define({
413
+ var awarenessProvider = Facet2.define({
378
414
  combine: (providers) => providers[0] ?? dummyProvider
379
415
  });
380
416
  var RemoteSelectionChangedAnnotation = Annotation2.define();
381
417
  var awareness = (provider = dummyProvider) => {
382
418
  return [
383
- AwarenessProvider.of(provider),
384
- ViewPlugin.fromClass(RemoteSelectionsDecorator, {
419
+ awarenessProvider.of(provider),
420
+ ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
385
421
  decorations: (value) => value.decorations
386
422
  }),
387
423
  styles
@@ -394,7 +430,7 @@ var RemoteSelectionsDecorator = class {
394
430
  this._lastAnchor = void 0;
395
431
  this._lastHead = void 0;
396
432
  this._cursorConverter = view.state.facet(Cursor.converter);
397
- this._provider = view.state.facet(AwarenessProvider);
433
+ this._provider = view.state.facet(awarenessProvider);
398
434
  this._provider.open();
399
435
  this._provider.remoteStateChange.on(this._ctx, () => {
400
436
  view.dispatch({
@@ -535,7 +571,7 @@ var RemoteCaretWidget = class extends WidgetType {
535
571
  return true;
536
572
  }
537
573
  };
538
- var styles = EditorView.baseTheme({
574
+ var styles = EditorView2.baseTheme({
539
575
  ".cm-collab-selection": {},
540
576
  ".cm-collab-selectionLine": {
541
577
  padding: 0,
@@ -548,39 +584,41 @@ var styles = EditorView.baseTheme({
548
584
  marginLeft: "-1px",
549
585
  marginRight: "-1px",
550
586
  boxSizing: "border-box",
551
- display: "inline"
587
+ display: "inline",
588
+ cursor: "pointer"
552
589
  },
553
590
  ".cm-collab-selectionCaretDot": {
554
591
  borderRadius: "50%",
555
592
  position: "absolute",
556
- width: ".4em",
557
- height: ".4em",
558
- top: "-.2em",
559
- left: "-.2em",
593
+ width: ".5em",
594
+ height: ".5em",
595
+ top: "-.25em",
596
+ left: "-.25em",
560
597
  backgroundColor: "inherit",
561
598
  transition: "transform .3s ease-in-out",
562
599
  boxSizing: "border-box"
563
600
  },
564
601
  ".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
565
- transformOrigin: "bottom center",
566
- transform: "scale(0)"
602
+ transform: "scale(0)",
603
+ transformOrigin: "center"
567
604
  },
568
605
  ".cm-collab-selectionInfo": {
569
606
  position: "absolute",
570
- top: "-1.05em",
571
- left: "-1px",
607
+ transform: "translate(-50%, 0)",
608
+ top: "-20px",
609
+ left: 0,
572
610
  fontSize: ".75em",
573
- fontFamily: "serif",
611
+ fontFamily: "sans-serif",
574
612
  fontStyle: "normal",
575
613
  fontWeight: "normal",
576
614
  lineHeight: "normal",
577
615
  userSelect: "none",
578
616
  color: "white",
579
- paddingLeft: "2px",
580
- paddingRight: "2px",
617
+ padding: "2px",
581
618
  zIndex: 101,
582
619
  transition: "opacity .3s ease-in-out",
583
620
  backgroundColor: "inherit",
621
+ borderRadius: "2px",
584
622
  // These should be separate.
585
623
  opacity: 0,
586
624
  transitionDelay: "0s",
@@ -594,23 +632,25 @@ var styles = EditorView.baseTheme({
594
632
 
595
633
  // packages/ui/react-ui-editor/src/extensions/basic.ts
596
634
  import { closeBrackets } from "@codemirror/autocomplete";
635
+ import { history } from "@codemirror/commands";
597
636
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
598
637
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
599
- import { drawSelection, EditorView as EditorView2, placeholder } from "@codemirror/view";
600
- var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
601
- EditorView2.lineWrapping,
602
- // TODO(burdon): Compare with minimalSetup.
638
+ import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
639
+ var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
640
+ lineWrapping ? EditorView3.lineWrapping : [],
603
641
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
604
642
  bracketMatching(),
605
643
  closeBrackets(),
606
644
  drawSelection(),
607
- _placeholder && placeholder(_placeholder),
608
- // TODO(burdon): Is this required?
645
+ highlightActiveLine(),
646
+ history(),
647
+ _placeholder ? placeholder(_placeholder) : [],
648
+ // https://github.com/codemirror/theme-one-dark
609
649
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
610
- ].filter(Boolean);
650
+ ];
611
651
 
612
652
  // packages/ui/react-ui-editor/src/extensions/blast.ts
613
- import { EditorView as EditorView3, keymap as keymap2 } from "@codemirror/view";
653
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
614
654
  import defaultsDeep from "lodash.defaultsdeep";
615
655
  import { invariant } from "@dxos/invariant";
616
656
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -646,10 +686,10 @@ var blast = (options = defaultOptions) => {
646
686
  const getPoint = (view, pos) => {
647
687
  const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
648
688
  const element = document.elementFromPoint(x, y);
649
- const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
689
+ const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
650
690
  const point = {
651
691
  x: x - left,
652
- y: y - top
692
+ y: y - top2
653
693
  };
654
694
  return {
655
695
  element,
@@ -658,7 +698,7 @@ var blast = (options = defaultOptions) => {
658
698
  };
659
699
  return [
660
700
  // Cursor moved.
661
- EditorView3.updateListener.of((update2) => {
701
+ EditorView4.updateListener.of((update2) => {
662
702
  if (blaster?.node !== update2.view.scrollDOM) {
663
703
  if (blaster) {
664
704
  blaster.destroy();
@@ -919,9 +959,14 @@ var random = (min, max) => {
919
959
  return min + ~~(Math.random() * (max - min + 1));
920
960
  };
921
961
 
922
- // packages/ui/react-ui-editor/src/extensions/code.ts
923
- import { ViewPlugin as ViewPlugin2, EditorView as EditorView4, Decoration as Decoration2 } from "@codemirror/view";
924
- import { mx as mx2 } from "@dxos/react-ui-theme";
962
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
963
+ import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
964
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
965
+ import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
966
+ import sortBy from "lodash.sortby";
967
+ import { useEffect } from "react";
968
+ import { debounce } from "@dxos/async";
969
+ import { nonNullable } from "@dxos/util";
925
970
 
926
971
  // packages/ui/react-ui-editor/src/styles/cursors.ts
927
972
  import * as random2 from "lib0/random";
@@ -964,34 +1009,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
964
1009
  // packages/ui/react-ui-editor/src/styles/markdown.ts
965
1010
  import { mx } from "@dxos/react-ui-theme";
966
1011
  var headings = {
967
- 1: [
968
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
969
- "-ml-[10px]"
970
- ],
971
- 2: [
972
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
973
- "-ml-[8px]"
974
- ],
975
- 3: [
976
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
977
- "-ml-[7px]"
978
- ],
979
- 4: [
980
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
981
- "-ml-[6px]"
982
- ],
983
- 5: [
984
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
985
- "-ml-[5px]"
986
- ],
987
- 6: [
988
- "mbs-4 mbe-2 font-medium text-inherit no-underline",
989
- "-ml-[4px]"
990
- ]
991
- };
992
- var heading = (level, readonly) => {
993
- const [style, offset] = headings[level];
994
- return mx(style, readonly && offset);
1012
+ 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
1013
+ 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1014
+ 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1015
+ 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1016
+ 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1017
+ 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
1018
+ };
1019
+ var heading = (level) => {
1020
+ return headings[level];
995
1021
  };
996
1022
  var light = "text-neutral-200 dark:text-neutral-800";
997
1023
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -1009,136 +1035,78 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
1009
1035
  var tokens = tailwindConfig({}).theme;
1010
1036
  var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
1011
1037
 
1012
- // packages/ui/react-ui-editor/src/extensions/code.ts
1013
- var CODE_REGEX = /```[\s\S]*?```/gs;
1014
- var styles2 = EditorView4.baseTheme({
1015
- "& .cm-codeblock": {
1016
- fontFamily: getToken("fontFamily.mono", []).join(",")
1017
- },
1018
- '&light [aria-readonly="true"] .cm-codeblock': {
1019
- background: getToken("extend.colors.neutral.50")
1020
- },
1021
- '&dark [aria-readonly="true"] .cm-codeblock': {
1022
- background: getToken("extend.colors.neutral.850")
1023
- },
1024
- '& [aria-readonly="true"] .cm-codeblock': {
1025
- display: "block",
1026
- paddingInline: "4px !important"
1027
- },
1028
- '& [aria-readonly="true"] .cm-codeblock-first': {
1029
- paddingTop: "4px !important",
1030
- borderTopLeftRadius: "4px",
1031
- borderTopRightRadius: "4px"
1032
- },
1033
- '& [aria-readonly="true"] .cm-codeblock-last': {
1034
- paddingBottom: "4px !important",
1035
- borderBottomLeftRadius: "4px",
1036
- borderBottomRightRadius: "4px"
1037
- }
1038
- });
1039
- var getLineRange = (lines, from, to) => {
1040
- const start = lines.findIndex((line) => line.from >= from);
1041
- const end = lines.findIndex((line) => line.to >= to);
1042
- return [
1043
- start,
1044
- end
1045
- ];
1046
- };
1047
- var code2 = () => {
1048
- const buildDecorations4 = (view) => {
1049
- const decorations = [];
1050
- const text = view.state.doc.sliceString(0);
1051
- const matches = text.matchAll(CODE_REGEX);
1052
- const blocks = view.viewportLineBlocks;
1053
- for (const match of matches) {
1054
- const range = getLineRange(blocks, match.index, match.index + match[0].length);
1055
- for (let i = range[0]; i <= range[1]; i++) {
1056
- const block = blocks[i];
1057
- decorations.push(Decoration2.line({
1058
- class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1059
- }).range(block.from));
1060
- }
1061
- }
1062
- return Decoration2.set(decorations);
1063
- };
1064
- return [
1065
- ViewPlugin2.fromClass(class {
1066
- constructor(view) {
1067
- this.hasFocus = false;
1068
- this.decorations = buildDecorations4(view);
1069
- }
1070
- update(update2) {
1071
- if (
1072
- // TODO(burdon): Generalize for other extensions.
1073
- this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
1074
- ) {
1075
- this.hasFocus = update2.view.hasFocus;
1076
- this.decorations = buildDecorations4(update2.view);
1077
- }
1078
- }
1079
- }, {
1080
- decorations: (value) => value.decorations
1081
- }),
1082
- styles2
1083
- ];
1084
- };
1085
-
1086
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1087
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1088
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
1089
- import sortBy from "lodash.sortby";
1090
- import { debounce } from "@dxos/async";
1091
- import { invariant as invariant2 } from "@dxos/invariant";
1092
- import { nonNullable } from "@dxos/util";
1093
-
1094
1038
  // packages/ui/react-ui-editor/src/util.ts
1095
1039
  import { log } from "@dxos/log";
1096
1040
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1097
1041
  var callbackWrapper = (fn) => (...args) => {
1098
1042
  try {
1099
1043
  return fn(...args);
1100
- } catch (error) {
1101
- log.catch(error, void 0, {
1044
+ } catch (err) {
1045
+ log.catch(err, void 0, {
1102
1046
  F: __dxlog_file2,
1103
- L: 16,
1047
+ L: 18,
1104
1048
  S: void 0,
1105
1049
  C: (f, a) => f(...a)
1106
1050
  });
1107
1051
  }
1108
1052
  };
1053
+ var logChanges = (trs) => {
1054
+ const changes = trs.flatMap((tr) => {
1055
+ if (tr.changes.empty) {
1056
+ return void 0;
1057
+ }
1058
+ const changes2 = [];
1059
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
1060
+ fromA,
1061
+ toA,
1062
+ fromB,
1063
+ toB,
1064
+ inserted: inserted.toString()
1065
+ })));
1066
+ return changes2;
1067
+ }).filter(Boolean);
1068
+ if (changes.length) {
1069
+ console.log("changes", changes);
1070
+ }
1071
+ };
1109
1072
 
1110
1073
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1111
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1112
- var styles3 = EditorView5.baseTheme({
1074
+ var styles2 = EditorView5.baseTheme({
1113
1075
  "& .cm-comment": {
1114
1076
  backgroundColor: getToken("extend.colors.yellow.50")
1115
1077
  },
1116
- "& .cm-comment-active": {
1078
+ "& .cm-comment-current": {
1117
1079
  backgroundColor: getToken("extend.colors.yellow.100")
1118
1080
  }
1119
1081
  });
1120
- var marks = {
1121
- highlight: Decoration3.mark({
1122
- class: "cm-comment"
1123
- }),
1124
- highlightActive: Decoration3.mark({
1125
- class: "cm-comment-active"
1126
- })
1127
- };
1128
- var setFocus = (view, thread) => {
1129
- const comment = view.state.field(commentsState).comments.find((range) => range.comment.id === thread);
1130
- if (!comment) {
1082
+ var commentMark = Decoration2.mark({
1083
+ class: "cm-comment"
1084
+ });
1085
+ var commentCurrentMark = Decoration2.mark({
1086
+ class: "cm-comment-current"
1087
+ });
1088
+ var setFocus = (view, id, center = true) => {
1089
+ const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1090
+ if (!comment?.comment.cursor) {
1131
1091
  return;
1132
1092
  }
1133
- view.dispatch({
1134
- effects: setSelection.of({
1135
- active: thread
1136
- }),
1137
- selection: {
1138
- anchor: comment.range.from
1139
- },
1140
- scrollIntoView: true
1141
- });
1093
+ const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
1094
+ if (range) {
1095
+ view.dispatch({
1096
+ selection: {
1097
+ anchor: range.from
1098
+ },
1099
+ effects: [
1100
+ //
1101
+ EditorView5.scrollIntoView(range.from, center ? {
1102
+ y: "center"
1103
+ } : void 0),
1104
+ setSelection.of({
1105
+ current: id
1106
+ })
1107
+ ]
1108
+ });
1109
+ }
1142
1110
  };
1143
1111
  var setComments = StateEffect2.define();
1144
1112
  var setSelection = StateEffect2.define();
@@ -1179,23 +1147,23 @@ var commentsState = StateField2.define({
1179
1147
  return value;
1180
1148
  }
1181
1149
  });
1182
- var highlightDecorations = EditorView5.decorations.compute([
1150
+ var commentsDecorations = EditorView5.decorations.compute([
1183
1151
  commentsState
1184
1152
  ], (state) => {
1185
- const { selection: { active }, comments: comments2 } = state.field(commentsState);
1153
+ const { selection: { current }, comments: comments2 } = state.field(commentsState);
1186
1154
  const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1187
1155
  const range = comment.range;
1188
1156
  if (!range || range.from === range.to) {
1189
1157
  console.warn("Invalid range:", range);
1190
1158
  return void 0;
1191
1159
  }
1192
- if (comment.comment.id === active) {
1193
- return marks.highlightActive.range(range.from, range.to);
1160
+ if (comment.comment.id === current) {
1161
+ return commentCurrentMark.range(range.from, range.to);
1194
1162
  } else {
1195
- return marks.highlight.range(range.from, range.to);
1163
+ return commentMark.range(range.from, range.to);
1196
1164
  }
1197
1165
  }).filter(nonNullable);
1198
- return Decoration3.set(decorations);
1166
+ return Decoration2.set(decorations);
1199
1167
  });
1200
1168
  var trackPastedComments = (onUpdate) => {
1201
1169
  let tracked = null;
@@ -1221,94 +1189,132 @@ var trackPastedComments = (onUpdate) => {
1221
1189
  cut: handleTrack,
1222
1190
  copy: handleTrack
1223
1191
  }),
1224
- // Handle paste.
1225
- EditorView5.updateListener.of(({ view, state, transactions }) => {
1192
+ // Track deleted comments.
1193
+ invertedEffects2.of((tr) => {
1194
+ const { comments: comments2 } = tr.startState.field(commentsState);
1195
+ const effects = [];
1196
+ tr.changes.iterChangedRanges((fromA, toA) => {
1197
+ for (const { comment: { id }, range: { from, to } } of comments2) {
1198
+ if (from < to && from >= fromA && to <= toA) {
1199
+ effects.push(restoreCommentEffect.of({
1200
+ id,
1201
+ from,
1202
+ to
1203
+ }));
1204
+ }
1205
+ }
1206
+ });
1207
+ return effects;
1208
+ }),
1209
+ // Handle paste or the undo of comment deletion.
1210
+ EditorView5.updateListener.of((update2) => {
1211
+ const restore = [];
1212
+ for (let i = 0; i < update2.transactions.length; i++) {
1213
+ const tr = update2.transactions[i];
1214
+ for (let j = 0; j < restore.length; j++) {
1215
+ restore[j] = mapTrackedComment(restore[j], tr.changes);
1216
+ }
1217
+ for (const effect of tr.effects) {
1218
+ if (effect.is(restoreCommentEffect)) {
1219
+ restore.push(effect.value);
1220
+ }
1221
+ }
1222
+ }
1226
1223
  if (tracked) {
1227
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1224
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1228
1225
  if (paste) {
1229
1226
  let found = -1;
1230
1227
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1231
1228
  if (text.eq(tracked.text)) {
1232
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1233
- fromB = transactions[i].changes.mapPos(fromB);
1229
+ for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1230
+ fromB = update2.transactions[i].changes.mapPos(fromB);
1234
1231
  }
1235
1232
  found = fromB;
1236
1233
  }
1237
1234
  });
1238
1235
  if (found > -1) {
1239
- state.facet(syncFacet)?.reconcile(view);
1240
1236
  for (const moved of tracked.comments) {
1241
- const range = {
1237
+ restore.push({
1238
+ id: moved.id,
1242
1239
  from: found + moved.from,
1243
1240
  to: found + moved.to
1244
- };
1245
- const cursor = Cursor.getCursorFromRange(state, range);
1246
- onUpdate(moved.id, cursor);
1241
+ });
1247
1242
  }
1248
1243
  }
1249
1244
  tracked = null;
1250
1245
  }
1251
1246
  }
1247
+ for (const comment of restore) {
1248
+ const { comments: comments2 } = update2.startState.field(commentsState);
1249
+ const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
1250
+ if (!exists) {
1251
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
1252
+ onUpdate(comment.id, cursor);
1253
+ }
1254
+ }
1252
1255
  })
1253
1256
  ];
1254
1257
  };
1255
- var comments = (options = {}) => {
1256
- const { key: shortcut = "meta-'" } = options;
1257
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1258
- const createCommentThread = (view) => {
1259
- invariant2(options.onCreate, void 0, {
1260
- F: __dxlog_file3,
1261
- L: 262,
1262
- S: void 0,
1263
- A: [
1264
- "options.onCreate",
1265
- ""
1266
- ]
1258
+ var mapTrackedComment = (comment, changes) => ({
1259
+ id: comment.id,
1260
+ from: changes.mapPos(comment.from, 1),
1261
+ to: changes.mapPos(comment.to, 1)
1262
+ });
1263
+ var restoreCommentEffect = StateEffect2.define({
1264
+ map: mapTrackedComment
1265
+ });
1266
+ var optionsFacet = Facet3.define({
1267
+ combine: (providers) => providers[0]
1268
+ });
1269
+ var createComment = (view) => {
1270
+ const options = view.state.facet(optionsFacet);
1271
+ const { from, to } = view.state.selection.main;
1272
+ if (from === to) {
1273
+ return false;
1274
+ }
1275
+ if (to === view.state.doc.length) {
1276
+ view.dispatch({
1277
+ changes: {
1278
+ from: to,
1279
+ insert: "\n"
1280
+ }
1267
1281
  });
1268
- const { from, to } = view.state.selection.main;
1269
- if (from === to) {
1270
- return false;
1271
- }
1272
- if (to === view.state.doc.length) {
1282
+ }
1283
+ const cursor = Cursor.getCursorFromRange(view.state, {
1284
+ from,
1285
+ to
1286
+ });
1287
+ if (cursor) {
1288
+ const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1289
+ if (id) {
1273
1290
  view.dispatch({
1274
- changes: {
1275
- from: to,
1276
- insert: "\n"
1291
+ effects: setSelection.of({
1292
+ current: id
1293
+ }),
1294
+ selection: {
1295
+ anchor: to
1277
1296
  }
1278
1297
  });
1298
+ return true;
1279
1299
  }
1280
- const cursor = Cursor.getCursorFromRange(view.state, {
1281
- from,
1282
- to
1283
- });
1284
- if (cursor) {
1285
- const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
1286
- if (id) {
1287
- view.dispatch({
1288
- effects: setSelection.of({
1289
- active: id
1290
- }),
1291
- selection: {
1292
- anchor: from
1293
- }
1294
- });
1295
- return true;
1296
- }
1297
- }
1298
- return false;
1299
- };
1300
+ }
1301
+ return false;
1302
+ };
1303
+ var comments = (options = {}) => {
1304
+ const { key: shortcut = "meta-'" } = options;
1305
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1300
1306
  return [
1307
+ optionsFacet.of(options),
1301
1308
  commentsState,
1302
- highlightDecorations,
1303
- styles3,
1309
+ commentsDecorations,
1310
+ styles2,
1304
1311
  //
1305
1312
  // Keymap.
1306
1313
  //
1307
1314
  options.onCreate ? keymap3.of([
1308
1315
  {
1309
1316
  key: shortcut,
1310
- // TODO(burdon): Check if there's a better way?
1311
- run: callbackWrapper(createCommentThread)
1317
+ run: callbackWrapper(createComment)
1312
1318
  }
1313
1319
  ]) : [],
1314
1320
  //
@@ -1374,17 +1380,17 @@ var comments = (options = {}) => {
1374
1380
  //
1375
1381
  // Track selection/proximity.
1376
1382
  //
1377
- EditorView5.updateListener.of(({ view, state, changes }) => {
1383
+ EditorView5.updateListener.of(({ view, state }) => {
1378
1384
  let min = Infinity;
1379
- const { selection: { active, closest }, comments: comments2 } = state.field(commentsState);
1385
+ const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1380
1386
  const { head } = state.selection.main;
1381
1387
  const selection = {};
1382
1388
  comments2.forEach(({ comment, range }) => {
1383
1389
  if (head >= range.from && head <= range.to) {
1384
- selection.active = comment.id;
1390
+ selection.current = comment.id;
1385
1391
  selection.closest = void 0;
1386
1392
  }
1387
- if (!selection.active) {
1393
+ if (!selection.current) {
1388
1394
  const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
1389
1395
  if (d < min) {
1390
1396
  selection.closest = comment.id;
@@ -1392,7 +1398,7 @@ var comments = (options = {}) => {
1392
1398
  }
1393
1399
  }
1394
1400
  });
1395
- if (selection.active !== active || selection.closest !== closest) {
1401
+ if (selection.current !== current || selection.closest !== closest) {
1396
1402
  view.dispatch({
1397
1403
  effects: setSelection.of(selection)
1398
1404
  });
@@ -1409,319 +1415,78 @@ var comments = (options = {}) => {
1409
1415
  options.onUpdate ? trackPastedComments(options.onUpdate) : []
1410
1416
  ];
1411
1417
  };
1418
+ var useComments = (view, comments2 = []) => {
1419
+ useEffect(() => {
1420
+ if (view) {
1421
+ view.dispatch({
1422
+ effects: setComments.of(comments2)
1423
+ });
1424
+ }
1425
+ }, [
1426
+ view,
1427
+ comments2
1428
+ ]);
1429
+ };
1412
1430
 
1413
- // packages/ui/react-ui-editor/src/extensions/hr.ts
1414
- import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1415
- var styles4 = EditorView6.baseTheme({
1416
- "& .cm-hr": {
1417
- // TODO(burdon): ???
1418
- // Note that block-level decorations should not have vertical margins,
1419
- borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
1431
+ // packages/ui/react-ui-editor/src/extensions/listener.ts
1432
+ import { EditorView as EditorView6 } from "@codemirror/view";
1433
+ var listener = ({ onFocus, onChange }) => {
1434
+ const extensions = [];
1435
+ onFocus && extensions.push(EditorView6.focusChangeEffect.of((_, focused) => {
1436
+ onFocus(focused);
1437
+ return null;
1438
+ }));
1439
+ onChange && extensions.push(EditorView6.updateListener.of((update2) => {
1440
+ onChange(update2.state.doc.toString());
1441
+ }));
1442
+ return extensions;
1443
+ };
1444
+
1445
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1446
+ import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1447
+ import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1448
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1449
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1450
+ import { languages } from "@codemirror/language-data";
1451
+ import { searchKeymap } from "@codemirror/search";
1452
+ import { EditorState } from "@codemirror/state";
1453
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1454
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1455
+
1456
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1457
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1458
+ import { HighlightStyle } from "@codemirror/language";
1459
+ import { tags, styleTags, Tag } from "@lezer/highlight";
1460
+ import { Table } from "@lezer/markdown";
1461
+ var markdownTags = {
1462
+ Blockquote: Tag.define(),
1463
+ CodeMark: Tag.define(),
1464
+ CodeText: Tag.define(),
1465
+ EmphasisMark: Tag.define(),
1466
+ HeaderMark: Tag.define(),
1467
+ InlineCode: Tag.define(),
1468
+ LinkLabel: Tag.define(),
1469
+ LinkReference: Tag.define(),
1470
+ ListMark: Tag.define(),
1471
+ QuoteMark: Tag.define(),
1472
+ URL: Tag.define(),
1473
+ // Custom.
1474
+ TableCell: Tag.define()
1475
+ };
1476
+ Table.defineNodes?.forEach((node) => {
1477
+ switch (node?.name) {
1478
+ case "TableCell": {
1479
+ node.style = markdownTags.TableCell;
1480
+ break;
1481
+ }
1420
1482
  }
1421
1483
  });
1422
- var HorizontalRuleWidget = class extends WidgetType2 {
1423
- constructor(_pos) {
1424
- super();
1425
- this._pos = _pos;
1426
- }
1427
- eq(other) {
1428
- return this._pos === other._pos;
1429
- }
1430
- toDOM(view) {
1431
- const el = document.createElement("div");
1432
- el.className = "cm-hr";
1433
- return el;
1434
- }
1435
- };
1436
- var placeholderMatcher = new MatchDecorator({
1437
- // regexp: /(?<=\n\n)---/gs,
1438
- regexp: /^---$/gs,
1439
- decoration: (match, view, pos) => Decoration4.replace({
1440
- widget: new HorizontalRuleWidget(pos)
1441
- })
1442
- });
1443
- var hr = () => [
1444
- styles4,
1445
- ViewPlugin3.fromClass(class {
1446
- constructor(view) {
1447
- this.rules = placeholderMatcher.createDeco(view);
1448
- }
1449
- update(update2) {
1450
- this.rules = placeholderMatcher.updateDeco(update2, this.rules);
1451
- }
1452
- }, {
1453
- decorations: (instance) => instance.rules
1454
- })
1455
- ];
1456
-
1457
- // packages/ui/react-ui-editor/src/extensions/image.ts
1458
- import { syntaxTree } from "@codemirror/language";
1459
- import { StateField as StateField3 } from "@codemirror/state";
1460
- import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
1461
- var image = (options = {}) => {
1462
- return StateField3.define({
1463
- create: (state) => {
1464
- return Decoration5.set(buildDecorations(0, state.doc.length, state));
1465
- },
1466
- update: (value, tr) => {
1467
- if (!tr.docChanged && !tr.selection) {
1468
- return value;
1469
- }
1470
- const cursor = tr.state.selection.main.head;
1471
- const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
1472
- let from = Math.min(cursor, oldCursor);
1473
- let to = Math.max(cursor, oldCursor);
1474
- tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
1475
- from = Math.min(from, fromB);
1476
- to = Math.max(to, toB);
1477
- });
1478
- from = tr.state.doc.lineAt(from).from;
1479
- to = tr.state.doc.lineAt(to).to;
1480
- return value.map(tr.changes).update({
1481
- filterFrom: from,
1482
- filterTo: to,
1483
- filter: () => false,
1484
- add: buildDecorations(from, to, tr.state)
1485
- });
1486
- },
1487
- provide: (field) => EditorView7.decorations.from(field)
1488
- });
1489
- };
1490
- var buildDecorations = (from, to, state) => {
1491
- const decorations = [];
1492
- const cursor = state.selection.main.head;
1493
- syntaxTree(state).iterate({
1494
- enter: (node) => {
1495
- if (node.name === "Image") {
1496
- const urlNode = node.node.getChild("URL");
1497
- if (urlNode) {
1498
- const hide = state.readOnly || cursor < node.from || cursor > node.to;
1499
- const url = state.sliceDoc(urlNode.from, urlNode.to);
1500
- decorations.push(Decoration5.replace({
1501
- block: true,
1502
- widget: new ImageWidget(url)
1503
- }).range(hide ? node.from : node.to, node.to));
1504
- }
1505
- }
1506
- },
1507
- from,
1508
- to
1509
- });
1510
- return decorations;
1511
- };
1512
- var ImageWidget = class extends WidgetType3 {
1513
- constructor(_url) {
1514
- super();
1515
- this._url = _url;
1516
- }
1517
- eq(other) {
1518
- return this._url === other._url;
1519
- }
1520
- toDOM(view) {
1521
- const img = document.createElement("img");
1522
- img.setAttribute("src", this._url);
1523
- img.setAttribute("class", "cm-image");
1524
- img.onload = () => img.classList.add("cm-loaded-image");
1525
- return img;
1526
- }
1527
- };
1528
-
1529
- // packages/ui/react-ui-editor/src/extensions/link.ts
1530
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1531
- import { RangeSetBuilder } from "@codemirror/state";
1532
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin4, WidgetType as WidgetType4 } from "@codemirror/view";
1533
- import { tooltipContent } from "@dxos/react-ui-theme";
1534
- var link = (options = {}) => {
1535
- const extensions = [
1536
- ViewPlugin4.fromClass(class {
1537
- constructor(view) {
1538
- this.decorations = buildDecorations2(view, options);
1539
- }
1540
- update(update2) {
1541
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
1542
- this.decorations = buildDecorations2(update2.view, options);
1543
- }
1544
- }
1545
- }, {
1546
- decorations: (v) => v.decorations
1547
- })
1548
- ];
1549
- if (options.onHover) {
1550
- extensions.push(
1551
- // https://codemirror.net/examples/tooltip
1552
- // https://codemirror.net/docs/ref/#view.hoverTooltip
1553
- // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1554
- hoverTooltip2((view, pos, side) => {
1555
- const syntax = syntaxTree2(view.state).resolveInner(pos, side);
1556
- let link2 = null;
1557
- for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1558
- link2 = node.name === "Link" ? node : null;
1559
- }
1560
- const url = link2 && link2.getChild("URL");
1561
- if (!url || !link2) {
1562
- return null;
1563
- }
1564
- const urlText = view.state.sliceDoc(url.from, url.to);
1565
- return {
1566
- pos: link2.from,
1567
- end: link2.to,
1568
- above: true,
1569
- create: () => {
1570
- const el = document.createElement("div");
1571
- el.className = tooltipContent({}, "pli-2 plb-1");
1572
- options.onHover(el, urlText);
1573
- return {
1574
- dom: el,
1575
- offset: {
1576
- x: 0,
1577
- y: 4
1578
- }
1579
- };
1580
- }
1581
- };
1582
- })
1583
- );
1584
- }
1585
- return extensions;
1586
- };
1587
- var LinkButton = class extends WidgetType4 {
1588
- constructor(_url, _onAttach) {
1589
- super();
1590
- this._url = _url;
1591
- this._onAttach = _onAttach;
1592
- }
1593
- eq(other) {
1594
- return this._url === other._url;
1595
- }
1596
- toDOM(view) {
1597
- const el = document.createElement("span");
1598
- this._onAttach(el, this._url);
1599
- return el;
1600
- }
1601
- };
1602
- var LinkText = class extends WidgetType4 {
1603
- constructor(_text, _url) {
1604
- super();
1605
- this._text = _text;
1606
- this._url = _url;
1607
- }
1608
- eq(other) {
1609
- return this._url === other._url;
1610
- }
1611
- toDOM(view) {
1612
- const link2 = document.createElement("a");
1613
- link2.setAttribute("class", "cm-link");
1614
- link2.textContent = this._text;
1615
- if (this._url) {
1616
- link2.setAttribute("rel", "noreferrer");
1617
- link2.setAttribute("target", "_blank");
1618
- link2.href = this._url;
1619
- } else {
1620
- link2.onclick = () => {
1621
- view.dispatch({
1622
- selection: {
1623
- anchor: view.posAtDOM(link2)
1624
- }
1625
- });
1626
- };
1627
- }
1628
- return link2;
1629
- }
1630
- };
1631
- var buildDecorations2 = (view, options) => {
1632
- const builder = new RangeSetBuilder();
1633
- const { state } = view;
1634
- const cursor = state.selection.main.head;
1635
- for (const { from, to } of view.visibleRanges) {
1636
- syntaxTree2(state).iterate({
1637
- enter: (node) => {
1638
- if (node.name === "Link") {
1639
- const marks2 = node.node.getChildren("LinkMark");
1640
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
1641
- const urlNode = node.node.getChild("URL");
1642
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
1643
- if (!url) {
1644
- return false;
1645
- }
1646
- if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
1647
- builder.add(node.from, node.to, Decoration6.replace({
1648
- widget: new LinkText(text, options.link ? url : void 0)
1649
- }));
1650
- }
1651
- if (options.onRender) {
1652
- builder.add(node.to, node.to, Decoration6.replace({
1653
- widget: new LinkButton(url, options.onRender)
1654
- }));
1655
- }
1656
- return false;
1657
- }
1658
- },
1659
- from,
1660
- to
1661
- });
1662
- }
1663
- return builder.finish();
1664
- };
1665
-
1666
- // packages/ui/react-ui-editor/src/extensions/listener.ts
1667
- import { EditorView as EditorView8 } from "@codemirror/view";
1668
- var listener = ({ onFocus, onChange }) => {
1669
- const extensions = [];
1670
- onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
1671
- onFocus(focused);
1672
- return null;
1673
- }));
1674
- onChange && extensions.push(EditorView8.updateListener.of((update2) => {
1675
- onChange(update2.state.doc.toString());
1676
- }));
1677
- return extensions;
1678
- };
1679
-
1680
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1681
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1682
- import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1683
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1684
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1685
- import { languages } from "@codemirror/language-data";
1686
- import { searchKeymap } from "@codemirror/search";
1687
- import { EditorState } from "@codemirror/state";
1688
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1689
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1690
-
1691
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1692
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1693
- import { HighlightStyle } from "@codemirror/language";
1694
- import { tags, styleTags, Tag } from "@lezer/highlight";
1695
- import { Table } from "@lezer/markdown";
1696
- var markdownTags = {
1697
- Blockquote: Tag.define(),
1698
- CodeMark: Tag.define(),
1699
- CodeText: Tag.define(),
1700
- EmphasisMark: Tag.define(),
1701
- HeaderMark: Tag.define(),
1702
- InlineCode: Tag.define(),
1703
- LinkLabel: Tag.define(),
1704
- LinkReference: Tag.define(),
1705
- ListMark: Tag.define(),
1706
- QuoteMark: Tag.define(),
1707
- URL: Tag.define(),
1708
- // Custom.
1709
- TableCell: Tag.define()
1710
- };
1711
- Table.defineNodes?.forEach((node) => {
1712
- switch (node?.name) {
1713
- case "TableCell": {
1714
- node.style = markdownTags.TableCell;
1715
- break;
1716
- }
1717
- }
1718
- });
1719
- var markdownTagsExtensions = [
1720
- Table,
1721
- {
1722
- props: [
1723
- styleTags(markdownTags)
1724
- ]
1484
+ var markdownTagsExtensions = [
1485
+ Table,
1486
+ {
1487
+ props: [
1488
+ styleTags(markdownTags)
1489
+ ]
1725
1490
  }
1726
1491
  ];
1727
1492
  var markdownHighlightStyle = (readonly) => {
@@ -1783,7 +1548,7 @@ var markdownHighlightStyle = (readonly) => {
1783
1548
  markdownTags.QuoteMark,
1784
1549
  markdownTags.EmphasisMark
1785
1550
  ],
1786
- class: readonly ? "hidden" : mark
1551
+ class: mark
1787
1552
  },
1788
1553
  // E.g., code block language (after ```).
1789
1554
  {
@@ -1791,7 +1556,7 @@ var markdownHighlightStyle = (readonly) => {
1791
1556
  tags.function(tags.variableName),
1792
1557
  tags.labelName
1793
1558
  ],
1794
- class: readonly ? "hidden" : codeMark
1559
+ class: codeMark
1795
1560
  },
1796
1561
  {
1797
1562
  tag: [
@@ -1802,27 +1567,27 @@ var markdownHighlightStyle = (readonly) => {
1802
1567
  // Headings.
1803
1568
  {
1804
1569
  tag: tags.heading1,
1805
- class: heading(1, readonly)
1570
+ class: heading(1)
1806
1571
  },
1807
1572
  {
1808
1573
  tag: tags.heading2,
1809
- class: heading(2, readonly)
1574
+ class: heading(2)
1810
1575
  },
1811
1576
  {
1812
1577
  tag: tags.heading3,
1813
- class: heading(3, readonly)
1578
+ class: heading(3)
1814
1579
  },
1815
1580
  {
1816
1581
  tag: tags.heading4,
1817
- class: heading(4, readonly)
1582
+ class: heading(4)
1818
1583
  },
1819
1584
  {
1820
1585
  tag: tags.heading5,
1821
- class: heading(5, readonly)
1586
+ class: heading(5)
1822
1587
  },
1823
1588
  {
1824
1589
  tag: tags.heading6,
1825
- class: heading(6, readonly)
1590
+ class: heading(6)
1826
1591
  },
1827
1592
  // Emphasis.
1828
1593
  {
@@ -1848,7 +1613,7 @@ var markdownHighlightStyle = (readonly) => {
1848
1613
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
1849
1614
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
1850
1615
  // the code, but all other CSS properties will be inherited.
1851
- // IMPORTANT: Therefore, the fenced code block will use the base editor font.
1616
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
1852
1617
  {
1853
1618
  tag: [
1854
1619
  markdownTags.CodeText,
@@ -1877,21 +1642,21 @@ var markdownHighlightStyle = (readonly) => {
1877
1642
  };
1878
1643
 
1879
1644
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1880
- var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1645
+ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1881
1646
  return [
1882
1647
  EditorState.allowMultipleSelections.of(true),
1883
1648
  EditorState.tabSize.of(2),
1884
- EditorView9.lineWrapping,
1885
- customKeymap(),
1649
+ EditorView7.lineWrapping,
1650
+ // https://github.com/codemirror/basic-setup
1886
1651
  bracketMatching2(),
1887
1652
  closeBrackets2(),
1888
1653
  crosshairCursor(),
1889
1654
  dropCursor(),
1890
1655
  drawSelection2(),
1891
- highlightActiveLine(),
1892
- history(),
1656
+ highlightActiveLine2(),
1657
+ history2(),
1893
1658
  indentOnInput(),
1894
- _placeholder && placeholder2(_placeholder),
1659
+ _placeholder ? placeholder2(_placeholder) : [],
1895
1660
  // Main extension.
1896
1661
  // https://github.com/codemirror/lang-markdown
1897
1662
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1913,63 +1678,575 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1913
1678
  // https://github.com/codemirror/theme-one-dark
1914
1679
  themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
1915
1680
  // Custom styles.
1916
- syntaxHighlighting2(markdownHighlightStyle(readonly))
1917
- ].filter(Boolean);
1918
- };
1919
- var customKeymap = () => keymap4.of([
1920
- // https://codemirror.net/docs/ref/#commands.indentWithTab
1921
- indentWithTab,
1922
- // https://codemirror.net/docs/ref/#commands.standardKeymap
1923
- ...standardKeymap,
1924
- // https://codemirror.net/docs/ref/#commands.historyKeymap
1925
- ...historyKeymap,
1926
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1927
- ...closeBracketsKeymap,
1928
- // https://codemirror.net/docs/ref/#search.searchKeymap
1929
- ...searchKeymap
1930
- ]);
1681
+ syntaxHighlighting2(markdownHighlightStyle()),
1682
+ keymap4.of([
1683
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
1684
+ indentWithTab,
1685
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
1686
+ ...closeBracketsKeymap,
1687
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
1688
+ ...historyKeymap,
1689
+ // https://codemirror.net/docs/ref/#search.searchKeymap
1690
+ ...searchKeymap,
1691
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
1692
+ ...standardKeymap
1693
+ ])
1694
+ ];
1695
+ };
1931
1696
 
1932
- // packages/ui/react-ui-editor/src/extensions/mention.ts
1933
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
1934
- var mention = ({ onSearch }) => {
1935
- return autocompletion2({
1936
- closeOnBlur: false,
1937
- override: [
1938
- (context) => {
1939
- const match = context.matchBefore(/@(\w+)?/);
1940
- if (!match || match.from === match.to && !context.explicit) {
1941
- return null;
1942
- }
1943
- console.log(match);
1944
- return {
1945
- from: match.from,
1946
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
1947
- label: `@${value}`
1948
- }))
1949
- };
1950
- }
1951
- ]
1952
- });
1697
+ // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1698
+ import { syntaxTree } from "@codemirror/language";
1699
+ import { RangeSetBuilder } from "@codemirror/state";
1700
+ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Decoration3, WidgetType as WidgetType2 } from "@codemirror/view";
1701
+ import { mx as mx2 } from "@dxos/react-ui-theme";
1702
+ var styles3 = EditorView8.baseTheme({
1703
+ "& .cm-code": {
1704
+ paddingInline: "1rem !important",
1705
+ fontFamily: getToken("fontFamily.mono", []).join(",")
1706
+ },
1707
+ "& .cm-codeblock": {
1708
+ display: "inline-block",
1709
+ width: "100%",
1710
+ position: "relative",
1711
+ "&::after": {
1712
+ content: '""',
1713
+ position: "absolute",
1714
+ inset: 0
1715
+ }
1716
+ },
1717
+ "&light .cm-codeblock": {
1718
+ "&::after": {
1719
+ background: getToken("extend.semanticColors.input.light"),
1720
+ mixBlendMode: "darken"
1721
+ }
1722
+ },
1723
+ "&dark .cm-codeblock": {
1724
+ "&::after": {
1725
+ background: getToken("extend.semanticColors.input.dark"),
1726
+ mixBlendMode: "lighten"
1727
+ }
1728
+ },
1729
+ "& .cm-codeblock-first": {
1730
+ "&::after": {
1731
+ borderTopLeftRadius: ".5rem",
1732
+ borderTopRightRadius: ".5rem"
1733
+ }
1734
+ },
1735
+ "& .cm-codeblock-last": {
1736
+ "&::after": {
1737
+ borderBottomLeftRadius: ".5rem",
1738
+ borderBottomRightRadius: ".5rem"
1739
+ }
1740
+ }
1741
+ });
1742
+ var getLineRange = (lines, from, to) => {
1743
+ const start = lines.findIndex((line) => line.from >= from);
1744
+ const end = lines.findIndex((line) => line.to >= to);
1745
+ return [
1746
+ start,
1747
+ end
1748
+ ];
1749
+ };
1750
+ var LineWidget = class extends WidgetType2 {
1751
+ constructor(_className) {
1752
+ super();
1753
+ this._className = _className;
1754
+ }
1755
+ toDOM() {
1756
+ const el = document.createElement("span");
1757
+ el.innerHTML = "&nbsp;";
1758
+ el.className = mx2("cm-code cm-codeblock", this._className);
1759
+ return el;
1760
+ }
1761
+ };
1762
+ var top = new LineWidget("cm-codeblock-first");
1763
+ var bottom = new LineWidget("cm-codeblock-last");
1764
+ var buildDecorations = (view) => {
1765
+ const builder = new RangeSetBuilder();
1766
+ const { state } = view;
1767
+ const blocks = view.viewportLineBlocks;
1768
+ const cursor = state.selection.main.head;
1769
+ for (const { from, to } of view.visibleRanges) {
1770
+ syntaxTree(state).iterate({
1771
+ enter: (node) => {
1772
+ if (node.name === "FencedCode") {
1773
+ const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
1774
+ const range = getLineRange(blocks, node.from, node.to);
1775
+ for (let i = range[0]; i <= range[1]; i++) {
1776
+ const block = blocks[i];
1777
+ if (!edit && (i === range[0] || i === range[1])) {
1778
+ builder.add(block.from, block.to, Decoration3.replace({
1779
+ widget: i === range[0] ? top : bottom
1780
+ }));
1781
+ } else {
1782
+ builder.add(block.from, block.from, Decoration3.line({
1783
+ class: mx2("cm-code cm-codeblock")
1784
+ }));
1785
+ }
1786
+ }
1787
+ }
1788
+ },
1789
+ from,
1790
+ to
1791
+ });
1792
+ }
1793
+ return builder.finish();
1794
+ };
1795
+ var code2 = () => {
1796
+ return [
1797
+ ViewPlugin3.fromClass(class {
1798
+ constructor(view) {
1799
+ this.decorations = buildDecorations(view);
1800
+ }
1801
+ update(update2) {
1802
+ this.decorations = buildDecorations(update2.view);
1803
+ }
1804
+ }, {
1805
+ decorations: (value) => value.decorations
1806
+ }),
1807
+ styles3
1808
+ ];
1809
+ };
1810
+
1811
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1812
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1813
+ import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1814
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1815
+ var setHeading = (level) => (view) => {
1816
+ const { selection: { ranges }, doc } = view.state;
1817
+ const changes = [];
1818
+ for (const range of ranges) {
1819
+ const { number } = doc.lineAt(range.anchor);
1820
+ const { from } = doc.line(number);
1821
+ const line = doc.sliceString(from, from + 7);
1822
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1823
+ const current = marks?.length ?? 0;
1824
+ if (level !== current) {
1825
+ changes.push({
1826
+ from,
1827
+ to: from + current + (spaces?.length ?? 0),
1828
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1829
+ });
1830
+ }
1831
+ }
1832
+ if (changes.length) {
1833
+ view.dispatch({
1834
+ changes
1835
+ });
1836
+ }
1837
+ return true;
1838
+ };
1839
+ var toggleStyle = (mark2) => (view) => {
1840
+ const { ranges } = view.state.selection;
1841
+ for (const range of ranges) {
1842
+ if (range.from === range.to) {
1843
+ return false;
1844
+ }
1845
+ view.dispatch({
1846
+ changes: [
1847
+ {
1848
+ from: range.from,
1849
+ insert: mark2
1850
+ },
1851
+ {
1852
+ from: range.to,
1853
+ insert: mark2
1854
+ }
1855
+ ]
1856
+ });
1857
+ }
1858
+ return true;
1859
+ };
1860
+ var toggleBold = toggleStyle("**");
1861
+ var toggleItalic = toggleStyle("_");
1862
+ var toggleStrikethrough = toggleStyle("~~");
1863
+ var toggleList = (view) => {
1864
+ };
1865
+ var formatting = (options = {}) => {
1866
+ return [
1867
+ keymap5.of([
1868
+ {
1869
+ key: "meta-b",
1870
+ run: toggleBold
1871
+ }
1872
+ ]),
1873
+ styling()
1874
+ ];
1875
+ };
1876
+ var styling = () => {
1877
+ const buildDecorations7 = (view) => {
1878
+ const builder = new RangeSetBuilder2();
1879
+ const { state } = view;
1880
+ const cursor = state.selection.main.head;
1881
+ const replace = (node, marks) => {
1882
+ if (cursor <= node.from || cursor >= node.to) {
1883
+ for (const mark2 of marks) {
1884
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1885
+ }
1886
+ }
1887
+ };
1888
+ for (const { from, to } of view.visibleRanges) {
1889
+ syntaxTree2(state).iterate({
1890
+ enter: (node) => {
1891
+ switch (node.name) {
1892
+ case "Emphasis":
1893
+ case "StrongEmphasis": {
1894
+ const marks = node.node.getChildren("EmphasisMark");
1895
+ replace(node, marks);
1896
+ break;
1897
+ }
1898
+ case "Strikethrough": {
1899
+ const marks = node.node.getChildren("StrikethroughMark");
1900
+ replace(node, marks);
1901
+ break;
1902
+ }
1903
+ }
1904
+ },
1905
+ from,
1906
+ to
1907
+ });
1908
+ }
1909
+ return builder.finish();
1910
+ };
1911
+ return [
1912
+ ViewPlugin4.fromClass(class {
1913
+ constructor(view) {
1914
+ this.decorations = buildDecorations7(view);
1915
+ }
1916
+ update(update2) {
1917
+ this.decorations = buildDecorations7(update2.view);
1918
+ }
1919
+ }, {
1920
+ decorations: (value) => value.decorations
1921
+ })
1922
+ ];
1923
+ };
1924
+
1925
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1926
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1927
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1928
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1929
+ var buildDecorations2 = (view) => {
1930
+ const builder = new RangeSetBuilder3();
1931
+ const { state } = view;
1932
+ const cursor = state.selection.main.head;
1933
+ for (const { from, to } of view.visibleRanges) {
1934
+ syntaxTree3(state).iterate({
1935
+ enter: (node) => {
1936
+ switch (node.name) {
1937
+ case "ATXHeading1":
1938
+ case "ATXHeading2":
1939
+ case "ATXHeading3":
1940
+ case "ATXHeading4":
1941
+ case "ATXHeading5":
1942
+ case "ATXHeading6": {
1943
+ const mark2 = node.node.getChild("HeaderMark");
1944
+ if (mark2) {
1945
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1946
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1947
+ }
1948
+ }
1949
+ }
1950
+ }
1951
+ },
1952
+ from,
1953
+ to
1954
+ });
1955
+ }
1956
+ return builder.finish();
1957
+ };
1958
+ var heading2 = () => {
1959
+ return [
1960
+ ViewPlugin5.fromClass(class {
1961
+ constructor(view) {
1962
+ this.decorations = buildDecorations2(view);
1963
+ }
1964
+ update(update2) {
1965
+ this.decorations = buildDecorations2(update2.view);
1966
+ }
1967
+ }, {
1968
+ decorations: (value) => value.decorations
1969
+ })
1970
+ ];
1971
+ };
1972
+
1973
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
1974
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
1975
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
1976
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1977
+ var styles4 = EditorView9.baseTheme({
1978
+ "& .cm-hr": {
1979
+ // Note that block-level decorations should not have vertical margins,
1980
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
1981
+ }
1982
+ });
1983
+ var HorizontalRuleWidget = class extends WidgetType3 {
1984
+ toDOM() {
1985
+ const el = document.createElement("div");
1986
+ el.className = "cm-hr";
1987
+ return el;
1988
+ }
1989
+ };
1990
+ var decoration = Decoration6.replace({
1991
+ widget: new HorizontalRuleWidget()
1992
+ });
1993
+ var buildDecorations3 = (view) => {
1994
+ const builder = new RangeSetBuilder4();
1995
+ const { state } = view;
1996
+ const cursor = state.selection.main.head;
1997
+ for (const { from, to } of view.visibleRanges) {
1998
+ syntaxTree4(state).iterate({
1999
+ enter: (node) => {
2000
+ if (node.name === "HorizontalRule") {
2001
+ if (cursor <= node.from || cursor >= node.to) {
2002
+ builder.add(node.from, node.to, decoration);
2003
+ }
2004
+ }
2005
+ },
2006
+ from,
2007
+ to
2008
+ });
2009
+ }
2010
+ return builder.finish();
2011
+ };
2012
+ var hr = () => {
2013
+ return [
2014
+ ViewPlugin6.fromClass(class {
2015
+ constructor(view) {
2016
+ this.decorations = buildDecorations3(view);
2017
+ }
2018
+ update(update2) {
2019
+ this.decorations = buildDecorations3(update2.view);
2020
+ }
2021
+ }, {
2022
+ decorations: (value) => value.decorations
2023
+ }),
2024
+ styles4
2025
+ ];
2026
+ };
2027
+
2028
+ // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
2029
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2030
+ import { StateField as StateField3 } from "@codemirror/state";
2031
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2032
+ var image = (options = {}) => {
2033
+ return StateField3.define({
2034
+ create: (state) => {
2035
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
2036
+ },
2037
+ update: (value, tr) => {
2038
+ if (!tr.docChanged && !tr.selection) {
2039
+ return value;
2040
+ }
2041
+ const cursor = tr.state.selection.main.head;
2042
+ const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
2043
+ let from = Math.min(cursor, oldCursor);
2044
+ let to = Math.max(cursor, oldCursor);
2045
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
2046
+ from = Math.min(from, fromB);
2047
+ to = Math.max(to, toB);
2048
+ });
2049
+ from = tr.state.doc.lineAt(from).from;
2050
+ to = tr.state.doc.lineAt(to).to;
2051
+ return value.map(tr.changes).update({
2052
+ filterFrom: from,
2053
+ filterTo: to,
2054
+ filter: () => false,
2055
+ add: buildDecorations4(from, to, tr.state)
2056
+ });
2057
+ },
2058
+ provide: (field) => EditorView10.decorations.from(field)
2059
+ });
2060
+ };
2061
+ var buildDecorations4 = (from, to, state) => {
2062
+ const decorations = [];
2063
+ const cursor = state.selection.main.head;
2064
+ syntaxTree5(state).iterate({
2065
+ enter: (node) => {
2066
+ if (node.name === "Image") {
2067
+ const urlNode = node.node.getChild("URL");
2068
+ if (urlNode) {
2069
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
2070
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
2071
+ decorations.push(Decoration7.replace({
2072
+ block: true,
2073
+ widget: new ImageWidget(url)
2074
+ }).range(hide ? node.from : node.to, node.to));
2075
+ }
2076
+ }
2077
+ },
2078
+ from,
2079
+ to
2080
+ });
2081
+ return decorations;
2082
+ };
2083
+ var ImageWidget = class extends WidgetType4 {
2084
+ constructor(_url) {
2085
+ super();
2086
+ this._url = _url;
2087
+ }
2088
+ eq(other) {
2089
+ return this._url === other._url;
2090
+ }
2091
+ toDOM(view) {
2092
+ const img = document.createElement("img");
2093
+ img.setAttribute("src", this._url);
2094
+ img.setAttribute("class", "cm-image");
2095
+ img.onload = () => img.classList.add("cm-loaded-image");
2096
+ return img;
2097
+ }
2098
+ };
2099
+
2100
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
2101
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2102
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2103
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
2104
+ import { tooltipContent } from "@dxos/react-ui-theme";
2105
+ var link = (options = {}) => {
2106
+ const extensions = [
2107
+ ViewPlugin7.fromClass(class {
2108
+ constructor(view) {
2109
+ this.decorations = buildDecorations5(view, options);
2110
+ }
2111
+ update(update2) {
2112
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2113
+ this.decorations = buildDecorations5(update2.view, options);
2114
+ }
2115
+ }
2116
+ }, {
2117
+ decorations: (value) => value.decorations
2118
+ })
2119
+ ];
2120
+ if (options.onHover) {
2121
+ extensions.push(
2122
+ // https://codemirror.net/examples/tooltip
2123
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
2124
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
2125
+ hoverTooltip2((view, pos, side) => {
2126
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
2127
+ let link2 = null;
2128
+ for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
2129
+ link2 = node.name === "Link" ? node : null;
2130
+ }
2131
+ const url = link2 && link2.getChild("URL");
2132
+ if (!url || !link2) {
2133
+ return null;
2134
+ }
2135
+ const urlText = view.state.sliceDoc(url.from, url.to);
2136
+ return {
2137
+ pos: link2.from,
2138
+ end: link2.to,
2139
+ above: true,
2140
+ create: () => {
2141
+ const el = document.createElement("div");
2142
+ el.className = tooltipContent({}, "pli-2 plb-1");
2143
+ options.onHover(el, urlText);
2144
+ return {
2145
+ dom: el,
2146
+ offset: {
2147
+ x: 0,
2148
+ y: 4
2149
+ }
2150
+ };
2151
+ }
2152
+ };
2153
+ })
2154
+ );
2155
+ }
2156
+ return extensions;
2157
+ };
2158
+ var LinkButton = class extends WidgetType5 {
2159
+ constructor(_url, _onAttach) {
2160
+ super();
2161
+ this._url = _url;
2162
+ this._onAttach = _onAttach;
2163
+ }
2164
+ eq(other) {
2165
+ return this._url === other._url;
2166
+ }
2167
+ toDOM(view) {
2168
+ const el = document.createElement("span");
2169
+ this._onAttach(el, this._url);
2170
+ return el;
2171
+ }
2172
+ };
2173
+ var LinkText = class extends WidgetType5 {
2174
+ constructor(_text, _url) {
2175
+ super();
2176
+ this._text = _text;
2177
+ this._url = _url;
2178
+ }
2179
+ eq(other) {
2180
+ return this._url === other._url;
2181
+ }
2182
+ toDOM(view) {
2183
+ const link2 = document.createElement("a");
2184
+ link2.setAttribute("class", "cm-link");
2185
+ link2.textContent = this._text;
2186
+ if (this._url) {
2187
+ link2.setAttribute("rel", "noreferrer");
2188
+ link2.setAttribute("target", "_blank");
2189
+ link2.href = this._url;
2190
+ } else {
2191
+ link2.onclick = () => {
2192
+ view.dispatch({
2193
+ selection: {
2194
+ anchor: view.posAtDOM(link2)
2195
+ }
2196
+ });
2197
+ };
2198
+ }
2199
+ return link2;
2200
+ }
1953
2201
  };
1954
-
1955
- // packages/ui/react-ui-editor/src/extensions/outliner.ts
1956
- var outliner = (options = {}) => {
1957
- return [];
2202
+ var buildDecorations5 = (view, options) => {
2203
+ const builder = new RangeSetBuilder5();
2204
+ const { state } = view;
2205
+ const cursor = state.selection.main.head;
2206
+ for (const { from, to } of view.visibleRanges) {
2207
+ syntaxTree6(state).iterate({
2208
+ enter: (node) => {
2209
+ if (node.name === "Link") {
2210
+ const marks = node.node.getChildren("LinkMark");
2211
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2212
+ const urlNode = node.node.getChild("URL");
2213
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2214
+ if (!url) {
2215
+ return false;
2216
+ }
2217
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2218
+ builder.add(node.from, node.to, Decoration8.replace({
2219
+ widget: new LinkText(text, options.link ? url : void 0)
2220
+ }));
2221
+ }
2222
+ if (options.onRender) {
2223
+ builder.add(node.to, node.to, Decoration8.replace({
2224
+ widget: new LinkButton(url, options.onRender)
2225
+ }));
2226
+ }
2227
+ return false;
2228
+ }
2229
+ },
2230
+ from,
2231
+ to
2232
+ });
2233
+ }
2234
+ return builder.finish();
1958
2235
  };
1959
2236
 
1960
- // packages/ui/react-ui-editor/src/extensions/table.ts
1961
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1962
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
1963
- import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
2237
+ // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2238
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2239
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2240
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
1964
2241
  var table = (options = {}) => {
1965
2242
  return StateField4.define({
1966
2243
  create: (state) => update(state, options),
1967
2244
  update: (_, tr) => update(tr.state, options),
1968
- provide: (field) => EditorView10.decorations.from(field)
2245
+ provide: (field) => EditorView11.decorations.from(field)
1969
2246
  });
1970
2247
  };
1971
2248
  var update = (state, options) => {
1972
- const builder = new RangeSetBuilder2();
2249
+ const builder = new RangeSetBuilder6();
1973
2250
  const cursor = state.selection.main.head;
1974
2251
  const tables = [];
1975
2252
  const getTable = () => tables[tables.length - 1];
@@ -1977,7 +2254,7 @@ var update = (state, options) => {
1977
2254
  const table2 = getTable();
1978
2255
  return table2.rows?.[table2.rows.length - 1];
1979
2256
  };
1980
- syntaxTree3(state).iterate({
2257
+ syntaxTree7(state).iterate({
1981
2258
  enter: (node) => {
1982
2259
  switch (node.name) {
1983
2260
  case "Table": {
@@ -2009,16 +2286,16 @@ var update = (state, options) => {
2009
2286
  });
2010
2287
  tables.forEach((table2) => {
2011
2288
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2012
- hide && builder.add(table2.from, table2.to, Decoration7.replace({
2289
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2013
2290
  widget: new TableWidget(table2)
2014
2291
  }));
2015
- builder.add(table2.from, table2.to, Decoration7.mark({
2292
+ builder.add(table2.from, table2.to, Decoration9.mark({
2016
2293
  class: "cm-table"
2017
2294
  }));
2018
2295
  });
2019
2296
  return builder.finish();
2020
2297
  };
2021
- var TableWidget = class extends WidgetType5 {
2298
+ var TableWidget = class extends WidgetType6 {
2022
2299
  constructor(_table) {
2023
2300
  super();
2024
2301
  this._table = _table;
@@ -2055,11 +2332,20 @@ var TableWidget = class extends WidgetType5 {
2055
2332
  }
2056
2333
  };
2057
2334
 
2058
- // packages/ui/react-ui-editor/src/extensions/tasklist.ts
2059
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2060
- import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
2061
- import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2062
- var CheckboxWidget = class extends WidgetType6 {
2335
+ // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2336
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2337
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2338
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2339
+ var styles5 = EditorView12.baseTheme({
2340
+ "& .cm-task": {
2341
+ color: getToken("extend.colors.blue.500")
2342
+ },
2343
+ "& .cm-task-checkbox": {
2344
+ marginLeft: "4px",
2345
+ marginRight: "4px"
2346
+ }
2347
+ });
2348
+ var CheckboxWidget = class extends WidgetType7 {
2063
2349
  constructor(_checked) {
2064
2350
  super();
2065
2351
  this._checked = _checked;
@@ -2069,6 +2355,7 @@ var CheckboxWidget = class extends WidgetType6 {
2069
2355
  }
2070
2356
  toDOM(view) {
2071
2357
  const input = document.createElement("input");
2358
+ input.className = "cm-task-checkbox";
2072
2359
  input.type = "checkbox";
2073
2360
  input.checked = this._checked;
2074
2361
  if (view.state.readOnly) {
@@ -2095,40 +2382,27 @@ var CheckboxWidget = class extends WidgetType6 {
2095
2382
  return false;
2096
2383
  }
2097
2384
  };
2098
- var checkedDeco = Decoration8.replace({
2385
+ var checkedDecoration = Decoration10.replace({
2099
2386
  widget: new CheckboxWidget(true)
2100
2387
  });
2101
- var uncheckedDeco = Decoration8.replace({
2388
+ var uncheckedDecoration = Decoration10.replace({
2102
2389
  widget: new CheckboxWidget(false)
2103
2390
  });
2104
- var tasklist = (options = {}) => {
2105
- return ViewPlugin5.fromClass(class {
2106
- constructor(view) {
2107
- this.decorations = buildDecorations3(view);
2108
- }
2109
- update(update2) {
2110
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2111
- this.decorations = buildDecorations3(update2.view);
2112
- }
2113
- }
2114
- }, {
2115
- decorations: (v) => v.decorations,
2116
- // TODO(burdon): Is this still required?
2117
- provide: (plugin) => EditorView11.atomicRanges.of((view) => {
2118
- return view.plugin(plugin)?.decorations || Decoration8.none;
2119
- })
2120
- });
2121
- };
2122
- var buildDecorations3 = (view) => {
2123
- const builder = new RangeSetBuilder3();
2391
+ var buildDecorations6 = (view) => {
2392
+ const builder = new RangeSetBuilder7();
2124
2393
  const { state } = view;
2125
2394
  const cursor = state.selection.main.head;
2126
2395
  for (const { from, to } of view.visibleRanges) {
2127
- syntaxTree4(state).iterate({
2396
+ syntaxTree8(state).iterate({
2128
2397
  enter: (node) => {
2129
- if (node.name === "TaskMarker" && (cursor < node.from || cursor > node.to)) {
2130
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2131
- builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
2398
+ if (node.name === "TaskMarker") {
2399
+ if (cursor < node.from || cursor > node.to) {
2400
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2401
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2402
+ class: "cm-task"
2403
+ }));
2404
+ builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
2405
+ }
2132
2406
  }
2133
2407
  },
2134
2408
  from,
@@ -2137,9 +2411,60 @@ var buildDecorations3 = (view) => {
2137
2411
  }
2138
2412
  return builder.finish();
2139
2413
  };
2414
+ var tasklist = (options = {}) => {
2415
+ return [
2416
+ ViewPlugin8.fromClass(class {
2417
+ constructor(view) {
2418
+ this.decorations = buildDecorations6(view);
2419
+ }
2420
+ update(update2) {
2421
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2422
+ this.decorations = buildDecorations6(update2.view);
2423
+ }
2424
+ }
2425
+ }, {
2426
+ decorations: (value) => value.decorations
2427
+ }),
2428
+ styles5
2429
+ ];
2430
+ };
2431
+
2432
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
2433
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
2434
+ var mention = ({ onSearch }) => {
2435
+ return autocompletion2({
2436
+ // TODO(burdon): Not working.
2437
+ activateOnTyping: true,
2438
+ // activateOnTypingDelay: 100,
2439
+ // selectOnOpen: true,
2440
+ closeOnBlur: false,
2441
+ // defaultKeymap: false,
2442
+ icons: false,
2443
+ override: [
2444
+ (context) => {
2445
+ console.log(context);
2446
+ const match = context.matchBefore(/@(\w+)?/);
2447
+ if (!match || match.from === match.to && !context.explicit) {
2448
+ return null;
2449
+ }
2450
+ return {
2451
+ from: match.from,
2452
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
2453
+ label: `@${value}`
2454
+ }))
2455
+ };
2456
+ }
2457
+ ]
2458
+ });
2459
+ };
2460
+
2461
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
2462
+ var outliner = (options = {}) => {
2463
+ return [];
2464
+ };
2140
2465
 
2141
2466
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2142
- import { keymap as keymap5 } from "@codemirror/view";
2467
+ import { keymap as keymap6 } from "@codemirror/view";
2143
2468
  var defaultItems = [
2144
2469
  "hello world!",
2145
2470
  "this is a test.",
@@ -2149,7 +2474,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2149
2474
  let t;
2150
2475
  let idx = 0;
2151
2476
  return [
2152
- keymap5.of([
2477
+ keymap6.of([
2153
2478
  {
2154
2479
  // Reset.
2155
2480
  key: "alt-meta-'",
@@ -2195,27 +2520,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2195
2520
  ];
2196
2521
  };
2197
2522
 
2198
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2199
- import { yCollab } from "y-codemirror.next";
2200
-
2201
- // packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
2202
- import * as Y from "yjs";
2203
- import { arrayToString, stringToArray } from "@dxos/util";
2204
- var cursorConverter2 = (text) => ({
2205
- toCursor: (index, assoc) => {
2206
- return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
2207
- },
2208
- fromCursor: (cursor) => {
2209
- return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
2210
- }
2211
- });
2212
-
2213
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2214
- var yjs = (content, awareness2) => [
2215
- Cursor.converter.of(cursorConverter2(content)),
2216
- yCollab(content, awareness2)
2217
- ];
2218
-
2219
2523
  // packages/ui/react-ui-editor/src/themes/default.ts
2220
2524
  import get3 from "lodash.get";
2221
2525
  var defaultTheme = {
@@ -2282,7 +2586,7 @@ var defaultTheme = {
2282
2586
  paddingInline: 0
2283
2587
  },
2284
2588
  ".cm-activeLine": {
2285
- background: "inherit"
2589
+ background: "transparent"
2286
2590
  },
2287
2591
  //
2288
2592
  // Selection
@@ -2453,101 +2757,80 @@ var codeTheme = {
2453
2757
  };
2454
2758
 
2455
2759
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2760
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2456
2761
  var EditorModes = [
2457
2762
  "default",
2458
2763
  "vim"
2459
2764
  ];
2460
- var useComments = (view, comments2) => {
2461
- useEffect(() => {
2462
- if (view && comments2 !== void 0) {
2463
- view.dispatch({
2464
- effects: setComments.of(comments2)
2465
- });
2466
- }
2467
- }, [
2468
- view,
2469
- comments2
2470
- ]);
2471
- };
2472
- var useAwareness = ({ awareness: awareness2, peer }) => {
2473
- const { themeMode } = useThemeContext();
2474
- useEffect(() => {
2475
- if (awareness2 && peer) {
2476
- awareness2.setLocalStateField("user", {
2477
- name: peer.name ?? generateName(peer.id),
2478
- color: getColorForValue({
2479
- value: peer.id,
2480
- type: "color"
2481
- }),
2482
- colorLight: getColorForValue({
2483
- value: peer.id,
2484
- themeMode,
2485
- type: "highlight"
2486
- })
2487
- });
2488
- }
2489
- }, [
2490
- awareness2,
2491
- peer,
2492
- themeMode
2493
- ]);
2494
- };
2495
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, selection, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
2496
- const tabsterDOMAttribute = useFocusableGroup({
2497
- tabBehavior: "limited"
2498
- });
2765
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2499
2766
  const { themeMode } = useThemeContext();
2500
2767
  const rootRef = useRef(null);
2501
2768
  const [view, setView] = useState(null);
2502
2769
  useImperativeHandle(forwardedRef, () => view, [
2503
2770
  view
2504
2771
  ]);
2505
- useEffect(() => {
2506
- if (autofocus) {
2772
+ useEffect2(() => {
2773
+ if (autoFocus) {
2507
2774
  view?.focus();
2508
2775
  }
2509
2776
  }, [
2510
2777
  view,
2511
- autofocus
2778
+ autoFocus
2512
2779
  ]);
2513
- useAwareness(model);
2514
- useComments(view, comments2);
2515
- useEffect(() => {
2780
+ useEffect2(() => {
2516
2781
  if (!model || !rootRef.current) {
2517
2782
  return;
2518
2783
  }
2519
2784
  const state = EditorState2.create({
2520
2785
  doc: model.text(),
2521
- // TODO(burdon): Composer should store and set selection when switching documents.
2522
2786
  selection,
2523
2787
  extensions: [
2524
- readonly && EditorState2.readOnly.of(readonly),
2788
+ // TODO(burdon): Doesn't catch errors in keymap functions.
2789
+ EditorView13.exceptionSink.of((err) => {
2790
+ log2.catch(err, void 0, {
2791
+ F: __dxlog_file3,
2792
+ L: 129,
2793
+ S: void 0,
2794
+ C: (f, a) => f(...a)
2795
+ });
2796
+ }),
2525
2797
  // Theme.
2526
- // TODO(burdon): Make theme configurable.
2527
- EditorView12.baseTheme(defaultTheme),
2528
- EditorView12.theme(theme ?? {}),
2529
- EditorView12.darkTheme.of(themeMode === "dark"),
2530
- // TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
2531
- editorMode === "vim" && vim(),
2532
- // Storage and replication (NOTE: must come before other extensions).
2798
+ // TODO(burdon): Make configurable.
2799
+ EditorView13.baseTheme(defaultTheme),
2800
+ EditorView13.theme(theme ?? {}),
2801
+ EditorView13.darkTheme.of(themeMode === "dark"),
2802
+ EditorView13.editorAttributes.of({
2803
+ class: slots.editor?.className ?? ""
2804
+ }),
2805
+ EditorView13.contentAttributes.of({
2806
+ class: slots.content?.className ?? ""
2807
+ }),
2808
+ // State.
2809
+ EditorView13.editable.of(!readonly),
2810
+ EditorState2.readOnly.of(!!readonly),
2811
+ // Storage and replication.
2812
+ // NOTE: This must come before user extensions.
2533
2813
  model.extension,
2814
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2815
+ editorMode === "vim" && vim(),
2534
2816
  // Custom.
2535
2817
  ...extensions
2536
- ].filter(Boolean)
2818
+ ].filter(isNotFalsy)
2537
2819
  });
2538
- view?.destroy();
2539
- const newView = new EditorView12({
2540
- parent: rootRef.current,
2820
+ const newView = new EditorView13({
2541
2821
  state,
2542
- // NOTE: Uncomment to spy on all transactions.
2822
+ parent: rootRef.current,
2823
+ scrollTo,
2824
+ // NOTE: Uncomment to debug/monitor all transactions.
2543
2825
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
2544
- dispatch: (transaction, view2) => {
2545
- view2.update([
2546
- transaction
2547
- ]);
2548
- view2.state.facet(syncFacet)?.reconcile(view2);
2826
+ dispatchTransactions: (trs, view2) => {
2827
+ if (debug) {
2828
+ logChanges(trs);
2829
+ }
2830
+ view2.update(trs);
2549
2831
  }
2550
2832
  });
2833
+ view?.destroy();
2551
2834
  setView(newView);
2552
2835
  return () => {
2553
2836
  newView?.destroy();
@@ -2563,9 +2846,13 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, s
2563
2846
  const handleKeyUp = useCallback((event) => {
2564
2847
  const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
2565
2848
  switch (key) {
2849
+ case "Enter": {
2850
+ view?.focus();
2851
+ break;
2852
+ }
2566
2853
  case "Escape": {
2567
2854
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2568
- rootRef.current?.focus();
2855
+ view?.focus();
2569
2856
  }
2570
2857
  break;
2571
2858
  }
@@ -2578,13 +2865,13 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, s
2578
2865
  key: model.id,
2579
2866
  role: "none",
2580
2867
  tabIndex: 0,
2581
- onKeyUp: handleKeyUp,
2582
2868
  ...slots.root,
2583
- ...editorMode !== "vim" && tabsterDOMAttribute,
2869
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2870
+ onKeyUp: handleKeyUp,
2584
2871
  ref: rootRef
2585
2872
  });
2586
2873
  });
2587
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
2874
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
2588
2875
  const { themeMode } = useThemeContext();
2589
2876
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
2590
2877
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2592,9 +2879,9 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2592
2879
  readonly,
2593
2880
  extensions: [
2594
2881
  basicBundle({
2595
- readonly,
2596
2882
  themeMode,
2597
- placeholder: placeholder3
2883
+ placeholder: placeholder3,
2884
+ lineWrapping
2598
2885
  }),
2599
2886
  ...extensions
2600
2887
  ],
@@ -2603,7 +2890,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2603
2890
  ...props
2604
2891
  });
2605
2892
  });
2606
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
2893
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
2607
2894
  const { themeMode } = useThemeContext();
2608
2895
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
2609
2896
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
@@ -2611,7 +2898,6 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2611
2898
  readonly,
2612
2899
  extensions: [
2613
2900
  markdownBundle({
2614
- readonly,
2615
2901
  themeMode,
2616
2902
  placeholder: placeholder3
2617
2903
  }),
@@ -2624,7 +2910,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2624
2910
  });
2625
2911
  var defaultSlots = {
2626
2912
  root: {
2627
- className: mx3("w-full p-2 overflow-y-auto", inputSurface)
2913
+ // TODO(burdon): Add focusRing by default/as property?
2914
+ className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
2915
+ },
2916
+ editor: {
2917
+ className: "h-full p-2"
2628
2918
  }
2629
2919
  };
2630
2920
  var defaultTextSlots = {
@@ -2634,22 +2924,17 @@ var defaultMarkdownSlots = {
2634
2924
  ...defaultSlots
2635
2925
  };
2636
2926
 
2637
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2638
- import get4 from "lodash.get";
2639
- import { getRawDoc } from "@dxos/echo-schema";
2640
- import { isNotNullOrUndefined } from "@dxos/util";
2641
-
2642
2927
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2643
2928
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2644
2929
  import { Context as Context2 } from "@dxos/context";
2645
- import { invariant as invariant3 } from "@dxos/invariant";
2646
- import { log as log2 } from "@dxos/log";
2930
+ import { invariant as invariant2 } from "@dxos/invariant";
2931
+ import { log as log3 } from "@dxos/log";
2647
2932
  var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2648
2933
  var DEBOUNCE_INTERVAL = 100;
2649
2934
  var SpaceAwarenessProvider = class {
2650
2935
  constructor(params) {
2651
- this.remoteStateChange = new Event2();
2652
2936
  this._remoteStates = /* @__PURE__ */ new Map();
2937
+ this.remoteStateChange = new Event2();
2653
2938
  this._space = params.space;
2654
2939
  this._channel = params.channel;
2655
2940
  this._peerId = params.peerId;
@@ -2666,7 +2951,7 @@ var SpaceAwarenessProvider = class {
2666
2951
  await sleep(DEBOUNCE_INTERVAL);
2667
2952
  }
2668
2953
  });
2669
- const unsubscribe = this._space.listen(this._channel, (message) => {
2954
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
2670
2955
  switch (message.payload.kind) {
2671
2956
  case "query": {
2672
2957
  this._handleQueryMessage();
@@ -2677,16 +2962,15 @@ var SpaceAwarenessProvider = class {
2677
2962
  break;
2678
2963
  }
2679
2964
  }
2680
- });
2681
- this._ctx.onDispose(unsubscribe);
2965
+ }));
2682
2966
  void this._space.postMessage(this._channel, {
2683
2967
  kind: "query"
2684
2968
  }).catch((err) => {
2685
- log2.debug("failed to query awareness", {
2969
+ log3.debug("failed to query awareness", {
2686
2970
  err
2687
2971
  }, {
2688
2972
  F: __dxlog_file4,
2689
- L: 85,
2973
+ L: 89,
2690
2974
  S: this,
2691
2975
  C: (f, a) => f(...a)
2692
2976
  });
@@ -2697,10 +2981,13 @@ var SpaceAwarenessProvider = class {
2697
2981
  this._ctx = void 0;
2698
2982
  this._postTask = void 0;
2699
2983
  }
2984
+ getRemoteStates() {
2985
+ return Array.from(this._remoteStates.values());
2986
+ }
2700
2987
  update(position) {
2701
- invariant3(this._postTask, void 0, {
2988
+ invariant2(this._postTask, void 0, {
2702
2989
  F: __dxlog_file4,
2703
- L: 96,
2990
+ L: 104,
2704
2991
  S: this,
2705
2992
  A: [
2706
2993
  "this._postTask",
@@ -2714,13 +3001,10 @@ var SpaceAwarenessProvider = class {
2714
3001
  };
2715
3002
  this._postTask.schedule();
2716
3003
  }
2717
- getRemoteStates() {
2718
- return Array.from(this._remoteStates.values());
2719
- }
2720
3004
  _handleQueryMessage() {
2721
- invariant3(this._postTask, void 0, {
3005
+ invariant2(this._postTask, void 0, {
2722
3006
  F: __dxlog_file4,
2723
- L: 111,
3007
+ L: 115,
2724
3008
  S: this,
2725
3009
  A: [
2726
3010
  "this._postTask",
@@ -2730,9 +3014,9 @@ var SpaceAwarenessProvider = class {
2730
3014
  this._postTask.schedule();
2731
3015
  }
2732
3016
  _handlePostMessage(message) {
2733
- invariant3(message.kind === "post", void 0, {
3017
+ invariant2(message.kind === "post", void 0, {
2734
3018
  F: __dxlog_file4,
2735
- L: 116,
3019
+ L: 120,
2736
3020
  S: this,
2737
3021
  A: [
2738
3022
  "message.kind === 'post'",
@@ -2751,76 +3035,108 @@ var modelState = StateField5.define({
2751
3035
  update: (model) => model
2752
3036
  });
2753
3037
 
2754
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2755
- var createAutomergeModel = ({ space, identity, text }) => {
3038
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3039
+ var useActionHandler = (view) => {
3040
+ return (action) => {
3041
+ if (!view) {
3042
+ return;
3043
+ }
3044
+ switch (action.type) {
3045
+ case "heading":
3046
+ setHeading(parseInt(action.data))(view);
3047
+ break;
3048
+ case "bold":
3049
+ toggleBold(view);
3050
+ break;
3051
+ case "italic":
3052
+ toggleItalic(view);
3053
+ break;
3054
+ case "strikethrough":
3055
+ toggleStrikethrough(view);
3056
+ break;
3057
+ case "list":
3058
+ toggleList(view);
3059
+ break;
3060
+ case "comment":
3061
+ createComment(view);
3062
+ break;
3063
+ }
3064
+ setTimeout(() => {
3065
+ view.focus();
3066
+ });
3067
+ };
3068
+ };
3069
+
3070
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3071
+ import { useState as useState2 } from "react";
3072
+ var useTextEditor = () => {
3073
+ const [view, setView] = useState2(null);
3074
+ return [
3075
+ (ref) => {
3076
+ setView(ref);
3077
+ },
3078
+ view
3079
+ ];
3080
+ };
3081
+
3082
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
3083
+ import get4 from "lodash.get";
3084
+ import { useEffect as useEffect3, useState as useState3 } from "react";
3085
+ import { generateName } from "@dxos/display-name";
3086
+ import { getRawDoc } from "@dxos/echo-schema";
3087
+ import { invariant as invariant3 } from "@dxos/invariant";
3088
+ import { isAutomergeObject } from "@dxos/react-client/echo";
3089
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3090
+ var useTextModel = (props) => {
3091
+ const { identity, space, text } = props;
3092
+ const [model, setModel] = useState3();
3093
+ useEffect3(() => setModel(createModel(props)), [
3094
+ identity,
3095
+ space,
3096
+ text
3097
+ ]);
3098
+ return model;
3099
+ };
3100
+ var createModel = ({ space, identity, text }) => {
3101
+ invariant3(isAutomergeObject(text), void 0, {
3102
+ F: __dxlog_file5,
3103
+ L: 34,
3104
+ S: void 0,
3105
+ A: [
3106
+ "isAutomergeObject(text)",
3107
+ ""
3108
+ ]
3109
+ });
2756
3110
  const obj = text;
2757
3111
  const doc = getRawDoc(obj, [
2758
3112
  obj.field
2759
3113
  ]);
2760
- const awarenessProvider = space && new SpaceAwarenessProvider({
3114
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2761
3115
  space,
2762
3116
  channel: `automerge.awareness.${obj.id}`,
2763
3117
  info: {
2764
- displayName: identity?.profile?.displayName ?? "",
3118
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
2765
3119
  color: cursorColor.color,
2766
3120
  lightColor: cursorColor.light
2767
3121
  },
2768
3122
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2769
3123
  });
3124
+ const extensions = [
3125
+ //
3126
+ modelState.init(() => model),
3127
+ automerge({
3128
+ handle: doc.handle,
3129
+ path: doc.path
3130
+ })
3131
+ ];
3132
+ if (awarenessProvider2) {
3133
+ extensions.push(awareness(awarenessProvider2));
3134
+ }
2770
3135
  const model = {
2771
3136
  id: obj.id,
2772
3137
  content: doc,
2773
3138
  text: () => get4(doc.handle.docSync(), doc.path),
2774
- // TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
2775
- extension: [
2776
- modelState.init(() => model),
2777
- automerge3({
2778
- handle: doc.handle,
2779
- path: doc.path
2780
- }),
2781
- awarenessProvider && AwarenessProvider.of(awarenessProvider),
2782
- awareness()
2783
- ].filter(isNotNullOrUndefined),
2784
- peer: identity ? {
2785
- id: identity.identityKey.toHex(),
2786
- name: identity.profile?.displayName
2787
- } : void 0
2788
- };
2789
- return model;
2790
- };
2791
-
2792
- // packages/ui/react-ui-editor/src/hooks/yjs.ts
2793
- import * as decoding from "lib0/decoding";
2794
- import * as encoding from "lib0/encoding";
2795
- import { Observable } from "lib0/observable";
2796
- import * as YP from "y-protocols/awareness";
2797
- import { invariant as invariant4 } from "@dxos/invariant";
2798
- import { log as log3 } from "@dxos/log";
2799
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
2800
- var createYjsModel = ({ identity, space, text }) => {
2801
- invariant4(text?.doc && text?.content, void 0, {
2802
- F: __dxlog_file5,
2803
- L: 22,
2804
- S: void 0,
2805
- A: [
2806
- "text?.doc && text?.content",
2807
- ""
2808
- ]
2809
- });
2810
- const provider = space ? new YAwarenessProvider({
2811
- space,
2812
- doc: text.doc,
2813
- channel: `yjs.awareness.${text.id}`
2814
- }) : void 0;
2815
- const model = {
2816
- id: text.doc.guid,
2817
- content: text.content,
2818
- text: () => text.content.toString(),
2819
- extension: [
2820
- modelState.init(() => model),
2821
- yjs(text.content, provider?.awareness)
2822
- ],
2823
- awareness: provider?.awareness,
3139
+ extension: extensions,
2824
3140
  peer: identity ? {
2825
3141
  id: identity.identityKey.toHex(),
2826
3142
  name: identity.profile?.displayName
@@ -2828,124 +3144,7 @@ var createYjsModel = ({ identity, space, text }) => {
2828
3144
  };
2829
3145
  return model;
2830
3146
  };
2831
- var messageAwareness = 1;
2832
- var messageQueryAwareness = 3;
2833
- var YAwarenessProvider = class extends Observable {
2834
- constructor({ space, doc, channel, awareness: awareness2 }) {
2835
- super();
2836
- this._space = space;
2837
- this._awareness = awareness2 ?? new YP.Awareness(doc);
2838
- this._channel = channel;
2839
- this._clientId = doc.clientID;
2840
- this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
2841
- this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
2842
- if (typeof window !== "undefined") {
2843
- window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
2844
- } else if (typeof process !== "undefined") {
2845
- process.on("exit", this._handleBeforeUnload.bind(this));
2846
- }
2847
- const encoderAwarenessQuery = encoding.createEncoder();
2848
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
2849
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
2850
- const encoderAwarenessState = encoding.createEncoder();
2851
- encoding.writeVarUint(encoderAwarenessState, messageAwareness);
2852
- encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
2853
- this._clientId
2854
- ]));
2855
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
2856
- }
2857
- get awareness() {
2858
- return this._awareness;
2859
- }
2860
- _handleAwarenessUpdate({ added, updated, removed }, origin) {
2861
- log3("awareness update", {
2862
- added,
2863
- updated,
2864
- removed,
2865
- origin
2866
- }, {
2867
- F: __dxlog_file5,
2868
- L: 101,
2869
- S: this,
2870
- C: (f, a) => f(...a)
2871
- });
2872
- const changedClients = added.concat(updated).concat(removed);
2873
- const encoderAwareness = encoding.createEncoder();
2874
- encoding.writeVarUint(encoderAwareness, messageAwareness);
2875
- encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
2876
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
2877
- }
2878
- _handleSpaceMessage({ payload }) {
2879
- log3("space message", payload, {
2880
- F: __dxlog_file5,
2881
- L: 110,
2882
- S: this,
2883
- C: (f, a) => f(...a)
2884
- });
2885
- const data = new Uint8Array(Array.from(Object.values(payload)));
2886
- const encoder = this._readMessage(data);
2887
- if (encoder) {
2888
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
2889
- }
2890
- }
2891
- _readMessage(message) {
2892
- const decoder = decoding.createDecoder(message);
2893
- const encoder = encoding.createEncoder();
2894
- const messageType = decoding.readVarUint(decoder);
2895
- let sendReply = false;
2896
- switch (messageType) {
2897
- case messageAwareness: {
2898
- YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
2899
- break;
2900
- }
2901
- case messageQueryAwareness: {
2902
- encoding.writeVarUint(encoder, messageAwareness);
2903
- encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
2904
- sendReply = true;
2905
- break;
2906
- }
2907
- default: {
2908
- console.error("Invalid message:", messageType);
2909
- return encoder;
2910
- }
2911
- }
2912
- if (!sendReply) {
2913
- return null;
2914
- }
2915
- return encoder;
2916
- }
2917
- _handleBeforeUnload() {
2918
- YP.removeAwarenessStates(this._awareness, [
2919
- this._clientId
2920
- ], "window unload");
2921
- }
2922
- };
2923
-
2924
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2925
- import { useEffect as useEffect2, useState as useState2 } from "react";
2926
- import { isAutomergeObject } from "@dxos/react-client/echo";
2927
- var useTextModel = (props) => {
2928
- const { identity, space, text } = props;
2929
- const [model, setModel] = useState2();
2930
- useEffect2(() => setModel(createModel(props)), [
2931
- identity,
2932
- space,
2933
- text
2934
- ]);
2935
- return model;
2936
- };
2937
- var createModel = (props) => {
2938
- const { text } = props;
2939
- if (isAutomergeObject(text)) {
2940
- return createAutomergeModel(props);
2941
- } else if (text?.doc) {
2942
- return createYjsModel(props);
2943
- } else {
2944
- return void 0;
2945
- }
2946
- };
2947
3147
  export {
2948
- AwarenessProvider,
2949
3148
  BaseTextEditor,
2950
3149
  Cursor,
2951
3150
  Doc,
@@ -2955,30 +3154,32 @@ export {
2955
3154
  SpaceAwarenessProvider,
2956
3155
  TextEditor,
2957
3156
  TextKind,
2958
- YAwarenessProvider,
2959
3157
  YText,
2960
3158
  YXmlFragment,
2961
3159
  autocomplete,
2962
- automerge3 as automerge,
3160
+ automerge,
2963
3161
  awareness,
3162
+ awarenessProvider,
2964
3163
  basicBundle,
2965
3164
  blast,
2966
3165
  callbackWrapper,
2967
3166
  code2 as code,
2968
3167
  codeTheme,
2969
3168
  comments,
2970
- createAutomergeModel,
2971
- createYjsModel,
3169
+ createComment,
2972
3170
  defaultMarkdownSlots,
2973
3171
  defaultOptions,
2974
3172
  defaultSlots,
2975
3173
  defaultTextSlots,
2976
3174
  defaultTheme,
3175
+ formatting,
2977
3176
  getToken,
3177
+ heading2 as heading,
2978
3178
  hr,
2979
3179
  image,
2980
3180
  link,
2981
3181
  listener,
3182
+ logChanges,
2982
3183
  markdownBundle,
2983
3184
  markdownHighlightStyle,
2984
3185
  markdownTags,
@@ -2989,16 +3190,21 @@ export {
2989
3190
  outliner,
2990
3191
  setComments,
2991
3192
  setFocus,
3193
+ setHeading,
2992
3194
  setSelection,
2993
- syncFacet,
2994
3195
  table,
2995
3196
  tags2 as tags,
2996
3197
  tasklist,
2997
3198
  textTheme,
3199
+ toggleBold,
3200
+ toggleItalic,
3201
+ toggleList,
3202
+ toggleStrikethrough,
3203
+ toggleStyle,
2998
3204
  typewriter,
2999
- useAwareness,
3205
+ useActionHandler,
3000
3206
  useComments,
3001
- useTextModel,
3002
- yjs
3207
+ useTextEditor,
3208
+ useTextModel
3003
3209
  };
3004
3210
  //# sourceMappingURL=index.mjs.map