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