@dxos/react-ui-editor 0.3.11-main.0f453da → 0.3.11-main.101b0f0

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