@dxos/react-ui-editor 0.8.2-main.fbd8ed0 → 0.8.2-staging.4d6ad0f

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 (180) hide show
  1. package/dist/lib/browser/index.mjs +1731 -926
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +3 -64
  5. package/dist/lib/browser/testing/index.mjs.map +4 -4
  6. package/dist/lib/node/index.cjs +1912 -1111
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +3 -75
  10. package/dist/lib/node/testing/index.cjs.map +4 -4
  11. package/dist/lib/node-esm/index.mjs +1731 -926
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +3 -64
  15. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  17. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
  18. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/util.d.ts +4 -6
  20. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  21. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +21 -0
  22. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -0
  23. package/dist/types/src/{testing → components/Popover}/RefPopover.d.ts +1 -1
  24. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -0
  25. package/dist/types/src/components/Popover/index.d.ts +3 -0
  26. package/dist/types/src/components/Popover/index.d.ts.map +1 -0
  27. package/dist/types/src/components/index.d.ts +1 -0
  28. package/dist/types/src/components/index.d.ts.map +1 -1
  29. package/dist/types/src/defaults.d.ts +2 -5
  30. package/dist/types/src/defaults.d.ts.map +1 -1
  31. package/dist/types/src/extensions/annotations.d.ts +4 -1
  32. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  33. package/dist/types/src/extensions/autocomplete.d.ts +1 -2
  34. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  36. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  37. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  38. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  39. package/dist/types/src/extensions/command/command.d.ts +1 -2
  40. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  41. package/dist/types/src/extensions/command/hint.d.ts +14 -2
  42. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  43. package/dist/types/src/extensions/command/index.d.ts +2 -0
  44. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  45. package/dist/types/src/extensions/command/menu.d.ts +7 -8
  46. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  47. package/dist/types/src/extensions/command/state.d.ts +1 -1
  48. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  49. package/dist/types/src/extensions/command/typeahead.d.ts +17 -0
  50. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -0
  51. package/dist/types/src/extensions/comments.d.ts +2 -12
  52. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  53. package/dist/types/src/extensions/factories.d.ts +4 -0
  54. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  55. package/dist/types/src/extensions/index.d.ts +2 -0
  56. package/dist/types/src/extensions/index.d.ts.map +1 -1
  57. package/dist/types/src/extensions/json.d.ts +7 -0
  58. package/dist/types/src/extensions/json.d.ts.map +1 -0
  59. package/dist/types/src/extensions/markdown/{editorAction.d.ts → action.d.ts} +1 -1
  60. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  61. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -1
  62. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  63. package/dist/types/src/extensions/markdown/index.d.ts +1 -2
  64. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  65. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  66. package/dist/types/src/extensions/outliner/commands.d.ts +9 -0
  67. package/dist/types/src/extensions/outliner/commands.d.ts.map +1 -0
  68. package/dist/types/src/extensions/outliner/editor.d.ts +5 -0
  69. package/dist/types/src/extensions/outliner/editor.d.ts.map +1 -0
  70. package/dist/types/src/extensions/outliner/editor.test.d.ts +2 -0
  71. package/dist/types/src/extensions/outliner/editor.test.d.ts.map +1 -0
  72. package/dist/types/src/extensions/outliner/index.d.ts +3 -0
  73. package/dist/types/src/extensions/outliner/index.d.ts.map +1 -0
  74. package/dist/types/src/extensions/outliner/outliner.d.ts +10 -0
  75. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -0
  76. package/dist/types/src/extensions/outliner/outliner.test.d.ts +2 -0
  77. package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +1 -0
  78. package/dist/types/src/extensions/outliner/selection.d.ts +12 -0
  79. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -0
  80. package/dist/types/src/extensions/outliner/tree.d.ts +79 -0
  81. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -0
  82. package/dist/types/src/extensions/outliner/tree.test.d.ts +2 -0
  83. package/dist/types/src/extensions/outliner/tree.test.d.ts.map +1 -0
  84. package/dist/types/src/stories/Command.stories.d.ts +7 -0
  85. package/dist/types/src/stories/Command.stories.d.ts.map +1 -0
  86. package/dist/types/src/stories/{TextEditorComments.stories.d.ts → Comments.stories.d.ts} +3 -3
  87. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -0
  88. package/dist/types/src/stories/EditorToolbar.stories.d.ts +12 -0
  89. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -0
  90. package/dist/types/src/stories/{TextEditorSpecial.stories.d.ts → Experimental.stories.d.ts} +3 -6
  91. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -0
  92. package/dist/types/src/stories/Markdown.stories.d.ts +46 -0
  93. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -0
  94. package/dist/types/src/stories/Outliner.stories.d.ts +26 -0
  95. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -0
  96. package/dist/types/src/stories/Preview.stories.d.ts +10 -0
  97. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -0
  98. package/dist/types/src/stories/{TextEditorBasic.stories.d.ts → TextEditor.stories.d.ts} +9 -39
  99. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -0
  100. package/dist/types/src/stories/{story-utils.d.ts → util.d.ts} +6 -6
  101. package/dist/types/src/stories/util.d.ts.map +1 -0
  102. package/dist/types/src/styles/theme.d.ts.map +1 -1
  103. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  104. package/dist/types/src/testing/index.d.ts +1 -1
  105. package/dist/types/src/testing/index.d.ts.map +1 -1
  106. package/dist/types/src/testing/util.d.ts +2 -0
  107. package/dist/types/src/testing/util.d.ts.map +1 -0
  108. package/package.json +40 -34
  109. package/src/components/EditorToolbar/EditorToolbar.tsx +81 -57
  110. package/src/components/EditorToolbar/index.ts +7 -1
  111. package/src/components/EditorToolbar/util.ts +3 -4
  112. package/src/components/Popover/RefDropdownMenu.tsx +77 -0
  113. package/src/{testing → components/Popover}/RefPopover.tsx +5 -4
  114. package/src/components/Popover/index.ts +6 -0
  115. package/src/components/index.ts +1 -0
  116. package/src/defaults.ts +10 -13
  117. package/src/extensions/annotations.ts +41 -64
  118. package/src/extensions/autocomplete.ts +5 -6
  119. package/src/extensions/automerge/automerge.stories.tsx +2 -7
  120. package/src/extensions/automerge/automerge.test.tsx +3 -2
  121. package/src/extensions/automerge/sync.ts +3 -3
  122. package/src/extensions/awareness/awareness-provider.ts +4 -4
  123. package/src/extensions/awareness/awareness.ts +7 -7
  124. package/src/extensions/blast.ts +9 -9
  125. package/src/extensions/command/command.ts +1 -3
  126. package/src/extensions/command/hint.ts +7 -7
  127. package/src/extensions/command/index.ts +2 -0
  128. package/src/extensions/command/menu.ts +43 -49
  129. package/src/extensions/command/typeahead.ts +116 -0
  130. package/src/extensions/comments.ts +4 -69
  131. package/src/extensions/factories.ts +13 -0
  132. package/src/extensions/index.ts +2 -0
  133. package/src/extensions/json.ts +56 -0
  134. package/src/extensions/markdown/bundle.ts +13 -9
  135. package/src/extensions/markdown/decorate.ts +7 -7
  136. package/src/extensions/markdown/image.ts +2 -2
  137. package/src/extensions/markdown/index.ts +1 -2
  138. package/src/extensions/markdown/styles.ts +2 -1
  139. package/src/extensions/markdown/table.ts +3 -3
  140. package/src/extensions/outliner/commands.ts +242 -0
  141. package/src/extensions/outliner/editor.test.ts +33 -0
  142. package/src/extensions/outliner/editor.ts +180 -0
  143. package/src/extensions/outliner/index.ts +6 -0
  144. package/src/extensions/outliner/outliner.test.ts +99 -0
  145. package/src/extensions/outliner/outliner.ts +162 -0
  146. package/src/extensions/outliner/selection.ts +50 -0
  147. package/src/extensions/outliner/tree.test.ts +164 -0
  148. package/src/extensions/outliner/tree.ts +315 -0
  149. package/src/extensions/preview/preview.ts +5 -5
  150. package/src/stories/Command.stories.tsx +97 -0
  151. package/src/stories/{TextEditorComments.stories.tsx → Comments.stories.tsx} +13 -14
  152. package/src/{components/EditorToolbar → stories}/EditorToolbar.stories.tsx +26 -20
  153. package/src/stories/{TextEditorSpecial.stories.tsx → Experimental.stories.tsx} +9 -30
  154. package/src/stories/Markdown.stories.tsx +121 -0
  155. package/src/stories/Outliner.stories.tsx +108 -0
  156. package/src/stories/{TextEditorPreview.stories.tsx → Preview.stories.tsx} +46 -136
  157. package/src/stories/TextEditor.stories.tsx +256 -0
  158. package/src/stories/{story-utils.tsx → util.tsx} +21 -22
  159. package/src/styles/theme.ts +12 -5
  160. package/src/styles/tokens.ts +1 -2
  161. package/src/testing/index.ts +1 -1
  162. package/src/testing/util.ts +5 -0
  163. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts +0 -53
  164. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +0 -1
  165. package/dist/types/src/components/EditorToolbar/comment.d.ts +0 -18
  166. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +0 -1
  167. package/dist/types/src/extensions/markdown/editorAction.d.ts.map +0 -1
  168. package/dist/types/src/extensions/markdown/outliner.d.ts +0 -12
  169. package/dist/types/src/extensions/markdown/outliner.d.ts.map +0 -1
  170. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +0 -1
  171. package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +0 -1
  172. package/dist/types/src/stories/TextEditorPreview.stories.d.ts +0 -13
  173. package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +0 -1
  174. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +0 -1
  175. package/dist/types/src/stories/story-utils.d.ts.map +0 -1
  176. package/dist/types/src/testing/RefPopover.d.ts.map +0 -1
  177. package/src/components/EditorToolbar/comment.ts +0 -30
  178. package/src/extensions/markdown/outliner.ts +0 -235
  179. package/src/stories/TextEditorBasic.stories.tsx +0 -333
  180. /package/src/extensions/markdown/{editorAction.ts → action.ts} +0 -0
@@ -35,12 +35,15 @@ var translations_default = [
35
35
 
36
36
  // packages/ui/react-ui-editor/src/index.ts
37
37
  import { EditorState as EditorState4 } from "@codemirror/state";
38
- import { EditorView as EditorView23, keymap as keymap11 } from "@codemirror/view";
38
+ import { EditorView as EditorView23, keymap as keymap13 } from "@codemirror/view";
39
39
  import { tags as tags2 } from "@lezer/highlight";
40
40
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
41
41
 
42
42
  // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
43
- import React3, { memo, useCallback } from "react";
43
+ import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
44
+ import { Rx } from "@effect-rx/rx-react";
45
+ import React3, { memo, useMemo as useMemo3 } from "react";
46
+ import { rxFromSignal } from "@dxos/app-graph";
44
47
  import { ElevationProvider } from "@dxos/react-ui";
45
48
  import { MenuProvider, ToolbarMenu, createGapSeparator, useMenuActions } from "@dxos/react-ui-menu";
46
49
  import { textBlockWidth } from "@dxos/react-ui-theme";
@@ -71,233 +74,51 @@ var createEditorActionGroup = (id, props, icon) => createMenuItemGroup(id, {
71
74
  });
72
75
 
73
76
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
74
- import { StateField } from "@codemirror/state";
75
- import { Decoration, EditorView } from "@codemirror/view";
76
- import { isNotFalsy } from "@dxos/util";
77
-
78
- // packages/ui/react-ui-editor/src/util/facet.ts
79
- import { Facet } from "@codemirror/state";
80
- var singleValueFacet = (defaultValue) => Facet.define({
81
- // Called immediately.
82
- combine: (providers) => {
83
- return providers[0] ?? defaultValue;
84
- }
85
- });
86
-
87
- // packages/ui/react-ui-editor/src/util/cursor.ts
88
- var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
89
- var defaultCursorConverter = {
90
- toCursor: (position) => position.toString(),
91
- fromCursor: (cursor) => parseInt(cursor)
92
- };
93
- var Cursor = class _Cursor {
94
- static {
95
- this.converter = singleValueFacet(defaultCursorConverter);
96
- }
97
- static {
98
- this.getCursorFromRange = (state, range) => {
99
- const cursorConverter2 = state.facet(_Cursor.converter);
100
- const from = cursorConverter2.toCursor(range.from);
101
- const to = cursorConverter2.toCursor(range.to, -1);
102
- return [
103
- from,
104
- to
105
- ].join(":");
106
- };
107
- }
108
- static {
109
- this.getRangeFromCursor = (state, cursor) => {
110
- const cursorConverter2 = state.facet(_Cursor.converter);
111
- const parts = cursor.split(":");
112
- const from = cursorConverter2.fromCursor(parts[0]);
113
- const to = cursorConverter2.fromCursor(parts[1]);
114
- return from !== void 0 && to !== void 0 ? {
115
- from,
116
- to
117
- } : void 0;
118
- };
119
- }
120
- };
121
-
122
- // packages/ui/react-ui-editor/src/util/debug.ts
123
- import { log } from "@dxos/log";
124
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
125
- var wrapWithCatch = (fn) => {
126
- return (...args) => {
127
- try {
128
- return fn(...args);
129
- } catch (err) {
130
- log.catch(err, void 0, {
131
- F: __dxlog_file,
132
- L: 15,
133
- S: void 0,
134
- C: (f, a) => f(...a)
135
- });
136
- }
137
- };
138
- };
139
- var callbackWrapper = (fn) => (...args) => {
140
- try {
141
- return fn(...args);
142
- } catch (err) {
143
- log.catch(err, void 0, {
144
- F: __dxlog_file,
145
- L: 29,
146
- S: void 0,
147
- C: (f, a) => f(...a)
148
- });
149
- }
150
- };
151
- var debugDispatcher = (trs, view) => {
152
- logChanges(trs);
153
- view.update(trs);
154
- };
155
- var logChanges = (trs) => {
156
- const changes = trs.flatMap((tr) => {
157
- if (tr.changes.empty) {
158
- return void 0;
159
- }
160
- const changes2 = [];
161
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
162
- fromA,
163
- toA,
164
- fromB,
165
- toB,
166
- inserted: inserted.toString()
167
- })));
168
- return changes2;
169
- }).filter(Boolean);
170
- if (changes.length) {
171
- log("changes", {
172
- changes
173
- }, {
174
- F: __dxlog_file,
175
- L: 62,
176
- S: void 0,
177
- C: (f, a) => f(...a)
178
- });
179
- }
180
- };
181
-
182
- // packages/ui/react-ui-editor/src/util/dom.ts
183
- var flattenRect = (rect, left) => {
184
- const x = left ? rect.left : rect.right;
185
- return {
186
- left: x,
187
- right: x,
188
- top: rect.top,
189
- bottom: rect.bottom
190
- };
191
- };
192
- var scratchRange;
193
- var textRange = (node, from, to = from) => {
194
- const range = scratchRange || (scratchRange = document.createRange());
195
- range.setEnd(node, to);
196
- range.setStart(node, from);
197
- return range;
198
- };
199
- var clientRectsFor = (dom) => {
200
- if (dom.nodeType === 3) {
201
- return textRange(dom, 0, dom.nodeValue.length).getClientRects();
202
- } else if (dom.nodeType === 1) {
203
- return dom.getClientRects();
204
- } else {
205
- return [];
206
- }
207
- };
208
-
209
- // packages/ui/react-ui-editor/src/util/react.tsx
210
- import React from "react";
211
- import { createRoot } from "react-dom/client";
212
- import { ThemeProvider, Tooltip } from "@dxos/react-ui";
213
- import { defaultTx } from "@dxos/react-ui-theme";
214
- var createElement = (tag, options, children) => {
215
- const el = document.createElement(tag);
216
- if (options?.className) {
217
- el.className = options.className;
218
- }
219
- if (children) {
220
- el.append(...Array.isArray(children) ? children : [
221
- children
222
- ]);
223
- }
224
- return el;
225
- };
226
- var renderRoot = (root, node) => {
227
- createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
228
- tx: defaultTx
229
- }, node));
230
- return root;
231
- };
232
- var createRenderer = (Component) => (el, props) => {
233
- renderRoot(el, /* @__PURE__ */ React.createElement(ThemeProvider, {
234
- tx: defaultTx
235
- }, /* @__PURE__ */ React.createElement(Tooltip.Provider, null, /* @__PURE__ */ React.createElement(Component, props))));
236
- };
237
-
238
- // packages/ui/react-ui-editor/src/extensions/annotations.ts
77
+ import { RangeSetBuilder } from "@codemirror/state";
78
+ import { Decoration, EditorView, ViewPlugin } from "@codemirror/view";
239
79
  var annotationMark = Decoration.mark({
240
80
  class: "cm-annotation"
241
81
  });
