@dxos/react-ui-editor 0.3.11-main.47875f3 → 0.3.11-main.47fbf79

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