@dxos/react-ui-editor 0.3.11-main.b00882f → 0.3.11-main.b3f97d8

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 (168) hide show
  1. package/dist/lib/browser/index.mjs +2386 -968
  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.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  8. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/{yjs.stories.d.ts → hooks.stories.d.ts} +13 -2
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts +29 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +25 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  16. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  17. package/dist/types/src/components/index.d.ts +1 -0
  18. package/dist/types/src/components/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  21. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  23. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  24. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  29. package/dist/types/src/extensions/awareness.d.ts +2 -4
  30. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  31. package/dist/types/src/extensions/basic.d.ts +1 -1
  32. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  33. package/dist/types/src/extensions/comments.d.ts +11 -3
  34. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  35. package/dist/types/src/extensions/index.d.ts +0 -7
  36. package/dist/types/src/extensions/index.d.ts.map +1 -1
  37. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  38. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  39. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  40. package/dist/types/src/extensions/markdown/formatting.d.ts +57 -0
  41. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  42. package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
  43. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
  44. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  45. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  46. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  48. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  49. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  50. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  51. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  52. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  53. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  54. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  55. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  56. package/dist/types/src/hooks/defs.d.ts +1 -4
  57. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  58. package/dist/types/src/hooks/index.d.ts +2 -2
  59. package/dist/types/src/hooks/index.d.ts.map +1 -1
  60. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  61. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  62. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  63. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  64. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  65. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  66. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  67. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  68. package/dist/types/src/index.d.ts +1 -1
  69. package/dist/types/src/index.d.ts.map +1 -1
  70. package/dist/types/src/styles/markdown.d.ts +2 -2
  71. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  72. package/dist/types/src/themes/default.d.ts.map +1 -1
  73. package/package.json +39 -29
  74. package/src/components/TextEditor/MarkdownEditor.stories.tsx +15 -19
  75. package/src/components/TextEditor/TextEditor.tsx +27 -19
  76. package/src/components/TextEditor/automerge.stories.tsx +1 -2
  77. package/src/components/TextEditor/hooks.stories.tsx +111 -0
  78. package/src/components/Toolbar/Toolbar.stories.tsx +102 -0
  79. package/src/components/Toolbar/Toolbar.tsx +245 -0
  80. package/src/components/Toolbar/index.ts +5 -0
  81. package/src/components/index.ts +1 -0
  82. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  83. package/src/extensions/automerge/automerge.test.ts +13 -0
  84. package/src/extensions/automerge/automerge.ts +26 -11
  85. package/src/extensions/automerge/cursor.ts +3 -3
  86. package/src/extensions/automerge/defs.ts +9 -9
  87. package/src/extensions/automerge/semaphore.ts +17 -19
  88. package/src/extensions/automerge/update-automerge.ts +12 -6
  89. package/src/extensions/automerge/update-codemirror.ts +5 -5
  90. package/src/extensions/awareness.ts +21 -35
  91. package/src/extensions/basic.ts +15 -13
  92. package/src/extensions/blast.ts +4 -1
  93. package/src/extensions/comments.ts +126 -68
  94. package/src/extensions/index.ts +0 -7
  95. package/src/extensions/markdown/bundle.ts +9 -8
  96. package/src/extensions/markdown/code.ts +150 -0
  97. package/src/extensions/markdown/formatting.test.ts +481 -0
  98. package/src/extensions/markdown/formatting.ts +1198 -0
  99. package/src/extensions/markdown/heading.ts +59 -0
  100. package/src/extensions/markdown/highlight.ts +9 -15
  101. package/src/extensions/{hr.ts → markdown/hr.ts} +3 -4
  102. package/src/extensions/markdown/index.ts +8 -0
  103. package/src/extensions/{link.ts → markdown/link.ts} +13 -4
  104. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +2 -3
  105. package/src/hooks/awareness-provider.ts +24 -20
  106. package/src/hooks/defs.ts +1 -6
  107. package/src/hooks/index.ts +2 -2
  108. package/src/hooks/useActionHandler.ts +93 -0
  109. package/src/hooks/useEditorView.ts +29 -0
  110. package/src/hooks/useTextEditor.ts +143 -19
  111. package/src/hooks/useTextModel.ts +65 -12
  112. package/src/index.ts +1 -1
  113. package/src/styles/markdown.ts +9 -10
  114. package/src/themes/default.ts +11 -8
  115. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  116. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  117. package/dist/types/src/components/TextEditor/comments.stories.d.ts +0 -26
  118. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  119. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  120. package/dist/types/src/extensions/code.d.ts.map +0 -1
  121. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  122. package/dist/types/src/extensions/image.d.ts.map +0 -1
  123. package/dist/types/src/extensions/link.d.ts.map +0 -1
  124. package/dist/types/src/extensions/table.d.ts.map +0 -1
  125. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  126. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  127. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  128. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  129. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  130. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  131. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  132. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  133. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  134. package/dist/types/src/hooks/automerge.d.ts +0 -4
  135. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  136. package/dist/types/src/hooks/yjs.d.ts +0 -28
  137. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  138. package/dist/types/src/testing/index.d.ts +0 -3
  139. package/dist/types/src/testing/index.d.ts.map +0 -1
  140. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  141. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  142. package/dist/types/src/testing/proto/index.d.ts +0 -2
  143. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  144. package/dist/types/src/testing/replicator.d.ts +0 -45
  145. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  146. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  147. package/src/components/TextEditor/comments.stories.tsx +0 -357
  148. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  149. package/src/extensions/code.ts +0 -104
  150. package/src/extensions/yjs/cursor.ts +0 -22
  151. package/src/extensions/yjs/index.ts +0 -5
  152. package/src/extensions/yjs/yjs.test.ts +0 -35
  153. package/src/extensions/yjs/yjs.ts +0 -16
  154. package/src/hooks/automerge.ts +0 -53
  155. package/src/hooks/yjs.ts +0 -157
  156. package/src/testing/index.ts +0 -6
  157. package/src/testing/proto/gen/schema.ts +0 -49
  158. package/src/testing/proto/index.ts +0 -5
  159. package/src/testing/proto/schema.proto +0 -15
  160. package/src/testing/replicator.ts +0 -184
  161. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  162. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  163. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  164. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  165. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  166. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
  167. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  168. /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
@@ -1,20 +1,21 @@
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";
7
8
 
8
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
10
  import { EditorState as EditorState2 } from "@codemirror/state";
10
- import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
11
+ import { EditorView as EditorView14 } from "@codemirror/view";
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 as useState2 } 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,14 +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
59
  import { EditorView, ViewPlugin } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
- import { invariant } from "@dxos/invariant";
60
+ import { next as A4 } from "@dxos/automerge/automerge";
60
61
 
61
62
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
62
63
  import get from "lodash.get";
63
- import { next as automerge } from "@dxos/automerge/automerge";
64
+ import { next as A } from "@dxos/automerge/automerge";
64
65
  var cursorConverter = (handle, path) => ({
65
66
  // TODO(burdon): Handle assoc to associate with a previous character.
66
67
  toCursor: (pos) => {
@@ -72,7 +73,7 @@ var cursorConverter = (handle, path) => ({
72
73
  if (typeof value === "string" && value.length <= pos) {
73
74
  return "end";
74
75
  }
75
- return automerge.getCursor(doc, path.slice(), pos);
76
+ return A.getCursor(doc, path.slice(), pos);
76
77
  },
77
78
  fromCursor: (cursor) => {
78
79
  if (cursor === "") {
@@ -90,26 +91,28 @@ var cursorConverter = (handle, path) => ({
90
91
  return 0;
91
92
  }
92
93
  }
93
- return automerge.getCursorPosition(doc, path.slice(), cursor);
94
+ return A.getCursorPosition(doc, path.slice(), cursor);
94
95
  }
95
96
  });
96
97
 
97
98
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
98
99
  import { Annotation, StateEffect } from "@codemirror/state";
99
- var effectType = StateEffect.define({});
100
- var reconcileAnnotationType = Annotation.define();
101
100
  var getPath = (state, field) => state.field(field).path;
102
101
  var getLastHeads = (state, field) => state.field(field).lastHeads;
103
- var updateHeads = (newHeads) => effectType.of({
102
+ var updateHeadsEffect = StateEffect.define({});
103
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
104
104
  newHeads
105
105
  });
106
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
106
+ var reconcileAnnotation = Annotation.define();
107
+ var isReconcile = (tr) => {
108
+ return !!tr.annotation(reconcileAnnotation);
109
+ };
107
110
 
108
111
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
109
- import { next as automerge2 } from "@dxos/automerge/automerge";
112
+ import { next as A3 } from "@dxos/automerge/automerge";
110
113
 
111
114
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
112
- import { next as am } from "@dxos/automerge/automerge";
115
+ import { next as A2 } from "@dxos/automerge/automerge";
113
116
  var updateAutomerge = (field, handle, transactions, state) => {
114
117
  const { lastHeads, path } = state.field(field);
115
118
  let hasChanges = false;
@@ -122,11 +125,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
122
125
  return void 0;
123
126
  }
124
127
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
+ const invertedTransactions = [];
125
129
  for (const tr of transactions) {
126
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
127
- 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
+ });
128
136
  });
129
137
  }
138
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
139
+ A2.splice(doc, path.slice(), from, del, insert.toString());
140
+ });
130
141
  });
131
142
  return newHeads ?? void 0;
132
143
  };
@@ -141,13 +152,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
141
152
  selection = selection.map(changeSet, 1);
142
153
  view.dispatch({
143
154
  changes: changeSet,
144
- annotations: reconcileAnnotationType.of({})
155
+ annotations: reconcileAnnotation.of(false)
145
156
  });
146
157
  }
147
158
  }
148
159
  view.dispatch({
149
160
  selection,
150
- annotations: reconcileAnnotationType.of({})
161
+ annotations: reconcileAnnotation.of(false)
151
162
  });
152
163
  };