242
- var annotations = (options = {}) => {
243
- const match = (state) => {
244
- const annotations2 = [];
245
- const text = state.doc.toString();
246
- if (options.match) {
247
- const matches = text.matchAll(options.match);
248
- for (const match2 of matches) {
249
- const from = match2.index;
250
- const to = from + match2[0].length;
251
- const cursor = Cursor.getCursorFromRange(state, {
252
- from,
253
- to
254
- });
255
- annotations2.push({
256
- cursor
257
- });
82
+ var annotations = ({ match } = {}) => {
83
+ return [
84
+ ViewPlugin.fromClass(class {
85
+ constructor() {
86
+ this.decorations = Decoration.none;
258
87
  }
259
- }
260
- return annotations2;
261
- };
262
- const annotationsState = StateField.define({
263
- create: (state) => {
264
- return match(state);
265
- },
266
- update: (value, tr) => {
267
- if (!tr.changes.empty) {
268
- return match(tr.state);
88
+ update(update2) {
89
+ const builder = new RangeSetBuilder();
90
+ if (match) {
91
+ const { from, to } = update2.view.viewport;
92
+ const text = update2.state.doc.sliceString(from, to);
93
+ const matches = text.matchAll(match);
94
+ for (const m of matches) {
95
+ if (m.index !== void 0) {
96
+ const start = from + m.index;
97
+ const end = start + m[0].length;
98
+ builder.add(start, end, annotationMark);
99
+ }
100
+ }
101
+ }
102
+ this.decorations = builder.finish();
269
103
  }
270
- return value;
271
- }
272
- });
273
- return [
274
- annotationsState,
275
- EditorView.decorations.compute([
276
- annotationsState
277
- ], (state) => {
278
- const annotations2 = state.field(annotationsState);
279
- const decorations = annotations2.map((annotation) => {
280
- const range = Cursor.getRangeFromCursor(state, annotation.cursor);
281
- return range && annotationMark.range(range.from, range.to);
282
- }).filter(isNotFalsy);
283
- return Decoration.set(decorations);
104
+ }, {
105
+ decorations: (v) => v.decorations
284
106
  }),
285
- styles
107
+ EditorView.theme({
108
+ ".cm-annotation": {
109
+ textDecoration: "underline",
110
+ textDecorationStyle: "wavy",
111
+ textDecorationColor: "var(--dx-errorText)"
112
+ }
113
+ })
286
114
  ];
287
115
  };
288
- var styles = EditorView.theme({
289
- ".cm-annotation": {
290
- textDecoration: "underline",
291
- textDecorationStyle: "wavy",
292
- textDecorationColor: "var(--dx-error)"
293
- }
294
- });
295
116
 
296
117
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
297
118
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
298
119
  import { markdownLanguage } from "@codemirror/lang-markdown";
299
120
  import { keymap } from "@codemirror/view";
300
- var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
121
+ var autocomplete = ({ activateOnTyping, override, onSearch } = {}) => {
301
122
  const extensions = [
302
123
  // https://codemirror.net/docs/ref/#view.keymap
303
124
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -306,15 +127,13 @@ var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
306
127
  // https://codemirror.net/examples/autocompletion
307
128
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
308
129
  autocompletion({
309
- activateOnTyping,
310
130
  override,
311
- closeOnBlur: !debug,
312
- tooltipClass: () => "shadow rounded"
131
+ activateOnTyping
313
132
  })
314
133
  ];
315
134
  if (onSearch) {
316
135
  extensions.push(
317
- // TODO(burdon): Optional decoration via addToOptions
136
+ // TODO(burdon): Optional decoration via addToOptions.
318
137
  markdownLanguage.data.of({
319
138
  autocomplete: (context) => {
320
139
  const match = context.matchBefore(/\w*/);
@@ -334,20 +153,20 @@ var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
334
153
 
335
154
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
336
155
  import { next as A3 } from "@automerge/automerge";
337
- import { StateField as StateField2 } from "@codemirror/state";
338
- import { EditorView as EditorView2, ViewPlugin } from "@codemirror/view";
156
+ import { StateField } from "@codemirror/state";
157
+ import { EditorView as EditorView2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
339
158
 
340
159
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
341
- import { log as log2 } from "@dxos/log";
160
+ import { log } from "@dxos/log";
342
161
  import { fromCursor, toCursor } from "@dxos/react-client/echo";
343
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts";
162
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts";
344
163
  var cursorConverter = (accessor) => ({
345
164
  toCursor: (pos, assoc) => {
346
165
  try {
347
166
  return toCursor(accessor, pos, assoc);
348
167
  } catch (err) {
349
- log2.catch(err, void 0, {
350
- F: __dxlog_file2,
168
+ log.catch(err, void 0, {
169
+ F: __dxlog_file,
351
170
  L: 15,
352
171
  S: void 0,
353
172
  C: (f, a) => f(...a)
@@ -359,8 +178,8 @@ var cursorConverter = (accessor) => ({
359
178
  try {
360
179
  return fromCursor(accessor, cursor);
361
180
  } catch (err) {
362
- log2.catch(err, void 0, {
363
- F: __dxlog_file2,
181
+ log.catch(err, void 0, {
182
+ F: __dxlog_file,
364
183
  L: 24,
365
184
  S: void 0,
366
185
  C: (f, a) => f(...a)
@@ -532,12 +351,12 @@ var Syncer = class {
532
351
  this._state = _state;
533
352
  this._pending = false;
534
353
  }
535
- reconcile(view, editor) {
354
+ reconcile(view, editor2) {
536
355
  if (this._pending) {
537
356
  return;
538
357
  }
539
358
  this._pending = true;
540
- if (editor) {
359
+ if (editor2) {
541
360
  this.onEditorChange(view);
542
361
  } else {
543
362
  this.onAutomergeChange(view);
@@ -568,70 +387,230 @@ var Syncer = class {
568
387
  }
569
388
  };
570
389
 
571
- // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
572
- var automerge = (accessor) => {
573
- const syncState = StateField2.define({
574
- create: () => ({
575
- path: accessor.path.slice(),
576
- lastHeads: A3.getHeads(accessor.handle.doc()),
577
- unreconciledTransactions: []
578
- }),
579
- update: (value, tr) => {
580
- const result = {
581
- path: accessor.path.slice(),
582
- lastHeads: value.lastHeads,
583
- unreconciledTransactions: value.unreconciledTransactions.slice()
584
- };
585
- let clearUnreconciled = false;
586
- for (const effect of tr.effects) {
587
- if (effect.is(updateHeadsEffect)) {
588
- result.lastHeads = effect.value.newHeads;
589
- clearUnreconciled = true;
590
- }
591
- }
592
- if (clearUnreconciled) {
593
- result.unreconciledTransactions = [];
594
- } else {
595
- if (!isReconcile(tr)) {
596
- result.unreconciledTransactions.push(tr);
597
- }
598
- }
599
- return result;
600
- }
601
- });
602
- const syncer = new Syncer(accessor.handle, syncState);
603
- return [
604
- Cursor.converter.of(cursorConverter(accessor)),
605
- // Track heads.
606
- syncState,
607
- // Reconcile external updates.
608
- ViewPlugin.fromClass(class {
609
- constructor(_view) {
610
- this._view = _view;
611
- this._handleChange = () => {
612
- syncer.reconcile(this._view, false);
613
- };
614
- accessor.handle.addListener("change", this._handleChange);
615
- }
616
- destroy() {
617
- accessor.handle.removeListener("change", this._handleChange);
618
- }
619
- }),
620
- // Reconcile local updates.
621
- EditorView2.updateListener.of(({ view, changes }) => {
622
- if (!changes.empty) {
623
- syncer.reconcile(view, true);
624
- }
625
- })
626
- ];
627
- };
628
-
629
- // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
630
- import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
631
- import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
632
- import { Event } from "@dxos/async";
633
- import { Context } from "@dxos/context";
634
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts";
390
+ // packages/ui/react-ui-editor/src/util/facet.ts
391
+ import { Facet } from "@codemirror/state";
392
+ var singleValueFacet = (defaultValue) => Facet.define({
393
+ // Called immediately.
394
+ combine: (providers) => {
395
+ return providers[0] ?? defaultValue;
396
+ }
397
+ });
398
+
399
+ // packages/ui/react-ui-editor/src/util/cursor.ts
400
+ var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
401
+ var defaultCursorConverter = {
402
+ toCursor: (position) => position.toString(),
403
+ fromCursor: (cursor) => parseInt(cursor)
404
+ };
405
+ var Cursor = class _Cursor {
406
+ static {
407
+ this.converter = singleValueFacet(defaultCursorConverter);
408
+ }
409
+ static {
410
+ this.getCursorFromRange = (state, range) => {
411
+ const cursorConverter2 = state.facet(_Cursor.converter);
412
+ const from = cursorConverter2.toCursor(range.from);
413
+ const to = cursorConverter2.toCursor(range.to, -1);
414
+ return [
415
+ from,
416
+ to
417
+ ].join(":");
418
+ };
419
+ }
420
+ static {
421
+ this.getRangeFromCursor = (state, cursor) => {
422
+ const cursorConverter2 = state.facet(_Cursor.converter);
423
+ const parts = cursor.split(":");
424
+ const from = cursorConverter2.fromCursor(parts[0]);
425
+ const to = cursorConverter2.fromCursor(parts[1]);
426
+ return from !== void 0 && to !== void 0 ? {
427
+ from,
428
+ to
429
+ } : void 0;
430
+ };
431
+ }
432
+ };
433
+
434
+ // packages/ui/react-ui-editor/src/util/debug.ts
435
+ import { log as log2 } from "@dxos/log";
436
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
437
+ var wrapWithCatch = (fn) => {
438
+ return (...args) => {
439
+ try {
440
+ return fn(...args);
441
+ } catch (err) {
442
+ log2.catch(err, void 0, {
443
+ F: __dxlog_file2,
444
+ L: 15,
445
+ S: void 0,
446
+ C: (f, a) => f(...a)
447
+ });
448
+ }
449
+ };
450
+ };
451
+ var callbackWrapper = (fn) => (...args) => {
452
+ try {
453
+ return fn(...args);
454
+ } catch (err) {
455
+ log2.catch(err, void 0, {
456
+ F: __dxlog_file2,
457
+ L: 29,
458
+ S: void 0,
459
+ C: (f, a) => f(...a)
460
+ });
461
+ }
462
+ };
463
+ var debugDispatcher = (trs, view) => {
464
+ logChanges(trs);
465
+ view.update(trs);
466
+ };
467
+ var logChanges = (trs) => {
468
+ const changes = trs.flatMap((tr) => {
469
+ if (tr.changes.empty) {
470
+ return void 0;
471
+ }
472
+ const changes2 = [];
473
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
474
+ fromA,
475
+ toA,
476
+ fromB,
477
+ toB,
478
+ inserted: inserted.toString()
479
+ })));
480
+ return changes2;
481
+ }).filter(Boolean);
482
+ if (changes.length) {
483
+ log2("changes", {
484
+ changes
485
+ }, {
486
+ F: __dxlog_file2,
487
+ L: 62,
488
+ S: void 0,
489
+ C: (f, a) => f(...a)
490
+ });
491
+ }
492
+ };
493
+
494
+ // packages/ui/react-ui-editor/src/util/dom.ts
495
+ var flattenRect = (rect, left) => {
496
+ const x = left ? rect.left : rect.right;
497
+ return {
498
+ left: x,
499
+ right: x,
500
+ top: rect.top,
501
+ bottom: rect.bottom
502
+ };
503
+ };
504
+ var scratchRange;
505
+ var textRange = (node, from, to = from) => {
506
+ const range = scratchRange || (scratchRange = document.createRange());
507
+ range.setEnd(node, to);
508
+ range.setStart(node, from);
509
+ return range;
510
+ };
511
+ var clientRectsFor = (dom) => {
512
+ if (dom.nodeType === 3) {
513
+ return textRange(dom, 0, dom.nodeValue.length).getClientRects();
514
+ } else if (dom.nodeType === 1) {
515
+ return dom.getClientRects();
516
+ } else {
517
+ return [];
518
+ }
519
+ };
520
+
521
+ // packages/ui/react-ui-editor/src/util/react.tsx
522
+ import React from "react";
523
+ import { createRoot } from "react-dom/client";
524
+ import { ThemeProvider, Tooltip } from "@dxos/react-ui";
525
+ import { defaultTx } from "@dxos/react-ui-theme";
526
+ var createElement = (tag, options, children) => {
527
+ const el = document.createElement(tag);
528
+ if (options?.className) {
529
+ el.className = options.className;
530
+ }
531
+ if (children) {
532
+ el.append(...Array.isArray(children) ? children : [
533
+ children
534
+ ]);
535
+ }
536
+ return el;
537
+ };
538
+ var renderRoot = (root, node) => {
539
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
540
+ tx: defaultTx
541
+ }, node));
542
+ return root;
543
+ };
544
+ var createRenderer = (Component) => (el, props) => {
545
+ renderRoot(el, /* @__PURE__ */ React.createElement(ThemeProvider, {
546
+ tx: defaultTx
547
+ }, /* @__PURE__ */ React.createElement(Tooltip.Provider, null, /* @__PURE__ */ React.createElement(Component, props))));
548
+ };
549
+
550
+ // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
551
+ var automerge = (accessor) => {
552
+ const syncState = StateField.define({
553
+ create: () => ({
554
+ path: accessor.path.slice(),
555
+ lastHeads: A3.getHeads(accessor.handle.doc()),
556
+ unreconciledTransactions: []
557
+ }),
558
+ update: (value, tr) => {
559
+ const result = {
560
+ path: accessor.path.slice(),
561
+ lastHeads: value.lastHeads,
562
+ unreconciledTransactions: value.unreconciledTransactions.slice()
563
+ };
564
+ let clearUnreconciled = false;
565
+ for (const effect of tr.effects) {
566
+ if (effect.is(updateHeadsEffect)) {
567
+ result.lastHeads = effect.value.newHeads;
568
+ clearUnreconciled = true;
569
+ }
570
+ }
571
+ if (clearUnreconciled) {
572
+ result.unreconciledTransactions = [];
573
+ } else {
574
+ if (!isReconcile(tr)) {
575
+ result.unreconciledTransactions.push(tr);
576
+ }
577
+ }
578
+ return result;
579
+ }
580
+ });
581
+ const syncer = new Syncer(accessor.handle, syncState);
582
+ return [
583
+ Cursor.converter.of(cursorConverter(accessor)),
584
+ // Track heads.
585
+ syncState,
586
+ // Reconcile external updates.
587
+ ViewPlugin2.fromClass(class {
588
+ constructor(_view) {
589
+ this._view = _view;
590
+ this._handleChange = () => {
591
+ syncer.reconcile(this._view, false);
592
+ };
593
+ accessor.handle.addListener("change", this._handleChange);
594
+ }
595
+ destroy() {
596
+ accessor.handle.removeListener("change", this._handleChange);
597
+ }
598
+ }),
599
+ // Reconcile local updates.
600
+ EditorView2.updateListener.of(({ view, changes }) => {
601
+ if (!changes.empty) {
602
+ syncer.reconcile(view, true);
603
+ }
604
+ })
605
+ ];
606
+ };
607
+
608
+ // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
609
+ import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
610
+ import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin3, WidgetType } from "@codemirror/view";
611
+ import { Event } from "@dxos/async";
612
+ import { Context } from "@dxos/context";
613
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts";
635
614
  var dummyProvider = {
636
615
  remoteStateChange: new Event(),
637
616
  open: () => {
@@ -647,10 +626,10 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
647
626
  var awareness = (provider = dummyProvider) => {
648
627
  return [
649
628
  awarenessProvider.of(provider),
650
- ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
629
+ ViewPlugin3.fromClass(RemoteSelectionsDecorator, {
651
630
  decorations: (value) => value.decorations
652
631
  }),
653
- styles2
632
+ styles
654
633
  ];
655
634
  };
656
635
  var RemoteSelectionsDecorator = class {
@@ -693,7 +672,7 @@ var RemoteSelectionsDecorator = class {
693
672
  } : void 0);
694
673
  }
695
674
  _updateRemoteSelections(view) {
696
- const decorations = [];
675
+ const decorations2 = [];
697
676
  const awarenessStates = this._provider.getRemoteStates();
698
677
  for (const state of awarenessStates) {
699
678
  const anchor = state.position?.anchor ? this._cursorConverter.fromCursor(state.position.anchor) : null;
@@ -708,7 +687,7 @@ var RemoteSelectionsDecorator = class {
708
687
  const darkColor = state.info.darkColor;
709
688
  const lightColor = state.info.lightColor;
710
689
  if (startLine.number === endLine.number) {
711
- decorations.push({
690
+ decorations2.push({
712
691
  from: start,
713
692
  to: end,
714
693
  value: Decoration2.mark({
@@ -719,7 +698,7 @@ var RemoteSelectionsDecorator = class {
719
698
  })
720
699
  });
721
700
  } else {
722
- decorations.push({
701
+ decorations2.push({
723
702
  from: start,
724
703
  to: startLine.from + startLine.length,
725
704
  value: Decoration2.mark({
@@ -729,7 +708,7 @@ var RemoteSelectionsDecorator = class {
729
708
  class: "cm-collab-selection"
730
709
  })
731
710
  });
732
- decorations.push({
711
+ decorations2.push({
733
712
  from: endLine.from,
734
713
  to: end,
735
714
  value: Decoration2.mark({
@@ -741,7 +720,7 @@ var RemoteSelectionsDecorator = class {
741
720
  });
742
721
  for (let i = startLine.number + 1; i < endLine.number; i++) {
743
722
  const linePos = view.state.doc.line(i).from;
744
- decorations.push({
723
+ decorations2.push({
745
724
  from: linePos,
746
725
  to: linePos,
747
726
  value: Decoration2.line({
@@ -753,7 +732,7 @@ var RemoteSelectionsDecorator = class {
753
732
  });
754
733
  }
755
734
  }
756
- decorations.push({
735
+ decorations2.push({
757
736
  from: head,
758
737
  to: head,
759
738
  value: Decoration2.widget({
@@ -763,14 +742,12 @@ var RemoteSelectionsDecorator = class {
763
742
  })
764
743
  });
765
744
  }
766
- this.decorations = Decoration2.set(decorations, true);
745
+ this.decorations = Decoration2.set(decorations2, true);
767
746
  }
768
747
  };
769
748
  var RemoteCaretWidget = class extends WidgetType {
770
749
  constructor(_name, _color) {
771
- super();
772
- this._name = _name;
773
- this._color = _color;
750
+ super(), this._name = _name, this._color = _color;
774
751
  }
775
752
  toDOM() {
776
753
  const span = document.createElement("span");
@@ -802,7 +779,7 @@ var RemoteCaretWidget = class extends WidgetType {
802
779
  return true;
803
780
  }
804
781
  };
805
- var styles2 = EditorView3.theme({
782
+ var styles = EditorView3.theme({
806
783
  ".cm-collab-selection": {},
807
784
  ".cm-collab-selectionLine": {
808
785
  padding: 0,
@@ -1281,10 +1258,10 @@ var random = (min, max) => {
1281
1258
  import { StateEffect as StateEffect2 } from "@codemirror/state";
1282
1259
 
1283
1260
  // packages/ui/react-ui-editor/src/extensions/command/state.ts
1284
- import { StateField as StateField3 } from "@codemirror/state";
1261
+ import { StateField as StateField2 } from "@codemirror/state";
1285
1262
  import { showTooltip } from "@codemirror/view";
1286
1263
  var commandConfig = singleValueFacet();
1287
- var commandState = StateField3.define({
1264
+ var commandState = StateField2.define({
1288
1265
  create: () => ({}),
1289
1266
  update: (state, tr) => {
1290
1267
  for (const effect of tr.effects) {
@@ -1393,14 +1370,14 @@ var commandKeyBindings = [
1393
1370
  import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1394
1371
 
1395
1372
  // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1396
- import { RangeSetBuilder } from "@codemirror/state";
1397
- import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1398
- var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1373
+ import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1374
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
1375
+ var hintViewPlugin = ({ onHint }) => ViewPlugin4.fromClass(class {
1399
1376
  constructor() {
1400
- this.deco = Decoration3.none;
1377
+ this.decorations = Decoration3.none;
1401
1378
  }
1402
1379
  update(update2) {
1403
- const builder = new RangeSetBuilder();
1380
+ const builder = new RangeSetBuilder2();
1404
1381
  const cState = update2.view.state.field(commandState, false);
1405
1382
  if (!cState?.tooltip) {
1406
1383
  const selection = update2.view.state.selection.main;
@@ -1409,22 +1386,21 @@ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1409
1386
  const hint = onHint();
1410
1387
  if (hint) {
1411
1388
  builder.add(selection.from, selection.to, Decoration3.widget({
1412
- widget: new CommandHint(hint)
1389
+ widget: new Hint(hint)
1413
1390
  }));
1414
1391
  }
1415
1392
  }
1416
1393
  }
1417
- this.deco = builder.finish();
1394
+ this.decorations = builder.finish();
1418
1395
  }
1419
1396
  }, {
1420
1397
  provide: (plugin) => [
1421
- EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1398
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.decorations ?? Decoration3.none)
1422
1399
  ]
1423
1400
  });
1424
- var CommandHint = class extends WidgetType2 {
1401
+ var Hint = class extends WidgetType2 {
1425
1402
  constructor(content) {
1426
- super();
1427
- this.content = content;
1403
+ super(), this.content = content;
1428
1404
  }
1429
1405
  toDOM() {
1430
1406
  const wrap = document.createElement("span");
@@ -1461,79 +1437,12 @@ var CommandHint = class extends WidgetType2 {
1461
1437
  }
1462
1438
  };
1463
1439
 
1464
- // packages/ui/react-ui-editor/src/extensions/command/menu.ts
1465
- import { ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1466
- var floatingMenu = (options) => ViewPlugin4.fromClass(class {
1467
- constructor(view) {
1468
- this.rafId = null;
1469
- this.view = view;
1470
- const container = view.scrollDOM;
1471
- if (getComputedStyle(container).position === "static") {
1472
- container.style.position = "relative";
1473
- }
1474
- this.button = document.createElement("div");
1475
- this.button.style.position = "absolute";
1476
- this.button.style.zIndex = "10";
1477
- this.button.style.display = "none";
1478
- options.renderMenu(this.button, {
1479
- onAction: () => openCommand(view)
1480
- }, view);
1481
- container.appendChild(this.button);
1482
- container.addEventListener("scroll", this.scheduleUpdate.bind(this));
1483
- this.scheduleUpdate();
1484
- }
1485
- update(update2) {
1486
- if (update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
1487
- this.button.style.display = "none";
1488
- } else if (update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
1489
- this.button.style.display = "block";
1490
- } else if (update2.selectionSet || update2.viewportChanged || update2.docChanged || update2.geometryChanged) {
1491
- this.scheduleUpdate();
1492
- }
1493
- }
1494
- scheduleUpdate() {
1495
- if (this.rafId != null) {
1496
- cancelAnimationFrame(this.rafId);
1497
- }
1498
- this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
1499
- }
1500
- updateButtonPosition() {
1501
- const pos = this.view.state.selection.main.head;
1502
- const lineBlock = this.view.lineBlockAt(pos);
1503
- const domInfo = this.view.domAtPos(lineBlock.from);
1504
- let node = domInfo.node;
1505
- while (node && !(node instanceof HTMLElement)) {
1506
- node = node.parentNode;
1507
- }
1508
- if (!node) {
1509
- this.button.style.display = "none";
1510
- return;
1511
- }
1512
- const lineRect = node.getBoundingClientRect();
1513
- const containerRect = this.view.scrollDOM.getBoundingClientRect();
1514
- const offsetTop = lineRect.top - containerRect.top + this.view.scrollDOM.scrollTop;
1515
- const offsetLeft = this.view.scrollDOM.clientWidth + this.view.scrollDOM.scrollLeft - lineRect.x;
1516
- this.button.style.top = `${offsetTop}px`;
1517
- this.button.style.left = `${offsetLeft}px`;
1518
- this.button.style.display = "block";
1519
- }
1520
- destroy() {
1521
- this.button.remove();
1522
- if (this.rafId != null) {
1523
- cancelAnimationFrame(this.rafId);
1524
- }
1525
- }
1526
- });
1527
-
1528
1440
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1529
1441
  var command = (options = {}) => {
1530
1442
  return [
1531
1443
  keymap3.of(commandKeyBindings),
1532
1444
  commandConfig.of(options),
1533
1445
  commandState,
1534
- options.renderMenu ? floatingMenu({
1535
- renderMenu: options.renderMenu
1536
- }) : [],
1537
1446
  options.onHint ? hintViewPlugin({
1538
1447
  onHint: options.onHint
1539
1448
  }) : [],
@@ -1548,28 +1457,174 @@ var command = (options = {}) => {
1548
1457
  ];
1549
1458
  };
1550
1459
 
1551
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1552
- import { invertedEffects } from "@codemirror/commands";
1553
- import { StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
1554
- import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView8, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1555
- import sortBy from "lodash.sortby";
1556
- import { useEffect, useMemo as useMemo2 } from "react";
1557
- import { debounce as debounce2 } from "@dxos/async";
1558
- import { log as log4 } from "@dxos/log";
1559
- import { isNonNullable } from "@dxos/util";
1560
-
1561
- // packages/ui/react-ui-editor/src/extensions/selection.ts
1562
- import { Transaction } from "@codemirror/state";
1563
- import { EditorView as EditorView7, keymap as keymap4 } from "@codemirror/view";
1564
- import { debounce } from "@dxos/async";
1565
- import { invariant as invariant3 } from "@dxos/invariant";
1566
- import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1567
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/selection.ts";
1568
- var documentId = singleValueFacet();
1569
- var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1570
- var createEditorStateTransaction = ({ scrollTo, selection }) => {
1571
- return {
1572
- selection,
1460
+ // packages/ui/react-ui-editor/src/extensions/command/menu.ts
1461
+ import { ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1462
+ var floatingMenu = (options = {}) => [
1463
+ ViewPlugin5.fromClass(class {
1464
+ constructor(view) {
1465
+ this.rafId = null;
1466
+ this.view = view;
1467
+ const container = view.scrollDOM;
1468
+ if (getComputedStyle(container).position === "static") {
1469
+ container.style.position = "relative";
1470
+ }
1471
+ const icon = document.createElement("dx-icon");
1472
+ icon.setAttribute("icon", options.icon ?? "ph--dots-three-outline--regular");
1473
+ const button = document.createElement("button");
1474
+ button.appendChild(icon);
1475
+ button.classList.add("grid", "items-center", "justify-center", "w-8", "h-8");
1476
+ this.tag = document.createElement("dx-ref-tag");
1477
+ this.tag.classList.add("border-none", "fixed", "p-0");
1478
+ this.tag.appendChild(button);
1479
+ container.appendChild(this.tag);
1480
+ container.addEventListener("scroll", this.scheduleUpdate.bind(this));
1481
+ this.scheduleUpdate();
1482
+ }
1483
+ update(update2) {
1484
+ if (update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
1485
+ this.tag.style.display = "none";
1486
+ } else if (update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
1487
+ this.tag.style.display = "block";
1488
+ } else if (update2.selectionSet || update2.viewportChanged || update2.docChanged || update2.geometryChanged) {
1489
+ this.scheduleUpdate();
1490
+ }
1491
+ }
1492
+ updateButtonPosition() {
1493
+ const { x, width } = this.view.contentDOM.getBoundingClientRect();
1494
+ const pos = this.view.state.selection.main.head;
1495
+ const line = this.view.lineBlockAt(pos);
1496
+ const coords = this.view.coordsAtPos(line.from);
1497
+ if (!coords) {
1498
+ return;
1499
+ }
1500
+ const lineHeight = coords.bottom - coords.top;
1501
+ const dy = (lineHeight - (options.height ?? 32)) / 2;
1502
+ const offsetTop = coords.top + dy;
1503
+ const offsetLeft = x + width + (options.padding ?? 8);
1504
+ this.tag.style.top = `${offsetTop}px`;
1505
+ this.tag.style.left = `${offsetLeft}px`;
1506
+ this.tag.style.display = "block";
1507
+ }
1508
+ scheduleUpdate() {
1509
+ if (this.rafId != null) {
1510
+ cancelAnimationFrame(this.rafId);
1511
+ }
1512
+ this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
1513
+ }
1514
+ destroy() {
1515
+ this.tag.remove();
1516
+ if (this.rafId != null) {
1517
+ cancelAnimationFrame(this.rafId);
1518
+ }
1519
+ }
1520
+ })
1521
+ ];
1522
+
1523
+ // packages/ui/react-ui-editor/src/extensions/command/typeahead.ts
1524
+ import { EditorSelection, Prec, RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1525
+ import { Decoration as Decoration4, keymap as keymap4, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
1526
+ var typeahead = ({ onComplete } = {}) => {
1527
+ let hint;
1528
+ const complete = (view) => {
1529
+ if (!hint) {
1530
+ return false;
1531
+ }
1532
+ const selection = view.state.selection.main;
1533
+ view.dispatch({
1534
+ changes: [
1535
+ {
1536
+ from: selection.from,
1537
+ to: selection.to,
1538
+ insert: hint
1539
+ }
1540
+ ],
1541
+ selection: EditorSelection.cursor(selection.from + hint.length)
1542
+ });
1543
+ return true;
1544
+ };
1545
+ return [
1546
+ ViewPlugin6.fromClass(class {
1547
+ constructor() {
1548
+ this.decorations = Decoration4.none;
1549
+ }
1550
+ update(update2) {
1551
+ const builder = new RangeSetBuilder3();
1552
+ const selection = update2.view.state.selection.main;
1553
+ const line = update2.view.state.doc.lineAt(selection.from);
1554
+ if (selection.from === selection.to && selection.from === line.to) {
1555
+ const str = update2.state.sliceDoc(line.from, selection.from);
1556
+ hint = onComplete?.({
1557
+ line: str
1558
+ });
1559
+ if (hint) {
1560
+ builder.add(selection.from, selection.to, Decoration4.widget({
1561
+ widget: new Hint(hint)
1562
+ }));
1563
+ }
1564
+ }
1565
+ this.decorations = builder.finish();
1566
+ }
1567
+ }, {
1568
+ decorations: (v) => v.decorations
1569
+ }),
1570
+ // Keys.
1571
+ Prec.highest(keymap4.of([
1572
+ {
1573
+ key: "Tab",
1574
+ preventDefault: true,
1575
+ run: complete
1576
+ },
1577
+ {
1578
+ key: "ArrowRight",
1579
+ preventDefault: true,
1580
+ run: complete
1581
+ }
1582
+ ]))
1583
+ ];
1584
+ };
1585
+ var staticCompletion = (completions, defaultCompletion) => ({ line }) => {
1586
+ if (line.length === 0 && defaultCompletion) {
1587
+ return defaultCompletion;
1588
+ }
1589
+ const words = line.split(/\s+/).filter(Boolean);
1590
+ if (words.length) {
1591
+ const word = words.at(-1);
1592
+ for (const completion of completions) {
1593
+ const match = matchCompletion(completion, word);
1594
+ if (match) {
1595
+ return match;
1596
+ }
1597
+ }
1598
+ }
1599
+ };
1600
+ var matchCompletion = (completion, word) => {
1601
+ if (completion.length > word.length && completion.startsWith(word)) {
1602
+ return completion.slice(word.length);
1603
+ }
1604
+ };
1605
+
1606
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
1607
+ import { invertedEffects } from "@codemirror/commands";
1608
+ import { StateEffect as StateEffect3, StateField as StateField3 } from "@codemirror/state";
1609
+ import { hoverTooltip, keymap as keymap6, Decoration as Decoration5, EditorView as EditorView8, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
1610
+ import sortBy from "lodash.sortby";
1611
+ import { useEffect } from "react";
1612
+ import { debounce as debounce2 } from "@dxos/async";
1613
+ import { log as log4 } from "@dxos/log";
1614
+ import { isNonNullable } from "@dxos/util";
1615
+
1616
+ // packages/ui/react-ui-editor/src/extensions/selection.ts
1617
+ import { Transaction } from "@codemirror/state";
1618
+ import { EditorView as EditorView7, keymap as keymap5 } from "@codemirror/view";
1619
+ import { debounce } from "@dxos/async";
1620
+ import { invariant as invariant3 } from "@dxos/invariant";
1621
+ import { isNotFalsy } from "@dxos/util";
1622
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/selection.ts";
1623
+ var documentId = singleValueFacet();
1624
+ var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1625
+ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1626
+ return {
1627
+ selection,
1573
1628
  scrollIntoView: !scrollTo,
1574
1629
  effects: scrollTo ? EditorView7.scrollIntoView(scrollTo, {
1575
1630
  yMargin: 96
@@ -1636,7 +1691,7 @@ var selectionState = ({ getState, setState } = {}) => {
1636
1691
  }
1637
1692
  }
1638
1693
  }),
1639
- getState && keymap4.of([
1694
+ getState && keymap5.of([
1640
1695
  {
1641
1696
  key: "ctrl-r",
1642
1697
  run: (view) => {
@@ -1648,7 +1703,7 @@ var selectionState = ({ getState, setState } = {}) => {
1648
1703
  }
1649
1704
  }
1650
1705
  ])
1651
- ].filter(isNotFalsy2);
1706
+ ].filter(isNotFalsy);
1652
1707
  };
1653
1708
 
1654
1709
  // packages/ui/react-ui-editor/src/extensions/comments.ts
@@ -1656,7 +1711,7 @@ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src
1656
1711
  var setComments = StateEffect3.define();
1657
1712
  var setSelection = StateEffect3.define();
1658
1713
  var setCommentState = StateEffect3.define();
1659
- var commentsState = StateField4.define({
1714
+ var commentsState = StateField3.define({
1660
1715
  create: (state) => ({
1661
1716
  id: state.facet(documentId),
1662
1717
  comments: [],
@@ -1694,7 +1749,7 @@ var commentsState = StateField4.define({
1694
1749
  return value;
1695
1750
  }
1696
1751
  });
1697
- var styles3 = EditorView8.theme({
1752
+ var styles2 = EditorView8.theme({
1698
1753
  ".cm-comment, .cm-comment-current": {
1699
1754
  margin: "0 -3px",
1700
1755
  padding: "3px",
@@ -1707,7 +1762,7 @@ var styles3 = EditorView8.theme({
1707
1762
  textDecoration: "underline"
1708
1763
  }
1709
1764
  });
1710
- var createCommentMark = (id, isCurrent) => Decoration4.mark({
1765
+ var createCommentMark = (id, isCurrent) => Decoration5.mark({
1711
1766
  class: isCurrent ? "cm-comment-current" : "cm-comment",
1712
1767
  attributes: {
1713
1768
  "data-testid": "cm-comment",
@@ -1718,12 +1773,12 @@ var commentsDecorations = EditorView8.decorations.compute([
1718
1773
  commentsState
1719
1774
  ], (state) => {
1720
1775
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
1721
- const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1776
+ const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1722
1777
  const range = comment.range;
1723
1778
  if (!range) {
1724
1779
  log4.warn("Invalid range:", range, {
1725
1780
  F: __dxlog_file7,
1726
- L: 144,
1781
+ L: 135,
1727
1782
  S: void 0,
1728
1783
  C: (f, a) => f(...a)
1729
1784
  });
@@ -1734,7 +1789,7 @@ var commentsDecorations = EditorView8.decorations.compute([
1734
1789
  const mark = createCommentMark(comment.comment.id, comment.comment.id === current);
1735
1790
  return mark.range(range.from, range.to);
1736
1791
  }).filter(isNonNullable);
1737
- return Decoration4.set(decorations);
1792
+ return Decoration5.set(decorations2);
1738
1793
  });
1739
1794
  var commentClickedEffect = StateEffect3.define();
1740
1795
  var handleCommentClick = EditorView8.domEventHandlers({
@@ -1895,11 +1950,11 @@ var comments = (options = {}) => {
1895
1950
  commentsState,
1896
1951
  commentsDecorations,
1897
1952
  handleCommentClick,
1898
- styles3,
1953
+ styles2,
1899
1954
  //
1900
1955
  // Keymap.
1901
1956
  //
1902
- options.onCreate && keymap5.of([
1957
+ options.onCreate && keymap6.of([
1903
1958
  {
1904
1959
  key: shortcut,
1905
1960
  run: callbackWrapper(createComment)
@@ -2037,22 +2092,6 @@ var scrollThreadIntoView = (view, id, center = true) => {
2037
2092
  }
2038
2093
  }
2039
2094
  };
2040
- var selectionOverlapsComment = (state) => {
2041
- const commentState = state.field(commentsState, false);
2042
- if (commentState === void 0) {
2043
- return false;
2044
- }
2045
- const { selection } = state;
2046
- for (const range of selection.ranges) {
2047
- if (commentState.comments.some(({ range: commentRange }) => overlap(commentRange, range))) {
2048
- return true;
2049
- }
2050
- }
2051
- return false;
2052
- };
2053
- var hasActiveSelection = (state) => {
2054
- return state.selection.ranges.some((range) => !range.empty);
2055
- };
2056
2095
  var ExternalCommentSync = class {
2057
2096
  constructor(view, id, subscribe, getComments) {
2058
2097
  this.destroy = () => {
@@ -2072,21 +2111,11 @@ var ExternalCommentSync = class {
2072
2111
  this.unsubscribe = subscribe(updateComments);
2073
2112
  }
2074
2113
  };
2075
- var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin5.fromClass(class {
2114
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin7.fromClass(class {
2076
2115
  constructor(view) {
2077
2116
  return new ExternalCommentSync(view, id, subscribe, getComments);
2078
2117
  }
2079
2118
  });
2080
- var useCommentState = (state) => {
2081
- return useMemo2(() => EditorView8.updateListener.of((update2) => {
2082
- if (update2.docChanged || update2.selectionSet) {
2083
- state.comment = selectionOverlapsComment(update2.state);
2084
- state.selection = hasActiveSelection(update2.state);
2085
- }
2086
- }), [
2087
- state
2088
- ]);
2089
- };
2090
2119
  var useComments = (view, id, comments2) => {
2091
2120
  useEffect(() => {
2092
2121
  if (view) {
@@ -2101,28 +2130,15 @@ var useComments = (view, id, comments2) => {
2101
2130
  }
2102
2131
  });
2103
2132
  };
2104
- var useCommentClickListener = (onCommentClick) => {
2105
- return useMemo2(() => EditorView8.updateListener.of((update2) => {
2106
- update2.transactions.forEach((transaction) => {
2107
- transaction.effects.forEach((effect) => {
2108
- if (effect.is(commentClickedEffect)) {
2109
- onCommentClick(effect.value);
2110
- }
2111
- });
2112
- });
2113
- }), [
2114
- onCommentClick
2115
- ]);
2116
- };
2117
2133
 
2118
2134
  // packages/ui/react-ui-editor/src/extensions/debug.ts
2119
2135
  import { syntaxTree } from "@codemirror/language";
2120
- import { StateField as StateField5 } from "@codemirror/state";
2136
+ import { StateField as StateField4 } from "@codemirror/state";
2121
2137
  var debugNodeLogger = (log9 = console.log) => {
2122
2138
  const logTokens = (state) => syntaxTree(state).iterate({
2123
2139
  enter: (node) => log9(node.type)
2124
2140
  });
2125
- return StateField5.define({
2141
+ return StateField4.define({
2126
2142
  create: (state) => logTokens(state),
2127
2143
  update: (_, tr) => logTokens(tr.state)
2128
2144
  });
@@ -2130,7 +2146,7 @@ var debugNodeLogger = (log9 = console.log) => {
2130
2146
 
2131
2147
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
2132
2148
  import { dropCursor, EditorView as EditorView9 } from "@codemirror/view";
2133
- var styles4 = EditorView9.theme({
2149
+ var styles3 = EditorView9.theme({
2134
2150
  ".cm-dropCursor": {
2135
2151
  borderLeft: "2px solid var(--dx-accentText)",
2136
2152
  color: "var(--dx-accentText)",
@@ -2142,7 +2158,7 @@ var styles4 = EditorView9.theme({
2142
2158
  });
2143
2159
  var dropFile = (options = {}) => {
2144
2160
  return [
2145
- styles4,
2161
+ styles3,
2146
2162
  dropCursor(),
2147
2163
  EditorView9.domEventHandlers({
2148
2164
  drop: (event, view) => {
@@ -2171,18 +2187,18 @@ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@cod
2171
2187
  import { searchKeymap } from "@codemirror/search";
2172
2188
  import { EditorState } from "@codemirror/state";
2173
2189
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2174
- import { EditorView as EditorView11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2190
+ import { EditorView as EditorView11, ViewPlugin as ViewPlugin8, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2175
2191
  import defaultsDeep2 from "lodash.defaultsdeep";
2176
2192
  import merge from "lodash.merge";
2177
2193
  import { generateName } from "@dxos/display-name";
2178
2194
  import { log as log5 } from "@dxos/log";
2179
- import { hexToHue, isNotFalsy as isNotFalsy3 } from "@dxos/util";
2195
+ import { hexToHue, isNotFalsy as isNotFalsy2 } from "@dxos/util";
2180
2196
 
2181
2197
  // packages/ui/react-ui-editor/src/extensions/focus.ts
2182
- import { StateEffect as StateEffect4, StateField as StateField6 } from "@codemirror/state";
2198
+ import { StateEffect as StateEffect4, StateField as StateField5 } from "@codemirror/state";
2183
2199
  import { EditorView as EditorView10 } from "@codemirror/view";
2184
2200
  var focusEffect = StateEffect4.define();
2185
- var focusField = StateField6.define({
2201
+ var focusField = StateField5.define({
2186
2202
  create: () => false,
2187
2203
  update: (value, tr) => {
2188
2204
  for (const effect of tr.effects) {
@@ -2229,8 +2245,8 @@ var theme = {
2229
2245
  };
2230
2246
 
2231
2247
  // packages/ui/react-ui-editor/src/styles/tokens.ts
2232
- import get from "lodash.get";
2233
2248
  import { tokens } from "@dxos/react-ui-theme";
2249
+ import { get } from "@dxos/util";
2234
2250
  var getToken = (path, defaultValue) => {
2235
2251
  const value = get(tokens, path, defaultValue);
2236
2252
  return value?.toString() ?? "";
@@ -2353,19 +2369,25 @@ var defaultTheme = {
2353
2369
  */
2354
2370
  ".cm-tooltip.cm-tooltip-autocomplete": {
2355
2371
  marginTop: "4px",
2356
- marginLeft: "-3px"
2372
+ marginLeft: "-3px",
2373
+ borderColor: "var(--dx-separator)",
2374
+ borderTop: "none"
2357
2375
  },
2358
2376
  ".cm-tooltip.cm-tooltip-autocomplete > ul": {
2359
2377
  maxHeight: "20em"
2360
2378
  },
2361
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
2362
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
2379
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {
2380
+ padding: "4px"
2381
+ },
2382
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2383
+ background: "var(--dx-hoverSurface)"
2384
+ },
2363
2385
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2364
2386
  paddingLeft: "4px !important",
2365
2387
  borderBottom: "none !important",
2366
2388
  color: "var(--dx-accentText)"
2367
2389
  },
2368
- ".cm-tooltip.cm-completionInfo": {
2390
+ ".cm-completionInfo": {
2369
2391
  width: "360px !important",
2370
2392
  margin: "-10px 1px 0 1px",
2371
2393
  padding: "8px !important",
@@ -2415,7 +2437,7 @@ var defaultTheme = {
2415
2437
  backgroundColor: "var(--dx-input)"
2416
2438
  },
2417
2439
  ".cm-panel input:focus, .cm-panel button:focus": {
2418
- outline: "1px solid var(--dx-accentFocusIndicator)"
2440
+ outline: "1px solid var(--dx-neutralFocusIndicator)"
2419
2441
  },
2420
2442
  ".cm-panel label": {
2421
2443
  display: "inline-flex",
@@ -2428,7 +2450,7 @@ var defaultTheme = {
2428
2450
  height: "8px",
2429
2451
  marginRight: "6px !important",
2430
2452
  padding: "2px !important",
2431
- color: "var(--dx-accentFocusIndicator)"
2453
+ color: "var(--dx-neutralFocusIndicator)"
2432
2454
  },
2433
2455
  ".cm-panel button": {
2434
2456
  "&:hover": {
@@ -2471,7 +2493,7 @@ var createBasicExtensions = (_props) => {
2471
2493
  EditorView11.exceptionSink.of((err) => {
2472
2494
  log5.catch(err, void 0, {
2473
2495
  F: __dxlog_file8,
2474
- L: 96,
2496
+ L: 98,
2475
2497
  S: void 0,
2476
2498
  C: (f, a) => f(...a)
2477
2499
  });
@@ -2494,7 +2516,7 @@ var createBasicExtensions = (_props) => {
2494
2516
  props.scrollPastEnd && scrollPastEnd(),
2495
2517
  props.tabSize && EditorState.tabSize.of(props.tabSize),
2496
2518
  // https://codemirror.net/docs/ref/#view.KeyBinding
2497
- keymap6.of([
2519
+ keymap7.of([
2498
2520
  ...(props.keymap && keymaps[props.keymap]) ?? [],
2499
2521
  // NOTE: Tabs are also configured by markdown extension.
2500
2522
  // https://codemirror.net/docs/ref/#commands.indentWithTab
@@ -2514,19 +2536,19 @@ var createBasicExtensions = (_props) => {
2514
2536
  preventDefault: true,
2515
2537
  run: () => true
2516
2538
  }
2517
- ].filter(isNotFalsy3))
2518
- ].filter(isNotFalsy3);
2539
+ ].filter(isNotFalsy2))
2540
+ ].filter(isNotFalsy2);
2519
2541
  };
2520
2542
  var defaultThemeSlots = {
2521
2543
  editor: {
2522
2544
  className: "w-full bs-full"
2523
2545
  }
2524
2546
  };
2525
- var createThemeExtensions = ({ themeMode, styles: styles5, syntaxHighlighting: _syntaxHighlighting, slots: _slots } = {}) => {
2547
+ var createThemeExtensions = ({ themeMode, styles: styles4, syntaxHighlighting: _syntaxHighlighting, slots: _slots } = {}) => {
2526
2548
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2527
2549
  return [
2528
2550
  EditorView11.darkTheme.of(themeMode === "dark"),
2529
- EditorView11.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2551
+ EditorView11.baseTheme(styles4 ? merge({}, defaultTheme, styles4) : defaultTheme),
2530
2552
  // https://github.com/codemirror/theme-one-dark
2531
2553
  _syntaxHighlighting && (themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
2532
2554
  slots.editor?.className && EditorView11.editorAttributes.of({
@@ -2534,8 +2556,13 @@ var createThemeExtensions = ({ themeMode, styles: styles5, syntaxHighlighting: _
2534
2556
  }),
2535
2557
  slots.content?.className && EditorView11.contentAttributes.of({
2536
2558
  class: slots.content.className
2559
+ }),
2560
+ slots.scroll?.className && ViewPlugin8.fromClass(class {
2561
+ constructor(view) {
2562
+ view.scrollDOM.classList.add(slots.scroll.className);
2563
+ }
2537
2564
  })
2538
- ].filter(isNotFalsy3);
2565
+ ].filter(isNotFalsy2);
2539
2566
  };
2540
2567
  var createDataExtensions = ({ id, text, space, identity }) => {
2541
2568
  const extensions = [];
@@ -2597,6 +2624,50 @@ var folding = (_props = {}) => [
2597
2624
  })
2598
2625
  ];
2599
2626
 
2627
+ // packages/ui/react-ui-editor/src/extensions/json.ts
2628
+ import { json, jsonParseLinter } from "@codemirror/lang-json";
2629
+ import { linter } from "@codemirror/lint";
2630
+ import Ajv from "ajv";
2631
+ var createJsonExtensions = ({ schema } = {}) => {
2632
+ let lintSource = jsonParseLinter();
2633
+ if (schema) {
2634
+ const ajv = new Ajv({
2635
+ allErrors: false
2636
+ });
2637
+ const validate = ajv.compile(schema);
2638
+ lintSource = schemaLinter(validate);
2639
+ }
2640
+ return [
2641
+ json(),
2642
+ linter(lintSource)
2643
+ ];
2644
+ };
2645
+ var schemaLinter = (validate) => (view) => {
2646
+ try {
2647
+ const jsonText = view.state.doc.toString();
2648
+ const jsonData = JSON.parse(jsonText);
2649
+ const valid = validate(jsonData);
2650
+ if (valid) {
2651
+ return [];
2652
+ }
2653
+ return validate.errors?.map((err) => ({
2654
+ from: 0,
2655
+ to: jsonText.length,
2656
+ severity: "error",
2657
+ message: `${err.instancePath || "(root)"} ${err.message}`
2658
+ })) ?? [];
2659
+ } catch (err) {
2660
+ return [
2661
+ {
2662
+ from: 0,
2663
+ to: view.state.doc.length,
2664
+ severity: "error",
2665
+ message: "Invalid JSON: " + err.message
2666
+ }
2667
+ ];
2668
+ }
2669
+ };
2670
+
2600
2671
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2601
2672
  import { EditorView as EditorView13 } from "@codemirror/view";
2602
2673
  var listener = ({ onFocus, onChange }) => {
@@ -2614,23 +2685,23 @@ var listener = ({ onFocus, onChange }) => {
2614
2685
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2615
2686
  import { snippet } from "@codemirror/autocomplete";
2616
2687
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2617
- import { EditorSelection } from "@codemirror/state";
2618
- import { EditorView as EditorView14, keymap as keymap7 } from "@codemirror/view";
2619
- import { useMemo as useMemo3 } from "react";
2688
+ import { EditorSelection as EditorSelection2 } from "@codemirror/state";
2689
+ import { EditorView as EditorView14, keymap as keymap8 } from "@codemirror/view";
2690
+ import { useMemo as useMemo2 } from "react";
2620
2691
  var formattingEquals = (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;
2621
- var Inline;
2622
- (function(Inline2) {
2692
+ var Inline = /* @__PURE__ */ function(Inline2) {
2623
2693
  Inline2[Inline2["Strong"] = 0] = "Strong";
2624
2694
  Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2625
2695
  Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2626
2696
  Inline2[Inline2["Code"] = 3] = "Code";
2627
- })(Inline || (Inline = {}));
2628
- var List;
2629
- (function(List2) {
2697
+ return Inline2;
2698
+ }({});
2699
+ var List = /* @__PURE__ */ function(List2) {
2630
2700
  List2[List2["Ordered"] = 0] = "Ordered";
2631
2701
  List2[List2["Bullet"] = 1] = "Bullet";
2632
2702
  List2[List2["Task"] = 2] = "Task";
2633
- })(List || (List = {}));
2703
+ return List2;
2704
+ }({});
2634
2705
  var setHeading = (level) => {
2635
2706
  return ({ state, dispatch }) => {
2636
2707
  const { selection: { ranges }, doc } = state;
@@ -2735,7 +2806,7 @@ var setStyle = (type, enable) => {
2735
2806
  to: range.head + found + marker.length
2736
2807
  }
2737
2808
  ],
2738
- range: EditorSelection.cursor(range.from - marker.length)
2809
+ range: EditorSelection2.cursor(range.from - marker.length)
2739
2810
  };
2740
2811
  }
2741
2812
  }
@@ -2863,13 +2934,13 @@ var setStyle = (type, enable) => {
2863
2934
  from: range.head,
2864
2935
  insert: marker + marker
2865
2936
  },
2866
- range: EditorSelection.cursor(range.head + marker.length)
2937
+ range: EditorSelection2.cursor(range.head + marker.length)
2867
2938
  };
2868
2939
  }
2869
2940
  const changeSet = state.changes(changes2.concat(changesAtEnd));
2870
2941
  return {
2871
2942
  changes: changeSet,
2872
- range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2943
+ range: range.empty && !changeSet.empty ? EditorSelection2.cursor(range.head + marker.length) : EditorSelection2.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2873
2944
  };
2874
2945
  });
2875
2946
  dispatch(state.update(changes, {
@@ -3069,7 +3140,7 @@ var addLink = ({ url, image: image2 } = {}) => {
3069
3140
  const changeSet = state.changes(changes2.concat(changesAfter));
3070
3141
  return {
3071
3142
  changes: changeSet,
3072
- range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
3143
+ range: EditorSelection2.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
3073
3144
  };
3074
3145
  });
3075
3146
  if (changes.changes.empty) {
@@ -3503,7 +3574,7 @@ var toggleCodeblock = (target) => {
3503
3574
  };
3504
3575
  var formattingKeymap = (_options = {}) => {
3505
3576
  return [
3506
- keymap7.of([
3577
+ keymap8.of([
3507
3578
  {
3508
3579
  key: "meta-b",
3509
3580
  run: toggleStrong
@@ -3704,7 +3775,7 @@ var getFormatting = (state) => {
3704
3775
  };
3705
3776
  };
3706
3777
  var useFormattingState = (state) => {
3707
- return useMemo3(() => EditorView14.updateListener.of((update2) => {
3778
+ return useMemo2(() => EditorView14.updateListener.of((update2) => {
3708
3779
  if (update2.docChanged || update2.selectionSet) {
3709
3780
  Object.entries(getFormatting(update2.state)).forEach(([key, active]) => {
3710
3781
  state[key] = active;
@@ -3713,7 +3784,7 @@ var useFormattingState = (state) => {
3713
3784
  }), []);
3714
3785
  };
3715
3786
 
3716
- // packages/ui/react-ui-editor/src/extensions/markdown/editorAction.ts
3787
+ // packages/ui/react-ui-editor/src/extensions/markdown/action.ts
3717
3788
  var processEditorPayload = (view, { type, data }) => {
3718
3789
  let inlineType, listType;
3719
3790
  switch (type) {
@@ -3769,7 +3840,8 @@ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lan
3769
3840
  import { syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
3770
3841
  import { languages } from "@codemirror/language-data";
3771
3842
  import { lintKeymap } from "@codemirror/lint";
3772
- import { keymap as keymap8 } from "@codemirror/view";
3843
+ import { keymap as keymap9 } from "@codemirror/view";
3844
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
3773
3845
 
3774
3846
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
3775
3847
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -3951,7 +4023,7 @@ var markdownHighlightStyle = (_options = {}) => {
3951
4023
  };
3952
4024
 
3953
4025
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3954
- var createMarkdownExtensions = ({ themeMode } = {}) => {
4026
+ var createMarkdownExtensions = (options = {}) => {
3955
4027
  return [
3956
4028
  // Main extension.
3957
4029
  // https://github.com/codemirror/lang-markdown
@@ -3975,21 +4047,21 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
3975
4047
  }),
3976
4048
  // Custom styles.
3977
4049
  syntaxHighlighting2(markdownHighlightStyle()),
3978
- keymap8.of([
4050
+ keymap9.of([
3979
4051
  // https://codemirror.net/docs/ref/#commands.indentWithTab
3980
- indentWithTab2,
4052
+ options.indentWithTab !== false && indentWithTab2,
3981
4053
  // https://codemirror.net/docs/ref/#commands.defaultKeymap
3982
4054
  ...defaultKeymap2,
3983
4055
  ...completionKeymap2,
3984
4056
  ...lintKeymap
3985
- ])
4057
+ ].filter(isNotFalsy3))
3986
4058
  ];
3987
4059
  };
3988
4060
 
3989
4061
  // packages/ui/react-ui-editor/src/extensions/markdown/debug.ts
3990
4062
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3991
- import { StateField as StateField7 } from "@codemirror/state";
3992
- var debugTree = (cb) => StateField7.define({
4063
+ import { StateField as StateField6 } from "@codemirror/state";
4064
+ var debugTree = (cb) => StateField6.define({
3993
4065
  create: (state) => cb(convertTreeToJson(state)),
3994
4066
  update: (value, tr) => cb(convertTreeToJson(tr.state))
3995
4067
  });
@@ -4015,17 +4087,17 @@ var convertTreeToJson = (state) => {
4015
4087
 
4016
4088
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
4017
4089
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4018
- import { RangeSetBuilder as RangeSetBuilder3, StateEffect as StateEffect5 } from "@codemirror/state";
4019
- import { EditorView as EditorView18, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4090
+ import { RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect5 } from "@codemirror/state";
4091
+ import { EditorView as EditorView18, Decoration as Decoration8, WidgetType as WidgetType5, ViewPlugin as ViewPlugin10 } from "@codemirror/view";
4020
4092
  import { invariant as invariant4 } from "@dxos/invariant";
4021
4093
  import { mx as mx2 } from "@dxos/react-ui-theme";
4022
4094
 
4023
4095
  // packages/ui/react-ui-editor/src/extensions/markdown/changes.ts
4024
4096
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4025
4097
  import { Transaction as Transaction2 } from "@codemirror/state";
4026
- import { ViewPlugin as ViewPlugin6 } from "@codemirror/view";
4098
+ import { ViewPlugin as ViewPlugin9 } from "@codemirror/view";
4027
4099
  var adjustChanges = () => {
4028
- return ViewPlugin6.fromClass(class {
4100
+ return ViewPlugin9.fromClass(class {
4029
4101
  update(update2) {
4030
4102
  const tree = syntaxTree4(update2.state);
4031
4103
  const adjustments = [];
@@ -4166,13 +4238,13 @@ var getValidUrl = (str) => {
4166
4238
 
4167
4239
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
4168
4240
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4169
- import { StateField as StateField8 } from "@codemirror/state";
4170
- import { Decoration as Decoration5, EditorView as EditorView15, WidgetType as WidgetType3 } from "@codemirror/view";
4241
+ import { StateField as StateField7 } from "@codemirror/state";
4242
+ import { Decoration as Decoration6, EditorView as EditorView15, WidgetType as WidgetType3 } from "@codemirror/view";
4171
4243
  var image = (_options = {}) => {
4172
4244
  return [
4173
- StateField8.define({
4245
+ StateField7.define({
4174
4246
  create: (state) => {
4175
- return Decoration5.set(buildDecorations(0, state.doc.length, state));
4247
+ return Decoration6.set(buildDecorations(0, state.doc.length, state));
4176
4248
  },
4177
4249
  update: (value, tr) => {
4178
4250
  if (!tr.docChanged && !tr.selection) {
@@ -4200,7 +4272,7 @@ var image = (_options = {}) => {
4200
4272
  ];
4201
4273
  };
4202
4274
  var buildDecorations = (from, to, state) => {
4203
- const decorations = [];
4275
+ const decorations2 = [];
4204
4276
  const cursor = state.selection.main.head;
4205
4277
  syntaxTree5(state).iterate({
4206
4278
  enter: (node) => {
@@ -4213,7 +4285,7 @@ var buildDecorations = (from, to, state) => {
4213
4285
  return;
4214
4286
  }
4215
4287
  preloadImage(url);
4216
- decorations.push(Decoration5.replace({
4288
+ decorations2.push(Decoration6.replace({
4217
4289
  block: true,
4218
4290
  widget: new ImageWidget(url)
4219
4291
  }).range(hide2 ? node.from : node.to, node.to));
@@ -4223,7 +4295,7 @@ var buildDecorations = (from, to, state) => {
4223
4295
  from,
4224
4296
  to
4225
4297
  });
4226
- return decorations;
4298
+ return decorations2;
4227
4299
  };
4228
4300
  var preloaded = /* @__PURE__ */ new Set();
4229
4301
  var preloadImage = (url) => {
@@ -4235,8 +4307,7 @@ var preloadImage = (url) => {
4235
4307
  };
4236
4308
  var ImageWidget = class extends WidgetType3 {
4237
4309
  constructor(_url) {
4238
- super();
4239
- this._url = _url;
4310
+ super(), this._url = _url;
4240
4311
  }
4241
4312
  eq(other) {
4242
4313
  return this._url === other._url;
@@ -4317,8 +4388,9 @@ var formattingStyles = EditorView16.theme({
4317
4388
  * Task list.
4318
4389
  */
4319
4390
  "& .cm-task": {
4320
- display: "inline-block",
4321
- width: `${bulletListIndentationWidth}px`
4391
+ display: "inline-flex",
4392
+ width: `${bulletListIndentationWidth}px`,
4393
+ height: "20px"
4322
4394
  },
4323
4395
  "& .cm-task-checkbox": {
4324
4396
  display: "grid",
@@ -4378,17 +4450,17 @@ var formattingStyles = EditorView16.theme({
4378
4450
 
4379
4451
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4380
4452
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4381
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField9 } from "@codemirror/state";
4382
- import { Decoration as Decoration6, EditorView as EditorView17, WidgetType as WidgetType4 } from "@codemirror/view";
4453
+ import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4454
+ import { Decoration as Decoration7, EditorView as EditorView17, WidgetType as WidgetType4 } from "@codemirror/view";
4383
4455
  var table = (options = {}) => {
4384
- return StateField9.define({
4456
+ return StateField8.define({
4385
4457
  create: (state) => update(state, options),
4386
4458
  update: (_, tr) => update(tr.state, options),
4387
4459
  provide: (field) => EditorView17.decorations.from(field)
4388
4460
  });
4389
4461
  };
4390
4462
  var update = (state, _options) => {
4391
- const builder = new RangeSetBuilder2();
4463
+ const builder = new RangeSetBuilder4();
4392
4464
  const cursor = state.selection.main.head;
4393
4465
  const tables = [];
4394
4466
  const getTable = () => tables[tables.length - 1];
@@ -4429,12 +4501,12 @@ var update = (state, _options) => {
4429
4501
  tables.forEach((table2) => {
4430
4502
  const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4431
4503
  if (replace) {
4432
- builder.add(table2.from, table2.to, Decoration6.replace({
4504
+ builder.add(table2.from, table2.to, Decoration7.replace({
4433
4505
  block: true,
4434
4506
  widget: new TableWidget(table2)
4435
4507
  }));
4436
4508
  } else {
4437
- builder.add(table2.from, table2.to, Decoration6.mark({
4509
+ builder.add(table2.from, table2.to, Decoration7.mark({
4438
4510
  class: "cm-table"
4439
4511
  }));
4440
4512
  }
@@ -4443,8 +4515,7 @@ var update = (state, _options) => {
4443
4515
  };
4444
4516
  var TableWidget = class extends WidgetType4 {
4445
4517
  constructor(_table) {
4446
- super();
4447
- this._table = _table;
4518
+ super(), this._table = _table;
4448
4519
  }
4449
4520
  eq(other) {
4450
4521
  return this._table.header?.join() === other._table.header?.join() && this._table.rows?.join() === other._table.rows?.join();
@@ -4492,9 +4563,7 @@ var HorizontalRuleWidget = class extends WidgetType5 {
4492
4563
  };
4493
4564
  var LinkButton = class extends WidgetType5 {
4494
4565
  constructor(url, render) {
4495
- super();
4496
- this.url = url;
4497
- this.render = render;
4566
+ super(), this.url = url, this.render = render;
4498
4567
  }
4499
4568
  eq(other) {
4500
4569
  return this.url === other.url;
@@ -4510,8 +4579,7 @@ var LinkButton = class extends WidgetType5 {
4510
4579
  };
4511
4580
  var CheckboxWidget = class extends WidgetType5 {
4512
4581
  constructor(_checked) {
4513
- super();
4514
- this._checked = _checked;
4582
+ super(), this._checked = _checked;
4515
4583
  }
4516
4584
  eq(other) {
4517
4585
  return this._checked === other._checked;
@@ -4555,9 +4623,7 @@ var CheckboxWidget = class extends WidgetType5 {
4555
4623
  };
4556
4624
  var TextWidget = class extends WidgetType5 {
4557
4625
  constructor(text, className) {
4558
- super();
4559
- this.text = text;
4560
- this.className = className;
4626
+ super(), this.text = text, this.className = className;
4561
4627
  }
4562
4628
  toDOM() {
4563
4629
  const el = document.createElement("span");
@@ -4568,29 +4634,29 @@ var TextWidget = class extends WidgetType5 {
4568
4634
  return el;
4569
4635
  }
4570
4636
  };
4571
- var hide = Decoration7.replace({});
4572
- var blockQuote = Decoration7.line({
4637
+ var hide = Decoration8.replace({});
4638
+ var blockQuote = Decoration8.line({
4573
4639
  class: "cm-blockquote"
4574
4640
  });
4575
- var fencedCodeLine = Decoration7.line({
4641
+ var fencedCodeLine = Decoration8.line({
4576
4642
  class: "cm-code cm-codeblock-line"
4577
4643
  });
4578
- var fencedCodeLineFirst = Decoration7.line({
4644
+ var fencedCodeLineFirst = Decoration8.line({
4579
4645
  class: mx2("cm-code cm-codeblock-line", "cm-codeblock-start")
4580
4646
  });
4581
- var fencedCodeLineLast = Decoration7.line({
4647
+ var fencedCodeLineLast = Decoration8.line({
4582
4648
  class: mx2("cm-code cm-codeblock-line", "cm-codeblock-end")
4583
4649
  });
4584
4650
  var commentBlockLine = fencedCodeLine;
4585
4651
  var commentBlockLineFirst = fencedCodeLineFirst;
4586
4652
  var commentBlockLineLast = fencedCodeLineLast;
4587
- var horizontalRule = Decoration7.replace({
4653
+ var horizontalRule = Decoration8.replace({
4588
4654
  widget: new HorizontalRuleWidget()
4589
4655
  });
4590
- var checkedTask = Decoration7.replace({
4656
+ var checkedTask = Decoration8.replace({
4591
4657
  widget: new CheckboxWidget(true)
4592
4658
  });
4593
- var uncheckedTask = Decoration7.replace({
4659
+ var uncheckedTask = Decoration8.replace({
4594
4660
  widget: new CheckboxWidget(false)
4595
4661
  });
4596
4662
  var editingRange = (state, range, focus2) => {
@@ -4606,8 +4672,8 @@ var autoHideTags = /* @__PURE__ */ new Set([
4606
4672
  "SuperscriptMark"
4607
4673
  ]);
4608
4674
  var buildDecorations2 = (view, options, focus2) => {
4609
- const deco = new RangeSetBuilder3();
4610
- const atomicDeco = new RangeSetBuilder3();
4675
+ const deco = new RangeSetBuilder5();
4676
+ const atomicDeco = new RangeSetBuilder5();
4611
4677
  const { state } = view;
4612
4678
  const headerLevels = [];
4613
4679
  const getHeaderLevels = (node, level) => {
@@ -4694,7 +4760,7 @@ var buildDecorations2 = (view, options, focus2) => {
4694
4760
  } else {
4695
4761
  const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4696
4762
  if (num.length) {
4697
- atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4763
+ atomicDeco.add(mark.from, mark.from + len, Decoration8.replace({
4698
4764
  widget: new TextWidget(num, theme.heading(level))
4699
4765
  }));
4700
4766
  }
@@ -4719,7 +4785,7 @@ var buildDecorations2 = (view, options, focus2) => {
4719
4785
  if (node.from === line.to - 1) {
4720
4786
  return false;
4721
4787
  }
4722
- deco.add(line.from, line.from, Decoration7.line({
4788
+ deco.add(line.from, line.from, Decoration8.line({
4723
4789
  class: "cm-list-item",
4724
4790
  attributes: {
4725
4791
  style: `padding-left: ${offset}px; text-indent: -${width}px;`
@@ -4736,7 +4802,7 @@ var buildDecorations2 = (view, options, focus2) => {
4736
4802
  const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
4737
4803
  const line = state.doc.lineAt(node.from);
4738
4804
  const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4739
- atomicDeco.add(line.from, to, Decoration7.replace({
4805
+ atomicDeco.add(line.from, to, Decoration8.replace({
4740
4806
  widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
4741
4807
  }));
4742
4808
  break;
@@ -4823,7 +4889,7 @@ var buildDecorations2 = (view, options, focus2) => {
4823
4889
  if (!editing) {
4824
4890
  atomicDeco.add(node.from, marks[0].to, hide);
4825
4891
  }
4826
- deco.add(marks[0].to, marks[1].from, Decoration7.mark({
4892
+ deco.add(marks[0].to, marks[1].from, Decoration8.mark({
4827
4893
  tagName: "a",
4828
4894
  attributes: {
4829
4895
  class: "cm-link",
@@ -4833,7 +4899,7 @@ var buildDecorations2 = (view, options, focus2) => {
4833
4899
  }
4834
4900
  }));
4835
4901
  if (!editing) {
4836
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
4902
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration8.replace({
4837
4903
  widget: new LinkButton(url, options.renderLinkButton)
4838
4904
  }) : hide);
4839
4905
  }
@@ -4891,7 +4957,7 @@ var buildDecorations2 = (view, options, focus2) => {
4891
4957
  var forceUpdate = StateEffect5.define();
4892
4958
  var decorateMarkdown = (options = {}) => {
4893
4959
  return [
4894
- ViewPlugin7.fromClass(class {
4960
+ ViewPlugin10.fromClass(class {
4895
4961
  constructor(view) {
4896
4962
  ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
4897
4963
  }
@@ -4923,9 +4989,9 @@ var decorateMarkdown = (options = {}) => {
4923
4989
  }
4924
4990
  }, {
4925
4991
  provide: (plugin) => [
4926
- EditorView18.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4927
- EditorView18.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4928
- EditorView18.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
4992
+ EditorView18.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration8.none),
4993
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration8.none),
4994
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration8.none)
4929
4995
  ]
4930
4996
  }),
4931
4997
  image(),
@@ -4970,65 +5036,748 @@ var linkTooltip = (renderTooltip) => {
4970
5036
  }
4971
5037
  };
4972
5038
  }
4973
- };
4974
- }, {
4975
- // NOTE: 0 = default of 300ms.
4976
- hoverTime: 1
5039
+ };
5040
+ }, {
5041
+ // NOTE: 0 = default of 300ms.
5042
+ hoverTime: 1
5043
+ });
5044
+ };
5045
+
5046
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
5047
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5048
+ import { log as log6 } from "@dxos/log";
5049
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5050
+ var mention = ({ debug, onSearch }) => {
5051
+ return autocompletion2({
5052
+ // TODO(burdon): Not working.
5053
+ activateOnTyping: true,
5054
+ // activateOnTypingDelay: 100,
5055
+ // selectOnOpen: true,
5056
+ closeOnBlur: !debug,
5057
+ // defaultKeymap: false,
5058
+ icons: false,
5059
+ override: [
5060
+ (context) => {
5061
+ log6.info("completion context", {
5062
+ context
5063
+ }, {
5064
+ F: __dxlog_file10,
5065
+ L: 27,
5066
+ S: void 0,
5067
+ C: (f, a) => f(...a)
5068
+ });
5069
+ const match = context.matchBefore(/@(\w+)?/);
5070
+ if (!match || match.from === match.to && !context.explicit) {
5071
+ return null;
5072
+ }
5073
+ return {
5074
+ from: match.from,
5075
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5076
+ label: `@${value}`
5077
+ }))
5078
+ };
5079
+ }
5080
+ ]
5081
+ });
5082
+ };
5083
+
5084
+ // packages/ui/react-ui-editor/src/extensions/modes.ts
5085
+ import { keymap as keymap10 } from "@codemirror/view";
5086
+ import { vim } from "@replit/codemirror-vim";
5087
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5088
+ import { Schema } from "effect";
5089
+ var EditorViewModes = [
5090
+ "preview",
5091
+ "readonly",
5092
+ "source"
5093
+ ];
5094
+ var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5095
+ var EditorInputModes = [
5096
+ "default",
5097
+ "vim",
5098
+ "vscode"
5099
+ ];
5100
+ var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5101
+ var editorInputMode = singleValueFacet({});
5102
+ var InputModeExtensions = {
5103
+ default: [],
5104
+ vscode: [
5105
+ // https://github.com/replit/codemirror-vscode-keymap
5106
+ editorInputMode.of({
5107
+ type: "vscode"
5108
+ }),
5109
+ keymap10.of(vscodeKeymap)
5110
+ ],
5111
+ vim: [
5112
+ // https://github.com/replit/codemirror-vim
5113
+ vim(),
5114
+ editorInputMode.of({
5115
+ type: "vim",
5116
+ noTabster: true
5117
+ }),
5118
+ keymap10.of([
5119
+ {
5120
+ key: "Alt-Escape",
5121
+ run: (view) => {
5122
+ view.dom.parentElement?.focus();
5123
+ return true;
5124
+ }
5125
+ }
5126
+ ])
5127
+ ]
5128
+ };
5129
+
5130
+ // packages/ui/react-ui-editor/src/extensions/outliner/outliner.ts
5131
+ import { Prec as Prec2 } from "@codemirror/state";
5132
+ import { Decoration as Decoration9, EditorView as EditorView19, ViewPlugin as ViewPlugin12 } from "@codemirror/view";
5133
+ import { mx as mx3 } from "@dxos/react-ui-theme";
5134
+
5135
+ // packages/ui/react-ui-editor/src/extensions/outliner/commands.ts
5136
+ import { indentMore } from "@codemirror/commands";
5137
+ import { getIndentUnit } from "@codemirror/language";
5138
+ import { EditorSelection as EditorSelection3 } from "@codemirror/state";
5139
+ import { keymap as keymap11 } from "@codemirror/view";
5140
+
5141
+ // packages/ui/react-ui-editor/src/extensions/outliner/selection.ts
5142
+ import { Compartment, Facet as Facet3 } from "@codemirror/state";
5143
+
5144
+ // packages/ui/react-ui-editor/src/extensions/outliner/tree.ts
5145
+ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5146
+ import { StateField as StateField9 } from "@codemirror/state";
5147
+ import { Facet as Facet2 } from "@codemirror/state";
5148
+ import { invariant as invariant5 } from "@dxos/invariant";
5149
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/outliner/tree.ts";
5150
+ var itemToJSON = ({ type, index, level, lineRange, contentRange, children }) => {
5151
+ return {
5152
+ type,
5153
+ index,
5154
+ level,
5155
+ lineRange,
5156
+ contentRange,
5157
+ children: children.map(itemToJSON)
5158
+ };
5159
+ };
5160
+ var Tree = class {
5161
+ constructor(node) {
5162
+ this.type = "root";
5163
+ this.index = -1;
5164
+ this.level = -1;
5165
+ this.children = [];
5166
+ this.node = node;
5167
+ this.lineRange = {
5168
+ from: node.from,
5169
+ to: node.to
5170
+ };
5171
+ this.contentRange = this.lineRange;
5172
+ }
5173
+ toJSON() {
5174
+ return itemToJSON(this);
5175
+ }
5176
+ get root() {
5177
+ return this;
5178
+ }
5179
+ traverse(itemOrCb, maybeCb) {
5180
+ if (typeof itemOrCb === "function") {
5181
+ return traverse(this, itemOrCb);
5182
+ } else {
5183
+ return traverse(itemOrCb, maybeCb);
5184
+ }
5185
+ }
5186
+ /**
5187
+ * Return the closest item.
5188
+ */
5189
+ find(pos) {
5190
+ return this.traverse((item) => item.lineRange.from <= pos && item.lineRange.to >= pos ? item : void 0);
5191
+ }
5192
+ /**
5193
+ * Return the first child, next sibling, or parent's next sibling.
5194
+ */
5195
+ next(item, enter = true) {
5196
+ if (enter && item.children.length > 0) {
5197
+ return item.children[0];
5198
+ }
5199
+ if (item.nextSibling) {
5200
+ return item.nextSibling;
5201
+ }
5202
+ if (item.parent) {
5203
+ return this.next(item.parent, false);
5204
+ }
5205
+ return void 0;
5206
+ }
5207
+ /**
5208
+ * Return the previous sibling, or parent.
5209
+ */
5210
+ prev(item) {
5211
+ if (item.prevSibling) {
5212
+ return this.lastDescendant(item.prevSibling);
5213
+ }
5214
+ return item.parent?.type === "root" ? void 0 : item.parent;
5215
+ }
5216
+ /**
5217
+ * Return the last descendant of the item, or the item itself if it has no children.
5218
+ */
5219
+ lastDescendant(item) {
5220
+ return item.children.length > 0 ? this.lastDescendant(item.children.at(-1)) : item;
5221
+ }
5222
+ };
5223
+ var getRange = (tree, item) => {
5224
+ const lastDescendant = tree.lastDescendant(item);
5225
+ return [
5226
+ item.lineRange.from,
5227
+ lastDescendant.lineRange.to
5228
+ ];
5229
+ };
5230
+ var traverse = (root, cb) => {
5231
+ const t = (item, level) => {
5232
+ if (item.type !== "root") {
5233
+ const value = cb(item, level);
5234
+ if (value != null) {
5235
+ return value;
5236
+ }
5237
+ }
5238
+ for (const child of item.children) {
5239
+ const value = t(child, level + 1);
5240
+ if (value != null) {
5241
+ return value;
5242
+ }
5243
+ }
5244
+ };
5245
+ return t(root, root.type === "root" ? -1 : 0);
5246
+ };
5247
+ var getListItemContent = (state, item) => {
5248
+ return state.doc.sliceString(item.contentRange.from, item.contentRange.to);
5249
+ };
5250
+ var listItemToString = (item, level = 0) => {
5251
+ const indent = " ".repeat(level);
5252
+ const data = {
5253
+ i: item.index,
5254
+ n: item.nextSibling?.index ?? "\u2205",
5255
+ p: item.prevSibling?.index ?? "\u2205",
5256
+ level: item.level,
5257
+ node: format([
5258
+ item.node.from,
5259
+ item.node.to
5260
+ ]),
5261
+ line: format([
5262
+ item.lineRange.from,
5263
+ item.lineRange.to
5264
+ ]),
5265
+ content: format([
5266
+ item.contentRange.from,
5267
+ item.contentRange.to
5268
+ ])
5269
+ };
5270
+ return `${indent}${item.type[0].toUpperCase()}(${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(", ")})`;
5271
+ };
5272
+ var format = (value) => JSON.stringify(value, (key, value2) => {
5273
+ if (typeof value2 === "number") {
5274
+ return value2.toString().padStart(3, " ");
5275
+ }
5276
+ return value2;
5277
+ }).replaceAll('"', "");
5278
+ var treeFacet = Facet2.define({
5279
+ combine: (values) => values[0]
5280
+ });
5281
+ var outlinerTree = (options = {}) => {
5282
+ const buildTree = (state) => {
5283
+ let tree;
5284
+ let parent;
5285
+ let current;
5286
+ let prev;
5287
+ let level = -1;
5288
+ let index = -1;
5289
+ const prevSiblings = [];
5290
+ syntaxTree9(state).iterate({
5291
+ enter: (node) => {
5292
+ switch (node.name) {
5293
+ case "Document": {
5294
+ tree = new Tree(node.node);
5295
+ current = tree;
5296
+ break;
5297
+ }
5298
+ case "BulletList": {
5299
+ invariant5(current, void 0, {
5300
+ F: __dxlog_file11,
5301
+ L: 217,
5302
+ S: void 0,
5303
+ A: [
5304
+ "current",
5305
+ ""
5306
+ ]
5307
+ });
5308
+ parent = current;
5309
+ if (current) {
5310
+ current.lineRange.to = current.node.from;
5311
+ }
5312
+ prevSiblings[++level] = void 0;
5313
+ break;
5314
+ }
5315
+ case "ListItem": {
5316
+ invariant5(parent, void 0, {
5317
+ F: __dxlog_file11,
5318
+ L: 226,
5319
+ S: void 0,
5320
+ A: [
5321
+ "parent",
5322
+ ""
5323
+ ]
5324
+ });
5325
+ const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5326
+ const docRange = {
5327
+ from: state.doc.lineAt(node.from).from,
5328
+ to: nextSibling ? nextSibling.from - 1 : state.doc.length
5329
+ };
5330
+ current = {
5331
+ type: "unknown",
5332
+ index: ++index,
5333
+ level,
5334
+ node: node.node,
5335
+ lineRange: docRange,
5336
+ contentRange: {
5337
+ ...docRange
5338
+ },
5339
+ parent,
5340
+ prevSibling: prevSiblings[level],
5341
+ children: []
5342
+ };
5343
+ if (current.prevSibling) {
5344
+ current.prevSibling.nextSibling = current;
5345
+ }
5346
+ prevSiblings[level] = current;
5347
+ if (prev) {
5348
+ prev.lineRange.to = prev.contentRange.to = current.lineRange.from - 1;
5349
+ }
5350
+ prev = current;
5351
+ parent.children.push(current);
5352
+ if (parent.lineRange.to === parent.node.from) {
5353
+ parent.lineRange.to = parent.contentRange.to = current.lineRange.from - 1;
5354
+ }
5355
+ break;
5356
+ }
5357
+ case "ListMark": {
5358
+ invariant5(current, void 0, {
5359
+ F: __dxlog_file11,
5360
+ L: 270,
5361
+ S: void 0,
5362
+ A: [
5363
+ "current",
5364
+ ""
5365
+ ]
5366
+ });
5367
+ current.type = "bullet";
5368
+ current.contentRange.from = node.from + "- ".length;
5369
+ break;
5370
+ }
5371
+ case "Task": {
5372
+ invariant5(current, void 0, {
5373
+ F: __dxlog_file11,
5374
+ L: 276,
5375
+ S: void 0,
5376
+ A: [
5377
+ "current",
5378
+ ""
5379
+ ]
5380
+ });
5381
+ current.type = "task";
5382
+ break;
5383
+ }
5384
+ case "TaskMarker": {
5385
+ invariant5(current, void 0, {
5386
+ F: __dxlog_file11,
5387
+ L: 281,
5388
+ S: void 0,
5389
+ A: [
5390
+ "current",
5391
+ ""
5392
+ ]
5393
+ });
5394
+ current.contentRange.from = node.from + "[ ] ".length;
5395
+ break;
5396
+ }
5397
+ }
5398
+ },
5399
+ leave: (node) => {
5400
+ if (node.name === "BulletList") {
5401
+ invariant5(parent, void 0, {
5402
+ F: __dxlog_file11,
5403
+ L: 289,
5404
+ S: void 0,
5405
+ A: [
5406
+ "parent",
5407
+ ""
5408
+ ]
5409
+ });
5410
+ prevSiblings[level--] = void 0;
5411
+ parent = parent.parent;
5412
+ }
5413
+ }
5414
+ });
5415
+ invariant5(tree, void 0, {
5416
+ F: __dxlog_file11,
5417
+ L: 296,
5418
+ S: void 0,
5419
+ A: [
5420
+ "tree",
5421
+ ""
5422
+ ]
5423
+ });
5424
+ return tree;
5425
+ };
5426
+ return [
5427
+ StateField9.define({
5428
+ create: (state) => {
5429
+ return buildTree(state);
5430
+ },
5431
+ update: (value, tr) => {
5432
+ if (!tr.docChanged) {
5433
+ return value;
5434
+ }
5435
+ return buildTree(tr.state);
5436
+ },
5437
+ provide: (field) => treeFacet.from(field)
5438
+ })
5439
+ ];
5440
+ };
5441
+
5442
+ // packages/ui/react-ui-editor/src/extensions/outliner/selection.ts
5443
+ var getSelection = (state) => state.selection.main;
5444
+ var selectionEquals = (a, b) => a.length === b.length && a.every((i) => b.includes(i));
5445
+ var selectionFacet = Facet3.define({
5446
+ combine: (values) => values[0]
5447
+ });
5448
+ var selectionCompartment = new Compartment();
5449
+ var selectNone = (view) => {
5450
+ view.dispatch({
5451
+ effects: selectionCompartment.reconfigure(selectionFacet.of([]))
5452
+ });
5453
+ return true;
5454
+ };
5455
+ var selectAll = (view) => {
5456
+ const tree = view.state.facet(treeFacet);
5457
+ const selection = view.state.facet(selectionFacet);
5458
+ const items = [];
5459
+ tree.traverse((item) => items.push(item.index));
5460
+ view.dispatch({
5461
+ effects: selectionCompartment.reconfigure(selectionFacet.of(selectionEquals(selection, items) ? [] : items))
5462
+ });
5463
+ return true;
5464
+ };
5465
+ var selectUp = (view) => {
5466
+ return true;
5467
+ };
5468
+ var selectDown = (view) => {
5469
+ return true;
5470
+ };
5471
+
5472
+ // packages/ui/react-ui-editor/src/extensions/outliner/commands.ts
5473
+ var indentItemMore = (view) => {
5474
+ const pos = getSelection(view.state).from;
5475
+ const tree = view.state.facet(treeFacet);
5476
+ const current = tree.find(pos);
5477
+ if (current) {
5478
+ const previous = tree.prev(current);
5479
+ if (previous && current.level <= previous.level) {
5480
+ indentMore(view);
5481
+ }
5482
+ }
5483
+ return true;
5484
+ };
5485
+ var indentItemLess = (view) => {
5486
+ const pos = getSelection(view.state).from;
5487
+ const tree = view.state.facet(treeFacet);
5488
+ const current = tree.find(pos);
5489
+ if (current) {
5490
+ if (current.level > 0) {
5491
+ const indentUnit = getIndentUnit(view.state);
5492
+ const changes = [];
5493
+ tree.traverse(current, (item) => {
5494
+ const line = view.state.doc.lineAt(item.lineRange.from);
5495
+ changes.push({
5496
+ from: line.from,
5497
+ to: line.from + indentUnit
5498
+ });
5499
+ });
5500
+ if (changes.length > 0) {
5501
+ view.dispatch({
5502
+ changes
5503
+ });
5504
+ }
5505
+ }
5506
+ }
5507
+ return true;
5508
+ };
5509
+ var moveItemDown = (view) => {
5510
+ const pos = getSelection(view.state)?.from;
5511
+ const tree = view.state.facet(treeFacet);
5512
+ const current = tree.find(pos);
5513
+ if (current && current.nextSibling) {
5514
+ const next = current.nextSibling;
5515
+ const currentContent = view.state.doc.sliceString(...getRange(tree, current));
5516
+ const nextContent = view.state.doc.sliceString(...getRange(tree, next));
5517
+ const changes = [
5518
+ {
5519
+ from: current.lineRange.from,
5520
+ to: current.lineRange.from + currentContent.length,
5521
+ insert: nextContent
5522
+ },
5523
+ {
5524
+ from: next.lineRange.from,
5525
+ to: next.lineRange.from + nextContent.length,
5526
+ insert: currentContent
5527
+ }
5528
+ ];
5529
+ view.dispatch({
5530
+ changes,
5531
+ selection: EditorSelection3.cursor(pos + nextContent.length + 1),
5532
+ scrollIntoView: true
5533
+ });
5534
+ }
5535
+ return true;
5536
+ };
5537
+ var moveItemUp = (view) => {
5538
+ const pos = getSelection(view.state)?.from;
5539
+ const tree = view.state.facet(treeFacet);
5540
+ const current = tree.find(pos);
5541
+ if (current && current.prevSibling) {
5542
+ const prev = current.prevSibling;
5543
+ const currentContent = view.state.doc.sliceString(...getRange(tree, current));
5544
+ const prevContent = view.state.doc.sliceString(...getRange(tree, prev));
5545
+ const changes = [
5546
+ {
5547
+ from: prev.lineRange.from,
5548
+ to: prev.lineRange.from + prevContent.length,
5549
+ insert: currentContent
5550
+ },
5551
+ {
5552
+ from: current.lineRange.from,
5553
+ to: current.lineRange.from + currentContent.length,
5554
+ insert: prevContent
5555
+ }
5556
+ ];
5557
+ view.dispatch({
5558
+ changes,
5559
+ selection: EditorSelection3.cursor(pos - prevContent.length - 1),
5560
+ scrollIntoView: true
5561
+ });
5562
+ }
5563
+ return true;
5564
+ };
5565
+ var toggleTask = (view) => {
5566
+ const pos = getSelection(view.state)?.from;
5567
+ const tree = view.state.facet(treeFacet);
5568
+ const current = tree.find(pos);
5569
+ if (current) {
5570
+ const type = current.type === "task" ? "bullet" : "task";
5571
+ const indent = " ".repeat(getIndentUnit(view.state) * current.level);
5572
+ view.dispatch({
5573
+ changes: [
5574
+ {
5575
+ from: current.lineRange.from,
5576
+ to: current.contentRange.from,
5577
+ insert: indent + (type === "task" ? "- [ ] " : "- ")
5578
+ }
5579
+ ]
5580
+ });
5581
+ }
5582
+ return true;
5583
+ };
5584
+ var commands = () => keymap11.of([
5585
+ //
5586
+ // Indentation.
5587
+ //
5588
+ {
5589
+ key: "Tab",
5590
+ preventDefault: true,
5591
+ run: indentItemMore,
5592
+ shift: indentItemLess
5593
+ },
5594
+ //
5595
+ // Continuation.
5596
+ //
5597
+ {
5598
+ key: "Enter",
5599
+ shift: (view) => {
5600
+ const pos = getSelection(view.state).from;
5601
+ const insert = "\n ";
5602
+ view.dispatch({
5603
+ changes: [
5604
+ {
5605
+ from: pos,
5606
+ to: pos,
5607
+ insert
5608
+ }
5609
+ ],
5610
+ selection: EditorSelection3.cursor(pos + insert.length)
5611
+ });
5612
+ return true;
5613
+ }
5614
+ },
5615
+ //
5616
+ // Navigation.
5617
+ //
5618
+ {
5619
+ key: "ArrowDown",
5620
+ // Jump to next item (default moves to end of currentline).
5621
+ run: (view) => {
5622
+ const tree = view.state.facet(treeFacet);
5623
+ const item = tree.find(getSelection(view.state).from);
5624
+ if (item && view.state.doc.lineAt(item.lineRange.to).number - view.state.doc.lineAt(item.lineRange.from).number === 0) {
5625
+ const next = tree.next(item);
5626
+ if (next) {
5627
+ view.dispatch({
5628
+ selection: EditorSelection3.cursor(next.contentRange.from)
5629
+ });
5630
+ return true;
5631
+ }
5632
+ }
5633
+ return false;
5634
+ }
5635
+ },
5636
+ //
5637
+ // Line selection.
5638
+ // TODO(burdon): Shortcut to select current item?
5639
+ //
5640
+ {
5641
+ key: "Mod-a",
5642
+ preventDefault: true,
5643
+ run: selectAll
5644
+ },
5645
+ {
5646
+ key: "Escape",
5647
+ preventDefault: true,
5648
+ run: selectNone
5649
+ },
5650
+ {
5651
+ key: "ArrowUp",
5652
+ shift: selectUp
5653
+ },
5654
+ {
5655
+ key: "ArrowDown",
5656
+ shift: selectDown
5657
+ },
5658
+ //
5659
+ // Move.
5660
+ //
5661
+ {
5662
+ key: "Alt-ArrowDown",
5663
+ preventDefault: true,
5664
+ run: moveItemDown
5665
+ },
5666
+ {
5667
+ key: "Alt-ArrowUp",
5668
+ preventDefault: true,
5669
+ run: moveItemUp
5670
+ },
5671
+ //
5672
+ // Misc.
5673
+ //
5674
+ {
5675
+ key: "Alt-t",
5676
+ run: toggleTask
5677
+ }
5678
+ ]);
5679
+
5680
+ // packages/ui/react-ui-editor/src/extensions/outliner/editor.ts
5681
+ import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
5682
+ import { ViewPlugin as ViewPlugin11 } from "@codemirror/view";
5683
+ import { log as log7 } from "@dxos/log";
5684
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/outliner/editor.ts";
5685
+ var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
5686
+ var initialize = () => {
5687
+ return ViewPlugin11.fromClass(class {
5688
+ constructor(view) {
5689
+ const first = view.state.doc.lineAt(0);
5690
+ const text = view.state.sliceDoc(first.from, first.to);
5691
+ const match = text.match(LIST_ITEM_REGEX);
5692
+ if (!match) {
5693
+ setTimeout(() => {
5694
+ const insert = "- [ ] ";
5695
+ view.dispatch({
5696
+ changes: [
5697
+ {
5698
+ from: 0,
5699
+ to: 0,
5700
+ insert
5701
+ }
5702
+ ],
5703
+ selection: EditorSelection4.cursor(insert.length)
5704
+ });
5705
+ });
5706
+ }
5707
+ }
4977
5708
  });
4978
5709
  };
4979
-
4980
- // packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts
4981
- import { syntaxTree as syntaxTree9 } from "@codemirror/language";
4982
- import { StateField as StateField10, EditorState as EditorState2 } from "@codemirror/state";
4983
- import { Decoration as Decoration8, EditorView as EditorView19 } from "@codemirror/view";
4984
- import { log as log6 } from "@dxos/log";
4985
- import { mx as mx3 } from "@dxos/react-ui-theme";
4986
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts";
4987
- var indentLevel = 2;
4988
- var matchTaskMarker = /^\s*- (\[ \]|\[x\])? /;
4989
- var getLineInfo = (line) => {
4990
- const match = line.text.match(matchTaskMarker);
4991
- const start = line.from + (match?.[0]?.length ?? 0);
4992
- return {
4993
- match,
4994
- start
4995
- };
4996
- };
4997
- var outliner = () => [
5710
+ var editor = () => [
5711
+ initialize(),
4998
5712
  EditorState2.transactionFilter.of((tr) => {
5713
+ const tree = tr.state.facet(treeFacet);
4999
5714
  if (!tr.docChanged) {
5000
- const pos = tr.selection?.ranges[tr.selection?.mainIndex]?.from;
5001
- if (pos != null) {
5002
- const { match, start } = getLineInfo(tr.startState.doc.lineAt(pos));
5003
- if (match) {
5004
- if (pos < start) {
5715
+ const current = getSelection(tr.state).from;
5716
+ if (current != null) {
5717
+ const currentItem = tree.find(current);
5718
+ if (!currentItem) {
5719
+ return [];
5720
+ }
5721
+ if (current < currentItem.contentRange.from || current > currentItem.contentRange.to) {
5722
+ const prev = getSelection(tr.startState).from;
5723
+ const prevItem = prev != null ? tree.find(prev) : void 0;
5724
+ if (!prevItem) {
5005
5725
  return [
5006
5726
  {
5007
- selection: {
5008
- anchor: start,
5009
- head: start
5010
- }
5727
+ selection: EditorSelection4.cursor(currentItem.contentRange.from)
5011
5728
  }
5012
5729
  ];
5730
+ } else {
5731
+ if (currentItem.index < prevItem.index) {
5732
+ return [
5733
+ {
5734
+ selection: EditorSelection4.cursor(currentItem.contentRange.to)
5735
+ }
5736
+ ];
5737
+ } else if (currentItem.index > prevItem.index) {
5738
+ return [
5739
+ {
5740
+ selection: EditorSelection4.cursor(currentItem.contentRange.from)
5741
+ }
5742
+ ];
5743
+ } else {
5744
+ if (current < prev) {
5745
+ if (currentItem.index === 0) {
5746
+ return [];
5747
+ } else {
5748
+ return [
5749
+ {
5750
+ selection: EditorSelection4.cursor(currentItem.lineRange.from - 1)
5751
+ }
5752
+ ];
5753
+ }
5754
+ } else {
5755
+ return [
5756
+ {
5757
+ selection: EditorSelection4.cursor(currentItem.contentRange.to)
5758
+ }
5759
+ ];
5760
+ }
5761
+ }
5013
5762
  }
5014
5763
  }
5015
5764
  }
5016
5765
  return tr;
5017
5766
  }
5767
+ let cancel = false;
5018
5768
  const changes = [];
5019
5769
  tr.changes.iterChanges((fromA, toA, fromB, toB, insert) => {
5020
5770
  const line = tr.startState.doc.lineAt(fromA);
5021
- const isTaskMarker = line.text.match(matchTaskMarker);
5022
- if (isTaskMarker) {
5023
- const { start } = getLineInfo(line);
5771
+ const match = line.text.match(LIST_ITEM_REGEX);
5772
+ if (match) {
5773
+ const currentItem = tree.find(tr.state.selection.main.from);
5774
+ if (!currentItem?.contentRange) {
5775
+ cancel = true;
5776
+ return;
5777
+ }
5778
+ const start = line.from + (match?.[0]?.length ?? 0);
5024
5779
  const replace = start === toA && toA - fromA === insert.length;
5025
5780
  if (replace) {
5026
- log6.info("delete line", void 0, {
5027
- F: __dxlog_file10,
5028
- L: 82,
5029
- S: void 0,
5030
- C: (f, a) => f(...a)
5031
- });
5032
5781
  changes.push({
5033
5782
  from: line.from - 1,
5034
5783
  to: toA
@@ -5040,24 +5789,9 @@ var outliner = () => [
5040
5789
  const line2 = tr.state.doc.lineAt(fromA);
5041
5790
  if (line2.text.match(/^\s*$/)) {
5042
5791
  if (line2.from === 0) {
5043
- log6.info("skip", void 0, {
5044
- F: __dxlog_file10,
5045
- L: 94,
5046
- S: void 0,
5047
- C: (f, a) => f(...a)
5048
- });
5049
- changes.push({
5050
- from: 0,
5051
- to: 0
5052
- });
5792
+ cancel = true;
5053
5793
  return;
5054
5794
  } else {
5055
- log6.info("delete line", void 0, {
5056
- F: __dxlog_file10,
5057
- L: 99,
5058
- S: void 0,
5059
- C: (f, a) => f(...a)
5060
- });
5061
5795
  changes.push({
5062
5796
  from: line2.from - 1,
5063
5797
  to: toA
@@ -5068,44 +5802,17 @@ var outliner = () => [
5068
5802
  }
5069
5803
  return;
5070
5804
  }
5071
- if (insert.length === indentLevel) {
5072
- if (line.number === 1) {
5073
- log6.info("skip", void 0, {
5074
- F: __dxlog_file10,
5075
- L: 111,
5076
- S: void 0,
5077
- C: (f, a) => f(...a)
5078
- });
5079
- changes.push({
5080
- from: 0,
5081
- to: 0
5082
- });
5083
- return;
5084
- } else {
5085
- const getIndent = (text) => (text.match(/^\s*/)?.[0]?.length ?? 0) / indentLevel;
5086
- const currentIndent = getIndent(line.text);
5087
- const indentPrevious = getIndent(tr.state.doc.lineAt(fromA - 1).text);
5088
- if (currentIndent > indentPrevious) {
5089
- log6.info("skip", void 0, {
5090
- F: __dxlog_file10,
5091
- L: 119,
5092
- S: void 0,
5093
- C: (f, a) => f(...a)
5094
- });
5095
- changes.push({
5096
- from: 0,
5097
- to: 0
5098
- });
5099
- return;
5100
- }
5101
- }
5805
+ const item = tree.find(fromA);
5806
+ if (item?.contentRange.from === item?.contentRange.to && fromA === toA) {
5807
+ cancel = true;
5808
+ return;
5102
5809
  }
5103
- log6.info("change", {
5810
+ log7("change", {
5811
+ item,
5104
5812
  line: {
5105
5813
  from: line.from,
5106
5814
  to: line.to
5107
5815
  },
5108
- start,
5109
5816
  a: [
5110
5817
  fromA,
5111
5818
  toA
@@ -5119,194 +5826,144 @@ var outliner = () => [
5119
5826
  length: insert.length
5120
5827
  }
5121
5828
  }, {
5122
- F: __dxlog_file10,
5123
- L: 134,
5829
+ F: __dxlog_file12,
5830
+ L: 160,
5124
5831
  S: void 0,
5125
5832
  C: (f, a) => f(...a)
5126
5833
  });
5127
5834
  }
5128
5835
  });
5129
5836
  if (changes.length > 0) {
5837
+ log7("modified,", {
5838
+ changes
5839
+ }, {
5840
+ F: __dxlog_file12,
5841
+ L: 171,
5842
+ S: void 0,
5843
+ C: (f, a) => f(...a)
5844
+ });
5130
5845
  return [
5131
5846
  {
5132
5847
  changes
5133
5848
  }
5134
5849
  ];
5850
+ } else if (cancel) {
5851
+ log7("cancel", void 0, {
5852
+ F: __dxlog_file12,
5853
+ L: 174,
5854
+ S: void 0,
5855
+ C: (f, a) => f(...a)
5856
+ });
5857
+ return [];
5135
5858
  }
5136
5859
  return tr;
5860
+ })
5861
+ ];
5862
+
5863
+ // packages/ui/react-ui-editor/src/extensions/outliner/outliner.ts
5864
+ var outliner = () => [
5865
+ // Commands.
5866
+ Prec2.highest(commands()),
5867
+ // Selection.
5868
+ selectionCompartment.of(selectionFacet.of([])),
5869
+ // State.
5870
+ outlinerTree(),
5871
+ // Filter and possibly modify changes.
5872
+ editor(),
5873
+ // Floating menu.
5874
+ floatingMenu(),
5875
+ // Line decorations.
5876
+ decorations(),
5877
+ // Default markdown decorations.
5878
+ decorateMarkdown({
5879
+ listPaddingLeft: 8
5137
5880
  }),
5138
- StateField10.define({
5139
- create: (state) => {
5140
- return Decoration8.set(buildDecorations3(0, state.doc.length, state));
5141
- },
5142
- update: (value, tr) => {
5143
- const from = 0;
5144
- const to = tr.state.doc.length;
5145
- return value.map(tr.changes).update({
5146
- filterFrom: 0,
5147
- filterTo: tr.state.doc.length,
5148
- filter: () => false,
5149
- add: buildDecorations3(from, to, tr.state)
5150
- });
5151
- },
5152
- provide: (field) => EditorView19.decorations.from(field)
5881
+ // Researve space for menu.
5882
+ EditorView19.contentAttributes.of({
5883
+ class: "is-full !mr-[3rem]"
5884
+ })
5885
+ ];
5886
+ var decorations = () => [
5887
+ ViewPlugin12.fromClass(class {
5888
+ constructor(view) {
5889
+ this.decorations = Decoration9.none;
5890
+ this.updateDecorations(view.state, view);
5891
+ }
5892
+ update(update2) {
5893
+ const selectionChanged = !selectionEquals(update2.state.facet(selectionFacet), update2.startState.facet(selectionFacet));
5894
+ if (update2.focusChanged || update2.docChanged || update2.viewportChanged || update2.selectionSet || selectionChanged) {
5895
+ this.updateDecorations(update2.state, update2.view);
5896
+ }
5897
+ }
5898
+ updateDecorations(state, { viewport: { from, to }, hasFocus }) {
5899
+ const selection = state.facet(selectionFacet);
5900
+ const tree = state.facet(treeFacet);
5901
+ const current = tree.find(state.selection.ranges[state.selection.mainIndex]?.from);
5902
+ const doc = state.doc;
5903
+ const decorations2 = [];
5904
+ for (let lineNum = doc.lineAt(from).number; lineNum <= doc.lineAt(to).number; lineNum++) {
5905
+ const line = doc.line(lineNum);
5906
+ const item = tree.find(line.from);
5907
+ if (item) {
5908
+ const lineFrom = doc.lineAt(item.contentRange.from);
5909
+ const lineTo = doc.lineAt(item.contentRange.to);
5910
+ const isSelected = selection.includes(item.index) || item === current;
5911
+ decorations2.push(Decoration9.line({
5912
+ class: mx3("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
5913
+ }).range(line.from, line.from));
5914
+ }
5915
+ }
5916
+ this.decorations = Decoration9.set(decorations2);
5917
+ }
5918
+ }, {
5919
+ decorations: (v) => v.decorations
5153
5920
  }),
5154
- // TODO(burdon): Increase indent padding by configuring decorate extension.
5155
- // TODO(burdon): Hover to select entire group.
5921
+ // Theme.
5156
5922
  EditorView19.theme({
5923
+ ".cm-list-item": {
5924
+ borderLeftWidth: "1px",
5925
+ borderRightWidth: "1px",
5926
+ paddingLeft: "32px",
5927
+ borderColor: "transparent"
5928
+ },
5929
+ ".cm-list-item.cm-codeblock-start": {
5930
+ borderRadius: "0"
5931
+ },
5157
5932
  ".cm-list-item-start": {
5158
- borderTop: "1px solid var(--dx-separator)",
5159
- borderLeft: "1px solid var(--dx-separator)",
5160
- borderRight: "1px solid var(--dx-separator)",
5933
+ borderTopWidth: "1px",
5161
5934
  borderTopLeftRadius: "4px",
5162
5935
  borderTopRightRadius: "4px",
5163
5936
  paddingTop: "4px",
5164
5937
  marginTop: "8px"
5165
5938
  },
5166
5939
  ".cm-list-item-end": {
5167
- borderLeft: "1px solid var(--dx-separator)",
5168
- borderRight: "1px solid var(--dx-separator)",
5169
- borderBottom: "1px solid var(--dx-separator)",
5940
+ borderBottomWidth: "1px",
5170
5941
  borderBottomLeftRadius: "4px",
5171
5942
  borderBottomRightRadius: "4px",
5172
5943
  paddingBottom: "4px",
5173
5944
  marginBottom: "8px"
5174
5945
  },
5175
- ".cm-list-item-continuation": {
5176
- borderLeft: "1px solid var(--dx-separator)",
5177
- borderRight: "1px solid var(--dx-separator)",
5178
- // TODO(burdon): Should match parent indentation.
5179
- paddingLeft: "24px"
5946
+ ".cm-list-item-selected": {
5947
+ borderColor: "var(--dx-separator)"
5180
5948
  },
5181
- // TODO(burdon): Set via options to decorate extension.
5182
- ".cm-list-item-continuation.cm-codeblock-start": {
5183
- borderRadius: "0"
5949
+ ".cm-list-item-focused": {
5950
+ borderColor: "var(--dx-accentFocusIndicator)"
5184
5951
  }
5185
5952
  })
5186
5953
  ];
5187
- var buildDecorations3 = (from, to, state) => {
5188
- const decorations = [];
5189
- syntaxTree9(state).iterate({
5190
- enter: (node) => {
5191
- if (node.name === "ListItem") {
5192
- const sub = node.node.getChild("BulletList");
5193
- const lineStart = state.doc.lineAt(node.from);
5194
- const lineEnd = sub ? state.doc.lineAt(state.doc.lineAt(sub.from).from - 1) : state.doc.lineAt(node.to);
5195
- decorations.push(Decoration8.line({
5196
- class: mx3("cm-list-item-start", lineStart.number === lineEnd.number && "cm-list-item-end")
5197
- }).range(lineStart.from, lineStart.from));
5198
- for (let i = lineStart.from + 1; i < lineEnd.from; i++) {
5199
- decorations.push(Decoration8.line({
5200
- class: mx3("cm-list-item-continuation")
5201
- }).range(i, i));
5202
- }
5203
- if (lineStart.number !== lineEnd.number) {
5204
- decorations.push(Decoration8.line({
5205
- class: mx3("cm-list-item-end")
5206
- }).range(lineEnd.from, lineEnd.from));
5207
- }
5208
- }
5209
- }
5210
- });
5211
- return decorations;
5212
- };
5213
-
5214
- // packages/ui/react-ui-editor/src/extensions/mention.ts
5215
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5216
- import { log as log7 } from "@dxos/log";
5217
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5218
- var mention = ({ debug, onSearch }) => {
5219
- return autocompletion2({
5220
- // TODO(burdon): Not working.
5221
- activateOnTyping: true,
5222
- // activateOnTypingDelay: 100,
5223
- // selectOnOpen: true,
5224
- closeOnBlur: !debug,
5225
- // defaultKeymap: false,
5226
- icons: false,
5227
- override: [
5228
- (context) => {
5229
- log7.info("completion context", {
5230
- context
5231
- }, {
5232
- F: __dxlog_file11,
5233
- L: 27,
5234
- S: void 0,
5235
- C: (f, a) => f(...a)
5236
- });
5237
- const match = context.matchBefore(/@(\w+)?/);
5238
- if (!match || match.from === match.to && !context.explicit) {
5239
- return null;
5240
- }
5241
- return {
5242
- from: match.from,
5243
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5244
- label: `@${value}`
5245
- }))
5246
- };
5247
- }
5248
- ]
5249
- });
5250
- };
5251
-
5252
- // packages/ui/react-ui-editor/src/extensions/modes.ts
5253
- import { keymap as keymap9 } from "@codemirror/view";
5254
- import { vim } from "@replit/codemirror-vim";
5255
- import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5256
- import { Schema } from "effect";
5257
- var EditorViewModes = [
5258
- "preview",
5259
- "readonly",
5260
- "source"
5261
- ];
5262
- var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5263
- var EditorInputModes = [
5264
- "default",
5265
- "vim",
5266
- "vscode"
5267
- ];
5268
- var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5269
- var editorInputMode = singleValueFacet({});
5270
- var InputModeExtensions = {
5271
- default: [],
5272
- vscode: [
5273
- // https://github.com/replit/codemirror-vscode-keymap
5274
- editorInputMode.of({
5275
- type: "vscode"
5276
- }),
5277
- keymap9.of(vscodeKeymap)
5278
- ],
5279
- vim: [
5280
- // https://github.com/replit/codemirror-vim
5281
- vim(),
5282
- editorInputMode.of({
5283
- type: "vim",
5284
- noTabster: true
5285
- }),
5286
- keymap9.of([
5287
- {
5288
- key: "Alt-Escape",
5289
- run: (view) => {
5290
- view.dom.parentElement?.focus();
5291
- return true;
5292
- }
5293
- }
5294
- ])
5295
- ]
5296
- };
5297
5954
 
5298
5955
  // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5299
5956
  import "@dxos/lit-ui/dx-ref-tag.pcss";
5300
5957
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
5301
- import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField11 } from "@codemirror/state";
5302
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5958
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField10 } from "@codemirror/state";
5959
+ import { Decoration as Decoration10, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5303
5960
  var preview = (options = {}) => {
5304
5961
  return [
5305
5962
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5306
5963
  // "Block decorations may not be specified via plugins"
5307
- StateField11.define({
5308
- create: (state) => buildDecorations4(state, options),
5309
- update: (_, tr) => buildDecorations4(tr.state, options),
5964
+ StateField10.define({
5965
+ create: (state) => buildDecorations3(state, options),
5966
+ update: (_, tr) => buildDecorations3(tr.state, options),
5310
5967
  provide: (field) => [
5311
5968
  EditorView20.decorations.from(field),
5312
5969
  EditorView20.atomicRanges.of((view) => view.state.field(field))
@@ -5337,8 +5994,8 @@ var getLinkRef = (state, node) => {
5337
5994
  };
5338
5995
  }
5339
5996
  };
5340
- var buildDecorations4 = (state, options) => {
5341
- const builder = new RangeSetBuilder4();
5997
+ var buildDecorations3 = (state, options) => {
5998
+ const builder = new RangeSetBuilder6();
5342
5999
  syntaxTree10(state).iterate({
5343
6000
  enter: (node) => {
5344
6001
  switch (node.name) {
@@ -5349,7 +6006,7 @@ var buildDecorations4 = (state, options) => {
5349
6006
  case "Link": {
5350
6007
  const link = getLinkRef(state, node.node);
5351
6008
  if (link) {
5352
- builder.add(node.from, node.to, Decoration9.replace({
6009
+ builder.add(node.from, node.to, Decoration10.replace({
5353
6010
  widget: new PreviewInlineWidget(options, link)
5354
6011
  }));
5355
6012
  }
@@ -5362,7 +6019,7 @@ var buildDecorations4 = (state, options) => {
5362
6019
  case "Image": {
5363
6020
  const link = getLinkRef(state, node.node);
5364
6021
  if (options.renderBlock && link) {
5365
- builder.add(node.from, node.to, Decoration9.replace({
6022
+ builder.add(node.from, node.to, Decoration10.replace({
5366
6023
  block: true,
5367
6024
  // atomic: true,
5368
6025
  widget: new PreviewBlockWidget(options, link)
@@ -5377,9 +6034,7 @@ var buildDecorations4 = (state, options) => {
5377
6034
  };
5378
6035
  var PreviewInlineWidget = class extends WidgetType6 {
5379
6036
  constructor(_options, _link) {
5380
- super();
5381
- this._options = _options;
5382
- this._link = _link;
6037
+ super(), this._options = _options, this._link = _link;
5383
6038
  }
5384
6039
  // override ignoreEvent() {
5385
6040
  // return false;
@@ -5390,15 +6045,13 @@ var PreviewInlineWidget = class extends WidgetType6 {
5390
6045
  toDOM(view) {
5391
6046
  const root = document.createElement("dx-ref-tag");
5392
6047
  root.textContent = this._link.label;
5393
- root.setAttribute("ref", this._link.ref);
6048
+ root.setAttribute("refId", this._link.ref);
5394
6049
  return root;
5395
6050
  }
5396
6051
  };
5397
6052
  var PreviewBlockWidget = class extends WidgetType6 {
5398
6053
  constructor(_options, _link) {
5399
- super();
5400
- this._options = _options;
5401
- this._link = _link;
6054
+ super(), this._options = _options, this._link = _link;
5402
6055
  }
5403
6056
  // override ignoreEvent() {
5404
6057
  // return true;
@@ -5455,7 +6108,7 @@ var PreviewBlockWidget = class extends WidgetType6 {
5455
6108
  };
5456
6109
 
5457
6110
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5458
- import { keymap as keymap10 } from "@codemirror/view";
6111
+ import { keymap as keymap12 } from "@codemirror/view";
5459
6112
  var defaultItems = [
5460
6113
  "hello world!",
5461
6114
  "this is a test.",
@@ -5465,7 +6118,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5465
6118
  let t;
5466
6119
  let idx = 0;
5467
6120
  return [
5468
- keymap10.of([
6121
+ keymap12.of([
5469
6122
  {
5470
6123
  // Reset.
5471
6124
  key: "alt-meta-'",
@@ -5569,30 +6222,6 @@ var createBlocks = (state, getView) => {
5569
6222
  };
5570
6223
  };
5571
6224
 
5572
- // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
5573
- var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
5574
- var createCommentAction = (label, getView) => createEditorAction("comment", () => createComment(getView()), {
5575
- testId: "editor.toolbar.comment",
5576
- icon: "ph--chat-text--regular",
5577
- label
5578
- });
5579
- var createComment2 = (state, getView) => ({
5580
- nodes: [
5581
- createCommentAction([
5582
- commentLabel(state.comment, state.selection),
5583
- {
5584
- ns: translationKey
5585
- }
5586
- ], getView)
5587
- ],
5588
- edges: [
5589
- {
5590
- source: "root",
5591
- target: "comment"
5592
- }
5593
- ]
5594
- });
5595
-
5596
6225
  // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
5597
6226
  var formats = {
5598
6227
  strong: "ph--text-b--regular",
@@ -5835,15 +6464,17 @@ var createViewMode = (state, onViewModeChange) => {
5835
6464
  // packages/ui/react-ui-editor/src/defaults.ts
5836
6465
  import { EditorView as EditorView21 } from "@codemirror/view";
5837
6466
  import { mx as mx4 } from "@dxos/react-ui-theme";
5838
- var margin = "!mt-[1rem]";
5839
6467
  var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
5840
- var editorContent = mx4(margin, editorWidth);
5841
- var editorFullWidth = mx4(margin);
6468
+ var editorSlots = {
6469
+ scroll: {
6470
+ className: "pbs-2"
6471
+ },
6472
+ content: {
6473
+ className: editorWidth
6474
+ }
6475
+ };
5842
6476
  var editorGutter = EditorView21.theme({
5843
- // Match margin from content.
5844
- // Gutter = 2rem + 1rem margin.
5845
6477
  ".cm-gutters": {
5846
- marginTop: "1rem",
5847
6478
  paddingRight: "1rem"
5848
6479
  }
5849
6480
  });
@@ -5854,104 +6485,264 @@ var editorMonospace = EditorView21.theme({
5854
6485
  });
5855
6486
  var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5856
6487
  var stackItemContentEditorClassNames = (role) => mx4("attention-surface dx-focus-ring-inset data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
5857
- var stackItemContentToolbarClassNames = (role) => mx4("relative z-[1] flex is-full bg-toolbarSurface border-be border-separator", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
6488
+ var stackItemContentToolbarClassNames = (role) => mx4("relative z-[1] flex is-full bg-toolbarSurface border-be border-subduedSeparator", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
5858
6489
 
5859
6490
  // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
5860
6491
  var createToolbar = ({ getView, state, customActions, ...features }) => {
5861
- const nodes = [];
5862
- const edges = [];
5863
- if (features.headings ?? true) {
5864
- const headings2 = createHeadings(state, getView);
5865
- nodes.push(...headings2.nodes);
5866
- edges.push(...headings2.edges);
5867
- }
5868
- if (features.formatting ?? true) {
5869
- const formatting = createFormatting(state, getView);
5870
- nodes.push(...formatting.nodes);
5871
- edges.push(...formatting.edges);
5872
- }
5873
- if (features.lists ?? true) {
5874
- const lists = createLists(state, getView);
5875
- nodes.push(...lists.nodes);
5876
- edges.push(...lists.edges);
5877
- }
5878
- if (features.blocks ?? true) {
5879
- const blocks = createBlocks(state, getView);
5880
- nodes.push(...blocks.nodes);
5881
- edges.push(...blocks.edges);
5882
- }
5883
- if (features.image) {
5884
- const image2 = createImageUpload(features.image);
5885
- nodes.push(...image2.nodes);
5886
- edges.push(...image2.edges);
5887
- }
5888
- if (customActions) {
5889
- const custom = customActions();
5890
- nodes.push(...custom.nodes);
5891
- edges.push(...custom.edges);
5892
- }
5893
- const editorToolbarGap = createGapSeparator();
5894
- nodes.push(...editorToolbarGap.nodes);
5895
- edges.push(...editorToolbarGap.edges);
5896
- if (features.comment) {
5897
- const comment = createComment2(state, getView);
5898
- nodes.push(...comment.nodes);
5899
- edges.push(...comment.edges);
5900
- }
5901
- if (features.search ?? true) {
5902
- const search = createSearch(getView);
5903
- nodes.push(...search.nodes);
5904
- edges.push(...search.edges);
5905
- }
5906
- if (features.viewMode) {
5907
- const viewMode = createViewMode(state, features.viewMode);
5908
- nodes.push(...viewMode.nodes);
5909
- edges.push(...viewMode.edges);
5910
- }
5911
- return {
5912
- nodes,
5913
- edges
5914
- };
6492
+ return Rx.make((get2) => {
6493
+ const nodes = [];
6494
+ const edges = [];
6495
+ if (features.headings ?? true) {
6496
+ const headings2 = get2(rxFromSignal(() => createHeadings(state, getView)));
6497
+ nodes.push(...headings2.nodes);
6498
+ edges.push(...headings2.edges);
6499
+ }
6500
+ if (features.formatting ?? true) {
6501
+ const formatting = get2(rxFromSignal(() => createFormatting(state, getView)));
6502
+ nodes.push(...formatting.nodes);
6503
+ edges.push(...formatting.edges);
6504
+ }
6505
+ if (features.lists ?? true) {
6506
+ const lists = get2(rxFromSignal(() => createLists(state, getView)));
6507
+ nodes.push(...lists.nodes);
6508
+ edges.push(...lists.edges);
6509
+ }
6510
+ if (features.blocks ?? true) {
6511
+ const blocks = get2(rxFromSignal(() => createBlocks(state, getView)));
6512
+ nodes.push(...blocks.nodes);
6513
+ edges.push(...blocks.edges);
6514
+ }
6515
+ if (features.image) {
6516
+ const image2 = get2(rxFromSignal(() => createImageUpload(features.image)));
6517
+ nodes.push(...image2.nodes);
6518
+ edges.push(...image2.edges);
6519
+ }
6520
+ const editorToolbarGap = createGapSeparator();
6521
+ nodes.push(...editorToolbarGap.nodes);
6522
+ edges.push(...editorToolbarGap.edges);
6523
+ if (customActions) {
6524
+ const custom = get2(customActions);
6525
+ nodes.push(...custom.nodes);
6526
+ edges.push(...custom.edges);
6527
+ }
6528
+ if (features.search ?? true) {
6529
+ const search = get2(rxFromSignal(() => createSearch(getView)));
6530
+ nodes.push(...search.nodes);
6531
+ edges.push(...search.edges);
6532
+ }
6533
+ if (features.viewMode) {
6534
+ const viewMode = get2(rxFromSignal(() => createViewMode(state, features.viewMode)));
6535
+ nodes.push(...viewMode.nodes);
6536
+ edges.push(...viewMode.edges);
6537
+ }
6538
+ return {
6539
+ nodes,
6540
+ edges
6541
+ };
6542
+ });
5915
6543
  };
5916
6544
  var useEditorToolbarActionGraph = (props) => {
5917
- const menuCreator = useCallback(() => createToolbar(props), [
5918
- props
6545
+ const menuCreator = useMemo3(() => createToolbar({
6546
+ getView: props.getView,
6547
+ state: props.state,
6548
+ customActions: props.customActions,
6549
+ headings: props.headings,
6550
+ formatting: props.formatting,
6551
+ lists: props.lists,
6552
+ blocks: props.blocks,
6553
+ image: props.image,
6554
+ search: props.search,
6555
+ viewMode: props.viewMode
6556
+ }), [
6557
+ props.getView,
6558
+ props.state,
6559
+ props.customActions,
6560
+ props.headings,
6561
+ props.formatting,
6562
+ props.lists,
6563
+ props.blocks,
6564
+ props.image,
6565
+ props.search,
6566
+ props.viewMode
5919
6567
  ]);
5920
6568
  return useMenuActions(menuCreator);
5921
6569
  };
5922
6570
  var EditorToolbar = /* @__PURE__ */ memo(({ classNames, attendableId, role, ...props }) => {
5923
- const menuProps = useEditorToolbarActionGraph(props);
5924
- return /* @__PURE__ */ React3.createElement("div", {
5925
- role: "none",
5926
- className: stackItemContentToolbarClassNames(role)
5927
- }, /* @__PURE__ */ React3.createElement(ElevationProvider, {
5928
- elevation: role === "section" ? "positioned" : "base"
5929
- }, /* @__PURE__ */ React3.createElement(MenuProvider, {
5930
- ...menuProps,
5931
- attendableId
5932
- }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
5933
- classNames: [
5934
- textBlockWidth,
5935
- classNames
5936
- ]
5937
- }))));
6571
+ var _effect = _useSignals();
6572
+ try {
6573
+ const menuProps = useEditorToolbarActionGraph(props);
6574
+ return /* @__PURE__ */ React3.createElement("div", {
6575
+ role: "none",
6576
+ className: stackItemContentToolbarClassNames(role)
6577
+ }, /* @__PURE__ */ React3.createElement(ElevationProvider, {
6578
+ elevation: role === "section" ? "positioned" : "base"
6579
+ }, /* @__PURE__ */ React3.createElement(MenuProvider, {
6580
+ ...menuProps,
6581
+ attendableId
6582
+ }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
6583
+ classNames: [
6584
+ textBlockWidth,
6585
+ classNames
6586
+ ]
6587
+ }))));
6588
+ } finally {
6589
+ _effect.f();
6590
+ }
5938
6591
  });
5939
6592
 
6593
+ // packages/ui/react-ui-editor/src/components/Popover/RefPopover.tsx
6594
+ import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
6595
+ import { createContext } from "@radix-ui/react-context";
6596
+ import React4, { useRef, useState, useEffect as useEffect2, useCallback } from "react";
6597
+ import { addEventListener } from "@dxos/async";
6598
+ import { Popover } from "@dxos/react-ui";
6599
+ var customEventOptions = {
6600
+ capture: true,
6601
+ passive: false
6602
+ };
6603
+ var REF_POPOVER = "RefPopover";
6604
+ var [RefPopoverContextProvider, useRefPopover] = createContext(REF_POPOVER, {});
6605
+ var RefPopoverProvider = ({ children, onLookup }) => {
6606
+ var _effect = _useSignals2();
6607
+ try {
6608
+ const trigger = useRef(null);
6609
+ const [value, setValue] = useState({});
6610
+ const [rootRef, setRootRef] = useState(null);
6611
+ const [open, setOpen] = useState(false);
6612
+ const handleDxRefTagActivate = useCallback((event) => {
6613
+ const { refId, label, trigger: dxTrigger } = event;
6614
+ setValue((value2) => ({
6615
+ ...value2,
6616
+ link: {
6617
+ label,
6618
+ ref: refId
6619
+ },
6620
+ pending: true
6621
+ }));
6622
+ trigger.current = dxTrigger;
6623
+ queueMicrotask(() => setOpen(true));
6624
+ void onLookup?.({
6625
+ label,
6626
+ ref: refId
6627
+ }).then((target) => setValue((value2) => ({
6628
+ ...value2,
6629
+ target: target ?? void 0,
6630
+ pending: false
6631
+ })));
6632
+ }, [
6633
+ onLookup
6634
+ ]);
6635
+ useEffect2(() => {
6636
+ return rootRef ? addEventListener(rootRef, "dx-ref-tag-activate", handleDxRefTagActivate, customEventOptions) : void 0;
6637
+ }, [
6638
+ rootRef
6639
+ ]);
6640
+ return /* @__PURE__ */ React4.createElement(RefPopoverContextProvider, {
6641
+ pending: value.pending,
6642
+ link: value.link,
6643
+ target: value.target
6644
+ }, /* @__PURE__ */ React4.createElement(Popover.Root, {
6645
+ open,
6646
+ onOpenChange: setOpen
6647
+ }, /* @__PURE__ */ React4.createElement(Popover.VirtualTrigger, {
6648
+ virtualRef: trigger
6649
+ }), /* @__PURE__ */ React4.createElement("div", {
6650
+ role: "none",
6651
+ className: "contents",
6652
+ ref: setRootRef
6653
+ }, children)));
6654
+ } finally {
6655
+ _effect.f();
6656
+ }
6657
+ };
6658
+ var RefPopover = {
6659
+ Provider: RefPopoverProvider
6660
+ };
6661
+
6662
+ // packages/ui/react-ui-editor/src/components/Popover/RefDropdownMenu.tsx
6663
+ import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
6664
+ import { createContext as createContext2 } from "@radix-ui/react-context";
6665
+ import React5, { useRef as useRef2, useState as useState2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
6666
+ import { addEventListener as addEventListener2 } from "@dxos/async";
6667
+ import { DropdownMenu } from "@dxos/react-ui";
6668
+ var customEventOptions2 = {
6669
+ capture: true,
6670
+ passive: false
6671
+ };
6672
+ var REF_DROPDOWN_MENU = "RefDropdownMenu";
6673
+ var [RefDropdownMenuContextProvider, useRefDropdownMenu] = createContext2(REF_DROPDOWN_MENU, {});
6674
+ var RefDropdownMenuProvider = ({ children, onLookup }) => {
6675
+ var _effect = _useSignals3();
6676
+ try {
6677
+ const trigger = useRef2(null);
6678
+ const [value, setValue] = useState2({});
6679
+ const [rootRef, setRootRef] = useState2(null);
6680
+ const [open, setOpen] = useState2(false);
6681
+ const handleDxRefTagActivate = useCallback2((event) => {
6682
+ const { refId, label, trigger: dxTrigger } = event;
6683
+ setValue((value2) => ({
6684
+ ...value2,
6685
+ link: {
6686
+ label,
6687
+ ref: refId
6688
+ },
6689
+ pending: true
6690
+ }));
6691
+ trigger.current = dxTrigger;
6692
+ queueMicrotask(() => setOpen(true));
6693
+ void onLookup?.({
6694
+ label,
6695
+ ref: refId
6696
+ }).then((target) => setValue((value2) => ({
6697
+ ...value2,
6698
+ target: target ?? void 0,
6699
+ pending: false
6700
+ })));
6701
+ }, [
6702
+ onLookup
6703
+ ]);
6704
+ useEffect3(() => {
6705
+ return rootRef ? addEventListener2(rootRef, "dx-ref-tag-activate", handleDxRefTagActivate, customEventOptions2) : void 0;
6706
+ }, [
6707
+ rootRef
6708
+ ]);
6709
+ return /* @__PURE__ */ React5.createElement(RefDropdownMenuContextProvider, {
6710
+ pending: value.pending,
6711
+ link: value.link,
6712
+ target: value.target
6713
+ }, /* @__PURE__ */ React5.createElement(DropdownMenu.Root, {
6714
+ open,
6715
+ onOpenChange: setOpen
6716
+ }, /* @__PURE__ */ React5.createElement(DropdownMenu.VirtualTrigger, {
6717
+ virtualRef: trigger
6718
+ }), /* @__PURE__ */ React5.createElement("div", {
6719
+ role: "none",
6720
+ className: "contents",
6721
+ ref: setRootRef
6722
+ }, children)));
6723
+ } finally {
6724
+ _effect.f();
6725
+ }
6726
+ };
6727
+ var RefDropdownMenu = {
6728
+ Provider: RefDropdownMenuProvider
6729
+ };
6730
+
5940
6731
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
5941
6732
  import { EditorState as EditorState3 } from "@codemirror/state";
5942
6733
  import { EditorView as EditorView22 } from "@codemirror/view";
5943
6734
  import { useFocusableGroup } from "@fluentui/react-tabster";
5944
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
6735
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
5945
6736
  import { log as log8 } from "@dxos/log";
5946
6737
  import { getProviderValue, isNotFalsy as isNotFalsy4 } from "@dxos/util";
5947
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
6738
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5948
6739
  var instanceCount = 0;
5949
6740
  var useTextEditor = (props = {}, deps = []) => {
5950
6741
  const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } = useMemo4(() => getProviderValue(props), deps ?? []);
5951
- const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
5952
- const [view, setView] = useState();
5953
- const parentRef = useRef(null);
5954
- useEffect2(() => {
6742
+ const [instanceId] = useState3(() => `text-editor-${++instanceCount}`);
6743
+ const [view, setView] = useState3();
6744
+ const parentRef = useRef3(null);
6745
+ useEffect4(() => {
5955
6746
  let view2;
5956
6747
  if (parentRef.current) {
5957
6748
  log8("create", {
@@ -5959,7 +6750,7 @@ var useTextEditor = (props = {}, deps = []) => {
5959
6750
  instanceId,
5960
6751
  doc: initialValue?.length ?? 0
5961
6752
  }, {
5962
- F: __dxlog_file12,
6753
+ F: __dxlog_file13,
5963
6754
  L: 76,
5964
6755
  S: void 0,
5965
6756
  C: (f, a) => f(...a)
@@ -5985,7 +6776,7 @@ var useTextEditor = (props = {}, deps = []) => {
5985
6776
  // NOTE: This doesn't catch errors in keymap functions.
5986
6777
  EditorView22.exceptionSink.of((err) => {
5987
6778
  log8.catch(err, void 0, {
5988
- F: __dxlog_file12,
6779
+ F: __dxlog_file13,
5989
6780
  L: 98,
5990
6781
  S: void 0,
5991
6782
  C: (f, a) => f(...a)
@@ -6017,7 +6808,7 @@ var useTextEditor = (props = {}, deps = []) => {
6017
6808
  log8("destroy", {
6018
6809
  id
6019
6810
  }, {
6020
- F: __dxlog_file12,
6811
+ F: __dxlog_file13,
6021
6812
  L: 135,
6022
6813
  S: void 0,
6023
6814
  C: (f, a) => f(...a)
@@ -6025,7 +6816,7 @@ var useTextEditor = (props = {}, deps = []) => {
6025
6816
  view2?.destroy();
6026
6817
  };
6027
6818
  }, deps);
6028
- useEffect2(() => {
6819
+ useEffect4(() => {
6029
6820
  if (view) {
6030
6821
  if (scrollTo || selection) {
6031
6822
  if (selection && selection.anchor > view.state.doc.length) {
@@ -6034,7 +6825,7 @@ var useTextEditor = (props = {}, deps = []) => {
6034
6825
  scrollTo,
6035
6826
  selection
6036
6827
  }, {
6037
- F: __dxlog_file12,
6828
+ F: __dxlog_file13,
6038
6829
  L: 144,
6039
6830
  S: void 0,
6040
6831
  C: (f, a) => f(...a)
@@ -6052,7 +6843,7 @@ var useTextEditor = (props = {}, deps = []) => {
6052
6843
  scrollTo,
6053
6844
  selection
6054
6845
  ]);
6055
- useEffect2(() => {
6846
+ useEffect4(() => {
6056
6847
  if (view && autoFocus) {
6057
6848
  view.focus();
6058
6849
  }
@@ -6066,7 +6857,7 @@ var useTextEditor = (props = {}, deps = []) => {
6066
6857
  Escape: view?.state.facet(editorInputMode).noTabster
6067
6858
  }
6068
6859
  });
6069
- const handleKeyUp = useCallback2((event) => {
6860
+ const handleKeyUp = useCallback3((event) => {
6070
6861
  const { key, target, currentTarget } = event;
6071
6862
  if (target === currentTarget) {
6072
6863
  switch (key) {
@@ -6101,9 +6892,12 @@ export {
6101
6892
  Inline,
6102
6893
  InputModeExtensions,
6103
6894
  List,
6895
+ RefDropdownMenu,
6896
+ RefPopover,
6104
6897
  RemoteSelectionsDecorator,
6105
6898
  SpaceAwarenessProvider,
6106
6899
  TextKind,
6900
+ Tree,
6107
6901
  addBlockquote,
6108
6902
  addCodeblock,
6109
6903
  addLink,
@@ -6121,6 +6915,7 @@ export {
6121
6915
  closeEffect,
6122
6916
  command,
6123
6917
  commandKeyBindings,
6918
+ commentClickedEffect,
6124
6919
  comments,
6125
6920
  commentsState,
6126
6921
  convertTreeToJson,
@@ -6133,6 +6928,7 @@ export {
6133
6928
  createEditorStateTransaction,
6134
6929
  createElement,
6135
6930
  createExternalCommentSync,
6931
+ createJsonExtensions,
6136
6932
  createMarkdownExtensions,
6137
6933
  createRenderer,
6138
6934
  createThemeExtensions,
@@ -6143,33 +6939,39 @@ export {
6143
6939
  defaultOptions,
6144
6940
  documentId,
6145
6941
  dropFile,
6146
- editorContent,
6147
- editorFullWidth,
6148
6942
  editorGutter,
6149
6943
  editorInputMode,
6150
6944
  editorMonospace,
6945
+ editorSlots,
6151
6946
  editorWidth,
6152
6947
  editorWithToolbarLayout,
6153
6948
  flattenRect,
6949
+ floatingMenu,
6154
6950
  focus,
6155
6951
  focusField,
6156
6952
  folding,
6157
6953
  formattingEquals,
6158
6954
  formattingKeymap,
6159
6955
  getFormatting,
6956
+ getListItemContent,
6957
+ getRange,
6160
6958
  image,
6161
6959
  insertTable,
6162
- keymap11 as keymap,
6960
+ itemToJSON,
6961
+ keymap13 as keymap,
6163
6962
  linkTooltip,
6963
+ listItemToString,
6164
6964
  listener,
6165
6965
  logChanges,
6166
6966
  markdownHighlightStyle,
6167
6967
  markdownTags,
6168
6968
  markdownTagsExtensions,
6969
+ matchCompletion,
6169
6970
  mention,
6170
6971
  openCommand,
6171
6972
  openEffect,
6172
6973
  outliner,
6974
+ outlinerTree,
6173
6975
  overlap,
6174
6976
  preventNewline,
6175
6977
  preview,
@@ -6181,7 +6983,6 @@ export {
6181
6983
  removeStyle,
6182
6984
  renderRoot,
6183
6985
  scrollThreadIntoView,
6184
- selectionOverlapsComment,
6185
6986
  selectionState,
6186
6987
  setBlockquote,
6187
6988
  setComments,
@@ -6191,6 +6992,7 @@ export {
6191
6992
  singleValueFacet,
6192
6993
  stackItemContentEditorClassNames,
6193
6994
  stackItemContentToolbarClassNames,
6995
+ staticCompletion,
6194
6996
  table,
6195
6997
  tags2 as tags,
6196
6998
  textRange,
@@ -6203,12 +7005,15 @@ export {
6203
7005
  toggleStrong,
6204
7006
  toggleStyle,
6205
7007
  translations_default as translations,
7008
+ traverse,
7009
+ treeFacet,
7010
+ typeahead,
6206
7011
  typewriter,
6207
- useCommentClickListener,
6208
- useCommentState,
6209
7012
  useComments,
6210
7013
  useEditorToolbarState,
6211
7014
  useFormattingState,
7015
+ useRefDropdownMenu,
7016
+ useRefPopover,
6212
7017
  useTextEditor,
6213
7018
  wrapWithCatch
6214
7019
  };