@dxos/react-ui-editor 0.3.11-main.264ec26 → 0.3.11-main.27967cf

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 (166) hide show
  1. package/dist/lib/browser/index.mjs +1406 -934
  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 +1 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +3 -2
  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/automerge.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/{yjs.stories.d.ts → hooks.stories.d.ts} +13 -2
  11. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  15. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  16. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  17. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  18. package/dist/types/src/components/index.d.ts +1 -0
  19. package/dist/types/src/components/index.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  22. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  23. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  24. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  25. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  26. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  28. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  29. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  30. package/dist/types/src/extensions/awareness.d.ts +2 -4
  31. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  32. package/dist/types/src/extensions/basic.d.ts +2 -2
  33. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  34. package/dist/types/src/extensions/comments.d.ts +14 -6
  35. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  36. package/dist/types/src/extensions/index.d.ts +0 -7
  37. package/dist/types/src/extensions/index.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  39. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  40. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  42. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  44. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  45. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  47. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  48. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  49. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  50. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  51. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  52. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  53. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  54. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  55. package/dist/types/src/hooks/defs.d.ts +1 -4
  56. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  57. package/dist/types/src/hooks/index.d.ts +3 -2
  58. package/dist/types/src/hooks/index.d.ts.map +1 -1
  59. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  60. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  61. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  62. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  63. package/dist/types/src/hooks/useTextEditor.d.ts +39 -0
  64. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  65. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  66. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  67. package/dist/types/src/index.d.ts +1 -1
  68. package/dist/types/src/index.d.ts.map +1 -1
  69. package/dist/types/src/styles/markdown.d.ts +2 -2
  70. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  71. package/dist/types/src/themes/default.d.ts.map +1 -1
  72. package/package.json +39 -29
  73. package/src/components/TextEditor/MarkdownEditor.stories.tsx +16 -20
  74. package/src/components/TextEditor/TextEditor.tsx +46 -27
  75. package/src/components/TextEditor/automerge.stories.tsx +1 -2
  76. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  77. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  78. package/src/components/Toolbar/Toolbar.tsx +166 -0
  79. package/src/components/Toolbar/index.ts +5 -0
  80. package/src/components/index.ts +1 -0
  81. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  82. package/src/extensions/automerge/automerge.test.ts +13 -0
  83. package/src/extensions/automerge/automerge.ts +44 -10
  84. package/src/extensions/automerge/cursor.ts +3 -3
  85. package/src/extensions/automerge/defs.ts +9 -9
  86. package/src/extensions/automerge/semaphore.ts +17 -19
  87. package/src/extensions/automerge/update-automerge.ts +12 -6
  88. package/src/extensions/automerge/update-codemirror.ts +5 -5
  89. package/src/extensions/awareness.ts +21 -35
  90. package/src/extensions/basic.ts +17 -15
  91. package/src/extensions/blast.ts +4 -1
  92. package/src/extensions/comments.ts +138 -80
  93. package/src/extensions/index.ts +0 -7
  94. package/src/extensions/markdown/bundle.ts +9 -8
  95. package/src/extensions/markdown/code.ts +150 -0
  96. package/src/extensions/markdown/formatting.ts +188 -0
  97. package/src/extensions/markdown/heading.ts +59 -0
  98. package/src/extensions/markdown/highlight.ts +9 -15
  99. package/src/extensions/{hr.ts → markdown/hr.ts} +3 -4
  100. package/src/extensions/markdown/index.ts +8 -0
  101. package/src/extensions/{link.ts → markdown/link.ts} +13 -4
  102. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +2 -3
  103. package/src/hooks/awareness-provider.ts +24 -20
  104. package/src/hooks/defs.ts +1 -6
  105. package/src/hooks/index.ts +3 -2
  106. package/src/hooks/useActionHandler.ts +62 -0
  107. package/src/hooks/useEditorView.ts +29 -0
  108. package/src/hooks/useTextEditor.ts +153 -0
  109. package/src/hooks/useTextModel.ts +65 -12
  110. package/src/index.ts +1 -1
  111. package/src/styles/markdown.ts +9 -10
  112. package/src/themes/default.ts +11 -8
  113. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  114. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  115. package/dist/types/src/components/TextEditor/comments.stories.d.ts +0 -26
  116. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  117. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  118. package/dist/types/src/extensions/code.d.ts.map +0 -1
  119. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  120. package/dist/types/src/extensions/image.d.ts.map +0 -1
  121. package/dist/types/src/extensions/link.d.ts.map +0 -1
  122. package/dist/types/src/extensions/table.d.ts.map +0 -1
  123. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  124. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  125. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  126. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  127. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  128. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  129. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  130. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  131. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  132. package/dist/types/src/hooks/automerge.d.ts +0 -4
  133. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  134. package/dist/types/src/hooks/yjs.d.ts +0 -28
  135. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  136. package/dist/types/src/testing/index.d.ts +0 -3
  137. package/dist/types/src/testing/index.d.ts.map +0 -1
  138. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  139. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  140. package/dist/types/src/testing/proto/index.d.ts +0 -2
  141. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  142. package/dist/types/src/testing/replicator.d.ts +0 -45
  143. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  144. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  145. package/src/components/TextEditor/comments.stories.tsx +0 -357
  146. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  147. package/src/extensions/code.ts +0 -104
  148. package/src/extensions/yjs/cursor.ts +0 -22
  149. package/src/extensions/yjs/index.ts +0 -5
  150. package/src/extensions/yjs/yjs.test.ts +0 -35
  151. package/src/extensions/yjs/yjs.ts +0 -16
  152. package/src/hooks/automerge.ts +0 -53
  153. package/src/hooks/yjs.ts +0 -157
  154. package/src/testing/index.ts +0 -6
  155. package/src/testing/proto/gen/schema.ts +0 -49
  156. package/src/testing/proto/index.ts +0 -5
  157. package/src/testing/proto/schema.proto +0 -15
  158. package/src/testing/replicator.ts +0 -184
  159. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  160. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  161. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  162. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  163. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  164. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
  165. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  166. /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
3
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
+ import { keymap as keymap7 } from "@codemirror/view";
4
5
 
5
6
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
7
  import { tags as tags2 } from "@lezer/highlight";
@@ -8,13 +9,13 @@ import { tags as tags2 } from "@lezer/highlight";
8
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
10
  import { EditorState as EditorState2 } from "@codemirror/state";
10
11
  import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
12
12
  import { vim } from "@replit/codemirror-vim";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
- import React, { forwardRef, useCallback, useEffect as useEffect3, useImperativeHandle, useRef, useState } from "react";
14
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
15
15
  import { log as log2 } from "@dxos/log";
16
- import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
17
- import { inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
16
+ import { useThemeContext } from "@dxos/react-ui";
17
+ import { focusRing, mx as mx3 } from "@dxos/react-ui-theme";
18
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
18
19
 
19
20
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
21
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -53,13 +54,14 @@ var autocomplete = ({ onSearch }) => {
53
54
  };
54
55
 
55
56
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
57
+ import { invertedEffects } from "@codemirror/commands";
56
58
  import { StateField } from "@codemirror/state";
57
- import { EditorView } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
+ import { EditorView, ViewPlugin } from "@codemirror/view";
60
+ import { next as A4 } from "@dxos/automerge/automerge";
59
61
 
60
62
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
61
63
  import get from "lodash.get";
62
- import { next as automerge } from "@dxos/automerge/automerge";
64
+ import { next as A } from "@dxos/automerge/automerge";
63
65
  var cursorConverter = (handle, path) => ({
64
66
  // TODO(burdon): Handle assoc to associate with a previous character.
65
67
  toCursor: (pos) => {
@@ -71,7 +73,7 @@ var cursorConverter = (handle, path) => ({
71
73
  if (typeof value === "string" && value.length <= pos) {
72
74
  return "end";
73
75
  }
74
- return automerge.getCursor(doc, path.slice(), pos);
76
+ return A.getCursor(doc, path.slice(), pos);
75
77
  },
76
78
  fromCursor: (cursor) => {
77
79
  if (cursor === "") {
@@ -89,26 +91,28 @@ var cursorConverter = (handle, path) => ({
89
91
  return 0;
90
92
  }
91
93
  }
92
- return automerge.getCursorPosition(doc, path.slice(), cursor);
94
+ return A.getCursorPosition(doc, path.slice(), cursor);
93
95
  }
94
96
  });
95
97
 
96
98
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
97
99
  import { Annotation, StateEffect } from "@codemirror/state";
98
- var effectType = StateEffect.define({});
99
- var reconcileAnnotationType = Annotation.define();
100
100
  var getPath = (state, field) => state.field(field).path;
101
101
  var getLastHeads = (state, field) => state.field(field).lastHeads;
102
- var updateHeads = (newHeads) => effectType.of({
102
+ var updateHeadsEffect = StateEffect.define({});
103
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
103
104
  newHeads
104
105
  });
105
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
106
+ var reconcileAnnotation = Annotation.define();
107
+ var isReconcile = (tr) => {
108
+ return !!tr.annotation(reconcileAnnotation);
109
+ };
106
110
 
107
111
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
108
- import { next as automerge2 } from "@dxos/automerge/automerge";
112
+ import { next as A3 } from "@dxos/automerge/automerge";
109
113
 
110
114
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
111
- import { next as am } from "@dxos/automerge/automerge";
115
+ import { next as A2 } from "@dxos/automerge/automerge";
112
116
  var updateAutomerge = (field, handle, transactions, state) => {
113
117
  const { lastHeads, path } = state.field(field);
114
118
  let hasChanges = false;
@@ -121,11 +125,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
121
125
  return void 0;
122
126
  }
123
127
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
+ const invertedTransactions = [];
124
129
  for (const tr of transactions) {
125
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
126
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
130
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
131
+ invertedTransactions.push({
132
+ from: fromA,
133
+ del: toA - fromA,
134
+ insert
135
+ });
127
136
  });
128
137
  }
138
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
139
+ A2.splice(doc, path.slice(), from, del, insert.toString());
140
+ });
129
141
  });
130
142
  return newHeads ?? void 0;
131
143
  };
@@ -140,13 +152,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
140
152
  selection = selection.map(changeSet, 1);
141
153
  view.dispatch({
142
154
  changes: changeSet,
143
- annotations: reconcileAnnotationType.of({})
155
+ annotations: reconcileAnnotation.of(false)
144
156
  });
145
157
  }
146
158
  }
147
159
  view.dispatch({
148
160
  selection,
149
- annotations: reconcileAnnotationType.of({})
161
+ annotations: reconcileAnnotation.of(false)
150
162
  });
151
163
  };