153
164
  var handlePatch = (patch, target, state) => {
@@ -168,7 +179,7 @@ var handleInsert = (target, patch) => {
168
179
  if (index == null) {
169
180
  return [];
170
181
  }
171
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
182
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
172
183
  return [
173
184
  {
174
185
  from: index,
@@ -246,36 +257,37 @@ var PatchSemaphore = class {
246
257
  this._state = _state;
247
258
  this._inReconcile = false;
248
259
  }
249
- // NOTE: Cannot destruct view.state.
250
260
  reconcile(view) {
251
- if (this._inReconcile) {
252
- return;
261
+ if (!this._inReconcile) {
262
+ this._inReconcile = true;
263
+ this.doReconcile(view);
264
+ this._inReconcile = false;
253
265
  }
254
- this._inReconcile = true;
266
+ }
267
+ doReconcile(view) {
255
268
  const path = getPath(view.state, this._state);
256
269
  const oldHeads = getLastHeads(view.state, this._state);
257
270
  let selection = view.state.selection;
258
- 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));
259
272
  const toInvert = transactions.slice().reverse();
260
- for (const tx of toInvert) {
261
- const inverted = tx.changes.invert(tx.startState.doc);
273
+ for (const tr of toInvert) {
274
+ const inverted = tr.changes.invert(tr.startState.doc);
262
275
  selection = selection.map(inverted);
263
276
  view.dispatch({
264
277
  changes: inverted,
265
- annotations: reconcileAnnotationType.of(true)
278
+ annotations: reconcileAnnotation.of(true)
266
279
  });
267
280
  }
268
281
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
282
  if (newHeads === null || newHeads === void 0) {
270
- newHeads = automerge2.getHeads(this._handle.docSync());
283
+ newHeads = A3.getHeads(this._handle.docSync());
271
284
  }
272
- 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);
273
286
  updateCodeMirror(view, selection, path, diff);
274
287
  view.dispatch({
275
288
  effects: updateHeads(newHeads),
276
- annotations: reconcileAnnotationType.of({})
289
+ annotations: reconcileAnnotation.of(false)
277
290
  });
278
- this._inReconcile = false;
279
291
  }
280
292
  };
281
293
 
@@ -293,9 +305,9 @@ var Cursor = class _Cursor {
293
305
  }
294
306
  static {
295
307
  this.getCursorFromRange = (state, range) => {
296
- const cursorConverter3 = state.facet(_Cursor.converter);
297
- const from = cursorConverter3.toCursor(range.from);
298
- 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);
299
311
  return [
300
312
  from,
301
313
  to
@@ -304,10 +316,10 @@ var Cursor = class _Cursor {
304
316
  }
305
317
  static {
306
318
  this.getRangeFromCursor = (state, cursor) => {
307
- const cursorConverter3 = state.facet(_Cursor.converter);
319
+ const cursorConverter2 = state.facet(_Cursor.converter);
308
320
  const parts = cursor.split(":");
309
- const from = cursorConverter3.fromCursor(parts[0]);
310
- const to = cursorConverter3.fromCursor(parts[1]);
321
+ const from = cursorConverter2.fromCursor(parts[0]);
322
+ const to = cursorConverter2.fromCursor(parts[1]);
311
323
  return from !== void 0 && to !== void 0 ? {
312
324
  from,
313
325
  to
@@ -317,12 +329,11 @@ var Cursor = class _Cursor {
317
329
  };
318
330
 
319
331
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
320
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
321
- var automerge3 = ({ handle, path }) => {
322
- const state = StateField.define({
332
+ var automerge = ({ handle, path }) => {
333
+ const syncState = StateField.define({
323
334
  create: () => ({
324
335
  path: path.slice(),
325
- lastHeads: A.getHeads(handle.docSync()),
336
+ lastHeads: A4.getHeads(handle.docSync()),
326
337
  unreconciledTransactions: []
327
338
  }),
328
339
  update: (value, tr) => {
@@ -333,7 +344,7 @@ var automerge3 = ({ handle, path }) => {
333
344
  };
334
345
  let clearUnreconciled = false;
335
346
  for (const effect of tr.effects) {
336
- if (effect.is(effectType)) {
347
+ if (effect.is(updateHeadsEffect)) {
337
348
  result.lastHeads = effect.value.newHeads;
338
349
  clearUnreconciled = true;
339
350
  }
@@ -341,29 +352,21 @@ var automerge3 = ({ handle, path }) => {
341
352
  if (clearUnreconciled) {
342
353
  result.unreconciledTransactions = [];
343
354
  } else {
344
- if (!isReconcileTx(tr)) {
355
+ if (!isReconcile(tr)) {
345
356
  result.unreconciledTransactions.push(tr);
346
357
  }
347
358
  }
348
359
  return result;
349
360
  }
350
361
  });
351
- const semaphore = new PatchSemaphore(handle, state);
362
+ const semaphore = new PatchSemaphore(handle, syncState);
352
363
  return [
353
364
  Cursor.converter.of(cursorConverter(handle, path)),
365
+ syncState,
354
366
  // Reconcile external updates.
355
367
  ViewPlugin.fromClass(class {
356
368
  constructor(_view) {
357
369
  this._view = _view;
358
- invariant(this._view, void 0, {
359
- F: __dxlog_file,
360
- L: 66,
361
- S: this,
362
- A: [
363
- "this._view",
364
- ""
365
- ]
366
- });
367
370
  handle.addListener("change", this._handleChange.bind(this));
368
371
  }
369
372
  destroy() {
@@ -379,65 +382,48 @@ var automerge3 = ({ handle, path }) => {
379
382
  semaphore.reconcile(view);
380
383
  }
381
384
  }),
382
- 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
+ })
383
396
  ];
384
397
  };
385
398
 
386
399
  // packages/ui/react-ui-editor/src/extensions/awareness.ts
387
400
  import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
388
401
  import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
389
- import { useEffect } from "react";
390
402
  import { Event } from "@dxos/async";
391
403
  import { Context } from "@dxos/context";
392
- import { generateName } from "@dxos/display-name";
393
- import { useThemeContext } from "@dxos/react-ui";
394
- import { getColorForValue } from "@dxos/react-ui-theme";
395
404
  var dummyProvider = {
396
405
  remoteStateChange: new Event(),
397
406
  open: () => {
398
407
  },
399
408
  close: () => {
400
409
  },
410
+ getRemoteStates: () => [],
401
411
  update: () => {
402
- },
403
- getRemoteStates: () => []
412
+ }
404
413
  };
405
- var AwarenessProvider = Facet2.define({
414
+ var awarenessProvider = Facet2.define({
406
415
  combine: (providers) => providers[0] ?? dummyProvider
407
416
  });
408
417
  var RemoteSelectionChangedAnnotation = Annotation2.define();
409
418
  var awareness = (provider = dummyProvider) => {
410
419
  return [
411
- AwarenessProvider.of(provider),
420
+ awarenessProvider.of(provider),
412
421
  ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
413
422
  decorations: (value) => value.decorations
414
423
  }),
415
424
  styles
416
425
  ];
417
426
  };
418
- var useAwareness = ({ awareness: awareness2, peer }) => {
419
- const { themeMode } = useThemeContext();
420
- useEffect(() => {
421
- if (awareness2 && peer) {
422
- awareness2.setLocalStateField("user", {
423
- name: peer.name ?? generateName(peer.id),
424
- color: getColorForValue({
425
- value: peer.id,
426
- type: "color"
427
- }),
428
- colorLight: getColorForValue({
429
- value: peer.id,
430
- themeMode,
431
- type: "highlight"
432
- })
433
- });
434
- }
435
- }, [
436
- awareness2,
437
- peer,
438
- themeMode
439
- ]);
440
- };
441
427
  var RemoteSelectionsDecorator = class {
442
428
  constructor(view) {
443
429
  this.decorations = RangeSet.of([]);
@@ -445,7 +431,7 @@ var RemoteSelectionsDecorator = class {
445
431
  this._lastAnchor = void 0;
446
432
  this._lastHead = void 0;
447
433
  this._cursorConverter = view.state.facet(Cursor.converter);
448
- this._provider = view.state.facet(AwarenessProvider);
434
+ this._provider = view.state.facet(awarenessProvider);
449
435
  this._provider.open();
450
436
  this._provider.remoteStateChange.on(this._ctx, () => {
451
437
  view.dispatch({
@@ -599,39 +585,41 @@ var styles = EditorView2.baseTheme({
599
585
  marginLeft: "-1px",
600
586
  marginRight: "-1px",
601
587
  boxSizing: "border-box",
602
- display: "inline"
588
+ display: "inline",
589
+ cursor: "pointer"
603
590
  },
604
591
  ".cm-collab-selectionCaretDot": {
605
592
  borderRadius: "50%",
606
593
  position: "absolute",
607
- width: ".4em",
608
- height: ".4em",
609
- top: "-.2em",
610
- left: "-.2em",
594
+ width: ".5em",
595
+ height: ".5em",
596
+ top: "-.25em",
597
+ left: "-.25em",
611
598
  backgroundColor: "inherit",
612
599
  transition: "transform .3s ease-in-out",
613
600
  boxSizing: "border-box"
614
601
  },
615
602
  ".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
616
- transformOrigin: "bottom center",
617
- transform: "scale(0)"
603
+ transform: "scale(0)",
604
+ transformOrigin: "center"
618
605
  },
619
606
  ".cm-collab-selectionInfo": {
620
607
  position: "absolute",
621
- top: "-1.05em",
622
- left: "-1px",
608
+ transform: "translate(-50%, 0)",
609
+ top: "-20px",
610
+ left: 0,
623
611
  fontSize: ".75em",
624
- fontFamily: "serif",
612
+ fontFamily: "sans-serif",
625
613
  fontStyle: "normal",
626
614
  fontWeight: "normal",
627
615
  lineHeight: "normal",
628
616
  userSelect: "none",
629
617
  color: "white",
630
- paddingLeft: "2px",
631
- paddingRight: "2px",
618
+ padding: "2px",
632
619
  zIndex: 101,
633
620
  transition: "opacity .3s ease-in-out",
634
621
  backgroundColor: "inherit",
622
+ borderRadius: "2px",
635
623
  // These should be separate.
636
624
  opacity: 0,
637
625
  transitionDelay: "0s",
@@ -649,24 +637,25 @@ import { history } from "@codemirror/commands";
649
637
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
650
638
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
651
639
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
652
- var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
653
- lineWrapping ? EditorView3.lineWrapping : [],
640
+ import { isNotFalsy } from "@dxos/util";
641
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
642
+ lineWrapping && EditorView3.lineWrapping,
654
643
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
655
644
  bracketMatching(),
656
645
  closeBrackets(),
657
646
  drawSelection(),
658
647
  highlightActiveLine(),
659
648
  history(),
660
- _placeholder ? placeholder(_placeholder) : [],
649
+ _placeholder && placeholder(_placeholder),
661
650
  // https://github.com/codemirror/theme-one-dark
662
651
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
663
- ];
652
+ ].filter(isNotFalsy);
664
653
 
665
654
  // packages/ui/react-ui-editor/src/extensions/blast.ts
666
655
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
667
656
  import defaultsDeep from "lodash.defaultsdeep";
668
- import { invariant as invariant2 } from "@dxos/invariant";
669
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
657
+ import { invariant } from "@dxos/invariant";
658
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
670
659
  var defaultOptions = {
671
660
  effect: 2,
672
661
  maxParticles: 200,
@@ -699,10 +688,10 @@ var blast = (options = defaultOptions) => {
699
688
  const getPoint = (view, pos) => {
700
689
  const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
701
690
  const element = document.elementFromPoint(x, y);
702
- const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
691
+ const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
703
692
  const point = {
704
693
  x: x - left,
705
- y: y - top
694
+ y: y - top2
706
695
  };
707
696
  return {
708
697
  element,
@@ -792,9 +781,9 @@ var Blaster = class {
792
781
  return this._node;
793
782
  }
794
783
  initialize() {
795
- invariant2(!this._canvas && !this._ctx, void 0, {
796
- F: __dxlog_file2,
797
- L: 138,
784
+ invariant(!this._canvas && !this._ctx, void 0, {
785
+ F: __dxlog_file,
786
+ L: 141,
798
787
  S: this,
799
788
  A: [
800
789
  "!this._canvas && !this._ctx",
@@ -829,9 +818,9 @@ var Blaster = class {
829
818
  }
830
819
  }
831
820
  start() {
832
- invariant2(this._canvas && this._ctx, void 0, {
833
- F: __dxlog_file2,
834
- L: 177,
821
+ invariant(this._canvas && this._ctx, void 0, {
822
+ F: __dxlog_file,
823
+ L: 180,
835
824
  S: this,
836
825
  A: [
837
826
  "this._canvas && this._ctx",
@@ -972,9 +961,14 @@ var random = (min, max) => {
972
961
  return min + ~~(Math.random() * (max - min + 1));
973
962
  };
974
963
 
975
- // packages/ui/react-ui-editor/src/extensions/code.ts
976
- import { ViewPlugin as ViewPlugin3, EditorView as EditorView5, Decoration as Decoration2 } from "@codemirror/view";
977
- 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";
978
972
 
979
973
  // packages/ui/react-ui-editor/src/styles/cursors.ts
980
974
  import * as random2 from "lib0/random";
@@ -1017,34 +1011,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1017
1011
  // packages/ui/react-ui-editor/src/styles/markdown.ts
1018
1012
  import { mx } from "@dxos/react-ui-theme";
1019
1013
  var headings = {
1020
- 1: [
1021
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
1022
- "-ml-[10px]"
1023
- ],
1024
- 2: [
1025
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1026
- "-ml-[8px]"
1027
- ],
1028
- 3: [
1029
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1030
- "-ml-[7px]"
1031
- ],
1032
- 4: [
1033
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1034
- "-ml-[6px]"
1035
- ],
1036
- 5: [
1037
- "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1038
- "-ml-[5px]"
1039
- ],
1040
- 6: [
1041
- "mbs-4 mbe-2 font-medium text-inherit no-underline",
1042
- "-ml-[4px]"
1043
- ]
1044
- };
1045
- var heading = (level, readonly) => {
1046
- const [style, offset] = headings[level];
1047
- 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];
1048
1023
  };
1049
1024
  var light = "text-neutral-200 dark:text-neutral-800";
1050
1025
  var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
@@ -1062,98 +1037,15 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
1062
1037
  var tokens = tailwindConfig({}).theme;
1063
1038
  var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
1064
1039
 
1065
- // packages/ui/react-ui-editor/src/extensions/code.ts
1066
- var CODE_REGEX = /```[\s\S]*?```/gs;
1067
- var styles2 = EditorView5.baseTheme({
1068
- "& .cm-codeblock": {
1069
- fontFamily: getToken("fontFamily.mono", []).join(",")
1070
- },
1071
- '&light [aria-readonly="true"] .cm-codeblock': {
1072
- background: getToken("extend.colors.neutral.50")
1073
- },
1074
- '&dark [aria-readonly="true"] .cm-codeblock': {
1075
- background: getToken("extend.colors.neutral.850")
1076
- },
1077
- '& [aria-readonly="true"] .cm-codeblock': {
1078
- display: "block",
1079
- paddingInline: "4px !important"
1080
- },
1081
- '& [aria-readonly="true"] .cm-codeblock-first': {
1082
- paddingTop: "4px !important",
1083
- borderTopLeftRadius: "4px",
1084
- borderTopRightRadius: "4px"
1085
- },
1086
- '& [aria-readonly="true"] .cm-codeblock-last': {
1087
- paddingBottom: "4px !important",
1088
- borderBottomLeftRadius: "4px",
1089
- borderBottomRightRadius: "4px"
1090
- }
1091
- });
1092
- var getLineRange = (lines, from, to) => {
1093
- const start = lines.findIndex((line) => line.from >= from);
1094
- const end = lines.findIndex((line) => line.to >= to);
1095
- return [
1096
- start,
1097
- end
1098
- ];
1099
- };
1100
- var code2 = () => {
1101
- const buildDecorations5 = (view) => {
1102
- const decorations = [];
1103
- const text = view.state.doc.sliceString(0);
1104
- const matches = text.matchAll(CODE_REGEX);
1105
- const blocks = view.viewportLineBlocks;
1106
- for (const match of matches) {
1107
- const range = getLineRange(blocks, match.index, match.index + match[0].length);
1108
- for (let i = range[0]; i <= range[1]; i++) {
1109
- const block = blocks[i];
1110
- decorations.push(Decoration2.line({
1111
- class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1112
- }).range(block.from));
1113
- }
1114
- }
1115
- return Decoration2.set(decorations);
1116
- };
1117
- return [
1118
- ViewPlugin3.fromClass(class {
1119
- constructor(view) {
1120
- this.hasFocus = false;
1121
- this.decorations = buildDecorations5(view);
1122
- }
1123
- update(update2) {
1124
- if (
1125
- // TODO(burdon): Generalize for other extensions.
1126
- this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
1127
- ) {
1128
- this.hasFocus = update2.view.hasFocus;
1129
- this.decorations = buildDecorations5(update2.view);
1130
- }
1131
- }
1132
- }, {
1133
- decorations: (value) => value.decorations
1134
- }),
1135
- styles2
1136
- ];
1137
- };
1138
-
1139
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1140
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1141
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView6 } from "@codemirror/view";
1142
- import sortBy from "lodash.sortby";
1143
- import { useEffect as useEffect2 } from "react";
1144
- import { debounce } from "@dxos/async";
1145
- import { invariant as invariant3 } from "@dxos/invariant";
1146
- import { nonNullable } from "@dxos/util";
1147
-
1148
1040
  // packages/ui/react-ui-editor/src/util.ts
1149
1041
  import { log } from "@dxos/log";
1150
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1042
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1151
1043
  var callbackWrapper = (fn) => (...args) => {
1152
1044
  try {
1153
1045
  return fn(...args);
1154
1046
  } catch (err) {
1155
1047
  log.catch(err, void 0, {
1156
- F: __dxlog_file3,
1048
+ F: __dxlog_file2,
1157
1049
  L: 18,
1158
1050
  S: void 0,
1159
1051
  C: (f, a) => f(...a)
@@ -1181,24 +1073,35 @@ var logChanges = (trs) => {
1181
1073
  };
1182
1074
 
1183
1075
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1184
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1185
- var styles3 = EditorView6.baseTheme({
1186
- "& .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": {
1187
1084
  backgroundColor: getToken("extend.colors.yellow.50")
1188
1085
  },
1189
- "& .cm-comment-current": {
1086
+ "&light .cm-comment-current": {
1190
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")
1191
1096
  }
1192
1097
  });
1193
- var marks = {
1194
- highlight: Decoration3.mark({
1195
- class: "cm-comment"
1196
- }),
1197
- highlightActive: Decoration3.mark({
1198
- class: "cm-comment-current"
1199
- })
1200
- };
1201
- 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) => {
1202
1105
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1203
1106
  if (!comment?.comment.cursor) {
1204
1107
  return;
@@ -1211,7 +1114,7 @@ var setFocus = (view, id, center = true) => {
1211
1114
  },
1212
1115
  effects: [
1213
1116
  //
1214
- EditorView6.scrollIntoView(range.from, center ? {
1117
+ EditorView5.scrollIntoView(range.from, center ? {
1215
1118
  y: "center"
1216
1119
  } : void 0),
1217
1120
  setSelection.of({
@@ -1260,7 +1163,7 @@ var commentsState = StateField2.define({
1260
1163
  return value;
1261
1164
  }
1262
1165
  });
1263
- var highlightDecorations = EditorView6.decorations.compute([
1166
+ var commentsDecorations = EditorView5.decorations.compute([
1264
1167
  commentsState
1265
1168
  ], (state) => {
1266
1169
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -1271,12 +1174,12 @@ var highlightDecorations = EditorView6.decorations.compute([
1271
1174
  return void 0;
1272
1175
  }
1273
1176
  if (comment.comment.id === current) {
1274
- return marks.highlightActive.range(range.from, range.to);
1177
+ return commentCurrentMark.range(range.from, range.to);
1275
1178
  } else {
1276
- return marks.highlight.range(range.from, range.to);
1179
+ return commentMark.range(range.from, range.to);
1277
1180
  }
1278
1181
  }).filter(nonNullable);
1279
- return Decoration3.set(decorations);
1182
+ return Decoration2.set(decorations);
1280
1183
  });
1281
1184
  var trackPastedComments = (onUpdate) => {
1282
1185
  let tracked = null;
@@ -1298,96 +1201,140 @@ var trackPastedComments = (onUpdate) => {
1298
1201
  }
1299
1202
  };
1300
1203
  return [
1301
- EditorView6.domEventHandlers({
1204
+ EditorView5.domEventHandlers({
1302
1205
  cut: handleTrack,
1303
1206
  copy: handleTrack
1304
1207
  }),
1305
- // Handle paste.
1306
- 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
+ }
1307
1239
  if (tracked) {
1308
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1240
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1309
1241
  if (paste) {
1310
1242
  let found = -1;
1311
1243
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1312
1244
  if (text.eq(tracked.text)) {
1313
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1314
- 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);
1315
1247
  }
1316
1248
  found = fromB;
1317
1249
  }
1318
1250
  });
1319
1251
  if (found > -1) {
1320
1252
  for (const moved of tracked.comments) {
1321
- const range = {
1253
+ restore.push({
1254
+ id: moved.id,
1322
1255
  from: found + moved.from,
1323
1256
  to: found + moved.to
1324
- };
1325
- const cursor = Cursor.getCursorFromRange(state, range);
1326
- onUpdate(moved.id, cursor);
1257
+ });
1327
1258
  }
1328
1259
  }
1329
1260
  tracked = null;
1330
1261
  }
1331
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
+ }
1332
1271
  })
1333
1272
  ];
1334
1273
  };
1335
- var comments = (options = {}) => {
1336
- const { key: shortcut = "meta-'" } = options;
1337
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1338
- const createCommentThread = (view) => {
1339
- invariant3(options.onCreate, void 0, {
1340
- F: __dxlog_file4,
1341
- L: 259,
1342
- S: void 0,
1343
- A: [
1344
- "options.onCreate",
1345
- ""
1346
- ]
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
+ }
1347
1297
  });
1348
- const { from, to } = view.state.selection.main;
1349
- if (from === to) {
1350
- return false;
1351
- }
1352
- 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) {
1353
1310
  view.dispatch({
1354
- changes: {
1355
- from: to,
1356
- insert: "\n"
1311
+ effects: setSelection.of({
1312
+ current: id
1313
+ }),
1314
+ selection: {
1315
+ anchor: to
1357
1316
  }
1358
1317
  });
1318
+ return true;
1359
1319
  }
1360
- const cursor = Cursor.getCursorFromRange(view.state, {
1361
- from,
1362
- to
1363
- });
1364
- if (cursor) {
1365
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1366
- if (id) {
1367
- view.dispatch({
1368
- effects: setSelection.of({
1369
- current: id
1370
- }),
1371
- selection: {
1372
- anchor: from
1373
- }
1374
- });
1375
- return true;
1376
- }
1377
- }
1378
- return false;
1379
- };
1320
+ }
1321
+ return false;
1322
+ };
1323
+ var comments = (options = {}) => {
1324
+ const { key: shortcut = "meta-'" } = options;
1325
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1380
1326
  return [
1327
+ optionsFacet.of(options),
1381
1328
  commentsState,
1382
- highlightDecorations,
1383
- styles3,
1329
+ commentsDecorations,
1330
+ styles2,
1384
1331
  //
1385
1332
  // Keymap.
1386
1333
  //
1387
1334
  options.onCreate ? keymap3.of([
1388
1335
  {
1389
1336
  key: shortcut,
1390
- run: callbackWrapper(createCommentThread)
1337
+ run: callbackWrapper(createComment)
1391
1338
  }
1392
1339
  ]) : [],
1393
1340
  //
@@ -1423,7 +1370,7 @@ var comments = (options = {}) => {
1423
1370
  //
1424
1371
  // Track deleted ranges and update ranges for decorations.
1425
1372
  //
1426
- EditorView6.updateListener.of(({ view, state, changes }) => {
1373
+ EditorView5.updateListener.of(({ view, state, changes }) => {
1427
1374
  let mod = false;
1428
1375
  const { comments: comments2, ...value } = state.field(commentsState);
1429
1376
  changes.iterChanges((from, to, from2, to2) => {
@@ -1453,7 +1400,7 @@ var comments = (options = {}) => {
1453
1400
  //
1454
1401
  // Track selection/proximity.
1455
1402
  //
1456
- EditorView6.updateListener.of(({ view, state }) => {
1403
+ EditorView5.updateListener.of(({ view, state }) => {
1457
1404
  let min = Infinity;
1458
1405
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1459
1406
  const { head } = state.selection.main;
@@ -1489,7 +1436,7 @@ var comments = (options = {}) => {
1489
1436
  ];
1490
1437
  };
1491
1438
  var useComments = (view, comments2 = []) => {
1492
- useEffect2(() => {
1439
+ useEffect(() => {
1493
1440
  if (view) {
1494
1441
  view.dispatch({
1495
1442
  effects: setComments.of(comments2)
@@ -1501,329 +1448,66 @@ var useComments = (view, comments2 = []) => {
1501
1448
  ]);
1502
1449
  };
1503
1450
 
1504
- // packages/ui/react-ui-editor/src/extensions/hr.ts
1505
- import { syntaxTree } from "@codemirror/language";
1506
- import { RangeSetBuilder } from "@codemirror/state";
1507
- import { Decoration as Decoration4, EditorView as EditorView7, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
1508
- var styles4 = EditorView7.baseTheme({
1509
- "& .cm-hr": {
1510
- // Note that block-level decorations should not have vertical margins,
1511
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
1512
- }
1513
- });
1514
- var HorizontalRuleWidget = class extends WidgetType2 {
1515
- toDOM() {
1516
- const el = document.createElement("div");
1517
- el.className = "cm-hr";
1518
- return el;
1519
- }
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;
1520
1463
  };
1521
- var decoration = Decoration4.replace({
1522
- widget: new HorizontalRuleWidget()
1464
+
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()
1496
+ };
1497
+ Table.defineNodes?.forEach((node) => {
1498
+ switch (node?.name) {
1499
+ case "TableCell": {
1500
+ node.style = markdownTags.TableCell;
1501
+ break;
1502
+ }
1503
+ }
1523
1504
  });
1524
- var buildDecorations = (view) => {
1525
- const builder = new RangeSetBuilder();
1526
- const { state } = view;
1527
- const cursor = state.selection.main.head;
1528
- for (const { from, to } of view.visibleRanges) {
1529
- syntaxTree(state).iterate({
1530
- enter: (node) => {
1531
- if (node.name === "HorizontalRule") {
1532
- if (cursor <= node.from || cursor >= node.to) {
1533
- builder.add(node.from, node.to, decoration);
1534
- }
1535
- }
1536
- },
1537
- from,
1538
- to
1539
- });
1540
- }
1541
- return builder.finish();
1542
- };
1543
- var hr = () => {
1544
- return [
1545
- styles4,
1546
- ViewPlugin4.fromClass(class {
1547
- constructor(view) {
1548
- this.decorations = buildDecorations(view);
1549
- }
1550
- update(update2) {
1551
- this.decorations = buildDecorations(update2.view);
1552
- }
1553
- }, {
1554
- decorations: (v) => v.decorations
1555
- })
1556
- ];
1557
- };
1558
-
1559
- // packages/ui/react-ui-editor/src/extensions/image.ts
1560
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1561
- import { StateField as StateField3 } from "@codemirror/state";
1562
- import { Decoration as Decoration5, EditorView as EditorView8, WidgetType as WidgetType3 } from "@codemirror/view";
1563
- var image = (options = {}) => {
1564
- return StateField3.define({
1565
- create: (state) => {
1566
- return Decoration5.set(buildDecorations2(0, state.doc.length, state));
1567
- },
1568
- update: (value, tr) => {
1569
- if (!tr.docChanged && !tr.selection) {
1570
- return value;
1571
- }
1572
- const cursor = tr.state.selection.main.head;
1573
- const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
1574
- let from = Math.min(cursor, oldCursor);
1575
- let to = Math.max(cursor, oldCursor);
1576
- tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
1577
- from = Math.min(from, fromB);
1578
- to = Math.max(to, toB);
1579
- });
1580
- from = tr.state.doc.lineAt(from).from;
1581
- to = tr.state.doc.lineAt(to).to;
1582
- return value.map(tr.changes).update({
1583
- filterFrom: from,
1584
- filterTo: to,
1585
- filter: () => false,
1586
- add: buildDecorations2(from, to, tr.state)
1587
- });
1588
- },
1589
- provide: (field) => EditorView8.decorations.from(field)
1590
- });
1591
- };
1592
- var buildDecorations2 = (from, to, state) => {
1593
- const decorations = [];
1594
- const cursor = state.selection.main.head;
1595
- syntaxTree2(state).iterate({
1596
- enter: (node) => {
1597
- if (node.name === "Image") {
1598
- const urlNode = node.node.getChild("URL");
1599
- if (urlNode) {
1600
- const hide = state.readOnly || cursor < node.from || cursor > node.to;
1601
- const url = state.sliceDoc(urlNode.from, urlNode.to);
1602
- decorations.push(Decoration5.replace({
1603
- block: true,
1604
- widget: new ImageWidget(url)
1605
- }).range(hide ? node.from : node.to, node.to));
1606
- }
1607
- }
1608
- },
1609
- from,
1610
- to
1611
- });
1612
- return decorations;
1613
- };
1614
- var ImageWidget = class extends WidgetType3 {
1615
- constructor(_url) {
1616
- super();
1617
- this._url = _url;
1618
- }
1619
- eq(other) {
1620
- return this._url === other._url;
1621
- }
1622
- toDOM(view) {
1623
- const img = document.createElement("img");
1624
- img.setAttribute("src", this._url);
1625
- img.setAttribute("class", "cm-image");
1626
- img.onload = () => img.classList.add("cm-loaded-image");
1627
- return img;
1628
- }
1629
- };
1630
-
1631
- // packages/ui/react-ui-editor/src/extensions/link.ts
1632
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1633
- import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1634
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin5, WidgetType as WidgetType4 } from "@codemirror/view";
1635
- import { tooltipContent } from "@dxos/react-ui-theme";
1636
- var link = (options = {}) => {
1637
- const extensions = [
1638
- ViewPlugin5.fromClass(class {
1639
- constructor(view) {
1640
- this.decorations = buildDecorations3(view, options);
1641
- }
1642
- update(update2) {
1643
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
1644
- this.decorations = buildDecorations3(update2.view, options);
1645
- }
1646
- }
1647
- }, {
1648
- decorations: (v) => v.decorations
1649
- })
1650
- ];
1651
- if (options.onHover) {
1652
- extensions.push(
1653
- // https://codemirror.net/examples/tooltip
1654
- // https://codemirror.net/docs/ref/#view.hoverTooltip
1655
- // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1656
- hoverTooltip2((view, pos, side) => {
1657
- const syntax = syntaxTree3(view.state).resolveInner(pos, side);
1658
- let link2 = null;
1659
- for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1660
- link2 = node.name === "Link" ? node : null;
1661
- }
1662
- const url = link2 && link2.getChild("URL");
1663
- if (!url || !link2) {
1664
- return null;
1665
- }
1666
- const urlText = view.state.sliceDoc(url.from, url.to);
1667
- return {
1668
- pos: link2.from,
1669
- end: link2.to,
1670
- above: true,
1671
- create: () => {
1672
- const el = document.createElement("div");
1673
- el.className = tooltipContent({}, "pli-2 plb-1");
1674
- options.onHover(el, urlText);
1675
- return {
1676
- dom: el,
1677
- offset: {
1678
- x: 0,
1679
- y: 4
1680
- }
1681
- };
1682
- }
1683
- };
1684
- })
1685
- );
1686
- }
1687
- return extensions;
1688
- };
1689
- var LinkButton = class extends WidgetType4 {
1690
- constructor(_url, _onAttach) {
1691
- super();
1692
- this._url = _url;
1693
- this._onAttach = _onAttach;
1694
- }
1695
- eq(other) {
1696
- return this._url === other._url;
1697
- }
1698
- toDOM(view) {
1699
- const el = document.createElement("span");
1700
- this._onAttach(el, this._url);
1701
- return el;
1702
- }
1703
- };
1704
- var LinkText = class extends WidgetType4 {
1705
- constructor(_text, _url) {
1706
- super();
1707
- this._text = _text;
1708
- this._url = _url;
1709
- }
1710
- eq(other) {
1711
- return this._url === other._url;
1712
- }
1713
- toDOM(view) {
1714
- const link2 = document.createElement("a");
1715
- link2.setAttribute("class", "cm-link");
1716
- link2.textContent = this._text;
1717
- if (this._url) {
1718
- link2.setAttribute("rel", "noreferrer");
1719
- link2.setAttribute("target", "_blank");
1720
- link2.href = this._url;
1721
- } else {
1722
- link2.onclick = () => {
1723
- view.dispatch({
1724
- selection: {
1725
- anchor: view.posAtDOM(link2)
1726
- }
1727
- });
1728
- };
1729
- }
1730
- return link2;
1731
- }
1732
- };
1733
- var buildDecorations3 = (view, options) => {
1734
- const builder = new RangeSetBuilder2();
1735
- const { state } = view;
1736
- const cursor = state.selection.main.head;
1737
- for (const { from, to } of view.visibleRanges) {
1738
- syntaxTree3(state).iterate({
1739
- enter: (node) => {
1740
- if (node.name === "Link") {
1741
- const marks2 = node.node.getChildren("LinkMark");
1742
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
1743
- const urlNode = node.node.getChild("URL");
1744
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
1745
- if (!url) {
1746
- return false;
1747
- }
1748
- if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
1749
- builder.add(node.from, node.to, Decoration6.replace({
1750
- widget: new LinkText(text, options.link ? url : void 0)
1751
- }));
1752
- }
1753
- if (options.onRender) {
1754
- builder.add(node.to, node.to, Decoration6.replace({
1755
- widget: new LinkButton(url, options.onRender)
1756
- }));
1757
- }
1758
- return false;
1759
- }
1760
- },
1761
- from,
1762
- to
1763
- });
1764
- }
1765
- return builder.finish();
1766
- };
1767
-
1768
- // packages/ui/react-ui-editor/src/extensions/listener.ts
1769
- import { EditorView as EditorView9 } from "@codemirror/view";
1770
- var listener = ({ onFocus, onChange }) => {
1771
- const extensions = [];
1772
- onFocus && extensions.push(EditorView9.focusChangeEffect.of((_, focused) => {
1773
- onFocus(focused);
1774
- return null;
1775
- }));
1776
- onChange && extensions.push(EditorView9.updateListener.of((update2) => {
1777
- onChange(update2.state.doc.toString());
1778
- }));
1779
- return extensions;
1780
- };
1781
-
1782
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1783
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1784
- import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1785
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1786
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1787
- import { languages } from "@codemirror/language-data";
1788
- import { searchKeymap } from "@codemirror/search";
1789
- import { EditorState } from "@codemirror/state";
1790
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1791
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView10, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1792
-
1793
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1794
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
1795
- import { HighlightStyle } from "@codemirror/language";
1796
- import { tags, styleTags, Tag } from "@lezer/highlight";
1797
- import { Table } from "@lezer/markdown";
1798
- var markdownTags = {
1799
- Blockquote: Tag.define(),
1800
- CodeMark: Tag.define(),
1801
- CodeText: Tag.define(),
1802
- EmphasisMark: Tag.define(),
1803
- HeaderMark: Tag.define(),
1804
- InlineCode: Tag.define(),
1805
- LinkLabel: Tag.define(),
1806
- LinkReference: Tag.define(),
1807
- ListMark: Tag.define(),
1808
- QuoteMark: Tag.define(),
1809
- URL: Tag.define(),
1810
- // Custom.
1811
- TableCell: Tag.define()
1812
- };
1813
- Table.defineNodes?.forEach((node) => {
1814
- switch (node?.name) {
1815
- case "TableCell": {
1816
- node.style = markdownTags.TableCell;
1817
- break;
1818
- }
1819
- }
1820
- });
1821
- var markdownTagsExtensions = [
1822
- Table,
1823
- {
1824
- props: [
1825
- styleTags(markdownTags)
1826
- ]
1505
+ var markdownTagsExtensions = [
1506
+ Table,
1507
+ {
1508
+ props: [
1509
+ styleTags(markdownTags)
1510
+ ]
1827
1511
  }
1828
1512
  ];
1829
1513
  var markdownHighlightStyle = (readonly) => {
@@ -1885,7 +1569,7 @@ var markdownHighlightStyle = (readonly) => {
1885
1569
  markdownTags.QuoteMark,
1886
1570
  markdownTags.EmphasisMark
1887
1571
  ],
1888
- class: readonly ? "hidden" : mark
1572
+ class: mark
1889
1573
  },
1890
1574
  // E.g., code block language (after ```).
1891
1575
  {
@@ -1893,7 +1577,7 @@ var markdownHighlightStyle = (readonly) => {
1893
1577
  tags.function(tags.variableName),
1894
1578
  tags.labelName
1895
1579
  ],
1896
- class: readonly ? "hidden" : codeMark
1580
+ class: codeMark
1897
1581
  },
1898
1582
  {
1899
1583
  tag: [
@@ -1904,27 +1588,27 @@ var markdownHighlightStyle = (readonly) => {
1904
1588
  // Headings.
1905
1589
  {
1906
1590
  tag: tags.heading1,
1907
- class: heading(1, readonly)
1591
+ class: heading(1)
1908
1592
  },
1909
1593
  {
1910
1594
  tag: tags.heading2,
1911
- class: heading(2, readonly)
1595
+ class: heading(2)
1912
1596
  },
1913
1597
  {
1914
1598
  tag: tags.heading3,
1915
- class: heading(3, readonly)
1599
+ class: heading(3)
1916
1600
  },
1917
1601
  {
1918
1602
  tag: tags.heading4,
1919
- class: heading(4, readonly)
1603
+ class: heading(4)
1920
1604
  },
1921
1605
  {
1922
1606
  tag: tags.heading5,
1923
- class: heading(5, readonly)
1607
+ class: heading(5)
1924
1608
  },
1925
1609
  {
1926
1610
  tag: tags.heading6,
1927
- class: heading(6, readonly)
1611
+ class: heading(6)
1928
1612
  },
1929
1613
  // Emphasis.
1930
1614
  {
@@ -1950,7 +1634,7 @@ var markdownHighlightStyle = (readonly) => {
1950
1634
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
1951
1635
  // when a language is specified. In this case, the syntax highlighting extensions will colorize
1952
1636
  // the code, but all other CSS properties will be inherited.
1953
- // 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.
1954
1638
  {
1955
1639
  tag: [
1956
1640
  markdownTags.CodeText,
@@ -1979,11 +1663,11 @@ var markdownHighlightStyle = (readonly) => {
1979
1663
  };
1980
1664
 
1981
1665
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1982
- var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1666
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1983
1667
  return [
1668
+ lineWrapping && EditorView7.lineWrapping,
1984
1669
  EditorState.allowMultipleSelections.of(true),
1985
1670
  EditorState.tabSize.of(2),
1986
- EditorView10.lineWrapping,
1987
1671
  // https://github.com/codemirror/basic-setup
1988
1672
  bracketMatching2(),
1989
1673
  closeBrackets2(),
@@ -1993,7 +1677,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
1993
1677
  highlightActiveLine2(),
1994
1678
  history2(),
1995
1679
  indentOnInput(),
1996
- _placeholder ? placeholder2(_placeholder) : [],
1680
+ _placeholder && placeholder2(_placeholder),
1997
1681
  // Main extension.
1998
1682
  // https://github.com/codemirror/lang-markdown
1999
1683
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -2015,7 +1699,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
2015
1699
  // https://github.com/codemirror/theme-one-dark
2016
1700
  themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
2017
1701
  // Custom styles.
2018
- syntaxHighlighting2(markdownHighlightStyle(readonly)),
1702
+ syntaxHighlighting2(markdownHighlightStyle()),
2019
1703
  keymap4.of([
2020
1704
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2021
1705
  indentWithTab,
@@ -2028,56 +1712,1519 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
2028
1712
  // https://codemirror.net/docs/ref/#commands.standardKeymap
2029
1713
  ...standardKeymap
2030
1714
  ])
2031
- ];
1715
+ ].filter(isNotFalsy2);
2032
1716
  };
2033
1717
 
2034
- // packages/ui/react-ui-editor/src/extensions/mention.ts
2035
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
2036
- var mention = ({ onSearch }) => {
2037
- return autocompletion2({
2038
- // TODO(burdon): Not working.
2039
- activateOnTyping: true,
2040
- // activateOnTypingDelay: 100,
2041
- // selectOnOpen: true,
2042
- closeOnBlur: false,
2043
- // defaultKeymap: false,
2044
- icons: false,
2045
- override: [
2046
- (context) => {
2047
- console.log(context);
2048
- const match = context.matchBefore(/@(\w+)?/);
2049
- if (!match || match.from === match.to && !context.explicit) {
2050
- return null;
2051
- }
2052
- return {
2053
- from: match.from,
2054
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
2055
- label: `@${value}`
2056
- }))
2057
- };
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
+ ];
1771
+ };
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, EditorSelection } from "@codemirror/state";
1837
+ import { Decoration as Decoration4, EditorView as EditorView9, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1838
+ import { useState, useMemo } from "react";
1839
+ var compareFormatting = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockquote === b.blockquote;
1840
+ var Inline;
1841
+ (function(Inline2) {
1842
+ Inline2[Inline2["Strong"] = 0] = "Strong";
1843
+ Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
1844
+ Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
1845
+ Inline2[Inline2["Code"] = 3] = "Code";
1846
+ })(Inline || (Inline = {}));
1847
+ var List;
1848
+ (function(List2) {
1849
+ List2[List2["Ordered"] = 0] = "Ordered";
1850
+ List2[List2["Bullet"] = 1] = "Bullet";
1851
+ List2[List2["Task"] = 2] = "Task";
1852
+ })(List || (List = {}));
1853
+ var setHeading = (level) => ({ state, dispatch }) => {
1854
+ const { selection: { ranges }, doc } = state;
1855
+ const changes = [];
1856
+ let prevBlock = -1;
1857
+ for (const range of ranges) {
1858
+ syntaxTree2(state).iterate({
1859
+ from: range.from,
1860
+ to: range.to,
1861
+ enter: (node) => {
1862
+ if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
1863
+ return;
1864
+ }
1865
+ prevBlock = node.from;
1866
+ const blockType = Textblocks[node.name];
1867
+ const isHeading = /heading(\d)/.exec(blockType);
1868
+ const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
1869
+ if (curLevel < 0 || curLevel === level) {
1870
+ return;
1871
+ }
1872
+ if (curLevel === 0) {
1873
+ changes.push({
1874
+ from: node.from,
1875
+ insert: "#".repeat(level) + " "
1876
+ });
1877
+ } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
1878
+ const nextLine = doc.lineAt(node.to);
1879
+ if (level) {
1880
+ changes.push({
1881
+ from: node.from,
1882
+ insert: "#".repeat(level) + " "
1883
+ });
1884
+ }
1885
+ changes.push({
1886
+ from: nextLine.from - 1,
1887
+ to: nextLine.to
1888
+ });
1889
+ } else {
1890
+ if (level === 0) {
1891
+ changes.push({
1892
+ from: node.from,
1893
+ to: Math.min(node.to, node.from + curLevel + 1)
1894
+ });
1895
+ } else if (level < curLevel) {
1896
+ changes.push({
1897
+ from: node.from,
1898
+ to: node.from + (curLevel - level)
1899
+ });
1900
+ } else {
1901
+ changes.push({
1902
+ from: node.from,
1903
+ insert: "#".repeat(level - curLevel)
1904
+ });
1905
+ }
1906
+ }
1907
+ }
1908
+ });
1909
+ }
1910
+ if (!changes.length) {
1911
+ return false;
1912
+ }
1913
+ dispatch(state.update({
1914
+ changes,
1915
+ userEvent: "format.setHeading",
1916
+ scrollIntoView: true
1917
+ }));
1918
+ return true;
1919
+ };
1920
+ var setStyle = (type, enable) => ({ state, dispatch }) => {
1921
+ const marker = inlineMarkerText(type);
1922
+ const changes = state.changeByRange((range) => {
1923
+ if (!enable && range.empty) {
1924
+ const after = state.doc.sliceString(range.head, range.head + 6);
1925
+ const found = after.indexOf(marker);
1926
+ if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
1927
+ const before = state.doc.sliceString(range.head - 6, range.head);
1928
+ if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
1929
+ ...before.slice(before.length - found)
1930
+ ].reverse().join("") === after.slice(0, found)) {
1931
+ return {
1932
+ changes: [
1933
+ {
1934
+ from: range.head - marker.length - found,
1935
+ to: range.head - found
1936
+ },
1937
+ {
1938
+ from: range.head + found,
1939
+ to: range.head + found + marker.length
1940
+ }
1941
+ ],
1942
+ range: EditorSelection.cursor(range.from - marker.length)
1943
+ };
1944
+ }
1945
+ }
1946
+ }
1947
+ const changes2 = [];
1948
+ const changesAtEnd = [];
1949
+ let blockStart = -1;
1950
+ let blockEnd = -1;
1951
+ let startCovered = false;
1952
+ let endCovered = false;
1953
+ let { from, to } = range;
1954
+ syntaxTree2(state).iterate({
1955
+ from,
1956
+ to,
1957
+ enter: (node) => {
1958
+ const { name } = node;
1959
+ if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
1960
+ blockStart = blockContentStart(node);
1961
+ blockEnd = blockContentEnd(node, state.doc);
1962
+ startCovered = endCovered = false;
1963
+ } else if (name === "Link" || name === "Image" && enable) {
1964
+ if (from < node.from && to > node.from && to <= node.to) {
1965
+ to = node.to;
1966
+ } else if (to > node.to && from >= node.from && from < node.to) {
1967
+ from = node.from;
1968
+ }
1969
+ } else if (IgnoreInline.has(name) && enable) {
1970
+ if (node.from < from && node.to > from) {
1971
+ if (to === from) {
1972
+ to = node.to;
1973
+ }
1974
+ from = node.to;
1975
+ }
1976
+ if (node.from < to && node.to > to) {
1977
+ to = node.from;
1978
+ }
1979
+ } else if (Object.hasOwn(InlineMarker, name)) {
1980
+ const markType = InlineMarker[name];
1981
+ const size = inlineMarkerText(markType).length;
1982
+ const openEnd = node.from + size;
1983
+ const closeStart = node.to - size;
1984
+ if (markType === type) {
1985
+ if (openEnd <= from && closeStart >= from) {
1986
+ startCovered = openEnd === from ? "adjacent" : true;
1987
+ }
1988
+ if (openEnd <= to && closeStart >= to) {
1989
+ endCovered = closeStart === to ? "adjacent" : true;
1990
+ }
1991
+ }
1992
+ if (markType === type || type === 3 && enable) {
1993
+ if (node.from >= from && openEnd <= to) {
1994
+ changes2.push({
1995
+ from: node.from,
1996
+ to: openEnd
1997
+ });
1998
+ if (markType !== type && closeStart >= to) {
1999
+ changesAtEnd.push({
2000
+ from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
2001
+ insert: inlineMarkerText(markType)
2002
+ });
2003
+ }
2004
+ }
2005
+ if (closeStart >= from && node.to <= to) {
2006
+ changes2.push({
2007
+ from: closeStart,
2008
+ to: node.to
2009
+ });
2010
+ if (markType !== type && openEnd <= from) {
2011
+ changes2.push({
2012
+ from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
2013
+ insert: inlineMarkerText(markType)
2014
+ });
2015
+ }
2016
+ }
2017
+ }
2018
+ }
2019
+ },
2020
+ leave: (node) => {
2021
+ if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
2022
+ const rangeStart = Math.max(from, blockStart);
2023
+ const rangeEnd = Math.min(to, blockEnd);
2024
+ if (enable) {
2025
+ if (!startCovered) {
2026
+ changes2.push({
2027
+ from: rangeStart,
2028
+ insert: marker
2029
+ });
2030
+ }
2031
+ if (!endCovered) {
2032
+ changes2.push({
2033
+ from: rangeEnd,
2034
+ insert: marker
2035
+ });
2036
+ }
2037
+ } else {
2038
+ if (startCovered === "adjacent") {
2039
+ changes2.push({
2040
+ from: from - marker.length,
2041
+ to: from
2042
+ });
2043
+ } else if (startCovered) {
2044
+ changes2.push({
2045
+ from: skipSpaces(rangeStart, state.doc, -1, blockStart),
2046
+ insert: marker
2047
+ });
2048
+ }
2049
+ if (endCovered === "adjacent") {
2050
+ changes2.push({
2051
+ from: to,
2052
+ to: to + marker.length
2053
+ });
2054
+ } else if (endCovered) {
2055
+ changes2.push({
2056
+ from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
2057
+ insert: marker
2058
+ });
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+ });
2064
+ const changeSet = state.changes(changes2.concat(changesAtEnd));
2065
+ return {
2066
+ changes: changeSet,
2067
+ range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2068
+ };
2069
+ });
2070
+ dispatch(state.update(changes, {
2071
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2072
+ scrollIntoView: true
2073
+ }));
2074
+ return true;
2075
+ };
2076
+ var addStyle = (style) => setStyle(style, true);
2077
+ var removeStyle = (style) => setStyle(style, false);
2078
+ var toggleStyle = (style) => (arg) => {
2079
+ const form = getFormatting(arg.state);
2080
+ return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
2081
+ };
2082
+ var toggleStrong = toggleStyle(0);
2083
+ var toggleEmphasis = toggleStyle(1);
2084
+ var toggleStrikethrough = toggleStyle(2);
2085
+ var toggleInlineCode = toggleStyle(3);
2086
+ var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
2087
+ var blockContentStart = (node) => {
2088
+ const atx = /^ATXHeading(\d)/.exec(node.name);
2089
+ if (atx) {
2090
+ return Math.min(node.to, node.from + +atx[1] + 1);
2091
+ }
2092
+ return node.from;
2093
+ };
2094
+ var blockContentEnd = (node, doc) => {
2095
+ const setext = /^SetextHeading(\d)/.exec(node.name);
2096
+ const lastLine = doc.lineAt(node.to);
2097
+ if (setext || /^[\s>]*$/.exec(lastLine.text)) {
2098
+ return lastLine.from - 1;
2099
+ }
2100
+ return node.to;
2101
+ };
2102
+ var skipSpaces = (pos, doc, dir, limit) => {
2103
+ const line = doc.lineAt(pos);
2104
+ while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
2105
+ pos += dir;
2106
+ }
2107
+ return pos;
2108
+ };
2109
+ var snippets = {
2110
+ codeblock: snippet([
2111
+ "```#{lang}",
2112
+ "#{}",
2113
+ "```"
2114
+ ].join("\n")),
2115
+ table: snippet([
2116
+ "| #{col1} | #{col2} |",
2117
+ "| ---- | ---- |",
2118
+ "| #{val1} | #{val2} |",
2119
+ "| #{val3} | #{val4} |"
2120
+ ].join("\n"))
2121
+ };
2122
+ var insertTable = (view) => {
2123
+ const { selection: { main }, doc } = view.state;
2124
+ const { number } = doc.lineAt(main.anchor);
2125
+ const { from } = doc.line(number);
2126
+ snippets.table(view, null, from, from);
2127
+ };
2128
+ var removeLinkInner = (from, to, changes, state) => {
2129
+ syntaxTree2(state).iterate({
2130
+ from,
2131
+ to,
2132
+ enter: (node) => {
2133
+ if (node.name === "Link" && node.from < to && node.to > from) {
2134
+ node.node.cursor().iterate((node2) => {
2135
+ const { name } = node2;
2136
+ if (name === "LinkMark" || name === "LinkLabel") {
2137
+ changes.push({
2138
+ from: node2.from,
2139
+ to: node2.to
2140
+ });
2141
+ } else if (name === "LinkTitle" || name === "URL") {
2142
+ changes.push({
2143
+ from: skipSpaces(node2.from, state.doc, -1),
2144
+ to: skipSpaces(node2.to, state.doc, 1)
2145
+ });
2146
+ }
2147
+ });
2148
+ return false;
2149
+ }
2150
+ }
2151
+ });
2152
+ };
2153
+ var removeLink = ({ state, dispatch }) => {
2154
+ const changes = [];
2155
+ for (const { from, to } of state.selection.ranges) {
2156
+ removeLinkInner(from, to, changes, state);
2157
+ }
2158
+ if (!changes) {
2159
+ return false;
2160
+ }
2161
+ dispatch(state.update({
2162
+ changes,
2163
+ userEvent: "format.link.remove",
2164
+ scrollIntoView: true
2165
+ }));
2166
+ return true;
2167
+ };
2168
+ var addLink = ({ state, dispatch }) => {
2169
+ const changes = state.changeByRange((range) => {
2170
+ let { from, to } = range;
2171
+ const cutStyles = [];
2172
+ let okay = null;
2173
+ syntaxTree2(state).iterate({
2174
+ from,
2175
+ to,
2176
+ enter: (node) => {
2177
+ if (Object.hasOwn(Textblocks, node.name)) {
2178
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
2179
+ } else if (Object.hasOwn(InlineMarker, node.name)) {
2180
+ const sNode = node.node;
2181
+ if (node.from < from && node.to <= to) {
2182
+ if (sNode.firstChild.to === from) {
2183
+ from = node.from;
2184
+ } else {
2185
+ cutStyles.push(sNode);
2186
+ }
2187
+ } else if (node.from >= from && node.to > to) {
2188
+ if (sNode.lastChild.from === to) {
2189
+ to = node.to;
2190
+ } else {
2191
+ cutStyles.push(sNode);
2192
+ }
2193
+ }
2194
+ }
2195
+ }
2196
+ });
2197
+ if (okay === null) {
2198
+ const line = state.doc.lineAt(from);
2199
+ okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
2200
+ }
2201
+ if (!okay) {
2202
+ return {
2203
+ range
2204
+ };
2205
+ }
2206
+ const changes2 = [];
2207
+ const changesAfter = [];
2208
+ removeLinkInner(from, to, changesAfter, state);
2209
+ let cursorOffset = 1;
2210
+ for (const style of cutStyles) {
2211
+ const type = InlineMarker[style.name];
2212
+ const mark2 = inlineMarkerText(type);
2213
+ if (style.from < from) {
2214
+ changes2.push({
2215
+ from: skipSpaces(from, state.doc, -1),
2216
+ insert: mark2
2217
+ });
2218
+ changesAfter.push({
2219
+ from: skipSpaces(from, state.doc, 1, to),
2220
+ insert: mark2
2221
+ });
2222
+ } else {
2223
+ changes2.push({
2224
+ from: skipSpaces(to, state.doc, -1, from),
2225
+ insert: mark2
2226
+ });
2227
+ const after = skipSpaces(to, state.doc, 1);
2228
+ if (after === to) {
2229
+ cursorOffset += mark2.length;
2230
+ }
2231
+ changesAfter.push({
2232
+ from: after,
2233
+ insert: mark2
2234
+ });
2235
+ }
2236
+ }
2237
+ changes2.push({
2238
+ from,
2239
+ insert: "["
2240
+ }, {
2241
+ from: to,
2242
+ insert: "]()"
2243
+ });
2244
+ const changeSet = state.changes(changes2.concat(changesAfter));
2245
+ return {
2246
+ changes: changeSet,
2247
+ range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
2248
+ };
2249
+ });
2250
+ if (changes.changes.empty) {
2251
+ return false;
2252
+ }
2253
+ dispatch(state.update(changes, {
2254
+ userEvent: "format.link.add",
2255
+ scrollIntoView: true
2256
+ }));
2257
+ return true;
2258
+ };
2259
+ var addList = (type) => ({ state, dispatch }) => {
2260
+ let lastBlock = -1;
2261
+ let counter = 1;
2262
+ let first = true;
2263
+ let parentColumn = null;
2264
+ const blocks = [];
2265
+ for (const { from, to } of state.selection.ranges) {
2266
+ syntaxTree2(state).iterate({
2267
+ from,
2268
+ to,
2269
+ enter: (node) => {
2270
+ if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
2271
+ if (first) {
2272
+ let before = node.node.prevSibling;
2273
+ while (before && /Mark$/.test(before.name)) {
2274
+ before = before.prevSibling;
2275
+ }
2276
+ if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
2277
+ const item = before.lastChild;
2278
+ const itemLine = state.doc.lineAt(item.from);
2279
+ const itemText = itemLine.text.slice(item.from - itemLine.from);
2280
+ parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2281
+ if (type === 0) {
2282
+ const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2283
+ if (mark2) {
2284
+ parentColumn += mark2[1].length;
2285
+ counter = +mark2[1] + 1;
2286
+ }
2287
+ }
2288
+ }
2289
+ first = false;
2290
+ }
2291
+ if (node.from === lastBlock) {
2292
+ return;
2293
+ }
2294
+ lastBlock = node.from;
2295
+ blocks.push({
2296
+ node: node.node,
2297
+ counter,
2298
+ parentColumn
2299
+ });
2300
+ counter++;
2301
+ return false;
2302
+ }
2303
+ },
2304
+ leave: (node) => {
2305
+ if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
2306
+ counter = 1;
2307
+ parentColumn = null;
2308
+ }
2309
+ }
2310
+ });
2311
+ }
2312
+ if (!blocks.length) {
2313
+ return false;
2314
+ }
2315
+ const changes = [];
2316
+ for (let i = 0; i < blocks.length; i++) {
2317
+ const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
2318
+ const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
2319
+ let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
2320
+ if (type === 0) {
2321
+ padding += String(counter2).length;
2322
+ }
2323
+ let line = state.doc.lineAt(nodeFrom);
2324
+ const column = nodeFrom - line.from;
2325
+ if (parentColumn2 !== null && parentColumn2 > column) {
2326
+ padding = Math.max(padding, parentColumn2 - column);
2327
+ }
2328
+ let mark2;
2329
+ if (type === 0) {
2330
+ let max = counter2;
2331
+ for (let j = i + 1; j < blocks.length; j++) {
2332
+ if (blocks[j].counter !== max + 1) {
2333
+ break;
2334
+ }
2335
+ max++;
2336
+ }
2337
+ const num = String(counter2);
2338
+ padding = Math.max(String(max).length, padding);
2339
+ mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
2340
+ } else {
2341
+ mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
2342
+ }
2343
+ changes.push({
2344
+ from: nodeFrom,
2345
+ insert: mark2
2346
+ });
2347
+ while (line.to < node.to) {
2348
+ line = state.doc.lineAt(line.to + 1);
2349
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2350
+ changes.push({
2351
+ from: line.from + Math.min(open, column),
2352
+ insert: " ".repeat(mark2.length)
2353
+ });
2354
+ }
2355
+ }
2356
+ if (type === 0) {
2357
+ const last = blocks[blocks.length - 1];
2358
+ let next = last.node.nextSibling;
2359
+ while (next && /Mark$/.test(next.name)) {
2360
+ next = next.nextSibling;
2361
+ }
2362
+ if (next?.name === "OrderedList") {
2363
+ renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
2364
+ }
2365
+ }
2366
+ dispatch(state.update({
2367
+ changes,
2368
+ userEvent: "format.list.add",
2369
+ scrollIntoView: true
2370
+ }));
2371
+ return true;
2372
+ };
2373
+ var removeList = (type) => ({ state, dispatch }) => {
2374
+ let lastBlock = -1;
2375
+ const changes = [];
2376
+ const stack = [];
2377
+ const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
2378
+ for (const { from, to } of state.selection.ranges) {
2379
+ syntaxTree2(state).iterate({
2380
+ from,
2381
+ to,
2382
+ enter: (node) => {
2383
+ const { name } = node;
2384
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2385
+ stack.push(name);
2386
+ } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2387
+ stack[stack.length - 1] = "TaskList";
2388
+ }
2389
+ },
2390
+ leave: (node) => {
2391
+ const { name } = node;
2392
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2393
+ stack.pop();
2394
+ } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
2395
+ lastBlock = node.from;
2396
+ let line = state.doc.lineAt(node.from);
2397
+ const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
2398
+ if (!mark2) {
2399
+ return false;
2400
+ }
2401
+ const column = node.from - line.from;
2402
+ changes.push({
2403
+ from: node.from,
2404
+ to: node.from + mark2[0].length
2405
+ });
2406
+ while (line.to < node.to) {
2407
+ line = state.doc.lineAt(line.to + 1);
2408
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2409
+ if (open > column) {
2410
+ changes.push({
2411
+ from: line.from + column,
2412
+ to: line.from + Math.min(column + mark2[0].length, open)
2413
+ });
2414
+ }
2415
+ }
2416
+ if (node.to >= to) {
2417
+ renumberListItems(node.node.nextSibling, 1, changes, state.doc);
2418
+ }
2419
+ return false;
2420
+ }
2421
+ }
2422
+ });
2423
+ }
2424
+ if (!changes.length) {
2425
+ return false;
2426
+ }
2427
+ dispatch(state.update({
2428
+ changes,
2429
+ userEvent: "format.list.remove",
2430
+ scrollIntoView: true
2431
+ }));
2432
+ return true;
2433
+ };
2434
+ var toggleList = (type) => (target) => {
2435
+ const formatting2 = getFormatting(target.state);
2436
+ const active = formatting2.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
2437
+ return (active ? removeList(type) : addList(type))(target);
2438
+ };
2439
+ var renumberListItems = (item, counter, changes, doc) => {
2440
+ for (; item; item = item.nextSibling) {
2441
+ if (item.name === "ListItem") {
2442
+ const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
2443
+ if (!number || +number[2] === counter) {
2444
+ break;
2445
+ }
2446
+ const size = number[1].length + number[2].length;
2447
+ const newNum = String(counter);
2448
+ changes.push({
2449
+ from: item.from + Math.max(0, size - newNum.length),
2450
+ to: item.from + size,
2451
+ insert: newNum
2452
+ });
2453
+ counter++;
2454
+ }
2455
+ }
2456
+ };
2457
+ var setBlockquote = (enable) => ({ state, dispatch }) => {
2458
+ const lines = [];
2459
+ let lastBlock = -1;
2460
+ for (const { from, to } of state.selection.ranges) {
2461
+ syntaxTree2(state).iterate({
2462
+ from,
2463
+ to,
2464
+ enter: (node) => {
2465
+ if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
2466
+ if (node.from === lastBlock) {
2467
+ return false;
2468
+ }
2469
+ lastBlock = node.from;
2470
+ let line = state.doc.lineAt(node.from);
2471
+ if (line.number > 1) {
2472
+ const prevLine = state.doc.line(line.number - 1);
2473
+ if (/^[>\s]*$/.test(prevLine.text)) {
2474
+ if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2475
+ lines.push(prevLine);
2476
+ }
2477
+ }
2478
+ }
2479
+ for (; ; ) {
2480
+ lines.push(line);
2481
+ if (line.to >= node.to) {
2482
+ break;
2483
+ }
2484
+ line = state.doc.line(line.number + 1);
2485
+ }
2486
+ if (!enable && line.number < state.doc.lines) {
2487
+ const nextLine = state.doc.line(line.number + 1);
2488
+ if (/^[>\s]*$/.test(nextLine.text)) {
2489
+ lines.push(nextLine);
2490
+ }
2491
+ }
2492
+ return false;
2493
+ }
2494
+ }
2495
+ });
2496
+ }
2497
+ const changes = [];
2498
+ for (const line of lines) {
2499
+ if (enable) {
2500
+ changes.push({
2501
+ from: line.from,
2502
+ insert: /\S/.test(line.text) ? "> " : ">"
2503
+ });
2504
+ } else {
2505
+ const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
2506
+ if (quote) {
2507
+ changes.push({
2508
+ from: line.from + quote[1].length,
2509
+ to: line.from + quote[0].length
2510
+ });
2511
+ }
2512
+ }
2513
+ }
2514
+ if (!changes.length) {
2515
+ return false;
2516
+ }
2517
+ dispatch(state.update({
2518
+ changes,
2519
+ userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
2520
+ scrollIntoView: true
2521
+ }));
2522
+ return true;
2523
+ };
2524
+ var addBlockquote = setBlockquote(true);
2525
+ var removeBlockquote = setBlockquote(false);
2526
+ var toggleBlockquote = (target) => {
2527
+ return (getFormatting(target.state).blockquote ? removeBlockquote : addBlockquote)(target);
2528
+ };
2529
+ var addCodeblock = (target) => {
2530
+ const { state, dispatch } = target;
2531
+ const { selection } = state;
2532
+ if (selection.ranges.length === 1 && selection.main.empty) {
2533
+ const { head } = selection.main;
2534
+ const line = state.doc.lineAt(head);
2535
+ if (!/\S/.test(line.text) && head === line.from) {
2536
+ snippets.codeblock(target, null, line.from, line.to);
2537
+ return true;
2538
+ }
2539
+ }
2540
+ const ranges = [];
2541
+ for (const { from, to } of selection.ranges) {
2542
+ let blockFrom = from;
2543
+ let blockTo = to;
2544
+ syntaxTree2(state).iterate({
2545
+ from,
2546
+ to,
2547
+ enter: (node) => {
2548
+ if (Object.hasOwn(Textblocks, node.name)) {
2549
+ if (from >= node.from && to <= node.to) {
2550
+ blockFrom = node.from;
2551
+ blockTo = node.to;
2552
+ } else {
2553
+ blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
2554
+ blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
2555
+ }
2556
+ }
2557
+ }
2558
+ });
2559
+ if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
2560
+ ranges[ranges.length - 1].to = blockTo;
2561
+ } else {
2562
+ ranges.push({
2563
+ from: blockFrom,
2564
+ to: blockTo
2565
+ });
2566
+ }
2567
+ }
2568
+ if (!ranges.length) {
2569
+ return false;
2570
+ }
2571
+ const changes = ranges.map(({ from, to }) => {
2572
+ const column = from - state.doc.lineAt(from).from;
2573
+ return [
2574
+ {
2575
+ from,
2576
+ insert: "```\n" + " ".repeat(column)
2577
+ },
2578
+ {
2579
+ from: to,
2580
+ insert: "\n" + " ".repeat(column) + "```"
2581
+ }
2582
+ ];
2583
+ });
2584
+ dispatch(state.update({
2585
+ changes,
2586
+ userEvent: "format.codeblock.add",
2587
+ scrollIntoView: true
2588
+ }));
2589
+ return true;
2590
+ };
2591
+ var removeCodeblock = ({ state, dispatch }) => {
2592
+ const changes = [];
2593
+ let lastBlock = -1;
2594
+ for (const { from, to } of state.selection.ranges) {
2595
+ syntaxTree2(state).iterate({
2596
+ from,
2597
+ to,
2598
+ enter: (node) => {
2599
+ if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
2600
+ lastBlock = node.from;
2601
+ const firstLine = state.doc.lineAt(node.from);
2602
+ if (node.name === "FencedCode") {
2603
+ changes.push({
2604
+ from: node.from,
2605
+ to: firstLine.to + 1 + node.from - firstLine.from
2606
+ });
2607
+ const lastLine = state.doc.lineAt(node.to);
2608
+ if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
2609
+ changes.push({
2610
+ from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
2611
+ to: lastLine.to
2612
+ });
2613
+ }
2614
+ } else {
2615
+ const column = node.from - firstLine.from;
2616
+ for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
2617
+ changes.push({
2618
+ from: line.from + column - 4,
2619
+ to: line.from + column
2620
+ });
2621
+ if (line.to >= node.to) {
2622
+ break;
2623
+ }
2624
+ }
2625
+ }
2626
+ }
2627
+ }
2628
+ });
2629
+ }
2630
+ if (!changes.length) {
2631
+ return false;
2632
+ }
2633
+ dispatch(state.update({
2634
+ changes,
2635
+ userEvent: "format.codeblock.remove",
2636
+ scrollIntoView: true
2637
+ }));
2638
+ return true;
2639
+ };
2640
+ var toggleCodeblock = (target) => {
2641
+ return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
2642
+ };
2643
+ var formatting = (options = {}) => {
2644
+ return [
2645
+ keymap5.of([
2646
+ {
2647
+ key: "meta-b",
2648
+ run: toggleStrong
2649
+ }
2650
+ ]),
2651
+ styling()
2652
+ ];
2653
+ };
2654
+ var styling = () => {
2655
+ const buildDecorations7 = (view) => {
2656
+ const builder = new RangeSetBuilder2();
2657
+ const { state } = view;
2658
+ const cursor = state.selection.main.head;
2659
+ const replace = (node, marks) => {
2660
+ if (cursor <= node.from || cursor >= node.to) {
2661
+ for (const mark2 of marks) {
2662
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
2663
+ }
2664
+ }
2665
+ };
2666
+ for (const { from, to } of view.visibleRanges) {
2667
+ syntaxTree2(state).iterate({
2668
+ enter: (node) => {
2669
+ switch (node.name) {
2670
+ case "Emphasis":
2671
+ case "StrongEmphasis": {
2672
+ const marks = node.node.getChildren("EmphasisMark");
2673
+ replace(node, marks);
2674
+ break;
2675
+ }
2676
+ case "Strikethrough": {
2677
+ const marks = node.node.getChildren("StrikethroughMark");
2678
+ replace(node, marks);
2679
+ break;
2680
+ }
2681
+ }
2682
+ },
2683
+ from,
2684
+ to
2685
+ });
2686
+ }
2687
+ return builder.finish();
2688
+ };
2689
+ return [
2690
+ ViewPlugin4.fromClass(class {
2691
+ constructor(view) {
2692
+ this.decorations = buildDecorations7(view);
2693
+ }
2694
+ update(update2) {
2695
+ this.decorations = buildDecorations7(update2.view);
2696
+ }
2697
+ }, {
2698
+ decorations: (value) => value.decorations
2699
+ })
2700
+ ];
2701
+ };
2702
+ var InlineMarker = {
2703
+ Emphasis: 1,
2704
+ StrongEmphasis: 0,
2705
+ InlineCode: 3,
2706
+ Strikethrough: 2
2707
+ };
2708
+ var IgnoreInline = /* @__PURE__ */ new Set([
2709
+ "Hardbreak",
2710
+ "HTMLTag",
2711
+ "Comment",
2712
+ "ProcessingInstruction",
2713
+ "Autolink",
2714
+ "HeaderMark",
2715
+ "QuoteMark",
2716
+ "ListMark",
2717
+ "LinkMark",
2718
+ "EmphasisMark",
2719
+ "CodeMark",
2720
+ "CodeText",
2721
+ "StrikethroughMark",
2722
+ "TaskMarker",
2723
+ "SuperscriptMark",
2724
+ "SubscriptMark"
2725
+ ]);
2726
+ var Textblocks = {
2727
+ Paragraph: "paragraph",
2728
+ Task: "paragraph",
2729
+ CodeBlock: "codeblock",
2730
+ FencedCode: "codeblock",
2731
+ ATXHeading1: "heading1",
2732
+ ATXHeading2: "heading2",
2733
+ ATXHeading3: "heading3",
2734
+ ATXHeading4: "heading4",
2735
+ ATXHeading5: "heading5",
2736
+ ATXHeading6: "heading6",
2737
+ SetextHeading1: "heading1",
2738
+ SetextHeading2: "heading2",
2739
+ TableCell: "tablecell"
2740
+ };
2741
+ var getFormatting = (state) => {
2742
+ let blockType = null;
2743
+ const inline = [
2744
+ null,
2745
+ null,
2746
+ null,
2747
+ null
2748
+ ];
2749
+ let link2 = false;
2750
+ let blockquote2 = null;
2751
+ let listStyle = null;
2752
+ const stack = [];
2753
+ let currentBlock = null;
2754
+ const advanceInline = (upto) => {
2755
+ if (!currentBlock) {
2756
+ return;
2757
+ }
2758
+ upto = Math.min(upto, currentBlock.end);
2759
+ if (upto <= currentBlock.pos) {
2760
+ return;
2761
+ }
2762
+ for (let i = 0; i < currentBlock.active.length; i++) {
2763
+ if (inline[i] === false) {
2764
+ continue;
2765
+ } else if (currentBlock.active[i]) {
2766
+ inline[i] = true;
2767
+ } else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
2768
+ inline[i] = false;
2769
+ }
2770
+ }
2771
+ currentBlock.pos = upto;
2772
+ };
2773
+ const skipInline = (upto) => {
2774
+ if (currentBlock && upto > currentBlock.pos) {
2775
+ currentBlock.pos = Math.min(upto, currentBlock.end);
2776
+ }
2777
+ };
2778
+ const { selection } = state;
2779
+ for (const range of selection.ranges) {
2780
+ if (range.empty && inline.some((v) => v === null)) {
2781
+ const contextSize = Math.min(range.head, 6);
2782
+ const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
2783
+ let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
2784
+ for (let i = 0; i < contextSize; i++) {
2785
+ const ch = contextAfter[i];
2786
+ if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
2787
+ contextAfter = contextAfter.slice(0, i);
2788
+ break;
2789
+ }
2790
+ }
2791
+ for (let i = 0; i < inline.length; i++) {
2792
+ const mark2 = inlineMarkerText(i);
2793
+ const found = contextAfter.indexOf(mark2);
2794
+ if (found > -1) {
2795
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
2796
+ if (inline[i] === null) {
2797
+ inline[i] = true;
2798
+ }
2799
+ }
2800
+ }
2801
+ }
2802
+ syntaxTree2(state).iterate({
2803
+ from: range.from,
2804
+ to: range.to,
2805
+ enter: (node) => {
2806
+ advanceInline(node.from);
2807
+ const { name } = node;
2808
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2809
+ stack.push(name);
2810
+ } else if (name === "Link") {
2811
+ link2 = true;
2812
+ } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
2813
+ if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2814
+ stack[stack.length - 1] = "TaskList";
2815
+ }
2816
+ const blockCode = Textblocks[name];
2817
+ if (blockType === null) {
2818
+ blockType = blockCode;
2819
+ } else if (blockType !== blockCode) {
2820
+ blockType = false;
2821
+ }
2822
+ if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
2823
+ currentBlock = {
2824
+ pos: Math.max(range.from, node.from),
2825
+ end: Math.min(range.to, node.to),
2826
+ active: [
2827
+ false,
2828
+ false,
2829
+ false,
2830
+ false
2831
+ ]
2832
+ };
2833
+ }
2834
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
2835
+ const index = InlineMarker[name];
2836
+ if (range.empty && inline[index] === null) {
2837
+ inline[index] = true;
2838
+ }
2839
+ currentBlock.active[index] = true;
2840
+ } else if (IgnoreInline.has(name)) {
2841
+ skipInline(node.to);
2842
+ }
2843
+ },
2844
+ leave: (node) => {
2845
+ advanceInline(node.to);
2846
+ const { name } = node;
2847
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2848
+ stack.pop();
2849
+ } else if (Object.hasOwn(Textblocks, name)) {
2850
+ let hasList = false;
2851
+ let hasQuote = false;
2852
+ for (let i = stack.length - 1; i >= 0; i--) {
2853
+ if (stack[i] === "Blockquote") {
2854
+ hasQuote = true;
2855
+ } else if (!hasList) {
2856
+ hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
2857
+ }
2858
+ }
2859
+ if (blockquote2 === null) {
2860
+ blockquote2 = hasQuote;
2861
+ } else if (!hasQuote && blockquote2) {
2862
+ blockquote2 = false;
2863
+ }
2864
+ if (listStyle === null) {
2865
+ listStyle = hasList;
2866
+ } else if (listStyle !== hasList) {
2867
+ listStyle = false;
2868
+ }
2869
+ currentBlock = null;
2870
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
2871
+ currentBlock.active[InlineMarker[name]] = false;
2872
+ }
2873
+ }
2874
+ });
2875
+ }
2876
+ return {
2877
+ blockType: blockType || null,
2878
+ strong: inline[0] ?? false,
2879
+ emphasis: inline[1] ?? false,
2880
+ code: inline[3] ?? false,
2881
+ strikethrough: inline[2] ?? false,
2882
+ link: link2,
2883
+ blockquote: blockquote2 ?? false,
2884
+ listStyle: listStyle || null
2885
+ };
2886
+ };
2887
+ var useFormattingState = () => {
2888
+ const [state, setState] = useState(null);
2889
+ const observer = useMemo(() => EditorView9.updateListener.of((update2) => {
2890
+ if (update2.docChanged || update2.selectionSet) {
2891
+ const newState = getFormatting(update2.state);
2892
+ if (!state || !compareFormatting(state, newState)) {
2893
+ setState(newState);
2894
+ }
2895
+ }
2896
+ }), []);
2897
+ return [
2898
+ state,
2899
+ observer
2900
+ ];
2901
+ };
2902
+
2903
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
2904
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
2905
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
2906
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2907
+ var buildDecorations2 = (view) => {
2908
+ const builder = new RangeSetBuilder3();
2909
+ const { state } = view;
2910
+ const cursor = state.selection.main.head;
2911
+ for (const { from, to } of view.visibleRanges) {
2912
+ syntaxTree3(state).iterate({
2913
+ enter: (node) => {
2914
+ switch (node.name) {
2915
+ case "ATXHeading1":
2916
+ case "ATXHeading2":
2917
+ case "ATXHeading3":
2918
+ case "ATXHeading4":
2919
+ case "ATXHeading5":
2920
+ case "ATXHeading6": {
2921
+ const mark2 = node.node.getChild("HeaderMark");
2922
+ if (mark2) {
2923
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
2924
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
2925
+ }
2926
+ }
2927
+ }
2928
+ }
2929
+ },
2930
+ from,
2931
+ to
2932
+ });
2933
+ }
2934
+ return builder.finish();
2935
+ };
2936
+ var heading2 = () => {
2937
+ return [
2938
+ ViewPlugin5.fromClass(class {
2939
+ constructor(view) {
2940
+ this.decorations = buildDecorations2(view);
2058
2941
  }
2059
- ]
2942
+ update(update2) {
2943
+ this.decorations = buildDecorations2(update2.view);
2944
+ }
2945
+ }, {
2946
+ decorations: (value) => value.decorations
2947
+ })
2948
+ ];
2949
+ };
2950
+
2951
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
2952
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2953
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2954
+ import { Decoration as Decoration6, EditorView as EditorView10, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2955
+ var styles4 = EditorView10.baseTheme({
2956
+ "& .cm-hr": {
2957
+ // Note that block-level decorations should not have vertical margins,
2958
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2959
+ }
2960
+ });
2961
+ var HorizontalRuleWidget = class extends WidgetType3 {
2962
+ toDOM() {
2963
+ const el = document.createElement("div");
2964
+ el.className = "cm-hr";
2965
+ return el;
2966
+ }
2967
+ };
2968
+ var decoration = Decoration6.replace({
2969
+ widget: new HorizontalRuleWidget()
2970
+ });
2971
+ var buildDecorations3 = (view) => {
2972
+ const builder = new RangeSetBuilder4();
2973
+ const { state } = view;
2974
+ const cursor = state.selection.main.head;
2975
+ for (const { from, to } of view.visibleRanges) {
2976
+ syntaxTree4(state).iterate({
2977
+ enter: (node) => {
2978
+ if (node.name === "HorizontalRule") {
2979
+ if (cursor <= node.from || cursor >= node.to) {
2980
+ builder.add(node.from, node.to, decoration);
2981
+ }
2982
+ }
2983
+ },
2984
+ from,
2985
+ to
2986
+ });
2987
+ }
2988
+ return builder.finish();
2989
+ };
2990
+ var hr = () => {
2991
+ return [
2992
+ ViewPlugin6.fromClass(class {
2993
+ constructor(view) {
2994
+ this.decorations = buildDecorations3(view);
2995
+ }
2996
+ update(update2) {
2997
+ this.decorations = buildDecorations3(update2.view);
2998
+ }
2999
+ }, {
3000
+ decorations: (value) => value.decorations
3001
+ }),
3002
+ styles4
3003
+ ];
3004
+ };
3005
+
3006
+ // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3007
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3008
+ import { StateField as StateField3 } from "@codemirror/state";
3009
+ import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType4 } from "@codemirror/view";
3010
+ var image = (options = {}) => {
3011
+ return StateField3.define({
3012
+ create: (state) => {
3013
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
3014
+ },
3015
+ update: (value, tr) => {
3016
+ if (!tr.docChanged && !tr.selection) {
3017
+ return value;
3018
+ }
3019
+ const cursor = tr.state.selection.main.head;
3020
+ const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
3021
+ let from = Math.min(cursor, oldCursor);
3022
+ let to = Math.max(cursor, oldCursor);
3023
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
3024
+ from = Math.min(from, fromB);
3025
+ to = Math.max(to, toB);
3026
+ });
3027
+ from = tr.state.doc.lineAt(from).from;
3028
+ to = tr.state.doc.lineAt(to).to;
3029
+ return value.map(tr.changes).update({
3030
+ filterFrom: from,
3031
+ filterTo: to,
3032
+ filter: () => false,
3033
+ add: buildDecorations4(from, to, tr.state)
3034
+ });
3035
+ },
3036
+ provide: (field) => EditorView11.decorations.from(field)
3037
+ });
3038
+ };
3039
+ var buildDecorations4 = (from, to, state) => {
3040
+ const decorations = [];
3041
+ const cursor = state.selection.main.head;
3042
+ syntaxTree5(state).iterate({
3043
+ enter: (node) => {
3044
+ if (node.name === "Image") {
3045
+ const urlNode = node.node.getChild("URL");
3046
+ if (urlNode) {
3047
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
3048
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
3049
+ decorations.push(Decoration7.replace({
3050
+ block: true,
3051
+ widget: new ImageWidget(url)
3052
+ }).range(hide ? node.from : node.to, node.to));
3053
+ }
3054
+ }
3055
+ },
3056
+ from,
3057
+ to
2060
3058
  });
3059
+ return decorations;
3060
+ };
3061
+ var ImageWidget = class extends WidgetType4 {
3062
+ constructor(_url) {
3063
+ super();
3064
+ this._url = _url;
3065
+ }
3066
+ eq(other) {
3067
+ return this._url === other._url;
3068
+ }
3069
+ toDOM(view) {
3070
+ const img = document.createElement("img");
3071
+ img.setAttribute("src", this._url);
3072
+ img.setAttribute("class", "cm-image");
3073
+ img.onload = () => img.classList.add("cm-loaded-image");
3074
+ return img;
3075
+ }
2061
3076
  };
2062
3077
 
2063
- // packages/ui/react-ui-editor/src/extensions/outliner.ts
2064
- var outliner = (options = {}) => {
2065
- return [];
3078
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3079
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
3080
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
3081
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
3082
+ import { tooltipContent } from "@dxos/react-ui-theme";
3083
+ var link = (options = {}) => {
3084
+ const extensions = [
3085
+ ViewPlugin7.fromClass(class {
3086
+ constructor(view) {
3087
+ this.decorations = buildDecorations5(view, options);
3088
+ }
3089
+ update(update2) {
3090
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
3091
+ this.decorations = buildDecorations5(update2.view, options);
3092
+ }
3093
+ }
3094
+ }, {
3095
+ decorations: (value) => value.decorations
3096
+ })
3097
+ ];
3098
+ if (options.onHover) {
3099
+ extensions.push(
3100
+ // https://codemirror.net/examples/tooltip
3101
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
3102
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
3103
+ hoverTooltip2((view, pos, side) => {
3104
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
3105
+ let link2 = null;
3106
+ for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
3107
+ link2 = node.name === "Link" ? node : null;
3108
+ }
3109
+ const url = link2 && link2.getChild("URL");
3110
+ if (!url || !link2) {
3111
+ return null;
3112
+ }
3113
+ const urlText = view.state.sliceDoc(url.from, url.to);
3114
+ return {
3115
+ pos: link2.from,
3116
+ end: link2.to,
3117
+ above: true,
3118
+ create: () => {
3119
+ const el = document.createElement("div");
3120
+ el.className = tooltipContent({}, "pli-2 plb-1");
3121
+ options.onHover(el, urlText);
3122
+ return {
3123
+ dom: el,
3124
+ offset: {
3125
+ x: 0,
3126
+ y: 4
3127
+ }
3128
+ };
3129
+ }
3130
+ };
3131
+ })
3132
+ );
3133
+ }
3134
+ return extensions;
3135
+ };
3136
+ var LinkButton = class extends WidgetType5 {
3137
+ constructor(_url, _onAttach) {
3138
+ super();
3139
+ this._url = _url;
3140
+ this._onAttach = _onAttach;
3141
+ }
3142
+ eq(other) {
3143
+ return this._url === other._url;
3144
+ }
3145
+ toDOM(view) {
3146
+ const el = document.createElement("span");
3147
+ this._onAttach(el, this._url);
3148
+ return el;
3149
+ }
3150
+ };
3151
+ var LinkText = class extends WidgetType5 {
3152
+ constructor(_text, _url) {
3153
+ super();
3154
+ this._text = _text;
3155
+ this._url = _url;
3156
+ }
3157
+ eq(other) {
3158
+ return this._url === other._url;
3159
+ }
3160
+ toDOM(view) {
3161
+ const link2 = document.createElement("a");
3162
+ link2.setAttribute("class", "cm-link");
3163
+ link2.textContent = this._text;
3164
+ if (this._url) {
3165
+ link2.setAttribute("rel", "noreferrer");
3166
+ link2.setAttribute("target", "_blank");
3167
+ link2.href = this._url;
3168
+ } else {
3169
+ link2.onclick = () => {
3170
+ view.dispatch({
3171
+ selection: {
3172
+ anchor: view.posAtDOM(link2)
3173
+ }
3174
+ });
3175
+ };
3176
+ }
3177
+ return link2;
3178
+ }
3179
+ };
3180
+ var buildDecorations5 = (view, options) => {
3181
+ const builder = new RangeSetBuilder5();
3182
+ const { state } = view;
3183
+ const cursor = state.selection.main.head;
3184
+ for (const { from, to } of view.visibleRanges) {
3185
+ syntaxTree6(state).iterate({
3186
+ enter: (node) => {
3187
+ if (node.name === "Link") {
3188
+ const marks = node.node.getChildren("LinkMark");
3189
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
3190
+ const urlNode = node.node.getChild("URL");
3191
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
3192
+ if (!url) {
3193
+ return false;
3194
+ }
3195
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
3196
+ builder.add(node.from, node.to, Decoration8.replace({
3197
+ widget: new LinkText(text, options.link ? url : void 0)
3198
+ }));
3199
+ }
3200
+ if (options.onRender) {
3201
+ builder.add(node.to, node.to, Decoration8.replace({
3202
+ widget: new LinkButton(url, options.onRender)
3203
+ }));
3204
+ }
3205
+ return false;
3206
+ }
3207
+ },
3208
+ from,
3209
+ to
3210
+ });
3211
+ }
3212
+ return builder.finish();
2066
3213
  };
2067
3214
 
2068
- // packages/ui/react-ui-editor/src/extensions/table.ts
2069
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2070
- import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField4 } from "@codemirror/state";
2071
- import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType5 } from "@codemirror/view";
3215
+ // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3216
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
3217
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
3218
+ import { Decoration as Decoration9, EditorView as EditorView12, WidgetType as WidgetType6 } from "@codemirror/view";
2072
3219
  var table = (options = {}) => {
2073
3220
  return StateField4.define({
2074
3221
  create: (state) => update(state, options),
2075
3222
  update: (_, tr) => update(tr.state, options),
2076
- provide: (field) => EditorView11.decorations.from(field)
3223
+ provide: (field) => EditorView12.decorations.from(field)
2077
3224
  });
2078
3225
  };
2079
3226
  var update = (state, options) => {
2080
- const builder = new RangeSetBuilder3();
3227
+ const builder = new RangeSetBuilder6();
2081
3228
  const cursor = state.selection.main.head;
2082
3229
  const tables = [];
2083
3230
  const getTable = () => tables[tables.length - 1];
@@ -2085,7 +3232,7 @@ var update = (state, options) => {
2085
3232
  const table2 = getTable();
2086
3233
  return table2.rows?.[table2.rows.length - 1];
2087
3234
  };
2088
- syntaxTree4(state).iterate({
3235
+ syntaxTree7(state).iterate({
2089
3236
  enter: (node) => {
2090
3237
  switch (node.name) {
2091
3238
  case "Table": {
@@ -2117,16 +3264,16 @@ var update = (state, options) => {
2117
3264
  });
2118
3265
  tables.forEach((table2) => {
2119
3266
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2120
- hide && builder.add(table2.from, table2.to, Decoration7.replace({
3267
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2121
3268
  widget: new TableWidget(table2)
2122
3269
  }));
2123
- builder.add(table2.from, table2.to, Decoration7.mark({
3270
+ builder.add(table2.from, table2.to, Decoration9.mark({
2124
3271
  class: "cm-table"
2125
3272
  }));
2126
3273
  });
2127
3274
  return builder.finish();
2128
3275
  };
2129
- var TableWidget = class extends WidgetType5 {
3276
+ var TableWidget = class extends WidgetType6 {
2130
3277
  constructor(_table) {
2131
3278
  super();
2132
3279
  this._table = _table;
@@ -2163,11 +3310,11 @@ var TableWidget = class extends WidgetType5 {
2163
3310
  }
2164
3311
  };
2165
3312
 
2166
- // packages/ui/react-ui-editor/src/extensions/tasklist.ts
2167
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2168
- import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2169
- import { EditorView as EditorView12, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
2170
- var styles5 = EditorView12.baseTheme({
3313
+ // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
3314
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
3315
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
3316
+ import { EditorView as EditorView13, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3317
+ var styles5 = EditorView13.baseTheme({
2171
3318
  "& .cm-task": {
2172
3319
  color: getToken("extend.colors.blue.500")
2173
3320
  },
@@ -2176,7 +3323,7 @@ var styles5 = EditorView12.baseTheme({
2176
3323
  marginRight: "4px"
2177
3324
  }
2178
3325
  });
2179
- var CheckboxWidget = class extends WidgetType6 {
3326
+ var CheckboxWidget = class extends WidgetType7 {
2180
3327
  constructor(_checked) {
2181
3328
  super();
2182
3329
  this._checked = _checked;
@@ -2213,23 +3360,23 @@ var CheckboxWidget = class extends WidgetType6 {
2213
3360
  return false;
2214
3361
  }
2215
3362
  };
2216
- var checkedDecoration = Decoration8.replace({
3363
+ var checkedDecoration = Decoration10.replace({
2217
3364
  widget: new CheckboxWidget(true)
2218
3365
  });
2219
- var uncheckedDecoration = Decoration8.replace({
3366
+ var uncheckedDecoration = Decoration10.replace({
2220
3367
  widget: new CheckboxWidget(false)
2221
3368
  });
2222
- var buildDecorations4 = (view) => {
2223
- const builder = new RangeSetBuilder4();
3369
+ var buildDecorations6 = (view) => {
3370
+ const builder = new RangeSetBuilder7();
2224
3371
  const { state } = view;
2225
3372
  const cursor = state.selection.main.head;
2226
3373
  for (const { from, to } of view.visibleRanges) {
2227
- syntaxTree5(state).iterate({
3374
+ syntaxTree8(state).iterate({
2228
3375
  enter: (node) => {
2229
3376
  if (node.name === "TaskMarker") {
2230
3377
  if (cursor < node.from || cursor > node.to) {
2231
3378
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2232
- builder.add(node.from - 2, node.from - 1, Decoration8.mark({
3379
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2233
3380
  class: "cm-task"
2234
3381
  }));
2235
3382
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2244,24 +3391,58 @@ var buildDecorations4 = (view) => {
2244
3391
  };
2245
3392
  var tasklist = (options = {}) => {
2246
3393
  return [
2247
- ViewPlugin6.fromClass(class {
3394
+ ViewPlugin8.fromClass(class {
2248
3395
  constructor(view) {
2249
- this.decorations = buildDecorations4(view);
3396
+ this.decorations = buildDecorations6(view);
2250
3397
  }
2251
3398
  update(update2) {
2252
3399
  if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
2253
- this.decorations = buildDecorations4(update2.view);
3400
+ this.decorations = buildDecorations6(update2.view);
2254
3401
  }
2255
3402
  }
2256
3403
  }, {
2257
- decorations: (v) => v.decorations
3404
+ decorations: (value) => value.decorations
2258
3405
  }),
2259
3406
  styles5
2260
3407
  ];
2261
3408
  };
2262
3409
 
3410
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
3411
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
3412
+ var mention = ({ onSearch }) => {
3413
+ return autocompletion2({
3414
+ // TODO(burdon): Not working.
3415
+ activateOnTyping: true,
3416
+ // activateOnTypingDelay: 100,
3417
+ // selectOnOpen: true,
3418
+ closeOnBlur: false,
3419
+ // defaultKeymap: false,
3420
+ icons: false,
3421
+ override: [
3422
+ (context) => {
3423
+ console.log(context);
3424
+ const match = context.matchBefore(/@(\w+)?/);
3425
+ if (!match || match.from === match.to && !context.explicit) {
3426
+ return null;
3427
+ }
3428
+ return {
3429
+ from: match.from,
3430
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
3431
+ label: `@${value}`
3432
+ }))
3433
+ };
3434
+ }
3435
+ ]
3436
+ });
3437
+ };
3438
+
3439
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
3440
+ var outliner = (options = {}) => {
3441
+ return [];
3442
+ };
3443
+
2263
3444
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2264
- import { keymap as keymap5 } from "@codemirror/view";
3445
+ import { keymap as keymap6 } from "@codemirror/view";
2265
3446
  var defaultItems = [
2266
3447
  "hello world!",
2267
3448
  "this is a test.",
@@ -2271,7 +3452,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2271
3452
  let t;
2272
3453
  let idx = 0;
2273
3454
  return [
2274
- keymap5.of([
3455
+ keymap6.of([
2275
3456
  {
2276
3457
  // Reset.
2277
3458
  key: "alt-meta-'",
@@ -2317,27 +3498,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2317
3498
  ];
2318
3499
  };
2319
3500
 
2320
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2321
- import { yCollab } from "y-codemirror.next";
2322
-
2323
- // packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
2324
- import * as Y from "yjs";
2325
- import { arrayToString, stringToArray } from "@dxos/util";
2326
- var cursorConverter2 = (text) => ({
2327
- toCursor: (index, assoc) => {
2328
- return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
2329
- },
2330
- fromCursor: (cursor) => {
2331
- return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
2332
- }
2333
- });
2334
-
2335
- // packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
2336
- var yjs = (content, awareness2) => [
2337
- Cursor.converter.of(cursorConverter2(content)),
2338
- yCollab(content, awareness2)
2339
- ];
2340
-
2341
3501
  // packages/ui/react-ui-editor/src/themes/default.ts
2342
3502
  import get3 from "lodash.get";
2343
3503
  var defaultTheme = {
@@ -2369,8 +3529,9 @@ var defaultTheme = {
2369
3529
  outline: "none"
2370
3530
  },
2371
3531
  ".cm-scroller": {
2372
- overflow: "visible",
2373
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
3532
+ // overflow: 'visible',
3533
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
3534
+ lineHeight: 1.4
2374
3535
  },
2375
3536
  ".cm-content": {
2376
3537
  padding: 0,
@@ -2378,11 +3539,11 @@ var defaultTheme = {
2378
3539
  fontSize: "16px"
2379
3540
  },
2380
3541
  "&light .cm-content": {
2381
- color: get3(tokens, "extend.colors.neutral.900", "black"),
3542
+ color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
2382
3543
  caretColor: "black"
2383
3544
  },
2384
3545
  "&dark .cm-content": {
2385
- color: get3(tokens, "extend.colors.neutral.100", "white"),
3546
+ color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
2386
3547
  caretColor: "white"
2387
3548
  },
2388
3549
  //
@@ -2394,8 +3555,11 @@ var defaultTheme = {
2394
3555
  "&dark .cm-cursor, &dark .cm-dropCursor": {
2395
3556
  borderLeft: "2px solid white"
2396
3557
  },
2397
- ".cm-placeholder": {
2398
- fontWeight: 100
3558
+ "&light .cm-placeholder": {
3559
+ color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3560
+ },
3561
+ "&dark .cm-placeholder": {
3562
+ color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2399
3563
  },
2400
3564
  //
2401
3565
  // line
@@ -2404,7 +3568,7 @@ var defaultTheme = {
2404
3568
  paddingInline: 0
2405
3569
  },
2406
3570
  ".cm-activeLine": {
2407
- background: "inherit"
3571
+ background: "transparent"
2408
3572
  },
2409
3573
  //
2410
3574
  // Selection
@@ -2575,31 +3739,35 @@ var codeTheme = {
2575
3739
  };
2576
3740
 
2577
3741
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2578
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
3742
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2579
3743
  var EditorModes = [
2580
3744
  "default",
2581
3745
  "vim"
2582
3746
  ];
2583
3747
  var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2584
- const tabsterDOMAttribute = useFocusableGroup({
2585
- tabBehavior: "limited"
2586
- });
2587
- const { themeMode } = useThemeContext2();
3748
+ const { themeMode } = useThemeContext();
2588
3749
  const rootRef = useRef(null);
2589
- const [view, setView] = useState(null);
3750
+ const [view, setView] = useState2(null);
2590
3751
  useImperativeHandle(forwardedRef, () => view, [
2591
3752
  view
2592
3753
  ]);
2593
- useEffect3(() => {
2594
- if (autoFocus) {
3754
+ useEffect2(() => {
3755
+ if (autoFocus && !view?.hasFocus) {
3756
+ if (view?.state.selection.main?.from === 0) {
3757
+ const { to } = view.state.doc.lineAt(0);
3758
+ view?.dispatch({
3759
+ selection: {
3760
+ anchor: to
3761
+ }
3762
+ });
3763
+ }
2595
3764
  view?.focus();
2596
3765
  }
2597
3766
  }, [
2598
3767
  view,
2599
3768
  autoFocus
2600
3769
  ]);
2601
- useAwareness(model);
2602
- useEffect3(() => {
3770
+ useEffect2(() => {
2603
3771
  if (!model || !rootRef.current) {
2604
3772
  return;
2605
3773
  }
@@ -2608,37 +3776,38 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2608
3776
  selection,
2609
3777
  extensions: [
2610
3778
  // TODO(burdon): Doesn't catch errors in keymap functions.
2611
- EditorView13.exceptionSink.of((err) => {
3779
+ EditorView14.exceptionSink.of((err) => {
2612
3780
  log2.catch(err, void 0, {
2613
- F: __dxlog_file5,
2614
- L: 126,
3781
+ F: __dxlog_file3,
3782
+ L: 134,
2615
3783
  S: void 0,
2616
3784
  C: (f, a) => f(...a)
2617
3785
  });
2618
3786
  }),
2619
3787
  // Theme.
2620
3788
  // TODO(burdon): Make configurable.
2621
- EditorView13.baseTheme(defaultTheme),
2622
- EditorView13.theme(theme ?? {}),
2623
- EditorView13.darkTheme.of(themeMode === "dark"),
2624
- EditorView13.editorAttributes.of({
3789
+ EditorView14.baseTheme(defaultTheme),
3790
+ EditorView14.theme(theme ?? {}),
3791
+ EditorView14.darkTheme.of(themeMode === "dark"),
3792
+ EditorView14.editorAttributes.of({
2625
3793
  class: slots.editor?.className ?? ""
2626
3794
  }),
2627
- EditorView13.contentAttributes.of({
3795
+ EditorView14.contentAttributes.of({
2628
3796
  class: slots.content?.className ?? ""
2629
3797
  }),
2630
3798
  // State.
3799
+ EditorView14.editable.of(!readonly),
2631
3800
  EditorState2.readOnly.of(!!readonly),
2632
3801
  // Storage and replication.
2633
3802
  // NOTE: This must come before user extensions.
2634
3803
  model.extension,
2635
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
3804
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2636
3805
  editorMode === "vim" && vim(),
2637
3806
  // Custom.
2638
3807
  ...extensions
2639
- ].filter(Boolean)
3808
+ ].filter(isNotFalsy3)
2640
3809
  });
2641
- const newView = new EditorView13({
3810
+ const newView = new EditorView14({
2642
3811
  state,
2643
3812
  parent: rootRef.current,
2644
3813
  scrollTo,
@@ -2673,7 +3842,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2673
3842
  }
2674
3843
  case "Escape": {
2675
3844
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2676
- rootRef.current?.focus();
3845
+ view?.focus();
2677
3846
  }
2678
3847
  break;
2679
3848
  }
@@ -2686,20 +3855,20 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2686
3855
  key: model.id,
2687
3856
  role: "none",
2688
3857
  tabIndex: 0,
2689
- onKeyUp: handleKeyUp,
2690
3858
  ...slots.root,
2691
- ...editorMode !== "vim" && tabsterDOMAttribute,
3859
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
3860
+ onKeyUp: handleKeyUp,
2692
3861
  ref: rootRef
2693
3862
  });
2694
3863
  });
2695
3864
  var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
2696
- const { themeMode } = useThemeContext2();
3865
+ const { themeMode } = useThemeContext();
2697
3866
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
2698
3867
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
2699
3868
  ref: forwardedRef,
2700
3869
  readonly,
2701
3870
  extensions: [
2702
- basicBundle({
3871
+ createBasicBundle({
2703
3872
  themeMode,
2704
3873
  placeholder: placeholder3,
2705
3874
  lineWrapping
@@ -2712,15 +3881,14 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2712
3881
  });
2713
3882
  });
2714
3883
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
2715
- const { themeMode } = useThemeContext2();
3884
+ const { themeMode } = useThemeContext();
2716
3885
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
2717
3886
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
2718
3887
  ref: forwardedRef,
2719
3888
  readonly,
2720
3889
  extensions: [
2721
- markdownBundle({
3890
+ createMarkdownExtensions({
2722
3891
  themeMode,
2723
- readonly,
2724
3892
  placeholder: placeholder3
2725
3893
  }),
2726
3894
  ...extensions
@@ -2732,11 +3900,10 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2732
3900
  });
2733
3901
  var defaultSlots = {
2734
3902
  root: {
2735
- // TODO(burdon): Add focusRing by default/as property?
2736
- className: mx3("flex flex-col grow overflow-y-auto", inputSurface)
3903
+ className: mx3("grow", focusRing)
2737
3904
  },
2738
3905
  editor: {
2739
- className: "h-full p-2"
3906
+ className: "bs-full"
2740
3907
  }
2741
3908
  };
2742
3909
  var defaultTextSlots = {
@@ -2746,22 +3913,205 @@ var defaultMarkdownSlots = {
2746
3913
  ...defaultSlots
2747
3914
  };
2748
3915
 
2749
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2750
- import get4 from "lodash.get";
2751
- import { getRawDoc } from "@dxos/echo-schema";
2752
- import { isNotNullOrUndefined } from "@dxos/util";
3916
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
3917
+ import { CaretRight, ChatText, Code, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
3918
+ import { createContext } from "@radix-ui/react-context";
3919
+ import React2 from "react";
3920
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
3921
+ import { getSize, mx as mx4 } from "@dxos/react-ui-theme";
3922
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
3923
+ var ToolbarRoot = ({ children, onAction, state }) => {
3924
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
3925
+ onAction,
3926
+ state
3927
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
3928
+ density: "fine"
3929
+ }, /* @__PURE__ */ React2.createElement("div", {
3930
+ role: "toolbar",
3931
+ className: "flex w-full shrink-0 p-1 gap-2 items-center whitespace-nowrap overflow-hidden"
3932
+ }, children)));
3933
+ };
3934
+ var ToolbarButton = ({ Icon, onClick, title, getState, disable }) => {
3935
+ const { onAction, state } = useToolbarContext("ToolbarButton");
3936
+ const active = getState && state ? getState(state) : false;
3937
+ const disabled = disable && state ? disable(state) : false;
3938
+ const handleClick = (event) => {
3939
+ const action = onClick(state);
3940
+ if (action) {
3941
+ onAction?.(action);
3942
+ event.preventDefault();
3943
+ }
3944
+ };
3945
+ return /* @__PURE__ */ React2.createElement(Button, {
3946
+ variant: "ghost",
3947
+ classNames: mx4("p-2", active && "ring-[1px]"),
3948
+ onMouseDown: handleClick,
3949
+ title,
3950
+ disabled
3951
+ }, /* @__PURE__ */ React2.createElement(Icon, {
3952
+ className: getSize(5)
3953
+ }));
3954
+ };
3955
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3956
+ className: "grow"
3957
+ });
3958
+ var MarkdownHeading = () => {
3959
+ const { onAction, state } = useToolbarContext("MarkdownFormatting");
3960
+ const blockType = state ? state.blockType : "paragraph";
3961
+ const header = blockType && /heading(\d)/.exec(blockType);
3962
+ const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : null;
3963
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3964
+ disabled: value === null,
3965
+ value: value ?? "0",
3966
+ onValueChange: (value2) => onAction?.({
3967
+ type: "heading",
3968
+ data: parseInt(value2)
3969
+ })
3970
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, null, /* @__PURE__ */ React2.createElement(Paragraph, {
3971
+ className: getSize(5)
3972
+ })), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3973
+ value: "0"
3974
+ }, "Paragraph"), [
3975
+ 1,
3976
+ 2,
3977
+ 3,
3978
+ 4,
3979
+ 5,
3980
+ 6
3981
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3982
+ key: level,
3983
+ value: String(level)
3984
+ }, "Heading ", level))))));
3985
+ };
3986
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3987
+ role: "none"
3988
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3989
+ Icon: TextB,
3990
+ title: "String",
3991
+ disable: (s) => s.blockType === "codeblock",
3992
+ getState: (s) => s.strong,
3993
+ onClick: (s) => ({
3994
+ type: "strong",
3995
+ data: s ? !s.strong : null
3996
+ })
3997
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3998
+ Icon: TextItalic,
3999
+ title: "Emphasis",
4000
+ disable: (s) => s.blockType === "codeblock",
4001
+ getState: (s) => s.emphasis,
4002
+ onClick: (s) => ({
4003
+ type: "emphasis",
4004
+ data: s ? !s.emphasis : null
4005
+ })
4006
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4007
+ Icon: TextStrikethrough,
4008
+ title: "Strike-through",
4009
+ disable: (s) => s.blockType === "codeblock",
4010
+ getState: (s) => s.strikethrough,
4011
+ onClick: (s) => ({
4012
+ type: "strikethrough",
4013
+ data: s ? !s.strikethrough : null
4014
+ })
4015
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4016
+ Icon: Code,
4017
+ title: "Inline code",
4018
+ disable: (s) => s.blockType === "codeblock",
4019
+ getState: (s) => s.code,
4020
+ onClick: (s) => ({
4021
+ type: "code",
4022
+ data: s ? !s.code : null
4023
+ })
4024
+ }));
4025
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
4026
+ role: "none"
4027
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4028
+ Icon: ListBullets,
4029
+ title: "Bullet list",
4030
+ getState: (s) => s.listStyle === "bullet",
4031
+ onClick: (s) => ({
4032
+ type: "list-bullet",
4033
+ data: s ? s.listStyle !== "bullet" : null
4034
+ })
4035
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4036
+ Icon: ListNumbers,
4037
+ title: "Numbered list",
4038
+ getState: (s) => s.listStyle === "ordered",
4039
+ onClick: (s) => ({
4040
+ type: "list-ordered",
4041
+ data: s ? s.listStyle !== "ordered" : null
4042
+ })
4043
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4044
+ Icon: ListChecks,
4045
+ title: "Task list",
4046
+ getState: (s) => s.listStyle === "task",
4047
+ onClick: (s) => ({
4048
+ type: "list-tasks",
4049
+ data: s ? s.listStyle !== "task" : null
4050
+ })
4051
+ }));
4052
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
4053
+ role: "none"
4054
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4055
+ Icon: CaretRight,
4056
+ title: "Block quote",
4057
+ getState: (s) => s.blockquote,
4058
+ onClick: (s) => ({
4059
+ type: "blockquote",
4060
+ data: s ? !s.blockquote : null
4061
+ })
4062
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4063
+ Icon: Code,
4064
+ title: "Code block",
4065
+ getState: (s) => s.blockType === "codeblock",
4066
+ onClick: (s) => ({
4067
+ type: "codeblock",
4068
+ data: s ? s.blockType !== "codeblock" : null
4069
+ })
4070
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4071
+ Icon: Table2,
4072
+ title: "Table",
4073
+ onClick: () => ({
4074
+ type: "table"
4075
+ })
4076
+ }));
4077
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
4078
+ role: "none"
4079
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4080
+ Icon: Link,
4081
+ title: "Link",
4082
+ getState: (s) => s.link,
4083
+ onClick: (s) => ({
4084
+ type: "link",
4085
+ data: s ? !s.link : null
4086
+ })
4087
+ }));
4088
+ 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));
4089
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
4090
+ Icon: ChatText,
4091
+ title: "Create comment",
4092
+ onClick: () => ({
4093
+ type: "comment"
4094
+ })
4095
+ });
4096
+ var Toolbar = {
4097
+ Root: ToolbarRoot,
4098
+ Button: ToolbarButton,
4099
+ Separator: ToolbarSeparator,
4100
+ Markdown: MarkdownStandard,
4101
+ Extended: MarkdownExtended
4102
+ };
2753
4103
 
2754
4104
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2755
4105
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2756
4106
  import { Context as Context2 } from "@dxos/context";
2757
- import { invariant as invariant4 } from "@dxos/invariant";
4107
+ import { invariant as invariant2 } from "@dxos/invariant";
2758
4108
  import { log as log3 } from "@dxos/log";
2759
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
4109
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2760
4110
  var DEBOUNCE_INTERVAL = 100;
2761
4111
  var SpaceAwarenessProvider = class {
2762
4112
  constructor(params) {
2763
- this.remoteStateChange = new Event2();
2764
4113
  this._remoteStates = /* @__PURE__ */ new Map();
4114
+ this.remoteStateChange = new Event2();
2765
4115
  this._space = params.space;
2766
4116
  this._channel = params.channel;
2767
4117
  this._peerId = params.peerId;
@@ -2778,7 +4128,7 @@ var SpaceAwarenessProvider = class {
2778
4128
  await sleep(DEBOUNCE_INTERVAL);
2779
4129
  }
2780
4130
  });
2781
- const unsubscribe = this._space.listen(this._channel, (message) => {
4131
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
2782
4132
  switch (message.payload.kind) {
2783
4133
  case "query": {
2784
4134
  this._handleQueryMessage();
@@ -2789,16 +4139,15 @@ var SpaceAwarenessProvider = class {
2789
4139
  break;
2790
4140
  }
2791
4141
  }
2792
- });
2793
- this._ctx.onDispose(unsubscribe);
4142
+ }));
2794
4143
  void this._space.postMessage(this._channel, {
2795
4144
  kind: "query"
2796
4145
  }).catch((err) => {
2797
4146
  log3.debug("failed to query awareness", {
2798
4147
  err
2799
4148
  }, {
2800
- F: __dxlog_file6,
2801
- L: 85,
4149
+ F: __dxlog_file4,
4150
+ L: 89,
2802
4151
  S: this,
2803
4152
  C: (f, a) => f(...a)
2804
4153
  });
@@ -2809,10 +4158,13 @@ var SpaceAwarenessProvider = class {
2809
4158
  this._ctx = void 0;
2810
4159
  this._postTask = void 0;
2811
4160
  }
4161
+ getRemoteStates() {
4162
+ return Array.from(this._remoteStates.values());
4163
+ }
2812
4164
  update(position) {
2813
- invariant4(this._postTask, void 0, {
2814
- F: __dxlog_file6,
2815
- L: 96,
4165
+ invariant2(this._postTask, void 0, {
4166
+ F: __dxlog_file4,
4167
+ L: 104,
2816
4168
  S: this,
2817
4169
  A: [
2818
4170
  "this._postTask",
@@ -2826,13 +4178,10 @@ var SpaceAwarenessProvider = class {
2826
4178
  };
2827
4179
  this._postTask.schedule();
2828
4180
  }
2829
- getRemoteStates() {
2830
- return Array.from(this._remoteStates.values());
2831
- }
2832
4181
  _handleQueryMessage() {
2833
- invariant4(this._postTask, void 0, {
2834
- F: __dxlog_file6,
2835
- L: 111,
4182
+ invariant2(this._postTask, void 0, {
4183
+ F: __dxlog_file4,
4184
+ L: 115,
2836
4185
  S: this,
2837
4186
  A: [
2838
4187
  "this._postTask",
@@ -2842,9 +4191,9 @@ var SpaceAwarenessProvider = class {
2842
4191
  this._postTask.schedule();
2843
4192
  }
2844
4193
  _handlePostMessage(message) {
2845
- invariant4(message.kind === "post", void 0, {
2846
- F: __dxlog_file6,
2847
- L: 116,
4194
+ invariant2(message.kind === "post", void 0, {
4195
+ F: __dxlog_file4,
4196
+ L: 120,
2848
4197
  S: this,
2849
4198
  A: [
2850
4199
  "message.kind === 'post'",
@@ -2863,180 +4212,58 @@ var modelState = StateField5.define({
2863
4212
  update: (model) => model
2864
4213
  });
2865
4214
 
2866
- // packages/ui/react-ui-editor/src/hooks/automerge.ts
2867
- var createAutomergeModel = ({ space, identity, text }) => {
2868
- const obj = text;
2869
- const doc = getRawDoc(obj, [
2870
- obj.field
2871
- ]);
2872
- const awarenessProvider = space && new SpaceAwarenessProvider({
2873
- space,
2874
- channel: `automerge.awareness.${obj.id}`,
2875
- info: {
2876
- displayName: identity?.profile?.displayName ?? "",
2877
- color: cursorColor.color,
2878
- lightColor: cursorColor.light
2879
- },
2880
- peerId: identity?.identityKey.toHex() ?? "Anonymous"
2881
- });
2882
- const model = {
2883
- id: obj.id,
2884
- content: doc,
2885
- text: () => get4(doc.handle.docSync(), doc.path),
2886
- // TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
2887
- extension: [
2888
- modelState.init(() => model),
2889
- automerge3({
2890
- handle: doc.handle,
2891
- path: doc.path
2892
- }),
2893
- awarenessProvider && AwarenessProvider.of(awarenessProvider),
2894
- awareness()
2895
- ].filter(isNotNullOrUndefined),
2896
- peer: identity ? {
2897
- id: identity.identityKey.toHex(),
2898
- name: identity.profile?.displayName
2899
- } : void 0
2900
- };
2901
- return model;
2902
- };
2903
-
2904
- // packages/ui/react-ui-editor/src/hooks/yjs.ts
2905
- import * as decoding from "lib0/decoding";
2906
- import * as encoding from "lib0/encoding";
2907
- import { Observable } from "lib0/observable";
2908
- import * as YP from "y-protocols/awareness";
2909
- import { invariant as invariant5 } from "@dxos/invariant";
2910
- import { log as log4 } from "@dxos/log";
2911
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
2912
- var createYjsModel = ({ identity, space, text }) => {
2913
- invariant5(text?.doc && text?.content, void 0, {
2914
- F: __dxlog_file7,
2915
- L: 22,
2916
- S: void 0,
2917
- A: [
2918
- "text?.doc && text?.content",
2919
- ""
2920
- ]
2921
- });
2922
- const provider = space ? new YAwarenessProvider({
2923
- space,
2924
- doc: text.doc,
2925
- channel: `yjs.awareness.${text.id}`
2926
- }) : void 0;
2927
- const model = {
2928
- id: text.doc.guid,
2929
- content: text.content,
2930
- text: () => text.content.toString(),
2931
- extension: [
2932
- modelState.init(() => model),
2933
- yjs(text.content, provider?.awareness)
2934
- ],
2935
- awareness: provider?.awareness,
2936
- peer: identity ? {
2937
- id: identity.identityKey.toHex(),
2938
- name: identity.profile?.displayName
2939
- } : void 0
2940
- };
2941
- return model;
2942
- };
2943
- var messageAwareness = 1;
2944
- var messageQueryAwareness = 3;
2945
- var YAwarenessProvider = class extends Observable {
2946
- constructor({ space, doc, channel, awareness: awareness2 }) {
2947
- super();
2948
- this._space = space;
2949
- this._awareness = awareness2 ?? new YP.Awareness(doc);
2950
- this._channel = channel;
2951
- this._clientId = doc.clientID;
2952
- this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
2953
- this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
2954
- if (typeof window !== "undefined") {
2955
- window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
2956
- } else if (typeof process !== "undefined") {
2957
- process.on("exit", this._handleBeforeUnload.bind(this));
2958
- }
2959
- const encoderAwarenessQuery = encoding.createEncoder();
2960
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
2961
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
2962
- const encoderAwarenessState = encoding.createEncoder();
2963
- encoding.writeVarUint(encoderAwarenessState, messageAwareness);
2964
- encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
2965
- this._clientId
2966
- ]));
2967
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
2968
- }
2969
- get awareness() {
2970
- return this._awareness;
2971
- }
2972
- _handleAwarenessUpdate({ added, updated, removed }, origin) {
2973
- log4("awareness update", {
2974
- added,
2975
- updated,
2976
- removed,
2977
- origin
2978
- }, {
2979
- F: __dxlog_file7,
2980
- L: 101,
2981
- S: this,
2982
- C: (f, a) => f(...a)
2983
- });
2984
- const changedClients = added.concat(updated).concat(removed);
2985
- const encoderAwareness = encoding.createEncoder();
2986
- encoding.writeVarUint(encoderAwareness, messageAwareness);
2987
- encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
2988
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
2989
- }
2990
- _handleSpaceMessage({ payload }) {
2991
- log4("space message", payload, {
2992
- F: __dxlog_file7,
2993
- L: 110,
2994
- S: this,
2995
- C: (f, a) => f(...a)
2996
- });
2997
- const data = new Uint8Array(Array.from(Object.values(payload)));
2998
- const encoder = this._readMessage(data);
2999
- if (encoder) {
3000
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
3001
- }
3002
- }
3003
- _readMessage(message) {
3004
- const decoder = decoding.createDecoder(message);
3005
- const encoder = encoding.createEncoder();
3006
- const messageType = decoding.readVarUint(decoder);
3007
- let sendReply = false;
3008
- switch (messageType) {
3009
- case messageAwareness: {
3010
- YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
4215
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4216
+ var useActionHandler = (view) => {
4217
+ return (action) => {
4218
+ if (!view) {
4219
+ return;
4220
+ }
4221
+ let inlineType, listType;
4222
+ switch (action.type) {
4223
+ case "heading":
4224
+ setHeading(parseInt(action.data))(view);
3011
4225
  break;
3012
- }
3013
- case messageQueryAwareness: {
3014
- encoding.writeVarUint(encoder, messageAwareness);
3015
- encoding.writeVarUint8Array(encoder, YP.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
3016
- sendReply = true;
4226
+ case "strong":
4227
+ case "emphasis":
4228
+ case "strikethrough":
4229
+ case "code":
4230
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4231
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4232
+ break;
4233
+ case "list-ordered":
4234
+ case "list-bullet":
4235
+ case "list-tasks":
4236
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4237
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4238
+ break;
4239
+ case "blockquote":
4240
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4241
+ break;
4242
+ case "codeblock":
4243
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
4244
+ break;
4245
+ case "table":
4246
+ insertTable(view);
4247
+ break;
4248
+ case "link":
4249
+ (action.data === false ? removeLink : addLink)(view);
4250
+ break;
4251
+ case "comment":
4252
+ createComment(view);
3017
4253
  break;
3018
- }
3019
- default: {
3020
- console.error("Invalid message:", messageType);
3021
- return encoder;
3022
- }
3023
- }
3024
- if (!sendReply) {
3025
- return null;
3026
4254
  }
3027
- return encoder;
3028
- }
3029
- _handleBeforeUnload() {
3030
- YP.removeAwarenessStates(this._awareness, [
3031
- this._clientId
3032
- ], "window unload");
3033
- }
4255
+ setTimeout(() => {
4256
+ if (!view.hasFocus) {
4257
+ view.focus();
4258
+ }
4259
+ });
4260
+ };
3034
4261
  };
3035
4262
 
3036
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3037
- import { useState as useState2 } from "react";
3038
- var useTextEditor = () => {
3039
- const [view, setView] = useState2(null);
4263
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
4264
+ import { useState as useState3 } from "react";
4265
+ var useEditorView = () => {
4266
+ const [view, setView] = useState3(null);
3040
4267
  return [
3041
4268
  (ref) => {
3042
4269
  setView(ref);
@@ -3045,12 +4272,124 @@ var useTextEditor = () => {
3045
4272
  ];
3046
4273
  };
3047
4274
 
4275
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4276
+ import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4277
+ import { EditorView as EditorView15 } from "@codemirror/view";
4278
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
4279
+ import { log as log4 } from "@dxos/log";
4280
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4281
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4282
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
4283
+ const onUpdate = useRef2();
4284
+ const [view, setView] = useState4();
4285
+ const parentRef = useRef2(null);
4286
+ useEffect3(() => {
4287
+ if (parentRef.current) {
4288
+ const state = EditorState3.create({
4289
+ doc,
4290
+ selection,
4291
+ extensions: [
4292
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4293
+ EditorView15.exceptionSink.of((err) => {
4294
+ log4.catch(err, void 0, {
4295
+ F: __dxlog_file5,
4296
+ L: 57,
4297
+ S: void 0,
4298
+ C: (f, a) => f(...a)
4299
+ });
4300
+ }),
4301
+ extensions,
4302
+ EditorView15.updateListener.of(() => {
4303
+ onUpdate.current?.();
4304
+ })
4305
+ ].filter(isNotFalsy4)
4306
+ });
4307
+ const view2 = new EditorView15({
4308
+ parent: parentRef.current,
4309
+ scrollTo,
4310
+ state,
4311
+ // NOTE: Uncomment to debug/monitor all transactions.
4312
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4313
+ dispatchTransactions: (trs, view3) => {
4314
+ if (debug) {
4315
+ logChanges(trs);
4316
+ }
4317
+ view3.update(trs);
4318
+ }
4319
+ });
4320
+ setView(view2);
4321
+ }
4322
+ return () => {
4323
+ view?.destroy();
4324
+ };
4325
+ }, [
4326
+ parentRef
4327
+ ]);
4328
+ useEffect3(() => {
4329
+ if (view) {
4330
+ if (!selection && !view.state.selection.main.anchor) {
4331
+ selection = EditorSelection2.single(view.state.doc.line(1).to);
4332
+ }
4333
+ if (selection || scrollTo) {
4334
+ onUpdate.current = () => {
4335
+ onUpdate.current = void 0;
4336
+ view.dispatch({
4337
+ selection,
4338
+ effects: scrollTo && [
4339
+ scrollTo
4340
+ ],
4341
+ scrollIntoView: !scrollTo
4342
+ });
4343
+ };
4344
+ }
4345
+ if (autoFocus) {
4346
+ view.focus();
4347
+ }
4348
+ }
4349
+ }, [
4350
+ view,
4351
+ autoFocus,
4352
+ selection,
4353
+ scrollTo
4354
+ ]);
4355
+ return {
4356
+ parentRef,
4357
+ view
4358
+ };
4359
+ };
4360
+ var createDataExtensions = ({ readonly = false } = {}) => {
4361
+ return [
4362
+ //
4363
+ EditorState3.readOnly.of(readonly),
4364
+ EditorView15.editable.of(!readonly)
4365
+ ];
4366
+ };
4367
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4368
+ return [
4369
+ //
4370
+ EditorView15.baseTheme(defaultTheme),
4371
+ theme && EditorView15.theme(theme),
4372
+ EditorView15.darkTheme.of(themeMode === "dark"),
4373
+ EditorView15.editorAttributes.of({
4374
+ class: slots?.editor?.className ?? ""
4375
+ }),
4376
+ EditorView15.contentAttributes.of({
4377
+ class: slots?.content?.className ?? ""
4378
+ })
4379
+ ].filter(isNotFalsy4);
4380
+ };
4381
+
3048
4382
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
3049
- import { useEffect as useEffect4, useState as useState3 } from "react";
4383
+ import get4 from "lodash.get";
4384
+ import { useEffect as useEffect4, useState as useState5 } from "react";
4385
+ import { generateName } from "@dxos/display-name";
4386
+ import { getRawDoc } from "@dxos/echo-schema";
4387
+ import { invariant as invariant3 } from "@dxos/invariant";
3050
4388
  import { isAutomergeObject } from "@dxos/react-client/echo";
4389
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3051
4390
  var useTextModel = (props) => {
3052
4391
  const { identity, space, text } = props;
3053
- const [model, setModel] = useState3();
4392
+ const [model, setModel] = useState5();
3054
4393
  useEffect4(() => setModel(createModel(props)), [
3055
4394
  identity,
3056
4395
  space,
@@ -3058,53 +4397,114 @@ var useTextModel = (props) => {
3058
4397
  ]);
3059
4398
  return model;
3060
4399
  };
3061
- var createModel = (props) => {
3062
- const { text } = props;
3063
- if (isAutomergeObject(text)) {
3064
- return createAutomergeModel(props);
3065
- } else if (text?.doc) {
3066
- return createYjsModel(props);
3067
- } else {
3068
- return void 0;
4400
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
4401
+ const [content, setContent] = useState5(defaultContent ?? "");
4402
+ return {
4403
+ id,
4404
+ content,
4405
+ setContent,
4406
+ text: () => content
4407
+ };
4408
+ };
4409
+ var createModel = ({ space, identity, text }) => {
4410
+ invariant3(isAutomergeObject(text), void 0, {
4411
+ F: __dxlog_file6,
4412
+ L: 50,
4413
+ S: void 0,
4414
+ A: [
4415
+ "isAutomergeObject(text)",
4416
+ ""
4417
+ ]
4418
+ });
4419
+ const obj = text;
4420
+ const doc = getRawDoc(obj, [
4421
+ obj.field
4422
+ ]);
4423
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
4424
+ space,
4425
+ channel: `automerge.awareness.${obj.id}`,
4426
+ info: {
4427
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4428
+ color: cursorColor.color,
4429
+ lightColor: cursorColor.light
4430
+ },
4431
+ peerId: identity?.identityKey.toHex() ?? "Anonymous"
4432
+ });
4433
+ const extensions = [
4434
+ //
4435
+ modelState.init(() => model),
4436
+ automerge({
4437
+ handle: doc.handle,
4438
+ path: doc.path
4439
+ })
4440
+ ];
4441
+ if (awarenessProvider2) {
4442
+ extensions.push(awareness(awarenessProvider2));
3069
4443
  }
4444
+ const model = {
4445
+ id: obj.id,
4446
+ content: doc,
4447
+ text: () => get4(doc.handle.docSync(), doc.path),
4448
+ extension: extensions,
4449
+ peer: identity ? {
4450
+ id: identity.identityKey.toHex(),
4451
+ name: identity.profile?.displayName
4452
+ } : void 0
4453
+ };
4454
+ return model;
3070
4455
  };
3071
4456
  export {
3072
- AwarenessProvider,
3073
4457
  BaseTextEditor,
3074
4458
  Cursor,
3075
4459
  Doc,
3076
4460
  EditorModes,
4461
+ Inline,
4462
+ List,
3077
4463
  MarkdownEditor,
3078
4464
  RemoteSelectionsDecorator,
3079
4465
  SpaceAwarenessProvider,
3080
4466
  TextEditor,
3081
4467
  TextKind,
3082
- YAwarenessProvider,
4468
+ Toolbar,
3083
4469
  YText,
3084
4470
  YXmlFragment,
4471
+ addBlockquote,
4472
+ addCodeblock,
4473
+ addLink,
4474
+ addList,
4475
+ addStyle,
3085
4476
  autocomplete,
3086
- automerge3 as automerge,
4477
+ automerge,
3087
4478
  awareness,
3088
- basicBundle,
4479
+ awarenessProvider,
3089
4480
  blast,
3090
4481
  callbackWrapper,
3091
4482
  code2 as code,
3092
4483
  codeTheme,
3093
4484
  comments,
3094
- createAutomergeModel,
3095
- createYjsModel,
4485
+ compareFormatting,
4486
+ createBasicBundle,
4487
+ createComment,
4488
+ createDataExtensions,
4489
+ createMarkdownExtensions,
4490
+ createThemeExtensions,
3096
4491
  defaultMarkdownSlots,
3097
4492
  defaultOptions,
3098
4493
  defaultSlots,
3099
4494
  defaultTextSlots,
3100
4495
  defaultTheme,
4496
+ focusComment,
4497
+ formatting,
4498
+ getFormatting,
3101
4499
  getToken,
4500
+ heading2 as heading,
3102
4501
  hr,
3103
4502
  image,
4503
+ insertTable,
4504
+ keymap7 as keymap,
3104
4505
  link,
3105
4506
  listener,
3106
4507
  logChanges,
3107
- markdownBundle,
3108
4508
  markdownHighlightStyle,
3109
4509
  markdownTags,
3110
4510
  markdownTagsExtensions,
@@ -3112,18 +4512,36 @@ export {
3112
4512
  mention,
3113
4513
  modelState,
3114
4514
  outliner,
4515
+ removeBlockquote,
4516
+ removeCodeblock,
4517
+ removeLink,
4518
+ removeList,
4519
+ removeStyle,
4520
+ setBlockquote,
3115
4521
  setComments,
3116
- setFocus,
4522
+ setHeading,
3117
4523
  setSelection,
4524
+ setStyle,
3118
4525
  table,
3119
4526
  tags2 as tags,
3120
4527
  tasklist,
3121
4528
  textTheme,
4529
+ toggleBlockquote,
4530
+ toggleCodeblock,
4531
+ toggleEmphasis,
4532
+ toggleInlineCode,
4533
+ toggleList,
4534
+ toggleStrikethrough,
4535
+ toggleStrong,
4536
+ toggleStyle,
3122
4537
  typewriter,
3123
- useAwareness,
4538
+ useActionHandler,
3124
4539
  useComments,
4540
+ useEditorView,
4541
+ useFormattingState,
4542
+ useInMemoryTextModel,
3125
4543
  useTextEditor,
3126
4544
  useTextModel,
3127
- yjs
4545
+ useToolbarContext
3128
4546
  };
3129
4547
  //# sourceMappingURL=index.mjs.map