152
164
  var handlePatch = (patch, target, state) => {
@@ -167,7 +179,7 @@ var handleInsert = (target, patch) => {
167
179
  if (index == null) {
168
180
  return [];
169
181
  }
170
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
182
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
171
183
  return [
172
184
  {
173
185
  from: index,
@@ -245,36 +257,37 @@ var PatchSemaphore = class {
245
257
  this._state = _state;
246
258
  this._inReconcile = false;
247
259
  }
248
- // NOTE: Cannot destruct view.state.
249
260
  reconcile(view) {
250
- if (this._inReconcile) {
251
- return;
261
+ if (!this._inReconcile) {
262
+ this._inReconcile = true;
263
+ this.doReconcile(view);
264
+ this._inReconcile = false;
252
265
  }
253
- this._inReconcile = true;
266
+ }
267
+ doReconcile(view) {
254
268
  const path = getPath(view.state, this._state);
255
269
  const oldHeads = getLastHeads(view.state, this._state);
256
270
  let selection = view.state.selection;
257
- const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
271
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
258
272
  const toInvert = transactions.slice().reverse();
259
- for (const tx of toInvert) {
260
- const inverted = tx.changes.invert(tx.startState.doc);
273
+ for (const tr of toInvert) {
274
+ const inverted = tr.changes.invert(tr.startState.doc);
261
275
  selection = selection.map(inverted);
262
276
  view.dispatch({
263
277
  changes: inverted,
264
- annotations: reconcileAnnotationType.of(true)
278
+ annotations: reconcileAnnotation.of(true)
265
279
  });
266
280
  }
267
281
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
268
282
  if (newHeads === null || newHeads === void 0) {
269
- newHeads = automerge2.getHeads(this._handle.docSync());
283
+ newHeads = A3.getHeads(this._handle.docSync());
270
284
  }
271
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
285
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
272
286
  updateCodeMirror(view, selection, path, diff);
273
287
  view.dispatch({
274
288
  effects: updateHeads(newHeads),
275
- annotations: reconcileAnnotationType.of({})
289
+ annotations: reconcileAnnotation.of(false)
276
290
  });
277
- this._inReconcile = false;
278
291
  }
279
292
  };
280
293
 
@@ -292,9 +305,9 @@ var Cursor = class _Cursor {
292
305
  }
293
306
  static {
294
307
  this.getCursorFromRange = (state, range) => {
295
- const cursorConverter3 = state.facet(_Cursor.converter);
296
- const from = cursorConverter3.toCursor(range.from);
297
- const to = cursorConverter3.toCursor(range.to, -1);
308
+ const cursorConverter2 = state.facet(_Cursor.converter);
309
+ const from = cursorConverter2.toCursor(range.from);
310
+ const to = cursorConverter2.toCursor(range.to, -1);
298
311
  return [
299
312
  from,
300
313
  to
@@ -303,10 +316,10 @@ var Cursor = class _Cursor {
303
316
  }
304
317
  static {
305
318
  this.getRangeFromCursor = (state, cursor) => {
306
- const cursorConverter3 = state.facet(_Cursor.converter);
319
+ const cursorConverter2 = state.facet(_Cursor.converter);
307
320
  const parts = cursor.split(":");
308
- const from = cursorConverter3.fromCursor(parts[0]);
309
- const to = cursorConverter3.fromCursor(parts[1]);
321
+ const from = cursorConverter2.fromCursor(parts[0]);
322
+ const to = cursorConverter2.fromCursor(parts[1]);
310
323
  return from !== void 0 && to !== void 0 ? {
311
324
  from,
312
325
  to
@@ -316,11 +329,11 @@ var Cursor = class _Cursor {
316
329
  };
317
330
 
318
331
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
319
- var automerge3 = ({ handle, path }) => {
320
- const state = StateField.define({
332
+ var automerge = ({ handle, path }) => {
333
+ const syncState = StateField.define({
321
334
  create: () => ({
322
335
  path: path.slice(),
323
- lastHeads: A.getHeads(handle.docSync()),
336
+ lastHeads: A4.getHeads(handle.docSync()),
324
337
  unreconciledTransactions: []
325
338
  }),
326
339
  update: (value, tr) => {
@@ -331,7 +344,7 @@ var automerge3 = ({ handle, path }) => {
331
344
  };
332
345
  let clearUnreconciled = false;
333
346
  for (const effect of tr.effects) {
334
- if (effect.is(effectType)) {
347
+ if (effect.is(updateHeadsEffect)) {
335
348
  result.lastHeads = effect.value.newHeads;
336
349
  clearUnreconciled = true;
337
350
  }
@@ -339,80 +352,78 @@ var automerge3 = ({ handle, path }) => {
339
352
  if (clearUnreconciled) {
340
353
  result.unreconciledTransactions = [];
341
354
  } else {
342
- if (!isReconcileTx(tr)) {
355
+ if (!isReconcile(tr)) {
343
356
  result.unreconciledTransactions.push(tr);
344
357
  }
345
358
  }
346
359
  return result;
347
360
  }
348
361
  });
349
- const semaphore = new PatchSemaphore(handle, state);
362
+ const semaphore = new PatchSemaphore(handle, syncState);
350
363
  return [
351
364
  Cursor.converter.of(cursorConverter(handle, path)),
365
+ syncState,
366
+ // Reconcile external updates.
367
+ ViewPlugin.fromClass(class {
368
+ constructor(_view) {
369
+ this._view = _view;
370
+ handle.addListener("change", this._handleChange.bind(this));
371
+ }
372
+ destroy() {
373
+ handle.removeListener("change", this._handleChange.bind(this));
374
+ }
375
+ _handleChange() {
376
+ semaphore.reconcile(this._view);
377
+ }
378
+ }),
379
+ // Reconcile local updates.
352
380
  EditorView.updateListener.of(({ view, changes }) => {
353
381
  if (!changes.empty) {
354
382
  semaphore.reconcile(view);
355
383
  }
356
384
  }),
357
- state
385
+ // TODO(burdon): Record undo transactions.
386
+ // See https://github.com/yjs/y-codemirror.next/tree/main
387
+ // https://codemirror.net/examples/inverted-effect
388
+ invertedEffects.of((tr) => {
389
+ const effects = [];
390
+ if (!tr.changes.empty) {
391
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
392
+ });
393
+ }
394
+ return effects;
395
+ })
358
396
  ];
359
397
  };
360
398
 
361
399
  // packages/ui/react-ui-editor/src/extensions/awareness.ts
362
400
  import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
363
- import { Decoration, EditorView as EditorView2, ViewPlugin, WidgetType } from "@codemirror/view";
364
- import { useEffect } from "react";
401
+ import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
365
402
  import { Event } from "@dxos/async";
366
403
  import { Context } from "@dxos/context";
367
- import { generateName } from "@dxos/display-name";
368
- import { useThemeContext } from "@dxos/react-ui";
369
- import { getColorForValue } from "@dxos/react-ui-theme";
370
404
  var dummyProvider = {
371
405
  remoteStateChange: new Event(),
372
406
  open: () => {
373
407
  },
374
408
  close: () => {
375
409
  },
410
+ getRemoteStates: () => [],
376
411
  update: () => {
377
- },
378
- getRemoteStates: () => []
412
+ }
379
413
  };
380
- var AwarenessProvider = Facet2.define({
414
+ var awarenessProvider = Facet2.define({
381
415
  combine: (providers) => providers[0] ?? dummyProvider
382
416
  });
383
417
  var RemoteSelectionChangedAnnotation = Annotation2.define();
384
418
  var awareness = (provider = dummyProvider) => {
385
419
  return [
386
- AwarenessProvider.of(provider),
387
- ViewPlugin.fromClass(RemoteSelectionsDecorator, {
420
+ awarenessProvider.of(provider),
421
+ ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
388
422
  decorations: (value) => value.decorations
389
423
  }),
390
424
  styles
391
425
  ];
392
426
  };
393
- var useAwareness = ({ awareness: awareness2, peer }) => {
394
- const { themeMode } = useThemeContext();
395
- useEffect(() => {
396
- if (awareness2 && peer) {
397
- awareness2.setLocalStateField("user", {
398
- name: peer.name ?? generateName(peer.id),
399
- color: getColorForValue({
400
- value: peer.id,
401
- type: "color"
402
- }),
403
- colorLight: getColorForValue({
404
- value: peer.id,
405
- themeMode,
406
- type: "highlight"
407
- })
408
- });
409
- }
410
- }, [
411
- awareness2,
412
- peer,
413
- themeMode
414
- ]);
415
- };
416
427
  var RemoteSelectionsDecorator = class {
417
428
  constructor(view) {
418
429
  this.decorations = RangeSet.of([]);
@@ -420,7 +431,7 @@ var RemoteSelectionsDecorator = class {
420
431
  this._lastAnchor = void 0;
421
432
  this._lastHead = void 0;
422
433
  this._cursorConverter = view.state.facet(Cursor.converter);
423
- this._provider = view.state.facet(AwarenessProvider);
434
+ this._provider = view.state.facet(awarenessProvider);
424
435
  this._provider.open();
425
436
  this._provider.remoteStateChange.on(this._ctx, () => {
426
437
  view.dispatch({
@@ -574,39 +585,41 @@ var styles = EditorView2.baseTheme({
574
585
  marginLeft: "-1px",
575
586
  marginRight: "-1px",
576
587
  boxSizing: "border-box",
577
- display: "inline"
588
+ display: "inline",
589
+ cursor: "pointer"
578
590
  },
579
591
  ".cm-collab-selectionCaretDot": {
580
592
  borderRadius: "50%",
581
593
  position: "absolute",
582
- width: ".4em",
583
- height: ".4em",
584
- top: "-.2em",
585
- left: "-.2em",
594
+ width: ".5em",
595
+ height: ".5em",
596
+ top: "-.25em",
597
+ left: "-.25em",
586
598
  backgroundColor: "inherit",
587
599
  transition: "transform .3s ease-in-out",
588
600
  boxSizing: "border-box"
589
601
  },
590
602
  ".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
591
- transformOrigin: "bottom center",
592
- transform: "scale(0)"
603
+ transform: "scale(0)",
604
+ transformOrigin: "center"
593
605
  },
594
606
  ".cm-collab-selectionInfo": {
595
607
  position: "absolute",
596
- top: "-1.05em",
597
- left: "-1px",
608
+ transform: "translate(-50%, 0)",
609
+ top: "-20px",
610
+ left: 0,
598
611
  fontSize: ".75em",
599
- fontFamily: "serif",
612
+ fontFamily: "sans-serif",
600
613
  fontStyle: "normal",
601
614
  fontWeight: "normal",
602
615
  lineHeight: "normal",
603
616
  userSelect: "none",
604
617
  color: "white",
605
- paddingLeft: "2px",
606
- paddingRight: "2px",
618
+ padding: "2px",
607
619
  zIndex: 101,
608
620
  transition: "opacity .3s ease-in-out",
609
621
  backgroundColor: "inherit",
622
+ borderRadius: "2px",
610
623
  // These should be separate.
611
624
  opacity: 0,
612
625
  transitionDelay: "0s",
@@ -624,18 +637,19 @@ import { history } from "@codemirror/commands";
624
637
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
625
638
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
626
639
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
627
- var basicBundle = ({ themeMode, placeholder: _placeholder, multiline = true }) => [
628
- multiline ? EditorView3.lineWrapping : [],
640
+ import { isNotFalsy } from "@dxos/util";
641
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
642
+ lineWrapping && EditorView3.lineWrapping,
629
643
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
630
644
  bracketMatching(),
631
645
  closeBrackets(),
632
646
  drawSelection(),
633
647
  highlightActiveLine(),
634
648
  history(),
635
- _placeholder ? placeholder(_placeholder) : [],
649
+ _placeholder && placeholder(_placeholder),
636
650
  // https://github.com/codemirror/theme-one-dark
637
651
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
638
- ];
652
+ ].filter(isNotFalsy);
639
653
 
640
654
  // packages/ui/react-ui-editor/src/extensions/blast.ts
641
655
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
@@ -674,10 +688,10 @@ var blast = (options = defaultOptions) => {
674
688
  const getPoint = (view, pos) => {
675
689
  const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
676
690
  const element = document.elementFromPoint(x, y);
677
- const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
691
+ const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
678
692
  const point = {
679
693
  x: x - left,
680
- y: y - top
694
+ y: y - top2
681
695
  };
682
696
  return {
683
697
  element,
@@ -769,7 +783,7 @@ var Blaster = class {
769
783
  initialize() {
770
784
  invariant(!this._canvas && !this._ctx, void 0, {
771
785
  F: __dxlog_file,
772
- L: 138,
786
+ L: 141,
773
787
  S: this,
774
788
  A: [
775
789
  "!this._canvas && !this._ctx",
@@ -806,7 +820,7 @@ var Blaster = class {
806
820
  start() {
807
821
  invariant(this._canvas && this._ctx, void 0, {
808
822
  F: __dxlog_file,
809
- L: 177,
823
+ L: 180,
810
824
  S: this,
811
825
  A: [
812
826
  "this._canvas && this._ctx",
@@ -947,9 +961,14 @@ var random = (min, max) => {
947
961
  return min + ~~(Math.random() * (max - min + 1));
948
962
  };
949
963
 
950
- // packages/ui/react-ui-editor/src/extensions/code.ts
951
- import { ViewPlugin as ViewPlugin2, EditorView as EditorView5, Decoration as Decoration2 } from "@codemirror/view";
952
- import { mx as mx2 } from "@dxos/react-ui-theme";
964
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
965
+ import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
966
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
967
+ import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
968
+ import sortBy from "lodash.sortby";
969
+ import { useEffect } from "react";
970
+ import { debounce } from "@dxos/async";
971
+ import { nonNullable } from "@dxos/util";
953
972
 
954
973
  // packages/ui/react-ui-editor/src/styles/cursors.ts
955
974
  import * as random2 from "lib0/random";
@@ -992,34 +1011,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
992
1011
  // packages/ui/react-ui-editor/src/styles/markdown.ts
993
1012
  import { mx } from "@dxos/react-ui-theme";
994
1013
  var headings = {
995
- 1: [
996
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
997
- "-ml-[10px]"
998
- ],
999
- 2: [
1000
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1001
- "-ml-[8px]"
1002
- ],
1003
- 3: [
1004
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1005
- "-ml-[7px]"
1006
- ],
1007
- 4: [
1008
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1009
- "-ml-[6px]"
1010
- ],
1011
- 5: [
1012
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1013
- "-ml-[5px]"
1014
- ],
1015
- 6: [
1016
- "mbs-4 mbe-2 font-medium text-inherit no-underline",
1017
- "-ml-[4px]"
1018
- ]
1019
- };
1020
- var heading = (level, readonly) => {
1021
- const [style, offset] = headings[level];
1022
- return mx(style, readonly && offset);
1014
+ 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
1015
+ 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1016
+ 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1017
+ 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1018
+ 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1019
+ 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
1020
+ };
1021
+ var heading = (level) => {
1022
+ return headings[level];
1023
1023
  };
1024
1024
  var light = "text-neutral-200 dark:text-neutral-800";
1025
1025
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -1037,89 +1037,6 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
1037
1037
  var tokens = tailwindConfig({}).theme;
1038
1038
  var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
1039
1039
 
1040
- // packages/ui/react-ui-editor/src/extensions/code.ts
1041
- var CODE_REGEX = /```[\s\S]*?```/gs;
1042
- var styles2 = EditorView5.baseTheme({
1043
- "& .cm-codeblock": {
1044
- fontFamily: getToken("fontFamily.mono", []).join(",")
1045
- },
1046
- '&light [aria-readonly="true"] .cm-codeblock': {
1047
- background: getToken("extend.colors.neutral.50")
1048
- },
1049
- '&dark [aria-readonly="true"] .cm-codeblock': {
1050
- background: getToken("extend.colors.neutral.850")
1051
- },
1052
- '& [aria-readonly="true"] .cm-codeblock': {
1053
- display: "block",
1054
- paddingInline: "4px !important"
1055
- },
1056
- '& [aria-readonly="true"] .cm-codeblock-first': {
1057
- paddingTop: "4px !important",
1058
- borderTopLeftRadius: "4px",
1059
- borderTopRightRadius: "4px"
1060
- },
1061
- '& [aria-readonly="true"] .cm-codeblock-last': {
1062
- paddingBottom: "4px !important",
1063
- borderBottomLeftRadius: "4px",
1064
- borderBottomRightRadius: "4px"
1065
- }
1066
- });
1067
- var getLineRange = (lines, from, to) => {
1068
- const start = lines.findIndex((line) => line.from >= from);
1069
- const end = lines.findIndex((line) => line.to >= to);
1070
- return [
1071
- start,
1072
- end
1073
- ];
1074
- };
1075
- var code2 = () => {
1076
- const buildDecorations5 = (view) => {
1077
- const decorations = [];
1078
- const text = view.state.doc.sliceString(0);
1079
- const matches = text.matchAll(CODE_REGEX);
1080
- const blocks = view.viewportLineBlocks;
1081
- for (const match of matches) {
1082
- const range = getLineRange(blocks, match.index, match.index + match[0].length);
1083
- for (let i = range[0]; i <= range[1]; i++) {
1084
- const block = blocks[i];
1085
- decorations.push(Decoration2.line({
1086
- class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1087
- }).range(block.from));
1088
- }
1089
- }
1090
- return Decoration2.set(decorations);
1091
- };
1092
- return [
1093
- ViewPlugin2.fromClass(class {
1094
- constructor(view) {
1095
- this.hasFocus = false;
1096
- this.decorations = buildDecorations5(view);
1097
- }
1098
- update(update2) {
1099
- if (
1100
- // TODO(burdon): Generalize for other extensions.
1101
- this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
1102
- ) {
1103
- this.hasFocus = update2.view.hasFocus;
1104
- this.decorations = buildDecorations5(update2.view);
1105
- }
1106
- }
1107
- }, {
1108
- decorations: (value) => value.decorations
1109
- }),
1110
- styles2
1111
- ];
1112
- };
1113
-
1114
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1115
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1116
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView6 } from "@codemirror/view";
1117
- import sortBy from "lodash.sortby";
1118
- import { useEffect as useEffect2 } from "react";
1119
- import { debounce } from "@dxos/async";
1120
- import { invariant as invariant2 } from "@dxos/invariant";
1121
- import { nonNullable } from "@dxos/util";
1122
-
1123
1040
  // packages/ui/react-ui-editor/src/util.ts
1124
1041
  import { log } from "@dxos/log";
1125
1042
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
@@ -1156,24 +1073,35 @@ var logChanges = (trs) => {
1156
1073
  };
1157
1074
 
1158
1075
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1159
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1160
- var styles3 = EditorView6.baseTheme({
1161
- "& .cm-comment": {
1076
+ var styles2 = EditorView5.baseTheme({
1077
+ "&light .cm-comment, &light .cm-comment-current": {
1078
+ mixBlendMode: "darken"
1079
+ },
1080
+ "&dark .cm-comment, &dark .cm-comment-current": {
1081
+ mixBlendMode: "plus-lighter"
1082
+ },
1083
+ "&light .cm-comment": {
1162
1084
  backgroundColor: getToken("extend.colors.yellow.50")
1163
1085
  },
1164
- "& .cm-comment-active": {
1086
+ "&light .cm-comment-current": {
1165
1087
  backgroundColor: getToken("extend.colors.yellow.100")
1088
+ },
1089
+ "&dark .cm-comment": {
1090
+ color: getToken("extend.colors.yellow.50"),
1091
+ backgroundColor: getToken("extend.colors.yellow.900")
1092
+ },
1093
+ "&dark .cm-comment-current": {
1094
+ color: getToken("extend.colors.yellow.100"),
1095
+ backgroundColor: getToken("extend.colors.yellow.950")
1166
1096
  }
1167
1097
  });
1168
- var marks = {
1169
- highlight: Decoration3.mark({
1170
- class: "cm-comment"
1171
- }),
1172
- highlightActive: Decoration3.mark({
1173
- class: "cm-comment-active"
1174
- })
1175
- };
1176
- var setFocus = (view, id, center = true) => {
1098
+ var commentMark = Decoration2.mark({
1099
+ class: "cm-comment"
1100
+ });
1101
+ var commentCurrentMark = Decoration2.mark({
1102
+ class: "cm-comment-current"
1103
+ });
1104
+ var focusComment = (view, id, center = true) => {
1177
1105
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1178
1106
  if (!comment?.comment.cursor) {
1179
1107
  return;
@@ -1186,11 +1114,11 @@ var setFocus = (view, id, center = true) => {
1186
1114
  },
1187
1115
  effects: [
1188
1116
  //
1189
- EditorView6.scrollIntoView(range.from, center ? {
1117
+ EditorView5.scrollIntoView(range.from, center ? {
1190
1118
  y: "center"
1191
1119
  } : void 0),
1192
1120
  setSelection.of({
1193
- active: id
1121
+ current: id
1194
1122
  })
1195
1123
  ]
1196
1124
  });
@@ -1235,23 +1163,23 @@ var commentsState = StateField2.define({
1235
1163
  return value;
1236
1164
  }
1237
1165
  });
1238
- var highlightDecorations = EditorView6.decorations.compute([
1166
+ var commentsDecorations = EditorView5.decorations.compute([
1239
1167
  commentsState
1240
1168
  ], (state) => {
1241
- const { selection: { active }, comments: comments2 } = state.field(commentsState);
1169
+ const { selection: { current }, comments: comments2 } = state.field(commentsState);
1242
1170
  const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1243
1171
  const range = comment.range;
1244
1172
  if (!range || range.from === range.to) {
1245
1173
  console.warn("Invalid range:", range);
1246
1174
  return void 0;
1247
1175
  }
1248
- if (comment.comment.id === active) {
1249
- return marks.highlightActive.range(range.from, range.to);
1176
+ if (comment.comment.id === current) {
1177
+ return commentCurrentMark.range(range.from, range.to);
1250
1178
  } else {
1251
- return marks.highlight.range(range.from, range.to);
1179
+ return commentMark.range(range.from, range.to);
1252
1180
  }
1253
1181
  }).filter(nonNullable);
1254
- return Decoration3.set(decorations);
1182
+ return Decoration2.set(decorations);
1255
1183
  });
1256
1184
  var trackPastedComments = (onUpdate) => {
1257
1185
  let tracked = null;
@@ -1273,96 +1201,140 @@ var trackPastedComments = (onUpdate) => {
1273
1201
  }
1274
1202
  };
1275
1203
  return [
1276
- EditorView6.domEventHandlers({
1204
+ EditorView5.domEventHandlers({
1277
1205
  cut: handleTrack,
1278
1206
  copy: handleTrack
1279
1207
  }),
1280
- // Handle paste.
1281
- EditorView6.updateListener.of(({ view, state, transactions }) => {
1208
+ // Track deleted comments.
1209
+ invertedEffects2.of((tr) => {
1210
+ const { comments: comments2 } = tr.startState.field(commentsState);
1211
+ const effects = [];
1212
+ tr.changes.iterChangedRanges((fromA, toA) => {
1213
+ for (const { comment: { id }, range: { from, to } } of comments2) {
1214
+ if (from < to && from >= fromA && to <= toA) {
1215
+ effects.push(restoreCommentEffect.of({
1216
+ id,
1217
+ from,
1218
+ to
1219
+ }));
1220
+ }
1221
+ }
1222
+ });
1223
+ return effects;
1224
+ }),
1225
+ // Handle paste or the undo of comment deletion.
1226
+ EditorView5.updateListener.of((update2) => {
1227
+ const restore = [];
1228
+ for (let i = 0; i < update2.transactions.length; i++) {
1229
+ const tr = update2.transactions[i];
1230
+ for (let j = 0; j < restore.length; j++) {
1231
+ restore[j] = mapTrackedComment(restore[j], tr.changes);
1232
+ }
1233
+ for (const effect of tr.effects) {
1234
+ if (effect.is(restoreCommentEffect)) {
1235
+ restore.push(effect.value);
1236
+ }
1237
+ }
1238
+ }
1282
1239
  if (tracked) {
1283
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1240
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1284
1241
  if (paste) {
1285
1242
  let found = -1;
1286
1243
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1287
1244
  if (text.eq(tracked.text)) {
1288
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1289
- fromB = transactions[i].changes.mapPos(fromB);
1245
+ for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1246
+ fromB = update2.transactions[i].changes.mapPos(fromB);
1290
1247
  }
1291
1248
  found = fromB;
1292
1249
  }
1293
1250
  });
1294
1251
  if (found > -1) {
1295
1252
  for (const moved of tracked.comments) {
1296
- const range = {
1253
+ restore.push({
1254
+ id: moved.id,
1297
1255
  from: found + moved.from,
1298
1256
  to: found + moved.to
1299
- };
1300
- const cursor = Cursor.getCursorFromRange(state, range);
1301
- onUpdate(moved.id, cursor);
1257
+ });
1302
1258
  }
1303
1259
  }
1304
1260
  tracked = null;
1305
1261
  }
1306
1262
  }
1263
+ for (const comment of restore) {
1264
+ const { comments: comments2 } = update2.startState.field(commentsState);
1265
+ const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
1266
+ if (!exists) {
1267
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
1268
+ onUpdate(comment.id, cursor);
1269
+ }
1270
+ }
1307
1271
  })
1308
1272
  ];
1309
1273
  };
1310
- var comments = (options = {}) => {
1311
- const { key: shortcut = "meta-'" } = options;
1312
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1313
- const createCommentThread = (view) => {
1314
- invariant2(options.onCreate, void 0, {
1315
- F: __dxlog_file3,
1316
- L: 259,
1317
- S: void 0,
1318
- A: [
1319
- "options.onCreate",
1320
- ""
1321
- ]
1274
+ var mapTrackedComment = (comment, changes) => ({
1275
+ id: comment.id,
1276
+ from: changes.mapPos(comment.from, 1),
1277
+ to: changes.mapPos(comment.to, 1)
1278
+ });
1279
+ var restoreCommentEffect = StateEffect2.define({
1280
+ map: mapTrackedComment
1281
+ });
1282
+ var optionsFacet = Facet3.define({
1283
+ combine: (providers) => providers[0]
1284
+ });
1285
+ var createComment = (view) => {
1286
+ const options = view.state.facet(optionsFacet);
1287
+ const { from, to } = view.state.selection.main;
1288
+ if (from === to) {
1289
+ return false;
1290
+ }
1291
+ if (to === view.state.doc.length) {
1292
+ view.dispatch({
1293
+ changes: {
1294
+ from: to,
1295
+ insert: "\n"
1296
+ }
1322
1297
  });
1323
- const { from, to } = view.state.selection.main;
1324
- if (from === to) {
1325
- return false;
1326
- }
1327
- if (to === view.state.doc.length) {
1298
+ }
1299
+ const cursor = Cursor.getCursorFromRange(view.state, {
1300
+ from,
1301
+ to
1302
+ });
1303
+ if (cursor) {
1304
+ const id = options.onCreate?.({
1305
+ cursor,
1306
+ from,
1307
+ location: view.coordsAtPos(from)
1308
+ });
1309
+ if (id) {
1328
1310
  view.dispatch({
1329
- changes: {
1330
- from: to,
1331
- insert: "\n"
1311
+ effects: setSelection.of({
1312
+ current: id
1313
+ }),
1314
+ selection: {
1315
+ anchor: to
1332
1316
  }
1333
1317
  });
1318
+ return true;
1334
1319
  }
1335
- const cursor = Cursor.getCursorFromRange(view.state, {
1336
- from,
1337
- to
1338
- });
1339
- if (cursor) {
1340
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1341
- if (id) {
1342
- view.dispatch({
1343
- effects: setSelection.of({
1344
- active: id
1345
- }),
1346
- selection: {
1347
- anchor: from
1348
- }
1349
- });
1350
- return true;
1351
- }
1352
- }
1353
- return false;
1354
- };
1320
+ }
1321
+ return false;
1322
+ };
1323
+ var comments = (options = {}) => {
1324
+ const { key: shortcut = "meta-'" } = options;
1325
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1355
1326
  return [
1327
+ optionsFacet.of(options),
1356
1328
  commentsState,
1357
- highlightDecorations,
1358
- styles3,
1329
+ commentsDecorations,
1330
+ styles2,
1359
1331
  //
1360
1332
  // Keymap.
1361
1333
  //
1362
1334
  options.onCreate ? keymap3.of([
1363
1335
  {
1364
1336
  key: shortcut,
1365
- run: callbackWrapper(createCommentThread)
1337
+ run: callbackWrapper(createComment)
1366
1338
  }
1367
1339
  ]) : [],
1368
1340
  //
@@ -1398,7 +1370,7 @@ var comments = (options = {}) => {
1398
1370
  //
1399
1371
  // Track deleted ranges and update ranges for decorations.
1400
1372
  //
1401
- EditorView6.updateListener.of(({ view, state, changes }) => {
1373
+ EditorView5.updateListener.of(({ view, state, changes }) => {
1402
1374
  let mod = false;
1403
1375
  const { comments: comments2, ...value } = state.field(commentsState);
1404
1376
  changes.iterChanges((from, to, from2, to2) => {
@@ -1428,17 +1400,17 @@ var comments = (options = {}) => {
1428
1400
  //
1429
1401
  // Track selection/proximity.
1430
1402
  //
1431
- EditorView6.updateListener.of(({ view, state, changes }) => {
1403
+ EditorView5.updateListener.of(({ view, state }) => {
1432
1404
  let min = Infinity;
1433
- const { selection: { active, closest }, comments: comments2 } = state.field(commentsState);
1405
+ const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1434
1406
  const { head } = state.selection.main;
1435
1407
  const selection = {};
1436
1408
  comments2.forEach(({ comment, range }) => {
1437
1409
  if (head >= range.from && head <= range.to) {
1438
- selection.active = comment.id;
1410
+ selection.current = comment.id;
1439
1411
  selection.closest = void 0;
1440
1412
  }
1441
- if (!selection.active) {
1413
+ if (!selection.current) {
1442
1414
  const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
1443
1415
  if (d < min) {
1444
1416
  selection.closest = comment.id;
@@ -1446,7 +1418,7 @@ var comments = (options = {}) => {
1446
1418
  }
1447
1419
  }
1448
1420
  });
1449
- if (selection.active !== active || selection.closest !== closest) {
1421
+ if (selection.current !== current || selection.closest !== closest) {
1450
1422
  view.dispatch({
1451
1423
  effects: setSelection.of(selection)
1452
1424
  });
@@ -1463,9 +1435,9 @@ var comments = (options = {}) => {
1463
1435
  options.onUpdate ? trackPastedComments(options.onUpdate) : []
1464
1436
  ];
1465
1437
  };
1466
- var useComments = (view, comments2) => {
1467
- useEffect2(() => {
1468
- if (view && comments2) {
1438
+ var useComments = (view, comments2 = []) => {
1439
+ useEffect(() => {
1440
+ if (view) {
1469
1441
  view.dispatch({
1470
1442
  effects: setComments.of(comments2)
1471
1443
  });
@@ -1476,314 +1448,51 @@ var useComments = (view, comments2) => {
1476
1448
  ]);
1477
1449
  };
1478
1450
 
1479
- // packages/ui/react-ui-editor/src/extensions/hr.ts
1480
- import { syntaxTree } from "@codemirror/language";
1481
- import { RangeSetBuilder } from "@codemirror/state";
1482
- import { Decoration as Decoration4, EditorView as EditorView7, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1483
- var styles4 = EditorView7.baseTheme({
1484
- "& .cm-hr": {
1485
- // Note that block-level decorations should not have vertical margins,
1486
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
1487
- }
1488
- });
1489
- var HorizontalRuleWidget = class extends WidgetType2 {
1490
- toDOM() {
1491
- const el = document.createElement("div");
1492
- el.className = "cm-hr";
1493
- return el;
1494
- }
1495
- };
1496
- var decoration = Decoration4.replace({
1497
- widget: new HorizontalRuleWidget()
1498
- });
1499
- var buildDecorations = (view) => {
1500
- const builder = new RangeSetBuilder();
1501
- const { state } = view;
1502
- const cursor = state.selection.main.head;
1503
- for (const { from, to } of view.visibleRanges) {
1504
- syntaxTree(state).iterate({
1505
- enter: (node) => {
1506
- if (node.name === "HorizontalRule") {
1507
- if (cursor <= node.from || cursor >= node.to) {
1508
- builder.add(node.from, node.to, decoration);
1509
- }
1510
- }
1511
- },
1512
- from,
1513
- to
1514
- });
1515
- }
1516
- return builder.finish();
1517
- };
1518
- var hr = () => {
1519
- return [
1520
- styles4,
1521
- ViewPlugin3.fromClass(class {
1522
- constructor(view) {
1523
- this.decorations = buildDecorations(view);
1524
- }
1525
- update(update2) {
1526
- this.decorations = buildDecorations(update2.view);
1527
- }
1528
- }, {
1529
- decorations: (v) => v.decorations
1530
- })
1531
- ];
1451
+ // packages/ui/react-ui-editor/src/extensions/listener.ts
1452
+ import { EditorView as EditorView6 } from "@codemirror/view";
1453
+ var listener = ({ onFocus, onChange }) => {
1454
+ const extensions = [];
1455
+ onFocus && extensions.push(EditorView6.focusChangeEffect.of((_, focused) => {
1456
+ onFocus(focused);
1457
+ return null;
1458
+ }));
1459
+ onChange && extensions.push(EditorView6.updateListener.of((update2) => {
1460
+ onChange(update2.state.doc.toString());
1461
+ }));
1462
+ return extensions;
1532
1463
  };
1533
1464
 
1534
- // packages/ui/react-ui-editor/src/extensions/image.ts
1535
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1536
- import { StateField as StateField3 } from "@codemirror/state";
1537
- import { Decoration as Decoration5, EditorView as EditorView8, WidgetType as WidgetType3 } from "@codemirror/view";
1538
- var image = (options = {}) => {
1539
- return StateField3.define({
1540
- create: (state) => {
1541
- return Decoration5.set(buildDecorations2(0, state.doc.length, state));
1542
- },
1543
- update: (value, tr) => {
1544
- if (!tr.docChanged && !tr.selection) {
1545
- return value;
1546
- }
1547
- const cursor = tr.state.selection.main.head;
1548
- const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
1549
- let from = Math.min(cursor, oldCursor);
1550
- let to = Math.max(cursor, oldCursor);
1551
- tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
1552
- from = Math.min(from, fromB);
1553
- to = Math.max(to, toB);
1554
- });
1555
- from = tr.state.doc.lineAt(from).from;
1556
- to = tr.state.doc.lineAt(to).to;
1557
- return value.map(tr.changes).update({
1558
- filterFrom: from,
1559
- filterTo: to,
1560
- filter: () => false,
1561
- add: buildDecorations2(from, to, tr.state)
1562
- });
1563
- },
1564
- provide: (field) => EditorView8.decorations.from(field)
1565
- });
1566
- };
1567
- var buildDecorations2 = (from, to, state) => {
1568
- const decorations = [];
1569
- const cursor = state.selection.main.head;
1570
- syntaxTree2(state).iterate({
1571
- enter: (node) => {
1572
- if (node.name === "Image") {
1573
- const urlNode = node.node.getChild("URL");
1574
- if (urlNode) {
1575
- const hide = state.readOnly || cursor < node.from || cursor > node.to;
1576
- const url = state.sliceDoc(urlNode.from, urlNode.to);
1577
- decorations.push(Decoration5.replace({
1578
- block: true,
1579
- widget: new ImageWidget(url)
1580
- }).range(hide ? node.from : node.to, node.to));
1581
- }
1582
- }
1583
- },
1584
- from,
1585
- to
1586
- });
1587
- return decorations;
1588
- };
1589
- var ImageWidget = class extends WidgetType3 {
1590
- constructor(_url) {
1591
- super();
1592
- this._url = _url;
1593
- }
1594
- eq(other) {
1595
- return this._url === other._url;
1596
- }
1597
- toDOM(view) {
1598
- const img = document.createElement("img");
1599
- img.setAttribute("src", this._url);
1600
- img.setAttribute("class", "cm-image");
1601
- img.onload = () => img.classList.add("cm-loaded-image");
1602
- return img;
1603
- }
1604
- };
1605
-
1606
- // packages/ui/react-ui-editor/src/extensions/link.ts
1607
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1608
- import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1609
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin4, WidgetType as WidgetType4 } from "@codemirror/view";
1610
- import { tooltipContent } from "@dxos/react-ui-theme";
1611
- var link = (options = {}) => {
1612
- const extensions = [
1613
- ViewPlugin4.fromClass(class {
1614
- constructor(view) {
1615
- this.decorations = buildDecorations3(view, options);
1616
- }
1617
- update(update2) {
1618
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
1619
- this.decorations = buildDecorations3(update2.view, options);
1620
- }
1621
- }
1622
- }, {
1623
- decorations: (v) => v.decorations
1624
- })
1625
- ];
1626
- if (options.onHover) {
1627
- extensions.push(
1628
- // https://codemirror.net/examples/tooltip
1629
- // https://codemirror.net/docs/ref/#view.hoverTooltip
1630
- // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1631
- hoverTooltip2((view, pos, side) => {
1632
- const syntax = syntaxTree3(view.state).resolveInner(pos, side);
1633
- let link2 = null;
1634
- for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1635
- link2 = node.name === "Link" ? node : null;
1636
- }
1637
- const url = link2 && link2.getChild("URL");
1638
- if (!url || !link2) {
1639
- return null;
1640
- }
1641
- const urlText = view.state.sliceDoc(url.from, url.to);
1642
- return {
1643
- pos: link2.from,
1644
- end: link2.to,
1645
- above: true,
1646
- create: () => {
1647
- const el = document.createElement("div");
1648
- el.className = tooltipContent({}, "pli-2 plb-1");
1649
- options.onHover(el, urlText);
1650
- return {
1651
- dom: el,
1652
- offset: {
1653
- x: 0,
1654
- y: 4
1655
- }
1656
- };
1657
- }
1658
- };
1659
- })
1660
- );
1661
- }
1662
- return extensions;
1663
- };
1664
- var LinkButton = class extends WidgetType4 {
1665
- constructor(_url, _onAttach) {
1666
- super();
1667
- this._url = _url;
1668
- this._onAttach = _onAttach;
1669
- }
1670
- eq(other) {
1671
- return this._url === other._url;
1672
- }
1673
- toDOM(view) {
1674
- const el = document.createElement("span");
1675
- this._onAttach(el, this._url);
1676
- return el;
1677
- }
1678
- };
1679
- var LinkText = class extends WidgetType4 {
1680
- constructor(_text, _url) {
1681
- super();
1682
- this._text = _text;
1683
- this._url = _url;
1684
- }
1685
- eq(other) {
1686
- return this._url === other._url;
1687
- }
1688
- toDOM(view) {
1689
- const link2 = document.createElement("a");
1690
- link2.setAttribute("class", "cm-link");
1691
- link2.textContent = this._text;
1692
- if (this._url) {
1693
- link2.setAttribute("rel", "noreferrer");
1694
- link2.setAttribute("target", "_blank");
1695
- link2.href = this._url;
1696
- } else {
1697
- link2.onclick = () => {
1698
- view.dispatch({
1699
- selection: {
1700
- anchor: view.posAtDOM(link2)
1701
- }
1702
- });
1703
- };
1704
- }
1705
- return link2;
1706
- }
1707
- };
1708
- var buildDecorations3 = (view, options) => {
1709
- const builder = new RangeSetBuilder2();
1710
- const { state } = view;
1711
- const cursor = state.selection.main.head;
1712
- for (const { from, to } of view.visibleRanges) {
1713
- syntaxTree3(state).iterate({
1714
- enter: (node) => {
1715
- if (node.name === "Link") {
1716
- const marks2 = node.node.getChildren("LinkMark");
1717
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
1718
- const urlNode = node.node.getChild("URL");
1719
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
1720
- if (!url) {
1721
- return false;
1722
- }
1723
- if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
1724
- builder.add(node.from, node.to, Decoration6.replace({
1725
- widget: new LinkText(text, options.link ? url : void 0)
1726
- }));
1727
- }
1728
- if (options.onRender) {
1729
- builder.add(node.to, node.to, Decoration6.replace({
1730
- widget: new LinkButton(url, options.onRender)
1731
- }));
1732
- }
1733
- return false;
1734
- }
1735
- },
1736
- from,
1737
- to
1738
- });
1739
- }
1740
- return builder.finish();
1741
- };
1742
-
1743
- // packages/ui/react-ui-editor/src/extensions/listener.ts
1744
- import { EditorView as EditorView9 } from "@codemirror/view";
1745
- var listener = ({ onFocus, onChange }) => {
1746
- const extensions = [];
1747
- onFocus && extensions.push(EditorView9.focusChangeEffect.of((_, focused) => {
1748
- onFocus(focused);
1749
- return null;
1750
- }));
1751
- onChange && extensions.push(EditorView9.updateListener.of((update2) => {
1752
- onChange(update2.state.doc.toString());
1753
- }));
1754
- return extensions;
1755
- };
1756
-
1757
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1758
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1759
- import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1760
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1761
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1762
- import { languages } from "@codemirror/language-data";
1763
- import { searchKeymap } from "@codemirror/search";
1764
- import { EditorState } from "@codemirror/state";
1765
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1766
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView10, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1767
-
1768
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1769
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1770
- import { HighlightStyle } from "@codemirror/language";
1771
- import { tags, styleTags, Tag } from "@lezer/highlight";
1772
- import { Table } from "@lezer/markdown";
1773
- var markdownTags = {
1774
- Blockquote: Tag.define(),
1775
- CodeMark: Tag.define(),
1776
- CodeText: Tag.define(),
1777
- EmphasisMark: Tag.define(),
1778
- HeaderMark: Tag.define(),
1779
- InlineCode: Tag.define(),
1780
- LinkLabel: Tag.define(),
1781
- LinkReference: Tag.define(),
1782
- ListMark: Tag.define(),
1783
- QuoteMark: Tag.define(),
1784
- URL: Tag.define(),
1785
- // Custom.
1786
- TableCell: Tag.define()
1465
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1466
+ import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1467
+ import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1468
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1469
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1470
+ import { languages } from "@codemirror/language-data";
1471
+ import { searchKeymap } from "@codemirror/search";
1472
+ import { EditorState } from "@codemirror/state";
1473
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1474
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1475
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1476
+
1477
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1478
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1479
+ import { HighlightStyle } from "@codemirror/language";
1480
+ import { tags, styleTags, Tag } from "@lezer/highlight";
1481
+ import { Table } from "@lezer/markdown";
1482
+ var markdownTags = {
1483
+ Blockquote: Tag.define(),
1484
+ CodeMark: Tag.define(),
1485
+ CodeText: Tag.define(),
1486
+ EmphasisMark: Tag.define(),
1487
+ HeaderMark: Tag.define(),
1488
+ InlineCode: Tag.define(),
1489
+ LinkLabel: Tag.define(),
1490
+ LinkReference: Tag.define(),
1491
+ ListMark: Tag.define(),
1492
+ QuoteMark: Tag.define(),
1493
+ URL: Tag.define(),
1494
+ // Custom.
1495
+ TableCell: Tag.define()
1787
1496
  };
1788
1497
  Table.defineNodes?.forEach((node) => {
1789
1498
  switch (node?.name) {
@@ -1860,7 +1569,7 @@ var markdownHighlightStyle = (readonly) => {
1860
1569
  markdownTags.QuoteMark,
1861
1570
  markdownTags.EmphasisMark
1862
1571
  ],
1863
- class: readonly ? "hidden" : mark
1572
+ class: mark
1864
1573
  },
1865
1574
  // E.g., code block language (after ```).
1866
1575
  {
@@ -1868,7 +1577,7 @@ var markdownHighlightStyle = (readonly) => {
1868
1577
  tags.function(tags.variableName),
1869
1578
  tags.labelName
1870
1579
  ],
1871
- class: readonly ? "hidden" : codeMark
1580
+ class: codeMark
1872
1581
  },
1873
1582
  {
1874
1583
  tag: [
@@ -1879,27 +1588,27 @@ var markdownHighlightStyle = (readonly) => {
1879
1588
  // Headings.
1880
1589
  {
1881
1590
  tag: tags.heading1,
1882
- class: heading(1, readonly)
1591
+ class: heading(1)
1883
1592
  },
1884
1593
  {
1885
1594
  tag: tags.heading2,
1886
- class: heading(2, readonly)
1595
+ class: heading(2)
1887
1596
  },
1888
1597
  {
1889
1598
  tag: tags.heading3,
1890
- class: heading(3, readonly)
1599
+ class: heading(3)
1891
1600
  },
1892
1601
  {
1893
1602
  tag: tags.heading4,
1894
- class: heading(4, readonly)
1603
+ class: heading(4)
1895
1604
  },
1896
1605
  {
1897
1606
  tag: tags.heading5,
1898
- class: heading(5, readonly)
1607
+ class: heading(5)
1899
1608
  },
1900
1609
  {
1901
1610
  tag: tags.heading6,
1902
- class: heading(6, readonly)
1611
+ class: heading(6)
1903
1612
  },
1904
1613
  // Emphasis.
1905
1614
  {
@@ -1925,7 +1634,7 @@ var markdownHighlightStyle = (readonly) => {
1925
1634
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
1926
1635
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
1927
1636
  // the code, but all other CSS properties will be inherited.
1928
- // IMPORTANT: Therefore, the fenced code block will use the base editor font.
1637
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
1929
1638
  {
1930
1639
  tag: [
1931
1640
  markdownTags.CodeText,
@@ -1954,11 +1663,11 @@ var markdownHighlightStyle = (readonly) => {
1954
1663
  };
1955
1664
 
1956
1665
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1957
- var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1666
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1958
1667
  return [
1668
+ lineWrapping && EditorView7.lineWrapping,
1959
1669
  EditorState.allowMultipleSelections.of(true),
1960
1670
  EditorState.tabSize.of(2),
1961
- EditorView10.lineWrapping,
1962
1671
  // https://github.com/codemirror/basic-setup
1963
1672
  bracketMatching2(),
1964
1673
  closeBrackets2(),
@@ -1968,7 +1677,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1968
1677
  highlightActiveLine2(),
1969
1678
  history2(),
1970
1679
  indentOnInput(),
1971
- _placeholder ? placeholder2(_placeholder) : [],
1680
+ _placeholder && placeholder2(_placeholder),
1972
1681
  // Main extension.
1973
1682
  // https://github.com/codemirror/lang-markdown
1974
1683
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1990,7 +1699,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1990
1699
  // https://github.com/codemirror/theme-one-dark
1991
1700
  themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
1992
1701
  // Custom styles.
1993
- syntaxHighlighting2(markdownHighlightStyle(readonly)),
1702
+ syntaxHighlighting2(markdownHighlightStyle()),
1994
1703
  keymap4.of([
1995
1704
  // https://codemirror.net/docs/ref/#commands.indentWithTab
1996
1705
  indentWithTab,
@@ -2003,47 +1712,580 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
2003
1712
  // https://codemirror.net/docs/ref/#commands.standardKeymap
2004
1713
  ...standardKeymap
2005
1714
  ])
2006
- ];
1715
+ ].filter(isNotFalsy2);
2007
1716
  };
2008
1717
 
2009
- // packages/ui/react-ui-editor/src/extensions/mention.ts
2010
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
2011
- var mention = ({ onSearch }) => {
2012
- return autocompletion2({
2013
- // TODO(burdon): Not working.
2014
- activateOnTyping: true,
2015
- // activateOnTypingDelay: 100,
2016
- // selectOnOpen: true,
2017
- closeOnBlur: false,
2018
- // defaultKeymap: false,
2019
- icons: false,
2020
- override: [
2021
- (context) => {
2022
- console.log(context);
2023
- const match = context.matchBefore(/@(\w+)?/);
2024
- if (!match || match.from === match.to && !context.explicit) {
2025
- return null;
2026
- }
2027
- return {
2028
- from: match.from,
2029
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
2030
- label: `@${value}`
2031
- }))
2032
- };
2033
- }
2034
- ]
2035
- });
1718
+ // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1719
+ import { syntaxTree } from "@codemirror/language";
1720
+ import { RangeSetBuilder } from "@codemirror/state";
1721
+ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Decoration3, WidgetType as WidgetType2 } from "@codemirror/view";
1722
+ import { mx as mx2 } from "@dxos/react-ui-theme";
1723
+ var styles3 = EditorView8.baseTheme({
1724
+ "& .cm-code": {
1725
+ fontFamily: getToken("fontFamily.mono", []).join(",")
1726
+ },
1727
+ "& .cm-codeblock": {
1728
+ width: "100%",
1729
+ paddingInline: "1rem !important",
1730
+ position: "relative",
1731
+ "&::after": {
1732
+ content: '""',
1733
+ position: "absolute",
1734
+ inset: 0
1735
+ }
1736
+ },
1737
+ "&light .cm-codeblock": {
1738
+ "&::after": {
1739
+ background: getToken("extend.semanticColors.input.light"),
1740
+ mixBlendMode: "darken"
1741
+ }
1742
+ },
1743
+ "&dark .cm-codeblock": {
1744
+ "&::after": {
1745
+ background: getToken("extend.semanticColors.input.dark"),
1746
+ mixBlendMode: "lighten"
1747
+ }
1748
+ },
1749
+ "& .cm-codeblock-first": {
1750
+ display: "inline-block",
1751
+ "&::after": {
1752
+ borderTopLeftRadius: ".5rem",
1753
+ borderTopRightRadius: ".5rem"
1754
+ }
1755
+ },
1756
+ "& .cm-codeblock-last": {
1757
+ display: "inline-block",
1758
+ "&::after": {
1759
+ borderBottomLeftRadius: ".5rem",
1760
+ borderBottomRightRadius: ".5rem"
1761
+ }
1762
+ }
1763
+ });
1764
+ var getLineRange = (lines, from, to) => {
1765
+ const start = lines.findIndex((line) => line.from >= from);
1766
+ const end = lines.findIndex((line) => line.to >= to);
1767
+ return [
1768
+ start,
1769
+ end
1770
+ ];
2036
1771
  };
2037
-
2038
- // packages/ui/react-ui-editor/src/extensions/outliner.ts
2039
- var outliner = (options = {}) => {
2040
- return [];
1772
+ var LineWidget = class extends WidgetType2 {
1773
+ constructor(_className) {
1774
+ super();
1775
+ this._className = _className;
1776
+ }
1777
+ toDOM() {
1778
+ const el = document.createElement("span");
1779
+ el.innerHTML = "&nbsp;";
1780
+ el.className = mx2("cm-code cm-codeblock", this._className);
1781
+ return el;
1782
+ }
1783
+ };
1784
+ var top = new LineWidget("cm-codeblock-first");
1785
+ var bottom = new LineWidget("cm-codeblock-last");
1786
+ var buildDecorations = (view) => {
1787
+ const builder = new RangeSetBuilder();
1788
+ const { state } = view;
1789
+ const blocks = view.viewportLineBlocks;
1790
+ const cursor = state.selection.main.head;
1791
+ for (const { from, to } of view.visibleRanges) {
1792
+ syntaxTree(state).iterate({
1793
+ enter: (node) => {
1794
+ if (node.name === "FencedCode") {
1795
+ const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
1796
+ const range = getLineRange(blocks, node.from, node.to);
1797
+ for (let i = range[0]; i <= range[1]; i++) {
1798
+ const block = blocks[i];
1799
+ if (!edit && (i === range[0] || i === range[1])) {
1800
+ builder.add(block.from, block.to, Decoration3.replace({
1801
+ widget: i === range[0] ? top : bottom
1802
+ }));
1803
+ } else {
1804
+ builder.add(block.from, block.from, Decoration3.line({
1805
+ class: mx2("cm-code cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1806
+ }));
1807
+ }
1808
+ }
1809
+ }
1810
+ },
1811
+ from,
1812
+ to
1813
+ });
1814
+ }
1815
+ return builder.finish();
1816
+ };
1817
+ var code2 = () => {
1818
+ return [
1819
+ ViewPlugin3.fromClass(class {
1820
+ constructor(view) {
1821
+ this.decorations = buildDecorations(view);
1822
+ }
1823
+ update(update2) {
1824
+ this.decorations = buildDecorations(update2.view);
1825
+ }
1826
+ }, {
1827
+ decorations: (value) => value.decorations
1828
+ }),
1829
+ styles3
1830
+ ];
1831
+ };
1832
+
1833
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1834
+ import { snippet } from "@codemirror/autocomplete";
1835
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1836
+ import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1837
+ import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1838
+ var setHeading = (level) => (view) => {
1839
+ const { selection: { ranges }, doc } = view.state;
1840
+ const changes = [];
1841
+ for (const range of ranges) {
1842
+ const { number } = doc.lineAt(range.anchor);
1843
+ const { from, to } = doc.line(number);
1844
+ const line = doc.sliceString(from, to);
1845
+ const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1846
+ const current = marks?.length ?? 0;
1847
+ if (level !== current) {
1848
+ changes.push({
1849
+ from,
1850
+ to: from + current + (spaces?.length ?? 0),
1851
+ insert: "#".repeat(level) + (level > 0 ? " " : "")
1852
+ });
1853
+ }
1854
+ }
1855
+ if (changes.length) {
1856
+ view.dispatch({
1857
+ changes
1858
+ });
1859
+ }
1860
+ return true;
1861
+ };
1862
+ var toggleStyle = (mark2) => (view) => {
1863
+ const { ranges } = view.state.selection;
1864
+ for (const range of ranges) {
1865
+ if (range.from === range.to) {
1866
+ return false;
1867
+ }
1868
+ view.dispatch({
1869
+ changes: [
1870
+ {
1871
+ from: range.from,
1872
+ insert: mark2
1873
+ },
1874
+ {
1875
+ from: range.to,
1876
+ insert: mark2
1877
+ }
1878
+ ]
1879
+ });
1880
+ }
1881
+ return true;
1882
+ };
1883
+ var snippets = {
1884
+ codeblock: snippet([
1885
+ "```#{lang}",
1886
+ " #{}",
1887
+ "```"
1888
+ ].join("\n")),
1889
+ table: snippet([
1890
+ "| #{col1} | #{col2} |",
1891
+ "| ---- | ---- |",
1892
+ "| #{val1} | #{val2} |",
1893
+ "| #{val3} | #{val4} |"
1894
+ ].join("\n"))
1895
+ };
1896
+ var insertCodeblock = (view) => {
1897
+ const { selection: { main }, doc } = view.state;
1898
+ const { number } = doc.lineAt(main.anchor);
1899
+ const { from } = doc.line(number);
1900
+ snippets.codeblock(view, null, from, from);
1901
+ };
1902
+ var insertTable = (view) => {
1903
+ const { selection: { main }, doc } = view.state;
1904
+ const { number } = doc.lineAt(main.anchor);
1905
+ const { from } = doc.line(number);
1906
+ snippets.table(view, null, from, from);
1907
+ };
1908
+ var toggleBold = toggleStyle("**");
1909
+ var toggleItalic = toggleStyle("_");
1910
+ var toggleStrikethrough = toggleStyle("~~");
1911
+ var toggleList = (view) => {
1912
+ };
1913
+ var formatting = (options = {}) => {
1914
+ return [
1915
+ keymap5.of([
1916
+ {
1917
+ key: "meta-b",
1918
+ run: toggleBold
1919
+ }
1920
+ ]),
1921
+ styling()
1922
+ ];
1923
+ };
1924
+ var styling = () => {
1925
+ const buildDecorations7 = (view) => {
1926
+ const builder = new RangeSetBuilder2();
1927
+ const { state } = view;
1928
+ const cursor = state.selection.main.head;
1929
+ const replace = (node, marks) => {
1930
+ if (cursor <= node.from || cursor >= node.to) {
1931
+ for (const mark2 of marks) {
1932
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
1933
+ }
1934
+ }
1935
+ };
1936
+ for (const { from, to } of view.visibleRanges) {
1937
+ syntaxTree2(state).iterate({
1938
+ enter: (node) => {
1939
+ switch (node.name) {
1940
+ case "Emphasis":
1941
+ case "StrongEmphasis": {
1942
+ const marks = node.node.getChildren("EmphasisMark");
1943
+ replace(node, marks);
1944
+ break;
1945
+ }
1946
+ case "Strikethrough": {
1947
+ const marks = node.node.getChildren("StrikethroughMark");
1948
+ replace(node, marks);
1949
+ break;
1950
+ }
1951
+ }
1952
+ },
1953
+ from,
1954
+ to
1955
+ });
1956
+ }
1957
+ return builder.finish();
1958
+ };
1959
+ return [
1960
+ ViewPlugin4.fromClass(class {
1961
+ constructor(view) {
1962
+ this.decorations = buildDecorations7(view);
1963
+ }
1964
+ update(update2) {
1965
+ this.decorations = buildDecorations7(update2.view);
1966
+ }
1967
+ }, {
1968
+ decorations: (value) => value.decorations
1969
+ })
1970
+ ];
1971
+ };
1972
+
1973
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1974
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1975
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1976
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1977
+ var buildDecorations2 = (view) => {
1978
+ const builder = new RangeSetBuilder3();
1979
+ const { state } = view;
1980
+ const cursor = state.selection.main.head;
1981
+ for (const { from, to } of view.visibleRanges) {
1982
+ syntaxTree3(state).iterate({
1983
+ enter: (node) => {
1984
+ switch (node.name) {
1985
+ case "ATXHeading1":
1986
+ case "ATXHeading2":
1987
+ case "ATXHeading3":
1988
+ case "ATXHeading4":
1989
+ case "ATXHeading5":
1990
+ case "ATXHeading6": {
1991
+ const mark2 = node.node.getChild("HeaderMark");
1992
+ if (mark2) {
1993
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
1994
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
1995
+ }
1996
+ }
1997
+ }
1998
+ }
1999
+ },
2000
+ from,
2001
+ to
2002
+ });
2003
+ }
2004
+ return builder.finish();
2005
+ };
2006
+ var heading2 = () => {
2007
+ return [
2008
+ ViewPlugin5.fromClass(class {
2009
+ constructor(view) {
2010
+ this.decorations = buildDecorations2(view);
2011
+ }
2012
+ update(update2) {
2013
+ this.decorations = buildDecorations2(update2.view);
2014
+ }
2015
+ }, {
2016
+ decorations: (value) => value.decorations
2017
+ })
2018
+ ];
2019
+ };
2020
+
2021
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
2022
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2023
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2024
+ import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2025
+ var styles4 = EditorView9.baseTheme({
2026
+ "& .cm-hr": {
2027
+ // Note that block-level decorations should not have vertical margins,
2028
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2029
+ }
2030
+ });
2031
+ var HorizontalRuleWidget = class extends WidgetType3 {
2032
+ toDOM() {
2033
+ const el = document.createElement("div");
2034
+ el.className = "cm-hr";
2035
+ return el;
2036
+ }
2037
+ };
2038
+ var decoration = Decoration6.replace({
2039
+ widget: new HorizontalRuleWidget()
2040
+ });
2041
+ var buildDecorations3 = (view) => {
2042
+ const builder = new RangeSetBuilder4();
2043
+ const { state } = view;
2044
+ const cursor = state.selection.main.head;
2045
+ for (const { from, to } of view.visibleRanges) {
2046
+ syntaxTree4(state).iterate({
2047
+ enter: (node) => {
2048
+ if (node.name === "HorizontalRule") {
2049
+ if (cursor <= node.from || cursor >= node.to) {
2050
+ builder.add(node.from, node.to, decoration);
2051
+ }
2052
+ }
2053
+ },
2054
+ from,
2055
+ to
2056
+ });
2057
+ }
2058
+ return builder.finish();
2059
+ };
2060
+ var hr = () => {
2061
+ return [
2062
+ ViewPlugin6.fromClass(class {
2063
+ constructor(view) {
2064
+ this.decorations = buildDecorations3(view);
2065
+ }
2066
+ update(update2) {
2067
+ this.decorations = buildDecorations3(update2.view);
2068
+ }
2069
+ }, {
2070
+ decorations: (value) => value.decorations
2071
+ }),
2072
+ styles4
2073
+ ];
2074
+ };
2075
+
2076
+ // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
2077
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2078
+ import { StateField as StateField3 } from "@codemirror/state";
2079
+ import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
2080
+ var image = (options = {}) => {
2081
+ return StateField3.define({
2082
+ create: (state) => {
2083
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
2084
+ },
2085
+ update: (value, tr) => {
2086
+ if (!tr.docChanged && !tr.selection) {
2087
+ return value;
2088
+ }
2089
+ const cursor = tr.state.selection.main.head;
2090
+ const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
2091
+ let from = Math.min(cursor, oldCursor);
2092
+ let to = Math.max(cursor, oldCursor);
2093
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
2094
+ from = Math.min(from, fromB);
2095
+ to = Math.max(to, toB);
2096
+ });
2097
+ from = tr.state.doc.lineAt(from).from;
2098
+ to = tr.state.doc.lineAt(to).to;
2099
+ return value.map(tr.changes).update({
2100
+ filterFrom: from,
2101
+ filterTo: to,
2102
+ filter: () => false,
2103
+ add: buildDecorations4(from, to, tr.state)
2104
+ });
2105
+ },
2106
+ provide: (field) => EditorView10.decorations.from(field)
2107
+ });
2108
+ };
2109
+ var buildDecorations4 = (from, to, state) => {
2110
+ const decorations = [];
2111
+ const cursor = state.selection.main.head;
2112
+ syntaxTree5(state).iterate({
2113
+ enter: (node) => {
2114
+ if (node.name === "Image") {
2115
+ const urlNode = node.node.getChild("URL");
2116
+ if (urlNode) {
2117
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
2118
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
2119
+ decorations.push(Decoration7.replace({
2120
+ block: true,
2121
+ widget: new ImageWidget(url)
2122
+ }).range(hide ? node.from : node.to, node.to));
2123
+ }
2124
+ }
2125
+ },
2126
+ from,
2127
+ to
2128
+ });
2129
+ return decorations;
2130
+ };
2131
+ var ImageWidget = class extends WidgetType4 {
2132
+ constructor(_url) {
2133
+ super();
2134
+ this._url = _url;
2135
+ }
2136
+ eq(other) {
2137
+ return this._url === other._url;
2138
+ }
2139
+ toDOM(view) {
2140
+ const img = document.createElement("img");
2141
+ img.setAttribute("src", this._url);
2142
+ img.setAttribute("class", "cm-image");
2143
+ img.onload = () => img.classList.add("cm-loaded-image");
2144
+ return img;
2145
+ }
2146
+ };
2147
+
2148
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
2149
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2150
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
2151
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
2152
+ import { tooltipContent } from "@dxos/react-ui-theme";
2153
+ var link = (options = {}) => {
2154
+ const extensions = [
2155
+ ViewPlugin7.fromClass(class {
2156
+ constructor(view) {
2157
+ this.decorations = buildDecorations5(view, options);
2158
+ }
2159
+ update(update2) {
2160
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2161
+ this.decorations = buildDecorations5(update2.view, options);
2162
+ }
2163
+ }
2164
+ }, {
2165
+ decorations: (value) => value.decorations
2166
+ })
2167
+ ];
2168
+ if (options.onHover) {
2169
+ extensions.push(
2170
+ // https://codemirror.net/examples/tooltip
2171
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
2172
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
2173
+ hoverTooltip2((view, pos, side) => {
2174
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
2175
+ let link2 = null;
2176
+ for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
2177
+ link2 = node.name === "Link" ? node : null;
2178
+ }
2179
+ const url = link2 && link2.getChild("URL");
2180
+ if (!url || !link2) {
2181
+ return null;
2182
+ }
2183
+ const urlText = view.state.sliceDoc(url.from, url.to);
2184
+ return {
2185
+ pos: link2.from,
2186
+ end: link2.to,
2187
+ above: true,
2188
+ create: () => {
2189
+ const el = document.createElement("div");
2190
+ el.className = tooltipContent({}, "pli-2 plb-1");
2191
+ options.onHover(el, urlText);
2192
+ return {
2193
+ dom: el,
2194
+ offset: {
2195
+ x: 0,
2196
+ y: 4
2197
+ }
2198
+ };
2199
+ }
2200
+ };
2201
+ })
2202
+ );
2203
+ }
2204
+ return extensions;
2205
+ };
2206
+ var LinkButton = class extends WidgetType5 {
2207
+ constructor(_url, _onAttach) {
2208
+ super();
2209
+ this._url = _url;
2210
+ this._onAttach = _onAttach;
2211
+ }
2212
+ eq(other) {
2213
+ return this._url === other._url;
2214
+ }
2215
+ toDOM(view) {
2216
+ const el = document.createElement("span");
2217
+ this._onAttach(el, this._url);
2218
+ return el;
2219
+ }
2220
+ };
2221
+ var LinkText = class extends WidgetType5 {
2222
+ constructor(_text, _url) {
2223
+ super();
2224
+ this._text = _text;
2225
+ this._url = _url;
2226
+ }
2227
+ eq(other) {
2228
+ return this._url === other._url;
2229
+ }
2230
+ toDOM(view) {
2231
+ const link2 = document.createElement("a");
2232
+ link2.setAttribute("class", "cm-link");
2233
+ link2.textContent = this._text;
2234
+ if (this._url) {
2235
+ link2.setAttribute("rel", "noreferrer");
2236
+ link2.setAttribute("target", "_blank");
2237
+ link2.href = this._url;
2238
+ } else {
2239
+ link2.onclick = () => {
2240
+ view.dispatch({
2241
+ selection: {
2242
+ anchor: view.posAtDOM(link2)
2243
+ }
2244
+ });
2245
+ };
2246
+ }
2247
+ return link2;
2248
+ }
2249
+ };
2250
+ var buildDecorations5 = (view, options) => {
2251
+ const builder = new RangeSetBuilder5();
2252
+ const { state } = view;
2253
+ const cursor = state.selection.main.head;
2254
+ for (const { from, to } of view.visibleRanges) {
2255
+ syntaxTree6(state).iterate({
2256
+ enter: (node) => {
2257
+ if (node.name === "Link") {
2258
+ const marks = node.node.getChildren("LinkMark");
2259
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2260
+ const urlNode = node.node.getChild("URL");
2261
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2262
+ if (!url) {
2263
+ return false;
2264
+ }
2265
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2266
+ builder.add(node.from, node.to, Decoration8.replace({
2267
+ widget: new LinkText(text, options.link ? url : void 0)
2268
+ }));
2269
+ }
2270
+ if (options.onRender) {
2271
+ builder.add(node.to, node.to, Decoration8.replace({
2272
+ widget: new LinkButton(url, options.onRender)
2273
+ }));
2274
+ }
2275
+ return false;
2276
+ }
2277
+ },
2278
+ from,
2279
+ to
2280
+ });
2281
+ }
2282
+ return builder.finish();
2041
2283
  };
2042
2284
 
2043
- // packages/ui/react-ui-editor/src/extensions/table.ts
2044
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2045
- import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField4 } from "@codemirror/state";
2046
- import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType5 } from "@codemirror/view";
2285
+ // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2286
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2287
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2288
+ import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
2047
2289
  var table = (options = {}) => {
2048
2290
  return StateField4.define({
2049
2291
  create: (state) => update(state, options),
@@ -2052,7 +2294,7 @@ var table = (options = {}) => {
2052
2294
  });
2053
2295
  };
2054
2296
  var update = (state, options) => {
2055
- const builder = new RangeSetBuilder3();
2297
+ const builder = new RangeSetBuilder6();
2056
2298
  const cursor = state.selection.main.head;
2057
2299
  const tables = [];
2058
2300
  const getTable = () => tables[tables.length - 1];
@@ -2060,7 +2302,7 @@ var update = (state, options) => {
2060
2302
  const table2 = getTable();
2061
2303
  return table2.rows?.[table2.rows.length - 1];
2062
2304
  };
2063
- syntaxTree4(state).iterate({
2305
+ syntaxTree7(state).iterate({
2064
2306
  enter: (node) => {
2065
2307
  switch (node.name) {
2066
2308
  case "Table": {
@@ -2092,16 +2334,16 @@ var update = (state, options) => {
2092
2334
  });
2093
2335
  tables.forEach((table2) => {
2094
2336
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2095
- hide && builder.add(table2.from, table2.to, Decoration7.replace({
2337
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2096
2338
  widget: new TableWidget(table2)
2097
2339
  }));
2098
- builder.add(table2.from, table2.to, Decoration7.mark({
2340
+ builder.add(table2.from, table2.to, Decoration9.mark({
2099
2341
  class: "cm-table"
2100
2342
  }));
2101
2343
  });
2102
2344
  return builder.finish();
2103
2345
  };
2104
- var TableWidget = class extends WidgetType5 {
2346
+ var TableWidget = class extends WidgetType6 {
2105
2347
  constructor(_table) {
2106
2348
  super();
2107
2349
  this._table = _table;
@@ -2138,10 +2380,10 @@ var TableWidget = class extends WidgetType5 {
2138
2380
  }
2139
2381
  };
2140
2382
 
2141
- // packages/ui/react-ui-editor/src/extensions/tasklist.ts
2142
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2143
- import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2144
- import { EditorView as EditorView12, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2383
+ // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2384
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2385
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2386
+ import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2145
2387
  var styles5 = EditorView12.baseTheme({
2146
2388
  "& .cm-task": {
2147
2389
  color: getToken("extend.colors.blue.500")
@@ -2151,7 +2393,7 @@ var styles5 = EditorView12.baseTheme({
2151
2393
  marginRight: "4px"
2152
2394
  }
2153
2395
  });
2154
- var CheckboxWidget = class extends WidgetType6 {
2396
+ var CheckboxWidget = class extends WidgetType7 {
2155
2397
  constructor(_checked) {
2156
2398
  super();
2157
2399
  this._checked = _checked;
@@ -2188,23 +2430,23 @@ var CheckboxWidget = class extends WidgetType6 {
2188
2430
  return false;
2189
2431
  }
2190
2432
  };
2191
- var checkedDecoration = Decoration8.replace({
2433
+ var checkedDecoration = Decoration10.replace({
2192
2434
  widget: new CheckboxWidget(true)
2193
2435
  });
2194
- var uncheckedDecoration = Decoration8.replace({
2436
+ var uncheckedDecoration = Decoration10.replace({
2195
2437
  widget: new CheckboxWidget(false)
2196
2438
  });
2197
- var buildDecorations4 = (view) => {
2198
- const builder = new RangeSetBuilder4();
2439
+ var buildDecorations6 = (view) => {
2440
+ const builder = new RangeSetBuilder7();
2199
2441
  const { state } = view;
2200
2442
  const cursor = state.selection.main.head;
2201
2443
  for (const { from, to } of view.visibleRanges) {
2202
- syntaxTree5(state).iterate({
2444
+ syntaxTree8(state).iterate({
2203
2445
  enter: (node) => {
2204
2446
  if (node.name === "TaskMarker") {
2205
2447
  if (cursor < node.from || cursor > node.to) {
2206
2448
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2207
- builder.add(node.from - 2, node.from - 1, Decoration8.mark({
2449
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2208
2450
  class: "cm-task"
2209
2451
  }));
2210
2452
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2219,24 +2461,58 @@ var buildDecorations4 = (view) => {
2219
2461
  };
2220
2462
  var tasklist = (options = {}) => {
2221
2463
  return [
2222
- ViewPlugin5.fromClass(class {
2464
+ ViewPlugin8.fromClass(class {
2223
2465
  constructor(view) {
2224
- this.decorations = buildDecorations4(view);
2466
+ this.decorations = buildDecorations6(view);
2225
2467
  }
2226
2468
  update(update2) {
2227
2469
  if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2228
- this.decorations = buildDecorations4(update2.view);
2470
+ this.decorations = buildDecorations6(update2.view);
2229
2471
  }
2230
2472
  }
2231
2473
  }, {
2232
- decorations: (v) => v.decorations
2474
+ decorations: (value) => value.decorations
2233
2475
  }),
2234
2476
  styles5
2235
2477
  ];
2236
2478
  };
2237
2479
 
2480
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
2481
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
2482
+ var mention = ({ onSearch }) => {
2483
+ return autocompletion2({
2484
+ // TODO(burdon): Not working.
2485
+ activateOnTyping: true,
2486
+ // activateOnTypingDelay: 100,
2487
+ // selectOnOpen: true,
2488
+ closeOnBlur: false,
2489
+ // defaultKeymap: false,
2490
+ icons: false,
2491
+ override: [
2492
+ (context) => {
2493
+ console.log(context);
2494
+ const match = context.matchBefore(/@(\w+)?/);
2495
+ if (!match || match.from === match.to && !context.explicit) {
2496
+ return null;
2497
+ }
2498
+ return {
2499
+ from: match.from,
2500
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
2501
+ label: `@${value}`
2502
+ }))
2503
+ };
2504
+ }
2505
+ ]
2506
+ });
2507
+ };
2508
+
2509
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
2510
+ var outliner = (options = {}) => {
2511
+ return [];
2512
+ };
2513
+
2238
2514
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2239
- import { keymap as keymap5 } from "@codemirror/view";
2515
+ import { keymap as keymap6 } from "@codemirror/view";
2240
2516
  var defaultItems = [
2241
2517
  "hello world!",
2242
2518
  "this is a test.",
@@ -2246,7 +2522,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2246
2522
  let t;
2247
2523
  let idx = 0;
2248
2524
  return [
2249
- keymap5.of([
2525
+ keymap6.of([
2250
2526
  {
2251
2527
  // Reset.
2252
2528
  key: "alt-meta-'",
@@ -2292,27 +2568,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2292
2568
  ];
2293
2569
  };
2294
2570
 
2295
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2296
- import { yCollab } from "y-codemirror.next";
2297
-
2298
- // packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
2299
- import * as Y from "yjs";
2300
- import { arrayToString, stringToArray } from "@dxos/util";
2301
- var cursorConverter2 = (text) => ({
2302
- toCursor: (index, assoc) => {
2303
- return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
2304
- },
2305
- fromCursor: (cursor) => {
2306
- return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
2307
- }
2308
- });
2309
-
2310
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2311
- var yjs = (content, awareness2) => [
2312
- Cursor.converter.of(cursorConverter2(content)),
2313
- yCollab(content, awareness2)
2314
- ];
2315
-
2316
2571
  // packages/ui/react-ui-editor/src/themes/default.ts
2317
2572
  import get3 from "lodash.get";
2318
2573
  var defaultTheme = {
@@ -2344,8 +2599,9 @@ var defaultTheme = {
2344
2599
  outline: "none"
2345
2600
  },
2346
2601
  ".cm-scroller": {
2347
- overflow: "visible",
2348
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
2602
+ // overflow: 'visible',
2603
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
2604
+ lineHeight: 1.4
2349
2605
  },
2350
2606
  ".cm-content": {
2351
2607
  padding: 0,
@@ -2353,11 +2609,11 @@ var defaultTheme = {
2353
2609
  fontSize: "16px"
2354
2610
  },
2355
2611
  "&light .cm-content": {
2356
- color: get3(tokens, "extend.colors.neutral.900", "black"),
2612
+ color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
2357
2613
  caretColor: "black"
2358
2614
  },
2359
2615
  "&dark .cm-content": {
2360
- color: get3(tokens, "extend.colors.neutral.100", "white"),
2616
+ color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
2361
2617
  caretColor: "white"
2362
2618
  },
2363
2619
  //
@@ -2369,8 +2625,11 @@ var defaultTheme = {
2369
2625
  "&dark .cm-cursor, &dark .cm-dropCursor": {
2370
2626
  borderLeft: "2px solid white"
2371
2627
  },
2372
- ".cm-placeholder": {
2373
- fontWeight: 100
2628
+ "&light .cm-placeholder": {
2629
+ color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
2630
+ },
2631
+ "&dark .cm-placeholder": {
2632
+ color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2374
2633
  },
2375
2634
  //
2376
2635
  // line
@@ -2379,7 +2638,7 @@ var defaultTheme = {
2379
2638
  paddingInline: 0
2380
2639
  },
2381
2640
  ".cm-activeLine": {
2382
- background: "inherit"
2641
+ background: "transparent"
2383
2642
  },
2384
2643
  //
2385
2644
  // Selection
@@ -2550,31 +2809,35 @@ var codeTheme = {
2550
2809
  };
2551
2810
 
2552
2811
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2553
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2812
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2554
2813
  var EditorModes = [
2555
2814
  "default",
2556
2815
  "vim"
2557
2816
  ];
2558
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [] }, forwardedRef) => {
2559
- const tabsterDOMAttribute = useFocusableGroup({
2560
- tabBehavior: "limited"
2561
- });
2562
- const { themeMode } = useThemeContext2();
2817
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2818
+ const { themeMode } = useThemeContext();
2563
2819
  const rootRef = useRef(null);
2564
2820
  const [view, setView] = useState(null);
2565
2821
  useImperativeHandle(forwardedRef, () => view, [
2566
2822
  view
2567
2823
  ]);
2568
- useEffect3(() => {
2569
- if (autofocus) {
2824
+ useEffect2(() => {
2825
+ if (autoFocus && !view?.hasFocus) {
2826
+ if (view?.state.selection.main?.from === 0) {
2827
+ const { to } = view.state.doc.lineAt(0);
2828
+ view?.dispatch({
2829
+ selection: {
2830
+ anchor: to
2831
+ }
2832
+ });
2833
+ }
2570
2834
  view?.focus();
2571
2835
  }
2572
2836
  }, [
2573
2837
  view,
2574
- autofocus
2838
+ autoFocus
2575
2839
  ]);
2576
- useAwareness(model);
2577
- useEffect3(() => {
2840
+ useEffect2(() => {
2578
2841
  if (!model || !rootRef.current) {
2579
2842
  return;
2580
2843
  }
@@ -2585,8 +2848,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, s
2585
2848
  // TODO(burdon): Doesn't catch errors in keymap functions.
2586
2849
  EditorView13.exceptionSink.of((err) => {
2587
2850
  log2.catch(err, void 0, {
2588
- F: __dxlog_file4,
2589
- L: 114,
2851
+ F: __dxlog_file3,
2852
+ L: 134,
2590
2853
  S: void 0,
2591
2854
  C: (f, a) => f(...a)
2592
2855
  });
@@ -2603,15 +2866,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, s
2603
2866
  class: slots.content?.className ?? ""
2604
2867
  }),
2605
2868
  // State.
2869
+ EditorView13.editable.of(!readonly),
2606
2870
  EditorState2.readOnly.of(!!readonly),
2607
2871
  // Storage and replication.
2608
2872
  // NOTE: This must come before user extensions.
2609
2873
  model.extension,
2610
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
2874
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2611
2875
  editorMode === "vim" && vim(),
2612
2876
  // Custom.
2613
2877
  ...extensions
2614
- ].filter(Boolean)
2878
+ ].filter(isNotFalsy3)
2615
2879
  });
2616
2880
  const newView = new EditorView13({
2617
2881
  state,
@@ -2620,11 +2884,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, s
2620
2884
  // NOTE: Uncomment to debug/monitor all transactions.
2621
2885
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
2622
2886
  dispatchTransactions: (trs, view2) => {
2623
- view2.update(trs);
2624
- const debug = false;
2625
2887
  if (debug) {
2626
2888
  logChanges(trs);
2627
2889
  }
2890
+ view2.update(trs);
2628
2891
  }
2629
2892
  });
2630
2893
  view?.destroy();
@@ -2649,7 +2912,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, s
2649
2912
  }
2650
2913
  case "Escape": {
2651
2914
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2652
- rootRef.current?.focus();
2915
+ view?.focus();
2653
2916
  }
2654
2917
  break;
2655
2918
  }
@@ -2662,23 +2925,23 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autofocus, s
2662
2925
  key: model.id,
2663
2926
  role: "none",
2664
2927
  tabIndex: 0,
2665
- onKeyUp: handleKeyUp,
2666
2928
  ...slots.root,
2667
- ...editorMode !== "vim" && tabsterDOMAttribute,
2929
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2930
+ onKeyUp: handleKeyUp,
2668
2931
  ref: rootRef
2669
2932
  });
2670
2933
  });
2671
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, multiline, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
2672
- const { themeMode } = useThemeContext2();
2934
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
2935
+ const { themeMode } = useThemeContext();
2673
2936
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
2674
2937
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
2675
2938
  ref: forwardedRef,
2676
2939
  readonly,
2677
2940
  extensions: [
2678
- basicBundle({
2941
+ createBasicBundle({
2679
2942
  themeMode,
2680
2943
  placeholder: placeholder3,
2681
- multiline
2944
+ lineWrapping
2682
2945
  }),
2683
2946
  ...extensions
2684
2947
  ],
@@ -2688,15 +2951,14 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2688
2951
  });
2689
2952
  });
2690
2953
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
2691
- const { themeMode } = useThemeContext2();
2954
+ const { themeMode } = useThemeContext();
2692
2955
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
2693
2956
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
2694
2957
  ref: forwardedRef,
2695
2958
  readonly,
2696
2959
  extensions: [
2697
- markdownBundle({
2960
+ createMarkdownExtensions({
2698
2961
  themeMode,
2699
- readonly,
2700
2962
  placeholder: placeholder3
2701
2963
  }),
2702
2964
  ...extensions
@@ -2708,11 +2970,10 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2708
2970
  });
2709
2971
  var defaultSlots = {
2710
2972
  root: {
2711
- // TODO(burdon): Add focusRing by default/as property?
2712
- className: mx3("flex flex-col grow overflow-y-auto", inputSurface)
2973
+ className: mx3("grow", focusRing)
2713
2974
  },
2714
2975
  editor: {
2715
- className: "h-full p-2"
2976
+ className: "bs-full"
2716
2977
  }
2717
2978
  };
2718
2979
  var defaultTextSlots = {
@@ -2722,22 +2983,178 @@ var defaultMarkdownSlots = {
2722
2983
  ...defaultSlots
2723
2984
  };
2724
2985
 
2725
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2726
- import get4 from "lodash.get";
2727
- import { getRawDoc } from "@dxos/echo-schema";
2728
- import { isNotNullOrUndefined } from "@dxos/util";
2986
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2987
+ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
2988
+ import { createContext } from "@radix-ui/react-context";
2989
+ import React2 from "react";
2990
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
2991
+ import { getSize } from "@dxos/react-ui-theme";
2992
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2993
+ var ToolbarRoot = ({ children, onAction }) => {
2994
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2995
+ onAction
2996
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2997
+ density: "fine"
2998
+ }, /* @__PURE__ */ React2.createElement("div", {
2999
+ role: "toolbar",
3000
+ className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
3001
+ }, children)));
3002
+ };
3003
+ var ToolbarButton = ({ Icon, onClick, title }) => {
3004
+ const { onAction } = useToolbarContext("ToolbarButton");
3005
+ const handleClick = () => {
3006
+ const action = onClick();
3007
+ if (action) {
3008
+ onAction?.(action);
3009
+ }
3010
+ };
3011
+ return /* @__PURE__ */ React2.createElement(Button, {
3012
+ variant: "ghost",
3013
+ classNames: "p-2",
3014
+ onClick: handleClick,
3015
+ title
3016
+ }, /* @__PURE__ */ React2.createElement(Icon, {
3017
+ className: getSize(5)
3018
+ }));
3019
+ };
3020
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3021
+ className: "grow"
3022
+ });
3023
+ var MarkdownHeading = () => {
3024
+ const { onAction } = useToolbarContext("MarkdownFormatting");
3025
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3026
+ defaultValue: "0",
3027
+ onValueChange: (value) => onAction?.({
3028
+ type: "heading",
3029
+ data: parseInt(value)
3030
+ })
3031
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3032
+ classNames: "w-[8rem]"
3033
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3034
+ value: "0"
3035
+ }, "Paragraph"), [
3036
+ 1,
3037
+ 2,
3038
+ 3,
3039
+ 4,
3040
+ 5,
3041
+ 6
3042
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3043
+ key: level,
3044
+ value: String(level)
3045
+ }, "Heading ", level))))));
3046
+ };
3047
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3048
+ role: "none"
3049
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3050
+ Icon: TextB,
3051
+ title: "Bold",
3052
+ onClick: () => ({
3053
+ type: "bold"
3054
+ })
3055
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3056
+ Icon: TextItalic,
3057
+ title: "Italic",
3058
+ onClick: () => ({
3059
+ type: "italic"
3060
+ })
3061
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3062
+ Icon: TextStrikethrough,
3063
+ title: "Strike-through",
3064
+ onClick: () => ({
3065
+ type: "strikethrough"
3066
+ })
3067
+ }));
3068
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3069
+ role: "none"
3070
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3071
+ Icon: ListBullets,
3072
+ title: "Bullet list",
3073
+ onClick: () => ({
3074
+ type: "list"
3075
+ })
3076
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3077
+ Icon: ListNumbers,
3078
+ title: "Numbered list",
3079
+ onClick: () => ({
3080
+ type: "list-ordered"
3081
+ })
3082
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3083
+ Icon: ListChecks,
3084
+ title: "Task list",
3085
+ onClick: () => ({
3086
+ type: "list-tasks"
3087
+ })
3088
+ }));
3089
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3090
+ role: "none"
3091
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3092
+ Icon: CaretRight,
3093
+ title: "Block quite",
3094
+ onClick: () => ({
3095
+ type: "blockquote"
3096
+ })
3097
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3098
+ Icon: Code,
3099
+ title: "Code block",
3100
+ onClick: () => ({
3101
+ type: "codeblock"
3102
+ })
3103
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3104
+ Icon: Table2,
3105
+ title: "Table",
3106
+ onClick: () => ({
3107
+ type: "table"
3108
+ })
3109
+ }));
3110
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3111
+ role: "none"
3112
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3113
+ Icon: Link,
3114
+ title: "Link",
3115
+ onClick: () => ({
3116
+ type: "link"
3117
+ })
3118
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3119
+ Icon: At,
3120
+ title: "Mention",
3121
+ onClick: () => ({
3122
+ type: "mention"
3123
+ })
3124
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3125
+ Icon: Image,
3126
+ title: "Image",
3127
+ onClick: () => ({
3128
+ type: "image"
3129
+ })
3130
+ }));
3131
+ var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null), /* @__PURE__ */ React2.createElement(MarkdownLinks, null));
3132
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
3133
+ Icon: ChatText,
3134
+ title: "Create comment",
3135
+ onClick: () => ({
3136
+ type: "comment"
3137
+ })
3138
+ });
3139
+ var Toolbar = {
3140
+ Root: ToolbarRoot,
3141
+ Button: ToolbarButton,
3142
+ Separator: ToolbarSeparator,
3143
+ Markdown: MarkdownStandard,
3144
+ Extended: MarkdownExtended
3145
+ };
2729
3146
 
2730
3147
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2731
3148
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2732
3149
  import { Context as Context2 } from "@dxos/context";
2733
- import { invariant as invariant3 } from "@dxos/invariant";
3150
+ import { invariant as invariant2 } from "@dxos/invariant";
2734
3151
  import { log as log3 } from "@dxos/log";
2735
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
3152
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2736
3153
  var DEBOUNCE_INTERVAL = 100;
2737
3154
  var SpaceAwarenessProvider = class {
2738
3155
  constructor(params) {
2739
- this.remoteStateChange = new Event2();
2740
3156
  this._remoteStates = /* @__PURE__ */ new Map();
3157
+ this.remoteStateChange = new Event2();
2741
3158
  this._space = params.space;
2742
3159
  this._channel = params.channel;
2743
3160
  this._peerId = params.peerId;
@@ -2754,7 +3171,7 @@ var SpaceAwarenessProvider = class {
2754
3171
  await sleep(DEBOUNCE_INTERVAL);
2755
3172
  }
2756
3173
  });
2757
- const unsubscribe = this._space.listen(this._channel, (message) => {
3174
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
2758
3175
  switch (message.payload.kind) {
2759
3176
  case "query": {
2760
3177
  this._handleQueryMessage();
@@ -2765,16 +3182,15 @@ var SpaceAwarenessProvider = class {
2765
3182
  break;
2766
3183
  }
2767
3184
  }
2768
- });
2769
- this._ctx.onDispose(unsubscribe);
3185
+ }));
2770
3186
  void this._space.postMessage(this._channel, {
2771
3187
  kind: "query"
2772
3188
  }).catch((err) => {
2773
3189
  log3.debug("failed to query awareness", {
2774
3190
  err
2775
3191
  }, {
2776
- F: __dxlog_file5,
2777
- L: 85,
3192
+ F: __dxlog_file4,
3193
+ L: 89,
2778
3194
  S: this,
2779
3195
  C: (f, a) => f(...a)
2780
3196
  });
@@ -2785,10 +3201,13 @@ var SpaceAwarenessProvider = class {
2785
3201
  this._ctx = void 0;
2786
3202
  this._postTask = void 0;
2787
3203
  }
3204
+ getRemoteStates() {
3205
+ return Array.from(this._remoteStates.values());
3206
+ }
2788
3207
  update(position) {
2789
- invariant3(this._postTask, void 0, {
2790
- F: __dxlog_file5,
2791
- L: 96,
3208
+ invariant2(this._postTask, void 0, {
3209
+ F: __dxlog_file4,
3210
+ L: 104,
2792
3211
  S: this,
2793
3212
  A: [
2794
3213
  "this._postTask",
@@ -2802,13 +3221,10 @@ var SpaceAwarenessProvider = class {
2802
3221
  };
2803
3222
  this._postTask.schedule();
2804
3223
  }
2805
- getRemoteStates() {
2806
- return Array.from(this._remoteStates.values());
2807
- }
2808
3224
  _handleQueryMessage() {
2809
- invariant3(this._postTask, void 0, {
2810
- F: __dxlog_file5,
2811
- L: 111,
3225
+ invariant2(this._postTask, void 0, {
3226
+ F: __dxlog_file4,
3227
+ L: 115,
2812
3228
  S: this,
2813
3229
  A: [
2814
3230
  "this._postTask",
@@ -2818,9 +3234,9 @@ var SpaceAwarenessProvider = class {
2818
3234
  this._postTask.schedule();
2819
3235
  }
2820
3236
  _handlePostMessage(message) {
2821
- invariant3(message.kind === "post", void 0, {
2822
- F: __dxlog_file5,
2823
- L: 116,
3237
+ invariant2(message.kind === "post", void 0, {
3238
+ F: __dxlog_file4,
3239
+ L: 120,
2824
3240
  S: this,
2825
3241
  A: [
2826
3242
  "message.kind === 'post'",
@@ -2839,182 +3255,177 @@ var modelState = StateField5.define({
2839
3255
  update: (model) => model
2840
3256
  });
2841
3257
 
2842
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2843
- var createAutomergeModel = ({ space, identity, text }) => {
2844
- const obj = text;
2845
- const doc = getRawDoc(obj, [
2846
- obj.field
2847
- ]);
2848
- const awarenessProvider = space && new SpaceAwarenessProvider({
2849
- space,
2850
- channel: `automerge.awareness.${obj.id}`,
2851
- info: {
2852
- displayName: identity?.profile?.displayName ?? "",
2853
- color: cursorColor.color,
2854
- lightColor: cursorColor.light
2855
- },
2856
- peerId: identity?.identityKey.toHex() ?? "Anonymous"
2857
- });
2858
- const model = {
2859
- id: obj.id,
2860
- content: doc,
2861
- text: () => get4(doc.handle.docSync(), doc.path),
2862
- // TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
2863
- extension: [
2864
- modelState.init(() => model),
2865
- automerge3({
2866
- handle: doc.handle,
2867
- path: doc.path
2868
- }),
2869
- awarenessProvider && AwarenessProvider.of(awarenessProvider),
2870
- awareness()
2871
- ].filter(isNotNullOrUndefined),
2872
- peer: identity ? {
2873
- id: identity.identityKey.toHex(),
2874
- name: identity.profile?.displayName
2875
- } : void 0
3258
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3259
+ var useActionHandler = (view) => {
3260
+ return (action) => {
3261
+ if (!view) {
3262
+ return;
3263
+ }
3264
+ switch (action.type) {
3265
+ case "heading":
3266
+ setHeading(parseInt(action.data))(view);
3267
+ break;
3268
+ case "bold":
3269
+ toggleBold(view);
3270
+ break;
3271
+ case "italic":
3272
+ toggleItalic(view);
3273
+ break;
3274
+ case "strikethrough":
3275
+ toggleStrikethrough(view);
3276
+ break;
3277
+ case "list":
3278
+ toggleList(view);
3279
+ break;
3280
+ case "codeblock":
3281
+ insertCodeblock(view);
3282
+ break;
3283
+ case "table":
3284
+ insertTable(view);
3285
+ break;
3286
+ case "comment":
3287
+ createComment(view);
3288
+ break;
3289
+ }
3290
+ setTimeout(() => {
3291
+ view.focus();
3292
+ view.dispatch({
3293
+ selection: view.state.selection
3294
+ });
3295
+ });
2876
3296
  };
2877
- return model;
2878
3297
  };
2879
3298
 
2880
- // packages/ui/react-ui-editor/src/hooks/yjs.ts
2881
- import * as decoding from "lib0/decoding";
2882
- import * as encoding from "lib0/encoding";
2883
- import { Observable } from "lib0/observable";
2884
- import * as YP from "y-protocols/awareness";
2885
- import { invariant as invariant4 } from "@dxos/invariant";
2886
- import { log as log4 } from "@dxos/log";
2887
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
2888
- var createYjsModel = ({ identity, space, text }) => {
2889
- invariant4(text?.doc && text?.content, void 0, {
2890
- F: __dxlog_file6,
2891
- L: 22,
2892
- S: void 0,
2893
- A: [
2894
- "text?.doc && text?.content",
2895
- ""
2896
- ]
2897
- });
2898
- const provider = space ? new YAwarenessProvider({
2899
- space,
2900
- doc: text.doc,
2901
- channel: `yjs.awareness.${text.id}`
2902
- }) : void 0;
2903
- const model = {
2904
- id: text.doc.guid,
2905
- content: text.content,
2906
- text: () => text.content.toString(),
2907
- extension: [
2908
- modelState.init(() => model),
2909
- yjs(text.content, provider?.awareness)
2910
- ],
2911
- awareness: provider?.awareness,
2912
- peer: identity ? {
2913
- id: identity.identityKey.toHex(),
2914
- name: identity.profile?.displayName
2915
- } : void 0
2916
- };
2917
- return model;
3299
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
3300
+ import { useState as useState2 } from "react";
3301
+ var useEditorView = () => {
3302
+ const [view, setView] = useState2(null);
3303
+ return [
3304
+ (ref) => {
3305
+ setView(ref);
3306
+ },
3307
+ view
3308
+ ];
2918
3309
  };
2919
- var messageAwareness = 1;
2920
- var messageQueryAwareness = 3;
2921
- var YAwarenessProvider = class extends Observable {
2922
- constructor({ space, doc, channel, awareness: awareness2 }) {
2923
- super();
2924
- this._space = space;
2925
- this._awareness = awareness2 ?? new YP.Awareness(doc);
2926
- this._channel = channel;
2927
- this._clientId = doc.clientID;
2928
- this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
2929
- this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
2930
- if (typeof window !== "undefined") {
2931
- window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
2932
- } else if (typeof process !== "undefined") {
2933
- process.on("exit", this._handleBeforeUnload.bind(this));
2934
- }
2935
- const encoderAwarenessQuery = encoding.createEncoder();
2936
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
2937
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
2938
- const encoderAwarenessState = encoding.createEncoder();
2939
- encoding.writeVarUint(encoderAwarenessState, messageAwareness);
2940
- encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
2941
- this._clientId
2942
- ]));
2943
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
2944
- }
2945
- get awareness() {
2946
- return this._awareness;
2947
- }
2948
- _handleAwarenessUpdate({ added, updated, removed }, origin) {
2949
- log4("awareness update", {
2950
- added,
2951
- updated,
2952
- removed,
2953
- origin
2954
- }, {
2955
- F: __dxlog_file6,
2956
- L: 101,
2957
- S: this,
2958
- C: (f, a) => f(...a)
2959
- });
2960
- const changedClients = added.concat(updated).concat(removed);
2961
- const encoderAwareness = encoding.createEncoder();
2962
- encoding.writeVarUint(encoderAwareness, messageAwareness);
2963
- encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
2964
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
2965
- }
2966
- _handleSpaceMessage({ payload }) {
2967
- log4("space message", payload, {
2968
- F: __dxlog_file6,
2969
- L: 110,
2970
- S: this,
2971
- C: (f, a) => f(...a)
2972
- });
2973
- const data = new Uint8Array(Array.from(Object.values(payload)));
2974
- const encoder = this._readMessage(data);
2975
- if (encoder) {
2976
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
3310
+
3311
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3312
+ import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3313
+ import { EditorView as EditorView14 } from "@codemirror/view";
3314
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
3315
+ import { log as log4 } from "@dxos/log";
3316
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3317
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3318
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3319
+ const onUpdate = useRef2();
3320
+ const [view, setView] = useState3();
3321
+ const parentRef = useRef2(null);
3322
+ useEffect3(() => {
3323
+ if (parentRef.current) {
3324
+ const state = EditorState3.create({
3325
+ doc,
3326
+ selection,
3327
+ extensions: [
3328
+ // TODO(burdon): Doesn't catch errors in keymap functions.
3329
+ EditorView14.exceptionSink.of((err) => {
3330
+ log4.catch(err, void 0, {
3331
+ F: __dxlog_file5,
3332
+ L: 57,
3333
+ S: void 0,
3334
+ C: (f, a) => f(...a)
3335
+ });
3336
+ }),
3337
+ extensions,
3338
+ EditorView14.updateListener.of(() => {
3339
+ onUpdate.current?.();
3340
+ })
3341
+ ].filter(isNotFalsy4)
3342
+ });
3343
+ const view2 = new EditorView14({
3344
+ parent: parentRef.current,
3345
+ scrollTo,
3346
+ state,
3347
+ // NOTE: Uncomment to debug/monitor all transactions.
3348
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
3349
+ dispatchTransactions: (trs, view3) => {
3350
+ if (debug) {
3351
+ logChanges(trs);
3352
+ }
3353
+ view3.update(trs);
3354
+ }
3355
+ });
3356
+ setView(view2);
2977
3357
  }
2978
- }
2979
- _readMessage(message) {
2980
- const decoder = decoding.createDecoder(message);
2981
- const encoder = encoding.createEncoder();
2982
- const messageType = decoding.readVarUint(decoder);
2983
- let sendReply = false;
2984
- switch (messageType) {
2985
- case messageAwareness: {
2986
- YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
2987
- break;
3358
+ return () => {
3359
+ view?.destroy();
3360
+ };
3361
+ }, [
3362
+ parentRef
3363
+ ]);
3364
+ useEffect3(() => {
3365
+ if (view) {
3366
+ if (!selection && !view.state.selection.main.anchor) {
3367
+ selection = EditorSelection.single(view.state.doc.line(1).to);
2988
3368
  }
2989
- case messageQueryAwareness: {
2990
- encoding.writeVarUint(encoder, messageAwareness);
2991
- encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
2992
- sendReply = true;
2993
- break;
3369
+ if (selection || scrollTo) {
3370
+ onUpdate.current = () => {
3371
+ onUpdate.current = void 0;
3372
+ view.dispatch({
3373
+ selection,
3374
+ effects: scrollTo && [
3375
+ scrollTo
3376
+ ],
3377
+ scrollIntoView: !scrollTo
3378
+ });
3379
+ };
2994
3380
  }
2995
- default: {
2996
- console.error("Invalid message:", messageType);
2997
- return encoder;
3381
+ if (autoFocus) {
3382
+ view.focus();
2998
3383
  }
2999
3384
  }
3000
- if (!sendReply) {
3001
- return null;
3002
- }
3003
- return encoder;
3004
- }
3005
- _handleBeforeUnload() {
3006
- YP.removeAwarenessStates(this._awareness, [
3007
- this._clientId
3008
- ], "window unload");
3009
- }
3385
+ }, [
3386
+ view,
3387
+ autoFocus,
3388
+ selection,
3389
+ scrollTo
3390
+ ]);
3391
+ return {
3392
+ parentRef,
3393
+ view
3394
+ };
3395
+ };
3396
+ var createDataExtensions = ({ readonly = false } = {}) => {
3397
+ return [
3398
+ //
3399
+ EditorState3.readOnly.of(readonly),
3400
+ EditorView14.editable.of(!readonly)
3401
+ ];
3402
+ };
3403
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3404
+ return [
3405
+ //
3406
+ EditorView14.baseTheme(defaultTheme),
3407
+ theme && EditorView14.theme(theme),
3408
+ EditorView14.darkTheme.of(themeMode === "dark"),
3409
+ EditorView14.editorAttributes.of({
3410
+ class: slots?.editor?.className ?? ""
3411
+ }),
3412
+ EditorView14.contentAttributes.of({
3413
+ class: slots?.content?.className ?? ""
3414
+ })
3415
+ ].filter(isNotFalsy4);
3010
3416
  };
3011
3417
 
3012
3418
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
3013
- import { useEffect as useEffect4, useState as useState2 } from "react";
3419
+ import get4 from "lodash.get";
3420
+ import { useEffect as useEffect4, useState as useState4 } from "react";
3421
+ import { generateName } from "@dxos/display-name";
3422
+ import { getRawDoc } from "@dxos/echo-schema";
3423
+ import { invariant as invariant3 } from "@dxos/invariant";
3014
3424
  import { isAutomergeObject } from "@dxos/react-client/echo";
3425
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3015
3426
  var useTextModel = (props) => {
3016
3427
  const { identity, space, text } = props;
3017
- const [model, setModel] = useState2();
3428
+ const [model, setModel] = useState4();
3018
3429
  useEffect4(() => setModel(createModel(props)), [
3019
3430
  identity,
3020
3431
  space,
@@ -3022,18 +3433,63 @@ var useTextModel = (props) => {
3022
3433
  ]);
3023
3434
  return model;
3024
3435
  };
3025
- var createModel = (props) => {
3026
- const { text } = props;
3027
- if (isAutomergeObject(text)) {
3028
- return createAutomergeModel(props);
3029
- } else if (text?.doc) {
3030
- return createYjsModel(props);
3031
- } else {
3032
- return void 0;
3436
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
3437
+ const [content, setContent] = useState4(defaultContent ?? "");
3438
+ return {
3439
+ id,
3440
+ content,
3441
+ setContent,
3442
+ text: () => content
3443
+ };
3444
+ };
3445
+ var createModel = ({ space, identity, text }) => {
3446
+ invariant3(isAutomergeObject(text), void 0, {
3447
+ F: __dxlog_file6,
3448
+ L: 50,
3449
+ S: void 0,
3450
+ A: [
3451
+ "isAutomergeObject(text)",
3452
+ ""
3453
+ ]
3454
+ });
3455
+ const obj = text;
3456
+ const doc = getRawDoc(obj, [
3457
+ obj.field
3458
+ ]);
3459
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
3460
+ space,
3461
+ channel: `automerge.awareness.${obj.id}`,
3462
+ info: {
3463
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
3464
+ color: cursorColor.color,
3465
+ lightColor: cursorColor.light
3466
+ },
3467
+ peerId: identity?.identityKey.toHex() ?? "Anonymous"
3468
+ });
3469
+ const extensions = [
3470
+ //
3471
+ modelState.init(() => model),
3472
+ automerge({
3473
+ handle: doc.handle,
3474
+ path: doc.path
3475
+ })
3476
+ ];
3477
+ if (awarenessProvider2) {
3478
+ extensions.push(awareness(awarenessProvider2));
3033
3479
  }
3480
+ const model = {
3481
+ id: obj.id,
3482
+ content: doc,
3483
+ text: () => get4(doc.handle.docSync(), doc.path),
3484
+ extension: extensions,
3485
+ peer: identity ? {
3486
+ id: identity.identityKey.toHex(),
3487
+ name: identity.profile?.displayName
3488
+ } : void 0
3489
+ };
3490
+ return model;
3034
3491
  };
3035
3492
  export {
3036
- AwarenessProvider,
3037
3493
  BaseTextEditor,
3038
3494
  Cursor,
3039
3495
  Doc,
@@ -3043,32 +3499,40 @@ export {
3043
3499
  SpaceAwarenessProvider,
3044
3500
  TextEditor,
3045
3501
  TextKind,
3046
- YAwarenessProvider,
3502
+ Toolbar,
3047
3503
  YText,
3048
3504
  YXmlFragment,
3049
3505
  autocomplete,
3050
- automerge3 as automerge,
3506
+ automerge,
3051
3507
  awareness,
3052
- basicBundle,
3508
+ awarenessProvider,
3053
3509
  blast,
3054
3510
  callbackWrapper,
3055
3511
  code2 as code,
3056
3512
  codeTheme,
3057
3513
  comments,
3058
- createAutomergeModel,
3059
- createYjsModel,
3514
+ createBasicBundle,
3515
+ createComment,
3516
+ createDataExtensions,
3517
+ createMarkdownExtensions,
3518
+ createThemeExtensions,
3060
3519
  defaultMarkdownSlots,
3061
3520
  defaultOptions,
3062
3521
  defaultSlots,
3063
3522
  defaultTextSlots,
3064
3523
  defaultTheme,
3524
+ focusComment,
3525
+ formatting,
3065
3526
  getToken,
3527
+ heading2 as heading,
3066
3528
  hr,
3067
3529
  image,
3530
+ insertCodeblock,
3531
+ insertTable,
3532
+ keymap7 as keymap,
3068
3533
  link,
3069
3534
  listener,
3070
3535
  logChanges,
3071
- markdownBundle,
3072
3536
  markdownHighlightStyle,
3073
3537
  markdownTags,
3074
3538
  markdownTagsExtensions,
@@ -3077,16 +3541,24 @@ export {
3077
3541
  modelState,
3078
3542
  outliner,
3079
3543
  setComments,
3080
- setFocus,
3544
+ setHeading,
3081
3545
  setSelection,
3082
3546
  table,
3083
3547
  tags2 as tags,
3084
3548
  tasklist,
3085
3549
  textTheme,
3550
+ toggleBold,
3551
+ toggleItalic,
3552
+ toggleList,
3553
+ toggleStrikethrough,
3554
+ toggleStyle,
3086
3555
  typewriter,
3087
- useAwareness,
3556
+ useActionHandler,
3088
3557
  useComments,
3558
+ useEditorView,
3559
+ useInMemoryTextModel,
3560
+ useTextEditor,
3089
3561
  useTextModel,
3090
- yjs
3562
+ useToolbarContext
3091
3563
  };
3092
3564
  //# sourceMappingURL=index.mjs.map