@dxos/react-ui-editor 0.8.2-main.5ca3450 → 0.8.2-main.600d381

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 (51) hide show
  1. package/dist/lib/browser/index.mjs +1591 -1523
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +1383 -1316
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +1591 -1523
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  11. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  12. package/dist/types/src/{stories/InputMode.stories.d.ts → components/EditorToolbar/EditorToolbar.stories.d.ts} +3 -7
  13. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  15. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/comment.d.ts +4 -3
  17. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  19. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  21. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/image.d.ts +16 -0
  23. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  24. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  25. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  27. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  28. package/dist/types/src/components/EditorToolbar/util.d.ts +11 -17
  29. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
  31. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  32. package/dist/types/src/hooks/index.d.ts +0 -1
  33. package/dist/types/src/hooks/index.d.ts.map +1 -1
  34. package/package.json +28 -28
  35. package/src/components/EditorToolbar/EditorToolbar.stories.tsx +90 -0
  36. package/src/components/EditorToolbar/EditorToolbar.tsx +30 -31
  37. package/src/components/EditorToolbar/blocks.ts +27 -6
  38. package/src/components/EditorToolbar/comment.ts +11 -4
  39. package/src/components/EditorToolbar/formatting.ts +34 -7
  40. package/src/components/EditorToolbar/headings.ts +9 -8
  41. package/src/components/EditorToolbar/image.ts +16 -0
  42. package/src/components/EditorToolbar/lists.ts +26 -7
  43. package/src/components/EditorToolbar/search.ts +19 -0
  44. package/src/components/EditorToolbar/util.ts +14 -14
  45. package/src/components/EditorToolbar/view-mode.ts +9 -8
  46. package/src/hooks/index.ts +0 -1
  47. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  48. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  49. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  50. package/src/hooks/useActionHandler.ts +0 -12
  51. package/src/stories/InputMode.stories.tsx +0 -124
@@ -40,7 +40,7 @@ import { tags as tags2 } from "@lezer/highlight";
40
40
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
41
41
 
42
42
  // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
43
- import React, { useCallback } from "react";
43
+ import React3, { memo, useCallback } from "react";
44
44
  import { ElevationProvider } from "@dxos/react-ui";
45
45
  import { MenuProvider, ToolbarMenu, createGapSeparator, useMenuActions } from "@dxos/react-ui-menu";
46
46
  import { textBlockWidth } from "@dxos/react-ui-theme";
@@ -52,890 +52,308 @@ import { createMenuAction, createMenuItemGroup } from "@dxos/react-ui-menu";
52
52
  var useEditorToolbarState = (initialState = {}) => {
53
53
  return useMemo(() => live(initialState), []);
54
54
  };
55
- var createEditorAction = (payload, icon, label = [
56
- `${payload.type} label`,
57
- {
58
- ns: translationKey
59
- }
60
- ], id = payload.type) => createMenuAction(id, {
61
- icon,
62
- label,
63
- ...payload
64
- });
55
+ var createEditorAction = (id, invoke, properties) => {
56
+ const { label = [
57
+ `${id} label`,
58
+ {
59
+ ns: translationKey
60
+ }
61
+ ], ...rest } = properties;
62
+ return createMenuAction(id, invoke, {
63
+ label,
64
+ ...rest
65
+ });
66
+ };
65
67
  var createEditorActionGroup = (id, props, icon) => createMenuItemGroup(id, {
66
68
  icon,
67
69
  iconOnly: true,
68
70
  ...props
69
71
  });
70
- var editorToolbarSearch = createEditorAction({
71
- type: "search"
72
- }, "ph--magnifying-glass--regular");
73
72
 
74
- // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
75
- var createBlockGroupAction = (value) => createEditorActionGroup("block", {
76
- variant: "toggleGroup",
77
- selectCardinality: "single",
78
- value
79
- });
80
- var createBlockActions = (value, blankLine) => Object.entries({
81
- blockquote: "ph--quotes--regular",
82
- codeblock: "ph--code-block--regular",
83
- table: "ph--table--regular"
84
- }).map(([type, icon]) => {
85
- return createEditorAction({
86
- type,
87
- checked: type === value,
88
- ...type === "table" && {
89
- disabled: !!blankLine
90
- }
91
- }, icon);
92
- });
93
- var createBlocks = (state) => {
94
- const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
95
- const blockGroupAction = createBlockGroupAction(value);
96
- const blockActions = createBlockActions(value, state.blankLine);
97
- return {
98
- nodes: [
99
- blockGroupAction,
100
- ...blockActions
101
- ],
102
- edges: [
103
- {
104
- source: "root",
105
- target: "block"
106
- },
107
- ...blockActions.map(({ id }) => ({
108
- source: blockGroupAction.id,
109
- target: id
110
- }))
111
- ]
112
- };
113
- };
73
+ // packages/ui/react-ui-editor/src/extensions/annotations.ts
74
+ import { StateField } from "@codemirror/state";
75
+ import { Decoration, EditorView } from "@codemirror/view";
76
+ import { isNotFalsy } from "@dxos/util";
114
77
 
115
- // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
116
- var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
117
- var createCommentAction = (label) => createEditorAction({
118
- type: "comment",
119
- testId: "editor.toolbar.comment"
120
- }, "ph--chat-text--regular", label);
121
- var createComment = (state) => ({
122
- nodes: [
123
- createCommentAction([
124
- commentLabel(state.comment, state.selection),
125
- {
126
- ns: translationKey
127
- }
128
- ])
129
- ],
130
- edges: [
131
- {
132
- source: "root",
133
- target: "comment"
134
- }
135
- ]
78
+ // packages/ui/react-ui-editor/src/util/facet.ts
79
+ import { Facet } from "@codemirror/state";
80
+ var singleValueFacet = (defaultValue) => Facet.define({
81
+ // Called immediately.
82
+ combine: (providers) => {
83
+ return providers[0] ?? defaultValue;
84
+ }
136
85
  });
137
86
 
138
- // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
139
- var formats = {
140
- strong: "ph--text-b--regular",
141
- emphasis: "ph--text-italic--regular",
142
- strikethrough: "ph--text-strikethrough--regular",
143
- code: "ph--code--regular",
144
- link: "ph--link--regular"
87
+ // packages/ui/react-ui-editor/src/util/cursor.ts
88
+ var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
89
+ var defaultCursorConverter = {
90
+ toCursor: (position) => position.toString(),
91
+ fromCursor: (cursor) => parseInt(cursor)
145
92
  };
146
- var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
147
- variant: "toggleGroup",
148
- selectCardinality: "multiple",
149
- value: Object.keys(formats).filter((key) => !!formatting[key])
150
- });
151
- var createFormattingActions = (formatting) => Object.entries(formats).map(([type, icon]) => createEditorAction({
152
- type,
153
- checked: !!formatting[type]
154
- }, icon));
155
- var createFormatting = (state) => {
156
- const formattingGroupAction = createFormattingGroup(state);
157
- const formattingActions = createFormattingActions(state);
158
- return {
159
- nodes: [
160
- formattingGroupAction,
161
- ...formattingActions
162
- ],
163
- edges: [
164
- {
165
- source: "root",
166
- target: "formatting"
167
- },
168
- ...formattingActions.map(({ id }) => ({
169
- source: formattingGroupAction.id,
170
- target: id
171
- }))
172
- ]
173
- };
93
+ var Cursor = class _Cursor {
94
+ static {
95
+ this.converter = singleValueFacet(defaultCursorConverter);
96
+ }
97
+ static {
98
+ this.getCursorFromRange = (state, range) => {
99
+ const cursorConverter2 = state.facet(_Cursor.converter);
100
+ const from = cursorConverter2.toCursor(range.from);
101
+ const to = cursorConverter2.toCursor(range.to, -1);
102
+ return [
103
+ from,
104
+ to
105
+ ].join(":");
106
+ };
107
+ }
108
+ static {
109
+ this.getRangeFromCursor = (state, cursor) => {
110
+ const cursorConverter2 = state.facet(_Cursor.converter);
111
+ const parts = cursor.split(":");
112
+ const from = cursorConverter2.fromCursor(parts[0]);
113
+ const to = cursorConverter2.fromCursor(parts[1]);
114
+ return from !== void 0 && to !== void 0 ? {
115
+ from,
116
+ to
117
+ } : void 0;
118
+ };
119
+ }
174
120
  };
175
121
 
176
- // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
177
- var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
178
- variant: "dropdownMenu",
179
- applyActive: true,
180
- selectCardinality: "single",
181
- value
182
- }, "ph--text-h--regular");
183
- var createHeadingActions = (value) => Object.entries({
184
- "0": "ph--paragraph--regular",
185
- "1": "ph--text-h-one--regular",
186
- "2": "ph--text-h-two--regular",
187
- "3": "ph--text-h-three--regular",
188
- "4": "ph--text-h-four--regular",
189
- "5": "ph--text-h-five--regular",
190
- "6": "ph--text-h-six--regular"
191
- }).map(([levelStr, icon]) => {
192
- const level = parseInt(levelStr);
193
- return createEditorAction({
194
- type: "heading",
195
- data: level,
196
- checked: value === levelStr
197
- }, icon, [
198
- "heading level label",
199
- {
200
- count: level,
201
- ns: translationKey
122
+ // packages/ui/react-ui-editor/src/util/debug.ts
123
+ import { log } from "@dxos/log";
124
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
125
+ var wrapWithCatch = (fn) => {
126
+ return (...args) => {
127
+ try {
128
+ return fn(...args);
129
+ } catch (err) {
130
+ log.catch(err, void 0, {
131
+ F: __dxlog_file,
132
+ L: 15,
133
+ S: void 0,
134
+ C: (f, a) => f(...a)
135
+ });
202
136
  }
203
- ], `heading--${levelStr}`);
204
- });
205
- var computeHeadingValue = (state) => {
206
- const blockType = state ? state.blockType : "paragraph";
207
- const header = blockType && /heading(\d)/.exec(blockType);
208
- return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
209
- };
210
- var createHeadings = (state) => {
211
- const headingValue = computeHeadingValue(state);
212
- const headingGroupAction = createHeadingGroupAction(headingValue);
213
- const headingActions = createHeadingActions(headingValue);
214
- return {
215
- nodes: [
216
- headingGroupAction,
217
- ...headingActions
218
- ],
219
- edges: [
220
- {
221
- source: "root",
222
- target: "heading"
223
- },
224
- ...headingActions.map(({ id }) => ({
225
- source: headingGroupAction.id,
226
- target: id
227
- }))
228
- ]
229
137
  };
230
138
  };
231
-
232
- // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
233
- var listStyles = {
234
- bullet: "ph--list-bullets--regular",
235
- ordered: "ph--list-numbers--regular",
236
- task: "ph--list-checks--regular"
139
+ var callbackWrapper = (fn) => (...args) => {
140
+ try {
141
+ return fn(...args);
142
+ } catch (err) {
143
+ log.catch(err, void 0, {
144
+ F: __dxlog_file,
145
+ L: 29,
146
+ S: void 0,
147
+ C: (f, a) => f(...a)
148
+ });
149
+ }
237
150
  };
238
- var createListGroupAction = (value) => createEditorActionGroup("list", {
239
- variant: "toggleGroup",
240
- selectCardinality: "single",
241
- value
242
- });
243
- var createListActions = (value) => Object.entries(listStyles).map(([listStyle, icon]) => createEditorAction({
244
- type: `list-${listStyle}`,
245
- checked: value === listStyle
246
- }, icon));
247
- var createLists = (state) => {
248
- const value = state.listStyle ?? "";
249
- const listGroupAction = createListGroupAction(value);
250
- const listActionsMap = createListActions(value);
251
- return {
252
- nodes: [
253
- listGroupAction,
254
- ...listActionsMap
255
- ],
256
- edges: [
257
- {
258
- source: "root",
259
- target: "list"
260
- },
261
- ...listActionsMap.map(({ id }) => ({
262
- source: listGroupAction.id,
263
- target: id
264
- }))
265
- ]
266
- };
151
+ var debugDispatcher = (trs, view) => {
152
+ logChanges(trs);
153
+ view.update(trs);
267
154
  };
268
-
269
- // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
270
- var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
271
- variant: "dropdownMenu",
272
- applyActive: true,
273
- selectCardinality: "single",
274
- value
275
- }, "ph--eye--regular");
276
- var createViewModeActions = (value) => Object.entries({
277
- preview: "ph--eye--regular",
278
- source: "ph--pencil-simple--regular",
279
- readonly: "ph--pencil-slash--regular"
280
- }).map(([viewMode, icon]) => {
281
- return createEditorAction({
282
- type: "view-mode",
283
- data: viewMode,
284
- checked: viewMode === value
285
- }, icon, [
286
- `${viewMode} mode label`,
287
- {
288
- ns: translationKey
155
+ var logChanges = (trs) => {
156
+ const changes = trs.flatMap((tr) => {
157
+ if (tr.changes.empty) {
158
+ return void 0;
289
159
  }
290
- ], `view-mode--${viewMode}`);
291
- });
292
- var createViewMode = (state) => {
293
- const value = state.viewMode ?? "source";
294
- const viewModeGroupAction = createViewModeGroupAction(value);
295
- const viewModeActions = createViewModeActions(value);
296
- return {
297
- nodes: [
298
- viewModeGroupAction,
299
- ...viewModeActions
300
- ],
301
- edges: [
302
- {
303
- source: "root",
304
- target: "viewMode"
305
- },
306
- ...viewModeActions.map(({ id }) => ({
307
- source: viewModeGroupAction.id,
308
- target: id
309
- }))
310
- ]
160
+ const changes2 = [];
161
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
162
+ fromA,
163
+ toA,
164
+ fromB,
165
+ toB,
166
+ inserted: inserted.toString()
167
+ })));
168
+ return changes2;
169
+ }).filter(Boolean);
170
+ if (changes.length) {
171
+ log("changes", {
172
+ changes
173
+ }, {
174
+ F: __dxlog_file,
175
+ L: 62,
176
+ S: void 0,
177
+ C: (f, a) => f(...a)
178
+ });
179
+ }
180
+ };
181
+
182
+ // packages/ui/react-ui-editor/src/util/dom.ts
183
+ var flattenRect = (rect, left) => {
184
+ const x = left ? rect.left : rect.right;
185
+ return {
186
+ left: x,
187
+ right: x,
188
+ top: rect.top,
189
+ bottom: rect.bottom
311
190
  };
312
191
  };
192
+ var scratchRange;
193
+ var textRange = (node, from, to = from) => {
194
+ const range = scratchRange || (scratchRange = document.createRange());
195
+ range.setEnd(node, to);
196
+ range.setStart(node, from);
197
+ return range;
198
+ };
199
+ var clientRectsFor = (dom) => {
200
+ if (dom.nodeType === 3) {
201
+ return textRange(dom, 0, dom.nodeValue.length).getClientRects();
202
+ } else if (dom.nodeType === 1) {
203
+ return dom.getClientRects();
204
+ } else {
205
+ return [];
206
+ }
207
+ };
313
208
 
314
- // packages/ui/react-ui-editor/src/defaults.ts
315
- import { EditorView } from "@codemirror/view";
316
- import { mx as mx2 } from "@dxos/react-ui-theme";
209
+ // packages/ui/react-ui-editor/src/util/react.tsx
210
+ import React from "react";
211
+ import { createRoot } from "react-dom/client";
212
+ import { ThemeProvider, Tooltip } from "@dxos/react-ui";
213
+ import { defaultTx } from "@dxos/react-ui-theme";
214
+ var createElement = (tag, options, children) => {
215
+ const el = document.createElement(tag);
216
+ if (options?.className) {
217
+ el.className = options.className;
218
+ }
219
+ if (children) {
220
+ el.append(...Array.isArray(children) ? children : [
221
+ children
222
+ ]);
223
+ }
224
+ return el;
225
+ };
226
+ var renderRoot = (root, node) => {
227
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
228
+ tx: defaultTx
229
+ }, node));
230
+ return root;
231
+ };
232
+ var createRenderer = (Component) => (el, props) => {
233
+ renderRoot(el, /* @__PURE__ */ React.createElement(ThemeProvider, {
234
+ tx: defaultTx
235
+ }, /* @__PURE__ */ React.createElement(Tooltip.Provider, null, /* @__PURE__ */ React.createElement(Component, props))));
236
+ };
317
237
 
318
- // packages/ui/react-ui-editor/src/styles/markdown.ts
319
- import { mx } from "@dxos/react-ui-theme";
320
- var headings = {
321
- 1: "text-4xl",
322
- 2: "text-3xl",
323
- 3: "text-2xl",
324
- 4: "text-xl",
325
- 5: "text-lg",
326
- 6: ""
238
+ // packages/ui/react-ui-editor/src/extensions/annotations.ts
239
+ var annotationMark = Decoration.mark({
240
+ class: "cm-annotation"
241
+ });
242
+ var annotations = (options = {}) => {
243
+ const match = (state) => {
244
+ const annotations2 = [];
245
+ const text = state.doc.toString();
246
+ if (options.match) {
247
+ const matches = text.matchAll(options.match);
248
+ for (const match2 of matches) {
249
+ const from = match2.index;
250
+ const to = from + match2[0].length;
251
+ const cursor = Cursor.getCursorFromRange(state, {
252
+ from,
253
+ to
254
+ });
255
+ annotations2.push({
256
+ cursor
257
+ });
258
+ }
259
+ }
260
+ return annotations2;
261
+ };
262
+ const annotationsState = StateField.define({
263
+ create: (state) => {
264
+ return match(state);
265
+ },
266
+ update: (value, tr) => {
267
+ if (!tr.changes.empty) {
268
+ return match(tr.state);
269
+ }
270
+ return value;
271
+ }
272
+ });
273
+ return [
274
+ annotationsState,
275
+ EditorView.decorations.compute([
276
+ annotationsState
277
+ ], (state) => {
278
+ const annotations2 = state.field(annotationsState);
279
+ const decorations = annotations2.map((annotation) => {
280
+ const range = Cursor.getRangeFromCursor(state, annotation.cursor);
281
+ return range && annotationMark.range(range.from, range.to);
282
+ }).filter(isNotFalsy);
283
+ return Decoration.set(decorations);
284
+ }),
285
+ styles
286
+ ];
327
287
  };
328
- var theme = {
329
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
330
- codeMark: "font-mono text-primary-500",
331
- mark: "opacity-50",
332
- heading: (level) => {
333
- return mx(headings[level], "dark:text-primary-400");
288
+ var styles = EditorView.theme({
289
+ ".cm-annotation": {
290
+ textDecoration: "underline",
291
+ textDecorationStyle: "wavy",
292
+ textDecorationColor: "var(--dx-error)"
334
293
  }
335
- };
294
+ });
336
295
 
337
- // packages/ui/react-ui-editor/src/styles/tokens.ts
338
- import get from "lodash.get";
339
- import { tokens } from "@dxos/react-ui-theme";
340
- var getToken = (path, defaultValue) => {
341
- const value = get(tokens, path, defaultValue);
342
- return value?.toString() ?? "";
296
+ // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
297
+ import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
298
+ import { markdownLanguage } from "@codemirror/lang-markdown";
299
+ import { keymap } from "@codemirror/view";
300
+ var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
301
+ const extensions = [
302
+ // https://codemirror.net/docs/ref/#view.keymap
303
+ // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
304
+ // TODO(burdon): Set custom keymap.
305
+ keymap.of(completionKeymap),
306
+ // https://codemirror.net/examples/autocompletion
307
+ // https://codemirror.net/docs/ref/#autocomplete.autocompletion
308
+ autocompletion({
309
+ activateOnTyping,
310
+ override,
311
+ closeOnBlur: !debug,
312
+ tooltipClass: () => "shadow rounded"
313
+ })
314
+ ];
315
+ if (onSearch) {
316
+ extensions.push(
317
+ // TODO(burdon): Optional decoration via addToOptions
318
+ markdownLanguage.data.of({
319
+ autocomplete: (context) => {
320
+ const match = context.matchBefore(/\w*/);
321
+ if (!match || match.from === match.to && !context.explicit) {
322
+ return null;
323
+ }
324
+ return {
325
+ from: match.from,
326
+ options: onSearch(match.text.toLowerCase())
327
+ };
328
+ }
329
+ })
330
+ );
331
+ }
332
+ return extensions;
343
333
  };
344
- var fontBody = getToken("fontFamily.body");
345
- var fontMono = getToken("fontFamily.mono");
346
334
 
347
- // packages/ui/react-ui-editor/src/styles/theme.ts
348
- var defaultTheme = {
349
- "&": {},
350
- "&.cm-focused": {
351
- outline: "none"
352
- },
353
- /**
354
- * Scroller
355
- */
356
- ".cm-scroller": {
357
- overflowY: "auto"
358
- },
359
- /**
360
- * Content
361
- * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
362
- */
363
- ".cm-content": {
364
- padding: "unset",
365
- fontFamily: fontBody,
366
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
367
- fontSize: "16px",
368
- lineHeight: 1.5,
369
- color: "unset"
370
- },
371
- /**
372
- * Gutters
373
- * NOTE: Gutters should have the same top margin as the content.
374
- */
375
- ".cm-gutters": {
376
- borderRight: "none",
377
- background: "transparent"
378
- },
379
- ".cm-gutter": {},
380
- ".cm-gutter.cm-lineNumbers": {
381
- paddingRight: "4px",
382
- borderRight: "1px solid var(--dx-separator)"
383
- },
384
- ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
385
- minWidth: "40px",
386
- alignContent: "center"
387
- },
388
- /**
389
- * Height is set to match the corresponding line.
390
- */
391
- ".cm-gutterElement": {
392
- alignItems: "center",
393
- fontSize: "12px"
394
- },
395
- /**
396
- * Line.
397
- */
398
- ".cm-line": {
399
- paddingInline: 0
400
- },
401
- ".cm-activeLine": {
402
- background: "var(--dx-cmActiveLine)"
403
- },
404
- /**
405
- * Cursor (layer).
406
- */
407
- ".cm-cursor, .cm-dropCursor": {
408
- borderLeft: "2px solid var(--dx-cmCursor)"
409
- },
410
- ".cm-placeholder": {
411
- color: "var(--dx-subdued)"
412
- },
413
- /**
414
- * Selection (layer).
415
- */
416
- ".cm-selectionBackground": {
417
- background: "var(--dx-cmSelection)"
418
- },
419
- /**
420
- * Search.
421
- * NOTE: Matches comment.
422
- */
423
- ".cm-searchMatch": {
424
- margin: "0 -3px",
425
- padding: "3px",
426
- borderRadius: "3px",
427
- background: "var(--dx-cmHighlightSurface)",
428
- color: "var(--dx-cmHighlight)"
429
- },
430
- ".cm-searchMatch-selected": {
431
- textDecoration: "underline"
432
- },
433
- /**
434
- * Link.
435
- */
436
- ".cm-link": {
437
- textDecorationLine: "underline",
438
- textDecorationThickness: "1px",
439
- textDecorationColor: "var(--dx-separator)",
440
- textUnderlineOffset: "2px",
441
- borderRadius: ".125rem"
442
- },
443
- ".cm-link > span": {
444
- color: "var(--dx-accentText)"
445
- },
446
- /**
447
- * Tooltip.
448
- */
449
- ".cm-tooltip": {
450
- background: "var(--dx-baseSurface)"
451
- },
452
- ".cm-tooltip-below": {},
453
- /**
454
- * Autocomplete.
455
- * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
456
- */
457
- ".cm-tooltip.cm-tooltip-autocomplete": {
458
- marginTop: "4px",
459
- marginLeft: "-3px"
460
- },
461
- ".cm-tooltip.cm-tooltip-autocomplete > ul": {
462
- maxHeight: "20em"
463
- },
464
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
465
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
466
- ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
467
- paddingLeft: "4px !important",
468
- borderBottom: "none !important",
469
- color: "var(--dx-accentText)"
470
- },
471
- ".cm-tooltip.cm-completionInfo": {
472
- width: "360px !important",
473
- margin: "-10px 1px 0 1px",
474
- padding: "8px !important",
475
- borderColor: "var(--dx-separator)"
476
- },
477
- ".cm-completionIcon": {
478
- display: "none"
479
- },
480
- ".cm-completionLabel": {
481
- fontFamily: fontBody
482
- },
483
- ".cm-completionMatchedText": {
484
- textDecoration: "none !important",
485
- opacity: 0.5
486
- },
487
- /**
488
- * Panels
489
- * https://github.com/codemirror/search/blob/main/src/search.ts#L745
490
- *
491
- * Find/replace panel.
492
- * <div class="cm-announced">...</div>
493
- * <div class="cm-scroller">...</div>
494
- * <div class="cm-panels cm-panels-bottom">
495
- * <div class="cm-search cm-panel">
496
- * <input class="cm-textfield" />
497
- * <button class="cm-button">...</button>
498
- * <label><input type="checkbox" />...</label>
499
- * </div>
500
- * </div
501
- */
502
- // TODO(burdon): Implement custom panel (with icon buttons).
503
- ".cm-panels": {},
504
- ".cm-panel": {
505
- fontFamily: fontBody,
506
- backgroundColor: "var(--surface-bg)"
507
- },
508
- ".cm-panel input, .cm-panel button, .cm-panel label": {
509
- color: "var(--dx-subdued)",
510
- fontFamily: fontBody,
511
- fontSize: "14px",
512
- all: "unset",
513
- margin: "3px !important",
514
- padding: "2px 6px !important",
515
- outline: "1px solid transparent"
516
- },
517
- ".cm-panel input, .cm-panel button": {
518
- backgroundColor: "var(--dx-input)"
519
- },
520
- ".cm-panel input:focus, .cm-panel button:focus": {
521
- outline: "1px solid var(--dx-accentFocusIndicator)"
522
- },
523
- ".cm-panel label": {
524
- display: "inline-flex",
525
- alignItems: "center",
526
- cursor: "pointer"
527
- },
528
- ".cm-panel input.cm-textfield": {},
529
- ".cm-panel input[type=checkbox]": {
530
- width: "8px",
531
- height: "8px",
532
- marginRight: "6px !important",
533
- padding: "2px !important",
534
- color: "var(--dx-accentFocusIndicator)"
535
- },
536
- ".cm-panel button": {
537
- "&:hover": {
538
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
539
- },
540
- "&:active": {
541
- backgroundColor: "var(--dx-accentSurfaceHover)"
542
- }
543
- },
544
- ".cm-panel.cm-search": {
545
- padding: "4px",
546
- borderTop: "1px solid var(--dx-separator)"
547
- }
548
- };
549
-
550
- // packages/ui/react-ui-editor/src/defaults.ts
551
- var margin = "!mt-[1rem]";
552
- var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
553
- var editorContent = mx2(margin, editorWidth);
554
- var editorFullWidth = mx2(margin);
555
- var editorGutter = EditorView.theme({
556
- // Match margin from content.
557
- // Gutter = 2rem + 1rem margin.
558
- ".cm-gutters": {
559
- marginTop: "1rem",
560
- paddingRight: "1rem"
561
- }
562
- });
563
- var editorMonospace = EditorView.theme({
564
- ".cm-content": {
565
- fontFamily: fontMono
566
- }
567
- });
568
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
569
- var stackItemContentEditorClassNames = (role) => mx2("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");
570
- var stackItemContentToolbarClassNames = (role) => mx2("attention-surface is-full border-be !border-separator relative z-[1]", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
571
-
572
- // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
573
- var createToolbar = ({ state, customActions, ...features }) => {
574
- const nodes = [];
575
- const edges = [];
576
- if (features.headings ?? true) {
577
- const headings2 = createHeadings(state);
578
- nodes.push(...headings2.nodes);
579
- edges.push(...headings2.edges);
580
- }
581
- if (features.formatting ?? true) {
582
- const formatting = createFormatting(state);
583
- nodes.push(...formatting.nodes);
584
- edges.push(...formatting.edges);
585
- }
586
- if (features.lists ?? true) {
587
- const lists = createLists(state);
588
- nodes.push(...lists.nodes);
589
- edges.push(...lists.edges);
590
- }
591
- if (features.blocks ?? true) {
592
- const blocks = createBlocks(state);
593
- nodes.push(...blocks.nodes);
594
- edges.push(...blocks.edges);
595
- }
596
- if (customActions) {
597
- const custom = customActions();
598
- nodes.push(...custom.nodes);
599
- edges.push(...custom.edges);
600
- }
601
- const editorToolbarGap = createGapSeparator();
602
- nodes.push(...editorToolbarGap.nodes);
603
- edges.push(...editorToolbarGap.edges);
604
- if (features.comment ?? true) {
605
- const comment = createComment(state);
606
- nodes.push(...comment.nodes);
607
- edges.push(...comment.edges);
608
- }
609
- if (features.search ?? true) {
610
- nodes.push(editorToolbarSearch);
611
- edges.push({
612
- source: "root",
613
- target: editorToolbarSearch.id
614
- });
615
- }
616
- if (features.viewMode ?? true) {
617
- const viewMode = createViewMode(state);
618
- nodes.push(...viewMode.nodes);
619
- edges.push(...viewMode.edges);
620
- }
621
- return {
622
- nodes,
623
- edges
624
- };
625
- };
626
- var useEditorToolbarActionGraph = ({ onAction, ...props }) => {
627
- const menuCreator = useCallback(() => createToolbar(props), [
628
- props
629
- ]);
630
- const { resolveGroupItems } = useMenuActions(menuCreator);
631
- return {
632
- resolveGroupItems,
633
- onAction
634
- };
635
- };
636
- var EditorToolbar = ({ classNames, attendableId, role, ...props }) => {
637
- const menuProps = useEditorToolbarActionGraph(props);
638
- return /* @__PURE__ */ React.createElement("div", {
639
- role: "none",
640
- className: stackItemContentToolbarClassNames(role)
641
- }, /* @__PURE__ */ React.createElement(ElevationProvider, {
642
- elevation: role === "section" ? "positioned" : "base"
643
- }, /* @__PURE__ */ React.createElement(MenuProvider, {
644
- ...menuProps,
645
- attendableId
646
- }, /* @__PURE__ */ React.createElement(ToolbarMenu, {
647
- classNames: [
648
- textBlockWidth,
649
- "!bg-transparent",
650
- classNames
651
- ]
652
- }))));
653
- };
654
-
655
- // packages/ui/react-ui-editor/src/extensions/annotations.ts
656
- import { StateField } from "@codemirror/state";
657
- import { Decoration, EditorView as EditorView2 } from "@codemirror/view";
658
- import { isNotFalsy } from "@dxos/util";
659
-
660
- // packages/ui/react-ui-editor/src/util/facet.ts
661
- import { Facet } from "@codemirror/state";
662
- var singleValueFacet = (defaultValue) => Facet.define({
663
- // Called immediately.
664
- combine: (providers) => {
665
- return providers[0] ?? defaultValue;
666
- }
667
- });
668
-
669
- // packages/ui/react-ui-editor/src/util/cursor.ts
670
- var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
671
- var defaultCursorConverter = {
672
- toCursor: (position) => position.toString(),
673
- fromCursor: (cursor) => parseInt(cursor)
674
- };
675
- var Cursor = class _Cursor {
676
- static {
677
- this.converter = singleValueFacet(defaultCursorConverter);
678
- }
679
- static {
680
- this.getCursorFromRange = (state, range) => {
681
- const cursorConverter2 = state.facet(_Cursor.converter);
682
- const from = cursorConverter2.toCursor(range.from);
683
- const to = cursorConverter2.toCursor(range.to, -1);
684
- return [
685
- from,
686
- to
687
- ].join(":");
688
- };
689
- }
690
- static {
691
- this.getRangeFromCursor = (state, cursor) => {
692
- const cursorConverter2 = state.facet(_Cursor.converter);
693
- const parts = cursor.split(":");
694
- const from = cursorConverter2.fromCursor(parts[0]);
695
- const to = cursorConverter2.fromCursor(parts[1]);
696
- return from !== void 0 && to !== void 0 ? {
697
- from,
698
- to
699
- } : void 0;
700
- };
701
- }
702
- };
703
-
704
- // packages/ui/react-ui-editor/src/util/debug.ts
705
- import { log } from "@dxos/log";
706
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
707
- var wrapWithCatch = (fn) => {
708
- return (...args) => {
709
- try {
710
- return fn(...args);
711
- } catch (err) {
712
- log.catch(err, void 0, {
713
- F: __dxlog_file,
714
- L: 15,
715
- S: void 0,
716
- C: (f, a) => f(...a)
717
- });
718
- }
719
- };
720
- };
721
- var callbackWrapper = (fn) => (...args) => {
722
- try {
723
- return fn(...args);
724
- } catch (err) {
725
- log.catch(err, void 0, {
726
- F: __dxlog_file,
727
- L: 29,
728
- S: void 0,
729
- C: (f, a) => f(...a)
730
- });
731
- }
732
- };
733
- var debugDispatcher = (trs, view) => {
734
- logChanges(trs);
735
- view.update(trs);
736
- };
737
- var logChanges = (trs) => {
738
- const changes = trs.flatMap((tr) => {
739
- if (tr.changes.empty) {
740
- return void 0;
741
- }
742
- const changes2 = [];
743
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
744
- fromA,
745
- toA,
746
- fromB,
747
- toB,
748
- inserted: inserted.toString()
749
- })));
750
- return changes2;
751
- }).filter(Boolean);
752
- if (changes.length) {
753
- log("changes", {
754
- changes
755
- }, {
756
- F: __dxlog_file,
757
- L: 62,
758
- S: void 0,
759
- C: (f, a) => f(...a)
760
- });
761
- }
762
- };
763
-
764
- // packages/ui/react-ui-editor/src/util/dom.ts
765
- var flattenRect = (rect, left) => {
766
- const x = left ? rect.left : rect.right;
767
- return {
768
- left: x,
769
- right: x,
770
- top: rect.top,
771
- bottom: rect.bottom
772
- };
773
- };
774
- var scratchRange;
775
- var textRange = (node, from, to = from) => {
776
- const range = scratchRange || (scratchRange = document.createRange());
777
- range.setEnd(node, to);
778
- range.setStart(node, from);
779
- return range;
780
- };
781
- var clientRectsFor = (dom) => {
782
- if (dom.nodeType === 3) {
783
- return textRange(dom, 0, dom.nodeValue.length).getClientRects();
784
- } else if (dom.nodeType === 1) {
785
- return dom.getClientRects();
786
- } else {
787
- return [];
788
- }
789
- };
790
-
791
- // packages/ui/react-ui-editor/src/util/react.tsx
792
- import React2 from "react";
793
- import { createRoot } from "react-dom/client";
794
- import { ThemeProvider, Tooltip } from "@dxos/react-ui";
795
- import { defaultTx } from "@dxos/react-ui-theme";
796
- var createElement = (tag, options, children) => {
797
- const el = document.createElement(tag);
798
- if (options?.className) {
799
- el.className = options.className;
800
- }
801
- if (children) {
802
- el.append(...Array.isArray(children) ? children : [
803
- children
804
- ]);
805
- }
806
- return el;
807
- };
808
- var renderRoot = (root, node) => {
809
- createRoot(root).render(/* @__PURE__ */ React2.createElement(ThemeProvider, {
810
- tx: defaultTx
811
- }, node));
812
- return root;
813
- };
814
- var createRenderer = (Component) => (el, props) => {
815
- renderRoot(el, /* @__PURE__ */ React2.createElement(ThemeProvider, {
816
- tx: defaultTx
817
- }, /* @__PURE__ */ React2.createElement(Tooltip.Provider, null, /* @__PURE__ */ React2.createElement(Component, props))));
818
- };
819
-
820
- // packages/ui/react-ui-editor/src/extensions/annotations.ts
821
- var annotationMark = Decoration.mark({
822
- class: "cm-annotation"
823
- });
824
- var annotations = (options = {}) => {
825
- const match = (state) => {
826
- const annotations2 = [];
827
- const text = state.doc.toString();
828
- if (options.match) {
829
- const matches = text.matchAll(options.match);
830
- for (const match2 of matches) {
831
- const from = match2.index;
832
- const to = from + match2[0].length;
833
- const cursor = Cursor.getCursorFromRange(state, {
834
- from,
835
- to
836
- });
837
- annotations2.push({
838
- cursor
839
- });
840
- }
841
- }
842
- return annotations2;
843
- };
844
- const annotationsState = StateField.define({
845
- create: (state) => {
846
- return match(state);
847
- },
848
- update: (value, tr) => {
849
- if (!tr.changes.empty) {
850
- return match(tr.state);
851
- }
852
- return value;
853
- }
854
- });
855
- return [
856
- annotationsState,
857
- EditorView2.decorations.compute([
858
- annotationsState
859
- ], (state) => {
860
- const annotations2 = state.field(annotationsState);
861
- const decorations = annotations2.map((annotation) => {
862
- const range = Cursor.getRangeFromCursor(state, annotation.cursor);
863
- return range && annotationMark.range(range.from, range.to);
864
- }).filter(isNotFalsy);
865
- return Decoration.set(decorations);
866
- }),
867
- styles
868
- ];
869
- };
870
- var styles = EditorView2.theme({
871
- ".cm-annotation": {
872
- textDecoration: "underline",
873
- textDecorationStyle: "wavy",
874
- textDecorationColor: "var(--dx-error)"
875
- }
876
- });
877
-
878
- // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
879
- import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
880
- import { markdownLanguage } from "@codemirror/lang-markdown";
881
- import { keymap } from "@codemirror/view";
882
- var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
883
- const extensions = [
884
- // https://codemirror.net/docs/ref/#view.keymap
885
- // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
886
- // TODO(burdon): Set custom keymap.
887
- keymap.of(completionKeymap),
888
- // https://codemirror.net/examples/autocompletion
889
- // https://codemirror.net/docs/ref/#autocomplete.autocompletion
890
- autocompletion({
891
- activateOnTyping,
892
- override,
893
- closeOnBlur: !debug,
894
- tooltipClass: () => "shadow rounded"
895
- })
896
- ];
897
- if (onSearch) {
898
- extensions.push(
899
- // TODO(burdon): Optional decoration via addToOptions
900
- markdownLanguage.data.of({
901
- autocomplete: (context) => {
902
- const match = context.matchBefore(/\w*/);
903
- if (!match || match.from === match.to && !context.explicit) {
904
- return null;
905
- }
906
- return {
907
- from: match.from,
908
- options: onSearch(match.text.toLowerCase())
909
- };
910
- }
911
- })
912
- );
913
- }
914
- return extensions;
915
- };
916
-
917
- // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
918
- import { StateField as StateField2 } from "@codemirror/state";
919
- import { EditorView as EditorView3, ViewPlugin } from "@codemirror/view";
920
- import { next as A3 } from "@dxos/automerge/automerge";
921
-
922
- // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
923
- import { log as log2 } from "@dxos/log";
924
- import { fromCursor, toCursor } from "@dxos/react-client/echo";
925
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts";
926
- var cursorConverter = (accessor) => ({
927
- toCursor: (pos, assoc) => {
928
- try {
929
- return toCursor(accessor, pos, assoc);
930
- } catch (err) {
931
- log2.catch(err, void 0, {
932
- F: __dxlog_file2,
933
- L: 15,
934
- S: void 0,
935
- C: (f, a) => f(...a)
936
- });
937
- return "";
938
- }
335
+ // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
336
+ import { StateField as StateField2 } from "@codemirror/state";
337
+ import { EditorView as EditorView2, ViewPlugin } from "@codemirror/view";
338
+ import { next as A3 } from "@dxos/automerge/automerge";
339
+
340
+ // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
341
+ import { log as log2 } from "@dxos/log";
342
+ import { fromCursor, toCursor } from "@dxos/react-client/echo";
343
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts";
344
+ var cursorConverter = (accessor) => ({
345
+ toCursor: (pos, assoc) => {
346
+ try {
347
+ return toCursor(accessor, pos, assoc);
348
+ } catch (err) {
349
+ log2.catch(err, void 0, {
350
+ F: __dxlog_file2,
351
+ L: 15,
352
+ S: void 0,
353
+ C: (f, a) => f(...a)
354
+ });
355
+ return "";
356
+ }
939
357
  },
940
358
  fromCursor: (cursor) => {
941
359
  try {
@@ -1200,7 +618,7 @@ var automerge = (accessor) => {
1200
618
  }
1201
619
  }),
1202
620
  // Reconcile local updates.
1203
- EditorView3.updateListener.of(({ view, changes }) => {
621
+ EditorView2.updateListener.of(({ view, changes }) => {
1204
622
  if (!changes.empty) {
1205
623
  syncer.reconcile(view, true);
1206
624
  }
@@ -1210,7 +628,7 @@ var automerge = (accessor) => {
1210
628
 
1211
629
  // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
1212
630
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1213
- import { Decoration as Decoration2, EditorView as EditorView4, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
631
+ import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
1214
632
  import { Event } from "@dxos/async";
1215
633
  import { Context } from "@dxos/context";
1216
634
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts";
@@ -1384,7 +802,7 @@ var RemoteCaretWidget = class extends WidgetType {
1384
802
  return true;
1385
803
  }
1386
804
  };
1387
- var styles2 = EditorView4.theme({
805
+ var styles2 = EditorView3.theme({
1388
806
  ".cm-collab-selection": {},
1389
807
  ".cm-collab-selectionLine": {
1390
808
  padding: 0,
@@ -1550,7 +968,7 @@ var SpaceAwarenessProvider = class {
1550
968
  };
1551
969
 
1552
970
  // packages/ui/react-ui-editor/src/extensions/blast.ts
1553
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
971
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
1554
972
  import defaultsDeep from "lodash.defaultsdeep";
1555
973
  import { invariant as invariant2 } from "@dxos/invariant";
1556
974
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -1598,7 +1016,7 @@ var blast = (options = defaultOptions) => {
1598
1016
  };
1599
1017
  return [
1600
1018
  // Cursor moved.
1601
- EditorView5.updateListener.of((update2) => {
1019
+ EditorView4.updateListener.of((update2) => {
1602
1020
  if (blaster?.node !== update2.view.scrollDOM) {
1603
1021
  if (blaster) {
1604
1022
  blaster.destroy();
@@ -1972,11 +1390,11 @@ var commandKeyBindings = [
1972
1390
  ];
1973
1391
 
1974
1392
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1975
- import { EditorView as EditorView7, keymap as keymap3 } from "@codemirror/view";
1393
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1976
1394
 
1977
1395
  // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1978
1396
  import { RangeSetBuilder } from "@codemirror/state";
1979
- import { Decoration as Decoration3, EditorView as EditorView6, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1397
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1980
1398
  var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1981
1399
  constructor() {
1982
1400
  this.deco = Decoration3.none;
@@ -2000,7 +1418,7 @@ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
2000
1418
  }
2001
1419
  }, {
2002
1420
  provide: (plugin) => [
2003
- EditorView6.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1421
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
2004
1422
  ]
2005
1423
  });
2006
1424
  var CommandHint = class extends WidgetType2 {
@@ -2119,10 +1537,10 @@ var command = (options = {}) => {
2119
1537
  options.onHint ? hintViewPlugin({
2120
1538
  onHint: options.onHint
2121
1539
  }) : [],
2122
- EditorView7.focusChangeEffect.of((_, focusing) => {
1540
+ EditorView6.focusChangeEffect.of((_, focusing) => {
2123
1541
  return focusing ? closeEffect.of(null) : null;
2124
1542
  }),
2125
- EditorView7.theme({
1543
+ EditorView6.theme({
2126
1544
  ".cm-tooltip": {
2127
1545
  background: "transparent"
2128
1546
  }
@@ -2133,7 +1551,7 @@ var command = (options = {}) => {
2133
1551
  // packages/ui/react-ui-editor/src/extensions/comments.ts
2134
1552
  import { invertedEffects } from "@codemirror/commands";
2135
1553
  import { StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
2136
- import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView9, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1554
+ import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView8, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2137
1555
  import sortBy from "lodash.sortby";
2138
1556
  import { useEffect, useMemo as useMemo2 } from "react";
2139
1557
  import { debounce as debounce2 } from "@dxos/async";
@@ -2142,7 +1560,7 @@ import { isNonNullable } from "@dxos/util";
2142
1560
 
2143
1561
  // packages/ui/react-ui-editor/src/extensions/selection.ts
2144
1562
  import { Transaction } from "@codemirror/state";
2145
- import { EditorView as EditorView8, keymap as keymap4 } from "@codemirror/view";
1563
+ import { EditorView as EditorView7, keymap as keymap4 } from "@codemirror/view";
2146
1564
  import { debounce } from "@dxos/async";
2147
1565
  import { invariant as invariant3 } from "@dxos/invariant";
2148
1566
  import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
@@ -2153,7 +1571,7 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
2153
1571
  return {
2154
1572
  selection,
2155
1573
  scrollIntoView: !scrollTo,
2156
- effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1574
+ effects: scrollTo ? EditorView7.scrollIntoView(scrollTo, {
2157
1575
  yMargin: 96
2158
1576
  }) : void 0,
2159
1577
  annotations: Transaction.userEvent.of(stateRestoreAnnotation)
@@ -2195,7 +1613,7 @@ var selectionState = ({ getState, setState } = {}) => {
2195
1613
  // setStateDebounced(id, {});
2196
1614
  // },
2197
1615
  // }),
2198
- EditorView8.updateListener.of(({ view, transactions }) => {
1616
+ EditorView7.updateListener.of(({ view, transactions }) => {
2199
1617
  const id = view.state.facet(documentId);
2200
1618
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
2201
1619
  return;
@@ -2276,7 +1694,7 @@ var commentsState = StateField4.define({
2276
1694
  return value;
2277
1695
  }
2278
1696
  });
2279
- var styles3 = EditorView9.theme({
1697
+ var styles3 = EditorView8.theme({
2280
1698
  ".cm-comment, .cm-comment-current": {
2281
1699
  margin: "0 -3px",
2282
1700
  padding: "3px",
@@ -2296,7 +1714,7 @@ var createCommentMark = (id, isCurrent) => Decoration4.mark({
2296
1714
  "data-comment-id": id
2297
1715
  }
2298
1716
  });
2299
- var commentsDecorations = EditorView9.decorations.compute([
1717
+ var commentsDecorations = EditorView8.decorations.compute([
2300
1718
  commentsState
2301
1719
  ], (state) => {
2302
1720
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -2319,7 +1737,7 @@ var commentsDecorations = EditorView9.decorations.compute([
2319
1737
  return Decoration4.set(decorations);
2320
1738
  });
2321
1739
  var commentClickedEffect = StateEffect3.define();
2322
- var handleCommentClick = EditorView9.domEventHandlers({
1740
+ var handleCommentClick = EditorView8.domEventHandlers({
2323
1741
  click: (event, view) => {
2324
1742
  let target = event.target;
2325
1743
  const editorRoot = view.dom;
@@ -2358,7 +1776,7 @@ var trackPastedComments = (onUpdate) => {
2358
1776
  }
2359
1777
  };
2360
1778
  return [
2361
- EditorView9.domEventHandlers({
1779
+ EditorView8.domEventHandlers({
2362
1780
  cut: handleTrack,
2363
1781
  copy: handleTrack
2364
1782
  }),
@@ -2380,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2380
1798
  return effects;
2381
1799
  }),
2382
1800
  // Handle paste or the undo of comment deletion.
2383
- EditorView9.updateListener.of((update2) => {
1801
+ EditorView8.updateListener.of((update2) => {
2384
1802
  const restore = [];
2385
1803
  for (let i = 0; i < update2.transactions.length; i++) {
2386
1804
  const tr = update2.transactions[i];
@@ -2439,7 +1857,7 @@ var mapTrackedComment = (comment, changes) => ({
2439
1857
  var restoreCommentEffect = StateEffect3.define({
2440
1858
  map: mapTrackedComment
2441
1859
  });
2442
- var createComment2 = (view) => {
1860
+ var createComment = (view) => {
2443
1861
  const options = view.state.facet(optionsFacet);
2444
1862
  const { from, to } = view.state.selection.main;
2445
1863
  if (from === to) {
@@ -2484,7 +1902,7 @@ var comments = (options = {}) => {
2484
1902
  options.onCreate && keymap5.of([
2485
1903
  {
2486
1904
  key: shortcut,
2487
- run: callbackWrapper(createComment2)
1905
+ run: callbackWrapper(createComment)
2488
1906
  }
2489
1907
  ]),
2490
1908
  //
@@ -2522,7 +1940,7 @@ var comments = (options = {}) => {
2522
1940
  //
2523
1941
  // Track deleted ranges and update ranges for decorations.
2524
1942
  //
2525
- EditorView9.updateListener.of(({ view, state, changes }) => {
1943
+ EditorView8.updateListener.of(({ view, state, changes }) => {
2526
1944
  let mod = false;
2527
1945
  const { comments: comments2, ...value } = state.field(commentsState);
2528
1946
  changes.iterChanges((from, to, from2, to2) => {
@@ -2554,7 +1972,7 @@ var comments = (options = {}) => {
2554
1972
  //
2555
1973
  // Track selection/proximity.
2556
1974
  //
2557
- EditorView9.updateListener.of(({ view, state }) => {
1975
+ EditorView8.updateListener.of(({ view, state }) => {
2558
1976
  let min = Infinity;
2559
1977
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2560
1978
  const { head } = state.selection.main;
@@ -2608,7 +2026,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2608
2026
  anchor: range.from
2609
2027
  } : void 0,
2610
2028
  effects: [
2611
- needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2029
+ needsScroll ? EditorView8.scrollIntoView(range.from, center ? {
2612
2030
  y: "center"
2613
2031
  } : void 0) : [],
2614
2032
  needsSelectionUpdate ? setSelection.of({
@@ -2660,7 +2078,7 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin5.from
2660
2078
  }
2661
2079
  });
2662
2080
  var useCommentState = (state) => {
2663
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2081
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2664
2082
  if (update2.docChanged || update2.selectionSet) {
2665
2083
  state.comment = selectionOverlapsComment(update2.state);
2666
2084
  state.selection = hasActiveSelection(update2.state);
@@ -2684,7 +2102,7 @@ var useComments = (view, id, comments2) => {
2684
2102
  });
2685
2103
  };
2686
2104
  var useCommentClickListener = (onCommentClick) => {
2687
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2105
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2688
2106
  update2.transactions.forEach((transaction) => {
2689
2107
  transaction.effects.forEach((effect) => {
2690
2108
  if (effect.is(commentClickedEffect)) {
@@ -2711,8 +2129,8 @@ var debugNodeLogger = (log8 = console.log) => {
2711
2129
  };
2712
2130
 
2713
2131
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
2714
- import { dropCursor, EditorView as EditorView10 } from "@codemirror/view";
2715
- var styles4 = EditorView10.theme({
2132
+ import { dropCursor, EditorView as EditorView9 } from "@codemirror/view";
2133
+ var styles4 = EditorView9.theme({
2716
2134
  ".cm-dropCursor": {
2717
2135
  borderLeft: "2px solid var(--dx-accentText)",
2718
2136
  color: "var(--dx-accentText)",
@@ -2726,7 +2144,7 @@ var dropFile = (options = {}) => {
2726
2144
  return [
2727
2145
  styles4,
2728
2146
  dropCursor(),
2729
- EditorView10.domEventHandlers({
2147
+ EditorView9.domEventHandlers({
2730
2148
  drop: (event, view) => {
2731
2149
  event.preventDefault();
2732
2150
  const files = event.dataTransfer?.files;
@@ -2753,7 +2171,7 @@ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@cod
2753
2171
  import { searchKeymap } from "@codemirror/search";
2754
2172
  import { EditorState } from "@codemirror/state";
2755
2173
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2756
- import { EditorView as EditorView12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2174
+ import { EditorView as EditorView11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2757
2175
  import defaultsDeep2 from "lodash.defaultsdeep";
2758
2176
  import merge from "lodash.merge";
2759
2177
  import { generateName } from "@dxos/display-name";
@@ -2762,7 +2180,7 @@ import { hexToHue, isNotFalsy as isNotFalsy3 } from "@dxos/util";
2762
2180
 
2763
2181
  // packages/ui/react-ui-editor/src/extensions/focus.ts
2764
2182
  import { StateEffect as StateEffect4, StateField as StateField6 } from "@codemirror/state";
2765
- import { EditorView as EditorView11 } from "@codemirror/view";
2183
+ import { EditorView as EditorView10 } from "@codemirror/view";
2766
2184
  var focusEffect = StateEffect4.define();
2767
2185
  var focusField = StateField6.define({
2768
2186
  create: () => false,
@@ -2777,19 +2195,251 @@ var focusField = StateField6.define({
2777
2195
  });
2778
2196
  var focus = [
2779
2197
  focusField,
2780
- EditorView11.domEventHandlers({
2198
+ EditorView10.domEventHandlers({
2781
2199
  focus: (event, view) => {
2782
2200
  setTimeout(() => view.dispatch({
2783
2201
  effects: focusEffect.of(true)
2784
2202
  }));
2785
2203
  },
2786
- blur: (event, view) => {
2787
- setTimeout(() => view.dispatch({
2788
- effects: focusEffect.of(false)
2789
- }));
2204
+ blur: (event, view) => {
2205
+ setTimeout(() => view.dispatch({
2206
+ effects: focusEffect.of(false)
2207
+ }));
2208
+ }
2209
+ })
2210
+ ];
2211
+
2212
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
2213
+ import { mx } from "@dxos/react-ui-theme";
2214
+ var headings = {
2215
+ 1: "text-4xl",
2216
+ 2: "text-3xl",
2217
+ 3: "text-2xl",
2218
+ 4: "text-xl",
2219
+ 5: "text-lg",
2220
+ 6: ""
2221
+ };
2222
+ var theme = {
2223
+ code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2224
+ codeMark: "font-mono text-primary-500",
2225
+ mark: "opacity-50",
2226
+ heading: (level) => {
2227
+ return mx(headings[level], "dark:text-primary-400");
2228
+ }
2229
+ };
2230
+
2231
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
2232
+ import get from "lodash.get";
2233
+ import { tokens } from "@dxos/react-ui-theme";
2234
+ var getToken = (path, defaultValue) => {
2235
+ const value = get(tokens, path, defaultValue);
2236
+ return value?.toString() ?? "";
2237
+ };
2238
+ var fontBody = getToken("fontFamily.body");
2239
+ var fontMono = getToken("fontFamily.mono");
2240
+
2241
+ // packages/ui/react-ui-editor/src/styles/theme.ts
2242
+ var defaultTheme = {
2243
+ "&": {},
2244
+ "&.cm-focused": {
2245
+ outline: "none"
2246
+ },
2247
+ /**
2248
+ * Scroller
2249
+ */
2250
+ ".cm-scroller": {
2251
+ overflowY: "auto"
2252
+ },
2253
+ /**
2254
+ * Content
2255
+ * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
2256
+ */
2257
+ ".cm-content": {
2258
+ padding: "unset",
2259
+ fontFamily: fontBody,
2260
+ // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
2261
+ fontSize: "16px",
2262
+ lineHeight: 1.5,
2263
+ color: "unset"
2264
+ },
2265
+ /**
2266
+ * Gutters
2267
+ * NOTE: Gutters should have the same top margin as the content.
2268
+ */
2269
+ ".cm-gutters": {
2270
+ borderRight: "none",
2271
+ background: "transparent"
2272
+ },
2273
+ ".cm-gutter": {},
2274
+ ".cm-gutter.cm-lineNumbers": {
2275
+ paddingRight: "4px",
2276
+ borderRight: "1px solid var(--dx-separator)"
2277
+ },
2278
+ ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2279
+ minWidth: "40px",
2280
+ alignContent: "center"
2281
+ },
2282
+ /**
2283
+ * Height is set to match the corresponding line.
2284
+ */
2285
+ ".cm-gutterElement": {
2286
+ alignItems: "center",
2287
+ fontSize: "12px"
2288
+ },
2289
+ /**
2290
+ * Line.
2291
+ */
2292
+ ".cm-line": {
2293
+ paddingInline: 0
2294
+ },
2295
+ ".cm-activeLine": {
2296
+ background: "var(--dx-cmActiveLine)"
2297
+ },
2298
+ /**
2299
+ * Cursor (layer).
2300
+ */
2301
+ ".cm-cursor, .cm-dropCursor": {
2302
+ borderLeft: "2px solid var(--dx-cmCursor)"
2303
+ },
2304
+ ".cm-placeholder": {
2305
+ color: "var(--dx-subdued)"
2306
+ },
2307
+ /**
2308
+ * Selection (layer).
2309
+ */
2310
+ ".cm-selectionBackground": {
2311
+ background: "var(--dx-cmSelection)"
2312
+ },
2313
+ /**
2314
+ * Search.
2315
+ * NOTE: Matches comment.
2316
+ */
2317
+ ".cm-searchMatch": {
2318
+ margin: "0 -3px",
2319
+ padding: "3px",
2320
+ borderRadius: "3px",
2321
+ background: "var(--dx-cmHighlightSurface)",
2322
+ color: "var(--dx-cmHighlight)"
2323
+ },
2324
+ ".cm-searchMatch-selected": {
2325
+ textDecoration: "underline"
2326
+ },
2327
+ /**
2328
+ * Link.
2329
+ */
2330
+ ".cm-link": {
2331
+ textDecorationLine: "underline",
2332
+ textDecorationThickness: "1px",
2333
+ textDecorationColor: "var(--dx-separator)",
2334
+ textUnderlineOffset: "2px",
2335
+ borderRadius: ".125rem"
2336
+ },
2337
+ ".cm-link > span": {
2338
+ color: "var(--dx-accentText)"
2339
+ },
2340
+ /**
2341
+ * Tooltip.
2342
+ */
2343
+ ".cm-tooltip": {
2344
+ background: "var(--dx-baseSurface)"
2345
+ },
2346
+ ".cm-tooltip-below": {},
2347
+ /**
2348
+ * Autocomplete.
2349
+ * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
2350
+ */
2351
+ ".cm-tooltip.cm-tooltip-autocomplete": {
2352
+ marginTop: "4px",
2353
+ marginLeft: "-3px"
2354
+ },
2355
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
2356
+ maxHeight: "20em"
2357
+ },
2358
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
2359
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
2360
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2361
+ paddingLeft: "4px !important",
2362
+ borderBottom: "none !important",
2363
+ color: "var(--dx-accentText)"
2364
+ },
2365
+ ".cm-tooltip.cm-completionInfo": {
2366
+ width: "360px !important",
2367
+ margin: "-10px 1px 0 1px",
2368
+ padding: "8px !important",
2369
+ borderColor: "var(--dx-separator)"
2370
+ },
2371
+ ".cm-completionIcon": {
2372
+ display: "none"
2373
+ },
2374
+ ".cm-completionLabel": {
2375
+ fontFamily: fontBody
2376
+ },
2377
+ ".cm-completionMatchedText": {
2378
+ textDecoration: "none !important",
2379
+ opacity: 0.5
2380
+ },
2381
+ /**
2382
+ * Panels
2383
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
2384
+ *
2385
+ * Find/replace panel.
2386
+ * <div class="cm-announced">...</div>
2387
+ * <div class="cm-scroller">...</div>
2388
+ * <div class="cm-panels cm-panels-bottom">
2389
+ * <div class="cm-search cm-panel">
2390
+ * <input class="cm-textfield" />
2391
+ * <button class="cm-button">...</button>
2392
+ * <label><input type="checkbox" />...</label>
2393
+ * </div>
2394
+ * </div
2395
+ */
2396
+ // TODO(burdon): Implement custom panel (with icon buttons).
2397
+ ".cm-panels": {},
2398
+ ".cm-panel": {
2399
+ fontFamily: fontBody,
2400
+ backgroundColor: "var(--surface-bg)"
2401
+ },
2402
+ ".cm-panel input, .cm-panel button, .cm-panel label": {
2403
+ color: "var(--dx-subdued)",
2404
+ fontFamily: fontBody,
2405
+ fontSize: "14px",
2406
+ all: "unset",
2407
+ margin: "3px !important",
2408
+ padding: "2px 6px !important",
2409
+ outline: "1px solid transparent"
2410
+ },
2411
+ ".cm-panel input, .cm-panel button": {
2412
+ backgroundColor: "var(--dx-input)"
2413
+ },
2414
+ ".cm-panel input:focus, .cm-panel button:focus": {
2415
+ outline: "1px solid var(--dx-accentFocusIndicator)"
2416
+ },
2417
+ ".cm-panel label": {
2418
+ display: "inline-flex",
2419
+ alignItems: "center",
2420
+ cursor: "pointer"
2421
+ },
2422
+ ".cm-panel input.cm-textfield": {},
2423
+ ".cm-panel input[type=checkbox]": {
2424
+ width: "8px",
2425
+ height: "8px",
2426
+ marginRight: "6px !important",
2427
+ padding: "2px !important",
2428
+ color: "var(--dx-accentFocusIndicator)"
2429
+ },
2430
+ ".cm-panel button": {
2431
+ "&:hover": {
2432
+ backgroundColor: "var(--dx-accentSurfaceHover) !important"
2433
+ },
2434
+ "&:active": {
2435
+ backgroundColor: "var(--dx-accentSurfaceHover)"
2790
2436
  }
2791
- })
2792
- ];
2437
+ },
2438
+ ".cm-panel.cm-search": {
2439
+ padding: "4px",
2440
+ borderTop: "1px solid var(--dx-separator)"
2441
+ }
2442
+ };
2793
2443
 
2794
2444
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2795
2445
  var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
@@ -2815,7 +2465,7 @@ var createBasicExtensions = (_props) => {
2815
2465
  const props = defaultsDeep2({}, _props, defaultBasicOptions);
2816
2466
  return [
2817
2467
  // NOTE: Doesn't catch errors in keymap functions.
2818
- EditorView12.exceptionSink.of((err) => {
2468
+ EditorView11.exceptionSink.of((err) => {
2819
2469
  log5.catch(err, void 0, {
2820
2470
  F: __dxlog_file8,
2821
2471
  L: 96,
@@ -2830,12 +2480,12 @@ var createBasicExtensions = (_props) => {
2830
2480
  props.drawSelection && drawSelection({
2831
2481
  cursorBlinkRate: 1200
2832
2482
  }),
2833
- props.editable !== void 0 && EditorView12.editable.of(props.editable),
2483
+ props.editable !== void 0 && EditorView11.editable.of(props.editable),
2834
2484
  props.focus && focus,
2835
2485
  props.highlightActiveLine && highlightActiveLine(),
2836
2486
  props.history && history(),
2837
2487
  props.lineNumbers && lineNumbers(),
2838
- props.lineWrapping && EditorView12.lineWrapping,
2488
+ props.lineWrapping && EditorView11.lineWrapping,
2839
2489
  props.placeholder && placeholder(props.placeholder),
2840
2490
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2841
2491
  props.scrollPastEnd && scrollPastEnd(),
@@ -2872,14 +2522,14 @@ var defaultThemeSlots = {
2872
2522
  var createThemeExtensions = ({ themeMode, styles: styles5, syntaxHighlighting: _syntaxHighlighting, slots: _slots } = {}) => {
2873
2523
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2874
2524
  return [
2875
- EditorView12.darkTheme.of(themeMode === "dark"),
2876
- EditorView12.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2525
+ EditorView11.darkTheme.of(themeMode === "dark"),
2526
+ EditorView11.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2877
2527
  // https://github.com/codemirror/theme-one-dark
2878
2528
  _syntaxHighlighting && (themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
2879
- slots.editor?.className && EditorView12.editorAttributes.of({
2529
+ slots.editor?.className && EditorView11.editorAttributes.of({
2880
2530
  class: slots.editor.className
2881
2531
  }),
2882
- slots.content?.className && EditorView12.contentAttributes.of({
2532
+ slots.content?.className && EditorView11.contentAttributes.of({
2883
2533
  class: slots.content.className
2884
2534
  })
2885
2535
  ].filter(isNotFalsy3);
@@ -2908,8 +2558,8 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2908
2558
 
2909
2559
  // packages/ui/react-ui-editor/src/extensions/folding.tsx
2910
2560
  import { codeFolding, foldGutter } from "@codemirror/language";
2911
- import { EditorView as EditorView13 } from "@codemirror/view";
2912
- import React3 from "react";
2561
+ import { EditorView as EditorView12 } from "@codemirror/view";
2562
+ import React2 from "react";
2913
2563
  import { Icon } from "@dxos/react-ui";
2914
2564
  var folding = (_props = {}) => [
2915
2565
  codeFolding({
@@ -2922,7 +2572,7 @@ var folding = (_props = {}) => [
2922
2572
  const el = createElement("div", {
2923
2573
  className: "flex h-full items-center"
2924
2574
  });
2925
- return renderRoot(el, /* @__PURE__ */ React3.createElement(Icon, {
2575
+ return renderRoot(el, /* @__PURE__ */ React2.createElement(Icon, {
2926
2576
  icon: "ph--caret-right--bold",
2927
2577
  size: 3,
2928
2578
  classNames: [
@@ -2932,7 +2582,7 @@ var folding = (_props = {}) => [
2932
2582
  }));
2933
2583
  }
2934
2584
  }),
2935
- EditorView13.theme({
2585
+ EditorView12.theme({
2936
2586
  ".cm-foldGutter": {
2937
2587
  opacity: 0.3,
2938
2588
  transition: "opacity 0.3s",
@@ -2945,14 +2595,14 @@ var folding = (_props = {}) => [
2945
2595
  ];
2946
2596
 
2947
2597
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2948
- import { EditorView as EditorView14 } from "@codemirror/view";
2598
+ import { EditorView as EditorView13 } from "@codemirror/view";
2949
2599
  var listener = ({ onFocus, onChange }) => {
2950
2600
  const extensions = [];
2951
- onFocus && extensions.push(EditorView14.focusChangeEffect.of((_, focusing) => {
2601
+ onFocus && extensions.push(EditorView13.focusChangeEffect.of((_, focusing) => {
2952
2602
  onFocus(focusing);
2953
2603
  return null;
2954
2604
  }));
2955
- onChange && extensions.push(EditorView14.updateListener.of((update2) => {
2605
+ onChange && extensions.push(EditorView13.updateListener.of((update2) => {
2956
2606
  onChange(update2.state.doc.toString(), update2.state.facet(documentId));
2957
2607
  }));
2958
2608
  return extensions;
@@ -2962,7 +2612,7 @@ var listener = ({ onFocus, onChange }) => {
2962
2612
  import { snippet } from "@codemirror/autocomplete";
2963
2613
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2964
2614
  import { EditorSelection } from "@codemirror/state";
2965
- import { EditorView as EditorView15, keymap as keymap7 } from "@codemirror/view";
2615
+ import { EditorView as EditorView14, keymap as keymap7 } from "@codemirror/view";
2966
2616
  import { useMemo as useMemo3 } from "react";
2967
2617
  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;
2968
2618
  var Inline;
@@ -4051,7 +3701,7 @@ var getFormatting = (state) => {
4051
3701
  };
4052
3702
  };
4053
3703
  var useFormattingState = (state) => {
4054
- return useMemo3(() => EditorView15.updateListener.of((update2) => {
3704
+ return useMemo3(() => EditorView14.updateListener.of((update2) => {
4055
3705
  if (update2.docChanged || update2.selectionSet) {
4056
3706
  Object.entries(getFormatting(update2.state)).forEach(([key, active]) => {
4057
3707
  state[key] = active;
@@ -4099,7 +3749,7 @@ var processEditorPayload = (view, { type, data }) => {
4099
3749
  })(view);
4100
3750
  break;
4101
3751
  case "comment":
4102
- createComment2(view);
3752
+ createComment(view);
4103
3753
  break;
4104
3754
  }
4105
3755
  requestAnimationFrame(() => {
@@ -4363,9 +4013,9 @@ var convertTreeToJson = (state) => {
4363
4013
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
4364
4014
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4365
4015
  import { RangeSetBuilder as RangeSetBuilder3, StateEffect as StateEffect5 } from "@codemirror/state";
4366
- import { EditorView as EditorView19, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4016
+ import { EditorView as EditorView18, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4367
4017
  import { invariant as invariant4 } from "@dxos/invariant";
4368
- import { mx as mx3 } from "@dxos/react-ui-theme";
4018
+ import { mx as mx2 } from "@dxos/react-ui-theme";
4369
4019
 
4370
4020
  // packages/ui/react-ui-editor/src/extensions/markdown/changes.ts
4371
4021
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -4514,7 +4164,7 @@ var getValidUrl = (str) => {
4514
4164
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
4515
4165
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4516
4166
  import { StateField as StateField8 } from "@codemirror/state";
4517
- import { Decoration as Decoration5, EditorView as EditorView16, WidgetType as WidgetType3 } from "@codemirror/view";
4167
+ import { Decoration as Decoration5, EditorView as EditorView15, WidgetType as WidgetType3 } from "@codemirror/view";
4518
4168
  var image = (_options = {}) => {
4519
4169
  return [
4520
4170
  StateField8.define({
@@ -4542,7 +4192,7 @@ var image = (_options = {}) => {
4542
4192
  add: buildDecorations(from, to, tr.state)
4543
4193
  });
4544
4194
  },
4545
- provide: (field) => EditorView16.decorations.from(field)
4195
+ provide: (field) => EditorView15.decorations.from(field)
4546
4196
  })
4547
4197
  ];
4548
4198
  };
@@ -4602,10 +4252,10 @@ var ImageWidget = class extends WidgetType3 {
4602
4252
  };
4603
4253
 
4604
4254
  // packages/ui/react-ui-editor/src/extensions/markdown/styles.ts
4605
- import { EditorView as EditorView17 } from "@codemirror/view";
4255
+ import { EditorView as EditorView16 } from "@codemirror/view";
4606
4256
  var bulletListIndentationWidth = 24;
4607
4257
  var orderedListIndentationWidth = 36;
4608
- var formattingStyles = EditorView17.theme({
4258
+ var formattingStyles = EditorView16.theme({
4609
4259
  /**
4610
4260
  * Horizontal rule.
4611
4261
  */
@@ -4726,12 +4376,12 @@ var formattingStyles = EditorView17.theme({
4726
4376
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4727
4377
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4728
4378
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField9 } from "@codemirror/state";
4729
- import { Decoration as Decoration6, EditorView as EditorView18, WidgetType as WidgetType4 } from "@codemirror/view";
4379
+ import { Decoration as Decoration6, EditorView as EditorView17, WidgetType as WidgetType4 } from "@codemirror/view";
4730
4380
  var table = (options = {}) => {
4731
4381
  return StateField9.define({
4732
4382
  create: (state) => update(state, options),
4733
4383
  update: (_, tr) => update(tr.state, options),
4734
- provide: (field) => EditorView18.decorations.from(field)
4384
+ provide: (field) => EditorView17.decorations.from(field)
4735
4385
  });
4736
4386
  };
4737
4387
  var update = (state, _options) => {
@@ -4917,16 +4567,16 @@ var TextWidget = class extends WidgetType5 {
4917
4567
  };
4918
4568
  var hide = Decoration7.replace({});
4919
4569
  var blockQuote = Decoration7.line({
4920
- class: mx3("cm-blockquote")
4570
+ class: mx2("cm-blockquote")
4921
4571
  });
4922
4572
  var fencedCodeLine = Decoration7.line({
4923
- class: mx3("cm-code cm-codeblock-line")
4573
+ class: mx2("cm-code cm-codeblock-line")
4924
4574
  });
4925
4575
  var fencedCodeLineFirst = Decoration7.line({
4926
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-first")
4576
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
4927
4577
  });
4928
4578
  var fencedCodeLineLast = Decoration7.line({
4929
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-last")
4579
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
4930
4580
  });
4931
4581
  var commentBlockLine = fencedCodeLine;
4932
4582
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -4981,662 +4631,1081 @@ var buildDecorations2 = (view, options, focus2) => {
4981
4631
  } else {
4982
4632
  headerLevels.splice(level);
4983
4633
  }
4984
- return headerLevels.slice(0, level);
4985
- };
4986
- const listLevels = [];
4987
- const enterList = (node) => {
4988
- listLevels.push({
4989
- type: node.name,
4990
- from: node.from,
4991
- to: node.to,
4992
- level: listLevels.length,
4993
- number: 0
4994
- });
4634
+ return headerLevels.slice(0, level);
4635
+ };
4636
+ const listLevels = [];
4637
+ const enterList = (node) => {
4638
+ listLevels.push({
4639
+ type: node.name,
4640
+ from: node.from,
4641
+ to: node.to,
4642
+ level: listLevels.length,
4643
+ number: 0
4644
+ });
4645
+ };
4646
+ const leaveList = () => {
4647
+ listLevels.pop();
4648
+ };
4649
+ const getCurrentListLevel = () => {
4650
+ invariant4(listLevels.length, void 0, {
4651
+ F: __dxlog_file9,
4652
+ L: 201,
4653
+ S: void 0,
4654
+ A: [
4655
+ "listLevels.length",
4656
+ ""
4657
+ ]
4658
+ });
4659
+ return listLevels[listLevels.length - 1];
4660
+ };
4661
+ const enterNode = (node) => {
4662
+ switch (node.name) {
4663
+ // ATXHeading > HeaderMark > Paragraph
4664
+ // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
4665
+ // processed and the numbering will be incorrect.
4666
+ case "ATXHeading1":
4667
+ case "ATXHeading2":
4668
+ case "ATXHeading3":
4669
+ case "ATXHeading4":
4670
+ case "ATXHeading5":
4671
+ case "ATXHeading6": {
4672
+ const level = parseInt(node.name["ATXHeading".length]);
4673
+ const headers = getHeaderLevels(node, level);
4674
+ if (options.numberedHeadings?.from !== void 0) {
4675
+ const header = headers[level - 1];
4676
+ if (header) {
4677
+ header.number++;
4678
+ }
4679
+ }
4680
+ const editing = editingRange(state, node, focus2);
4681
+ if (editing) {
4682
+ break;
4683
+ }
4684
+ const mark = node.node.firstChild;
4685
+ if (mark?.name === "HeaderMark") {
4686
+ const { from, to = 6 } = options.numberedHeadings ?? {};
4687
+ const text = view.state.sliceDoc(node.from, node.to);
4688
+ const len = text.match(/[#\s]+/)[0].length;
4689
+ if (!from || level < from || level > to) {
4690
+ atomicDeco.add(mark.from, mark.from + len, hide);
4691
+ } else {
4692
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4693
+ if (num.length) {
4694
+ atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4695
+ widget: new TextWidget(num, theme.heading(level))
4696
+ }));
4697
+ }
4698
+ }
4699
+ }
4700
+ return false;
4701
+ }
4702
+ //
4703
+ // Lists.
4704
+ // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
4705
+ //
4706
+ case "BulletList":
4707
+ case "OrderedList": {
4708
+ enterList(node);
4709
+ break;
4710
+ }
4711
+ case "ListItem": {
4712
+ const line = state.doc.lineAt(node.from);
4713
+ const list = getCurrentListLevel();
4714
+ const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
4715
+ const offset = ((list.level ?? 0) + 1) * width;
4716
+ if (node.from === line.to - 1) {
4717
+ return false;
4718
+ }
4719
+ deco.add(line.from, line.from, Decoration7.line({
4720
+ class: "cm-list-item",
4721
+ attributes: {
4722
+ style: `padding-left: ${offset}px; text-indent: -${width}px;`
4723
+ }
4724
+ }));
4725
+ break;
4726
+ }
4727
+ case "ListMark": {
4728
+ const list = getCurrentListLevel();
4729
+ const next = tree.resolve(node.to + 1, 1);
4730
+ if (next?.name === "TaskMarker") {
4731
+ break;
4732
+ }
4733
+ const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
4734
+ const line = state.doc.lineAt(node.from);
4735
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4736
+ atomicDeco.add(line.from, to, Decoration7.replace({
4737
+ widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
4738
+ }));
4739
+ break;
4740
+ }
4741
+ case "TaskMarker": {
4742
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
4743
+ const line = state.doc.lineAt(node.from);
4744
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4745
+ atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
4746
+ break;
4747
+ }
4748
+ //
4749
+ // Blockquote > QuoteMark > Paragraph
4750
+ //
4751
+ case "Blockquote": {
4752
+ const editing = editingRange(state, node, focus2);
4753
+ const quoteMark = node.node.getChild("QuoteMark");
4754
+ const paragraph = node.node.getChild("Paragraph");
4755
+ if (!editing && quoteMark && paragraph) {
4756
+ atomicDeco.add(quoteMark.from, paragraph.from, hide);
4757
+ }
4758
+ for (const block of view.viewportLineBlocks) {
4759
+ if (block.to < node.from) {
4760
+ continue;
4761
+ }
4762
+ if (block.from > node.to) {
4763
+ break;
4764
+ }
4765
+ deco.add(block.from, block.from, blockQuote);
4766
+ }
4767
+ break;
4768
+ }
4769
+ //
4770
+ // CommentBlock
4771
+ //
4772
+ case "CommentBlock": {
4773
+ const editing = editingRange(state, node, focus2);
4774
+ for (const block of view.viewportLineBlocks) {
4775
+ if (block.to < node.from) {
4776
+ continue;
4777
+ }
4778
+ if (block.from > node.to) {
4779
+ break;
4780
+ }
4781
+ const isFirst = block.from <= node.from;
4782
+ const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
4783
+ deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
4784
+ if (!editing && (isFirst || isLast)) {
4785
+ atomicDeco.add(block.from, block.to, hide);
4786
+ }
4787
+ }
4788
+ break;
4789
+ }
4790
+ //
4791
+ // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
4792
+ //
4793
+ case "FencedCode": {
4794
+ for (const block of view.viewportLineBlocks) {
4795
+ if (block.to < node.from) {
4796
+ continue;
4797
+ }
4798
+ if (block.from > node.to) {
4799
+ break;
4800
+ }
4801
+ const first = block.from <= node.from;
4802
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
4803
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
4804
+ const editing = editingRange(state, node, focus2);
4805
+ if (!editing && (first || last)) {
4806
+ atomicDeco.add(block.from, block.to, hide);
4807
+ }
4808
+ }
4809
+ return false;
4810
+ }
4811
+ //
4812
+ // Link > [LinkMark, URL]
4813
+ //
4814
+ case "Link": {
4815
+ const marks = node.node.getChildren("LinkMark");
4816
+ const urlNode = node.node.getChild("URL");
4817
+ const editing = editingRange(state, node, focus2);
4818
+ if (urlNode && marks.length >= 2) {
4819
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
4820
+ if (!editing) {
4821
+ atomicDeco.add(node.from, marks[0].to, hide);
4822
+ }
4823
+ deco.add(marks[0].to, marks[1].from, Decoration7.mark({
4824
+ tagName: "a",
4825
+ attributes: {
4826
+ class: "cm-link",
4827
+ href: url,
4828
+ rel: "noreferrer",
4829
+ target: "_blank"
4830
+ }
4831
+ }));
4832
+ if (!editing) {
4833
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
4834
+ widget: new LinkButton(url, options.renderLinkButton)
4835
+ }) : hide);
4836
+ }
4837
+ }
4838
+ break;
4839
+ }
4840
+ //
4841
+ // HR
4842
+ //
4843
+ case "HorizontalRule": {
4844
+ if (!editingRange(state, node, focus2)) {
4845
+ deco.add(node.from, node.to, horizontalRule);
4846
+ }
4847
+ break;
4848
+ }
4849
+ default: {
4850
+ if (autoHideTags.has(node.name)) {
4851
+ if (!editingRange(state, node.node.parent, focus2)) {
4852
+ atomicDeco.add(node.from, node.to, hide);
4853
+ }
4854
+ }
4855
+ }
4856
+ }
4995
4857
  };
4996
- const leaveList = () => {
4997
- listLevels.pop();
4858
+ const leaveNode = (node) => {
4859
+ switch (node.name) {
4860
+ case "BulletList":
4861
+ case "OrderedList": {
4862
+ leaveList();
4863
+ break;
4864
+ }
4865
+ }
4998
4866
  };
4999
- const getCurrentListLevel = () => {
5000
- invariant4(listLevels.length, void 0, {
5001
- F: __dxlog_file9,
5002
- L: 201,
5003
- S: void 0,
5004
- A: [
5005
- "listLevels.length",
5006
- ""
5007
- ]
4867
+ const tree = syntaxTree7(state);
4868
+ if (options.numberedHeadings?.from === void 0) {
4869
+ for (const { from, to } of view.visibleRanges) {
4870
+ tree.iterate({
4871
+ from,
4872
+ to,
4873
+ enter: wrapWithCatch(enterNode),
4874
+ leave: wrapWithCatch(leaveNode)
4875
+ });
4876
+ }
4877
+ } else {
4878
+ tree.iterate({
4879
+ enter: wrapWithCatch(enterNode),
4880
+ leave: wrapWithCatch(leaveNode)
5008
4881
  });
5009
- return listLevels[listLevels.length - 1];
4882
+ }
4883
+ return {
4884
+ deco: deco.finish(),
4885
+ atomicDeco: atomicDeco.finish()
5010
4886
  };
5011
- const enterNode = (node) => {
5012
- switch (node.name) {
5013
- // ATXHeading > HeaderMark > Paragraph
5014
- // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
5015
- // processed and the numbering will be incorrect.
5016
- case "ATXHeading1":
5017
- case "ATXHeading2":
5018
- case "ATXHeading3":
5019
- case "ATXHeading4":
5020
- case "ATXHeading5":
5021
- case "ATXHeading6": {
5022
- const level = parseInt(node.name["ATXHeading".length]);
5023
- const headers = getHeaderLevels(node, level);
5024
- if (options.numberedHeadings?.from !== void 0) {
5025
- const header = headers[level - 1];
5026
- if (header) {
5027
- header.number++;
5028
- }
4887
+ };
4888
+ var forceUpdate = StateEffect5.define();
4889
+ var decorateMarkdown = (options = {}) => {
4890
+ return [
4891
+ ViewPlugin7.fromClass(class {
4892
+ constructor(view) {
4893
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
4894
+ }
4895
+ update(update2) {
4896
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
4897
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
4898
+ this.clearUpdate();
4899
+ } else if (update2.selectionSet) {
4900
+ this.scheduleUpdate(update2.view);
5029
4901
  }
5030
- const editing = editingRange(state, node, focus2);
5031
- if (editing) {
5032
- break;
4902
+ }
4903
+ // Defer update in case moving through the document.
4904
+ scheduleUpdate(view) {
4905
+ this.clearUpdate();
4906
+ this.pendingUpdate = setTimeout(() => {
4907
+ view.dispatch({
4908
+ effects: forceUpdate.of(null)
4909
+ });
4910
+ }, options.selectionChangeDelay);
4911
+ }
4912
+ clearUpdate() {
4913
+ if (this.pendingUpdate) {
4914
+ clearTimeout(this.pendingUpdate);
4915
+ this.pendingUpdate = void 0;
5033
4916
  }
5034
- const mark = node.node.firstChild;
5035
- if (mark?.name === "HeaderMark") {
5036
- const { from, to = 6 } = options.numberedHeadings ?? {};
5037
- const text = view.state.sliceDoc(node.from, node.to);
5038
- const len = text.match(/[#\s]+/)[0].length;
5039
- if (!from || level < from || level > to) {
5040
- atomicDeco.add(mark.from, mark.from + len, hide);
5041
- } else {
5042
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5043
- if (num.length) {
5044
- atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
5045
- widget: new TextWidget(num, theme.heading(level))
5046
- }));
5047
- }
4917
+ }
4918
+ destroy() {
4919
+ this.clearUpdate();
4920
+ }
4921
+ }, {
4922
+ provide: (plugin) => [
4923
+ EditorView18.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4924
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4925
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
4926
+ ]
4927
+ }),
4928
+ image(),
4929
+ table(),
4930
+ adjustChanges(),
4931
+ formattingStyles
4932
+ ];
4933
+ };
4934
+
4935
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
4936
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
4937
+ import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
4938
+ import { tooltipContent } from "@dxos/react-ui-theme";
4939
+ var linkTooltip = (renderTooltip) => {
4940
+ return hoverTooltip2((view, pos, side) => {
4941
+ const syntax = syntaxTree8(view.state).resolveInner(pos, side);
4942
+ let link = null;
4943
+ for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
4944
+ link = node.name === "Link" ? node : null;
4945
+ }
4946
+ const url = link && link.getChild("URL");
4947
+ if (!url || !link) {
4948
+ return null;
4949
+ }
4950
+ const urlText = view.state.sliceDoc(url.from, url.to);
4951
+ return {
4952
+ pos: link.from,
4953
+ end: link.to,
4954
+ // NOTE: Forcing above causes the tooltip to flicker.
4955
+ // above: true,
4956
+ create: () => {
4957
+ const el = document.createElement("div");
4958
+ el.className = tooltipContent({});
4959
+ renderTooltip(el, {
4960
+ url: urlText
4961
+ }, view);
4962
+ return {
4963
+ dom: el,
4964
+ offset: {
4965
+ x: 0,
4966
+ y: 4
5048
4967
  }
4968
+ };
4969
+ }
4970
+ };
4971
+ }, {
4972
+ // NOTE: 0 = default of 300ms.
4973
+ hoverTime: 1
4974
+ });
4975
+ };
4976
+
4977
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
4978
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4979
+ import { log as log6 } from "@dxos/log";
4980
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4981
+ var mention = ({ debug, onSearch }) => {
4982
+ return autocompletion2({
4983
+ // TODO(burdon): Not working.
4984
+ activateOnTyping: true,
4985
+ // activateOnTypingDelay: 100,
4986
+ // selectOnOpen: true,
4987
+ closeOnBlur: !debug,
4988
+ // defaultKeymap: false,
4989
+ icons: false,
4990
+ override: [
4991
+ (context) => {
4992
+ log6.info("completion context", {
4993
+ context
4994
+ }, {
4995
+ F: __dxlog_file10,
4996
+ L: 27,
4997
+ S: void 0,
4998
+ C: (f, a) => f(...a)
4999
+ });
5000
+ const match = context.matchBefore(/@(\w+)?/);
5001
+ if (!match || match.from === match.to && !context.explicit) {
5002
+ return null;
5003
+ }
5004
+ return {
5005
+ from: match.from,
5006
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5007
+ label: `@${value}`
5008
+ }))
5009
+ };
5010
+ }
5011
+ ]
5012
+ });
5013
+ };
5014
+
5015
+ // packages/ui/react-ui-editor/src/extensions/modes.ts
5016
+ import { keymap as keymap9 } from "@codemirror/view";
5017
+ import { vim } from "@replit/codemirror-vim";
5018
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5019
+ import { Schema } from "effect";
5020
+ var EditorViewModes = [
5021
+ "preview",
5022
+ "readonly",
5023
+ "source"
5024
+ ];
5025
+ var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5026
+ var EditorInputModes = [
5027
+ "default",
5028
+ "vim",
5029
+ "vscode"
5030
+ ];
5031
+ var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5032
+ var editorInputMode = singleValueFacet({});
5033
+ var InputModeExtensions = {
5034
+ default: [],
5035
+ vscode: [
5036
+ // https://github.com/replit/codemirror-vscode-keymap
5037
+ editorInputMode.of({
5038
+ type: "vscode"
5039
+ }),
5040
+ keymap9.of(vscodeKeymap)
5041
+ ],
5042
+ vim: [
5043
+ // https://github.com/replit/codemirror-vim
5044
+ vim(),
5045
+ editorInputMode.of({
5046
+ type: "vim",
5047
+ noTabster: true
5048
+ }),
5049
+ keymap9.of([
5050
+ {
5051
+ key: "Alt-Escape",
5052
+ run: (view) => {
5053
+ view.dom.parentElement?.focus();
5054
+ return true;
5049
5055
  }
5050
- return false;
5051
5056
  }
5052
- //
5053
- // Lists.
5054
- // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
5055
- //
5056
- case "BulletList":
5057
- case "OrderedList": {
5058
- enterList(node);
5059
- break;
5057
+ ])
5058
+ ]
5059
+ };
5060
+
5061
+ // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5062
+ import "@dxos/lit-ui/dx-ref-tag.pcss";
5063
+ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5064
+ import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField10 } from "@codemirror/state";
5065
+ import { Decoration as Decoration8, EditorView as EditorView19, WidgetType as WidgetType6 } from "@codemirror/view";
5066
+ var preview = (options = {}) => {
5067
+ return [
5068
+ // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5069
+ // "Block decorations may not be specified via plugins"
5070
+ StateField10.define({
5071
+ create: (state) => buildDecorations3(state, options),
5072
+ update: (_, tr) => buildDecorations3(tr.state, options),
5073
+ provide: (field) => [
5074
+ EditorView19.decorations.from(field),
5075
+ EditorView19.atomicRanges.of((view) => view.state.field(field))
5076
+ ]
5077
+ }),
5078
+ EditorView19.theme({
5079
+ ".cm-preview-block": {
5080
+ marginLeft: "-1rem",
5081
+ marginRight: "-1rem",
5082
+ padding: "1rem",
5083
+ borderRadius: "0.5rem",
5084
+ background: "var(--dx-modalSurface)",
5085
+ border: "1px solid var(--dx-separator)"
5060
5086
  }
5061
- case "ListItem": {
5062
- const line = state.doc.lineAt(node.from);
5063
- const list = getCurrentListLevel();
5064
- const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
5065
- const offset = ((list.level ?? 0) + 1) * width;
5066
- if (node.from === line.to - 1) {
5067
- return false;
5068
- }
5069
- deco.add(line.from, line.from, Decoration7.line({
5070
- class: "cm-list-item",
5071
- attributes: {
5072
- style: `padding-left: ${offset}px; text-indent: -${width}px;`
5087
+ })
5088
+ ];
5089
+ };
5090
+ var getLinkRef = (state, node) => {
5091
+ const mark = node.getChild("LinkMark");
5092
+ const label = node.getChild("LinkLabel");
5093
+ if (mark && label) {
5094
+ const ref = state.sliceDoc(label.from + 1, label.to - 1);
5095
+ return {
5096
+ suggest: ref.startsWith("?"),
5097
+ block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5098
+ label: state.sliceDoc(mark.to, label.from - 1),
5099
+ ref: ref.startsWith("?") ? ref.slice(1) : ref
5100
+ };
5101
+ }
5102
+ };
5103
+ var buildDecorations3 = (state, options) => {
5104
+ const builder = new RangeSetBuilder4();
5105
+ syntaxTree9(state).iterate({
5106
+ enter: (node) => {
5107
+ switch (node.name) {
5108
+ //
5109
+ // Decoration.
5110
+ // [Label][dxn:echo:123]
5111
+ //
5112
+ case "Link": {
5113
+ const link = getLinkRef(state, node.node);
5114
+ if (link) {
5115
+ builder.add(node.from, node.to, Decoration8.replace({
5116
+ widget: new PreviewInlineWidget(options, link)
5117
+ }));
5073
5118
  }
5074
- }));
5075
- break;
5076
- }
5077
- case "ListMark": {
5078
- const list = getCurrentListLevel();
5079
- const next = tree.resolve(node.to + 1, 1);
5080
- if (next?.name === "TaskMarker") {
5081
5119
  break;
5082
5120
  }
5083
- const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
5084
- const line = state.doc.lineAt(node.from);
5085
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5086
- atomicDeco.add(line.from, to, Decoration7.replace({
5087
- widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
5088
- }));
5089
- break;
5090
- }
5091
- case "TaskMarker": {
5092
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
5093
- const line = state.doc.lineAt(node.from);
5094
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5095
- atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
5096
- break;
5097
- }
5098
- //
5099
- // Blockquote > QuoteMark > Paragraph
5100
- //
5101
- case "Blockquote": {
5102
- const editing = editingRange(state, node, focus2);
5103
- const quoteMark = node.node.getChild("QuoteMark");
5104
- const paragraph = node.node.getChild("Paragraph");
5105
- if (!editing && quoteMark && paragraph) {
5106
- atomicDeco.add(quoteMark.from, paragraph.from, hide);
5107
- }
5108
- for (const block of view.viewportLineBlocks) {
5109
- if (block.to < node.from) {
5110
- continue;
5111
- }
5112
- if (block.from > node.to) {
5113
- break;
5114
- }
5115
- deco.add(block.from, block.from, blockQuote);
5116
- }
5117
- break;
5118
- }
5119
- //
5120
- // CommentBlock
5121
- //
5122
- case "CommentBlock": {
5123
- const editing = editingRange(state, node, focus2);
5124
- for (const block of view.viewportLineBlocks) {
5125
- if (block.to < node.from) {
5126
- continue;
5127
- }
5128
- if (block.from > node.to) {
5129
- break;
5130
- }
5131
- const isFirst = block.from <= node.from;
5132
- const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
5133
- deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
5134
- if (!editing && (isFirst || isLast)) {
5135
- atomicDeco.add(block.from, block.to, hide);
5136
- }
5137
- }
5138
- break;
5139
- }
5140
- //
5141
- // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
5142
- //
5143
- case "FencedCode": {
5144
- for (const block of view.viewportLineBlocks) {
5145
- if (block.to < node.from) {
5146
- continue;
5147
- }
5148
- if (block.from > node.to) {
5149
- break;
5150
- }
5151
- const first = block.from <= node.from;
5152
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
5153
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
5154
- const editing = editingRange(state, node, focus2);
5155
- if (!editing && (first || last)) {
5156
- atomicDeco.add(block.from, block.to, hide);
5121
+ //
5122
+ // Block widget.
5123
+ // ![Label][dxn:echo:123]
5124
+ //
5125
+ case "Image": {
5126
+ const link = getLinkRef(state, node.node);
5127
+ if (options.renderBlock && link) {
5128
+ builder.add(node.from, node.to, Decoration8.replace({
5129
+ block: true,
5130
+ // atomic: true,
5131
+ widget: new PreviewBlockWidget(options, link)
5132
+ }));
5157
5133
  }
5134
+ break;
5158
5135
  }
5159
- return false;
5160
5136
  }
5161
- //
5162
- // Link > [LinkMark, URL]
5163
- //
5164
- case "Link": {
5165
- const marks = node.node.getChildren("LinkMark");
5166
- const urlNode = node.node.getChild("URL");
5167
- const editing = editingRange(state, node, focus2);
5168
- if (urlNode && marks.length >= 2) {
5169
- const url = state.sliceDoc(urlNode.from, urlNode.to);
5170
- if (!editing) {
5171
- atomicDeco.add(node.from, marks[0].to, hide);
5172
- }
5173
- deco.add(marks[0].to, marks[1].from, Decoration7.mark({
5174
- tagName: "a",
5175
- attributes: {
5176
- class: "cm-link",
5177
- href: url,
5178
- rel: "noreferrer",
5179
- target: "_blank"
5180
- }
5181
- }));
5182
- if (!editing) {
5183
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
5184
- widget: new LinkButton(url, options.renderLinkButton)
5185
- }) : hide);
5186
- }
5187
- }
5188
- break;
5137
+ }
5138
+ });
5139
+ return builder.finish();
5140
+ };
5141
+ var PreviewInlineWidget = class extends WidgetType6 {
5142
+ constructor(_options, _link) {
5143
+ super();
5144
+ this._options = _options;
5145
+ this._link = _link;
5146
+ }
5147
+ // override ignoreEvent() {
5148
+ // return false;
5149
+ // }
5150
+ eq(other) {
5151
+ return this._link.ref === other._link.ref && this._link.label === other._link.label;
5152
+ }
5153
+ toDOM(view) {
5154
+ const root = document.createElement("dx-ref-tag");
5155
+ root.textContent = this._link.label;
5156
+ root.setAttribute("ref", this._link.ref);
5157
+ return root;
5158
+ }
5159
+ };
5160
+ var PreviewBlockWidget = class extends WidgetType6 {
5161
+ constructor(_options, _link) {
5162
+ super();
5163
+ this._options = _options;
5164
+ this._link = _link;
5165
+ }
5166
+ // override ignoreEvent() {
5167
+ // return true;
5168
+ // }
5169
+ eq(other) {
5170
+ return this._link.ref === other._link.ref;
5171
+ }
5172
+ toDOM(view) {
5173
+ const root = document.createElement("div");
5174
+ root.classList.add("cm-preview-block");
5175
+ const handleAction = (action) => {
5176
+ const pos = view.posAtDOM(root);
5177
+ const node = syntaxTree9(view.state).resolve(pos + 1).node.parent;
5178
+ if (!node) {
5179
+ return;
5189
5180
  }
5190
- //
5191
- // HR
5192
- //
5193
- case "HorizontalRule": {
5194
- if (!editingRange(state, node, focus2)) {
5195
- deco.add(node.from, node.to, horizontalRule);
5196
- }
5197
- break;
5181
+ const link = getLinkRef(view.state, node);
5182
+ if (link?.ref !== action.link.ref) {
5183
+ return;
5198
5184
  }
5199
- default: {
5200
- if (autoHideTags.has(node.name)) {
5201
- if (!editingRange(state, node.node.parent, focus2)) {
5202
- atomicDeco.add(node.from, node.to, hide);
5203
- }
5185
+ switch (action.type) {
5186
+ // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5187
+ // Insert ref text.
5188
+ case "insert": {
5189
+ view.dispatch({
5190
+ changes: {
5191
+ from: node.from,
5192
+ to: node.to,
5193
+ insert: action.target.text
5194
+ }
5195
+ });
5196
+ break;
5197
+ }
5198
+ // Remove ref.
5199
+ case "delete": {
5200
+ view.dispatch({
5201
+ changes: {
5202
+ from: node.from,
5203
+ to: node.to
5204
+ }
5205
+ });
5206
+ break;
5204
5207
  }
5205
5208
  }
5206
- }
5207
- };
5208
- const leaveNode = (node) => {
5209
- switch (node.name) {
5210
- case "BulletList":
5211
- case "OrderedList": {
5212
- leaveList();
5213
- break;
5214
- }
5215
- }
5216
- };
5217
- const tree = syntaxTree7(state);
5218
- if (options.numberedHeadings?.from === void 0) {
5219
- for (const { from, to } of view.visibleRanges) {
5220
- tree.iterate({
5221
- from,
5222
- to,
5223
- enter: wrapWithCatch(enterNode),
5224
- leave: wrapWithCatch(leaveNode)
5225
- });
5226
- }
5227
- } else {
5228
- tree.iterate({
5229
- enter: wrapWithCatch(enterNode),
5230
- leave: wrapWithCatch(leaveNode)
5231
- });
5209
+ };
5210
+ this._options.renderBlock(root, {
5211
+ readonly: view.state.readOnly,
5212
+ link: this._link,
5213
+ onAction: handleAction,
5214
+ onLookup: this._options.onLookup
5215
+ }, view);
5216
+ return root;
5232
5217
  }
5233
- return {
5234
- deco: deco.finish(),
5235
- atomicDeco: atomicDeco.finish()
5236
- };
5237
5218
  };
5238
- var forceUpdate = StateEffect5.define();
5239
- var decorateMarkdown = (options = {}) => {
5219
+
5220
+ // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5221
+ import { keymap as keymap10 } from "@codemirror/view";
5222
+ var defaultItems = [
5223
+ "hello world!",
5224
+ "this is a test.",
5225
+ "this is [DXOS](https://dxos.org)"
5226
+ ];
5227
+ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5228
+ let t;
5229
+ let idx = 0;
5240
5230
  return [
5241
- ViewPlugin7.fromClass(class {
5242
- constructor(view) {
5243
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
5244
- }
5245
- update(update2) {
5246
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
5247
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
5248
- this.clearUpdate();
5249
- } else if (update2.selectionSet) {
5250
- this.scheduleUpdate(update2.view);
5231
+ keymap10.of([
5232
+ {
5233
+ // Reset.
5234
+ key: "alt-meta-'",
5235
+ run: (view) => {
5236
+ clearTimeout(t);
5237
+ idx = 0;
5238
+ return true;
5251
5239
  }
5252
- }
5253
- // Defer update in case moving through the document.
5254
- scheduleUpdate(view) {
5255
- this.clearUpdate();
5256
- this.pendingUpdate = setTimeout(() => {
5257
- view.dispatch({
5258
- effects: forceUpdate.of(null)
5259
- });
5260
- }, options.selectionChangeDelay);
5261
- }
5262
- clearUpdate() {
5263
- if (this.pendingUpdate) {
5264
- clearTimeout(this.pendingUpdate);
5265
- this.pendingUpdate = void 0;
5240
+ },
5241
+ {
5242
+ // Next prompt.
5243
+ // TODO(burdon): Press 1-9 to select prompt?
5244
+ key: "shift-meta-'",
5245
+ run: (view) => {
5246
+ clearTimeout(t);
5247
+ const text = items[idx++];
5248
+ if (idx === items?.length) {
5249
+ idx = 0;
5250
+ }
5251
+ let i = 0;
5252
+ const insert = (d = 0) => {
5253
+ t = setTimeout(() => {
5254
+ const pos = view.state.selection.main.head;
5255
+ view.dispatch({
5256
+ changes: {
5257
+ from: pos,
5258
+ insert: text[i++]
5259
+ },
5260
+ selection: {
5261
+ anchor: pos + 1
5262
+ }
5263
+ });
5264
+ if (i < text.length) {
5265
+ insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5266
+ }
5267
+ }, d);
5268
+ };
5269
+ insert();
5270
+ return true;
5266
5271
  }
5267
5272
  }
5268
- destroy() {
5269
- this.clearUpdate();
5270
- }
5271
- }, {
5272
- provide: (plugin) => [
5273
- EditorView19.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5274
- EditorView19.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5275
- EditorView19.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
5276
- ]
5277
- }),
5278
- image(),
5279
- table(),
5280
- adjustChanges(),
5281
- formattingStyles
5273
+ ])
5282
5274
  ];
5283
5275
  };
5284
5276
 
5285
- // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
5286
- import { syntaxTree as syntaxTree8 } from "@codemirror/language";
5287
- import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
5288
- import { tooltipContent } from "@dxos/react-ui-theme";
5289
- var linkTooltip = (renderTooltip) => {
5290
- return hoverTooltip2((view, pos, side) => {
5291
- const syntax = syntaxTree8(view.state).resolveInner(pos, side);
5292
- let link = null;
5293
- for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
5294
- link = node.name === "Link" ? node : null;
5277
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
5278
+ var createBlockGroupAction = (value) => createEditorActionGroup("block", {
5279
+ variant: "toggleGroup",
5280
+ selectCardinality: "single",
5281
+ value
5282
+ });
5283
+ var createBlockActions = (value, getView, blankLine) => Object.entries({
5284
+ blockquote: "ph--quotes--regular",
5285
+ codeblock: "ph--code-block--regular",
5286
+ table: "ph--table--regular"
5287
+ }).map(([type, icon]) => {
5288
+ const checked = type === value;
5289
+ return createEditorAction(type, () => {
5290
+ const view = getView();
5291
+ if (!view) {
5292
+ return;
5295
5293
  }
5296
- const url = link && link.getChild("URL");
5297
- if (!url || !link) {
5298
- return null;
5294
+ switch (type) {
5295
+ case "blockquote":
5296
+ checked ? removeBlockquote(view) : addBlockquote(view);
5297
+ break;
5298
+ case "codeblock":
5299
+ checked ? removeCodeblock(view) : addCodeblock(view);
5300
+ break;
5301
+ case "table":
5302
+ insertTable(view);
5303
+ break;
5299
5304
  }
5300
- const urlText = view.state.sliceDoc(url.from, url.to);
5301
- return {
5302
- pos: link.from,
5303
- end: link.to,
5304
- // NOTE: Forcing above causes the tooltip to flicker.
5305
- // above: true,
5306
- create: () => {
5307
- const el = document.createElement("div");
5308
- el.className = tooltipContent({});
5309
- renderTooltip(el, {
5310
- url: urlText
5311
- }, view);
5312
- return {
5313
- dom: el,
5314
- offset: {
5315
- x: 0,
5316
- y: 4
5317
- }
5318
- };
5305
+ }, {
5306
+ checked,
5307
+ ...type === "table" && {
5308
+ disabled: !!blankLine
5309
+ },
5310
+ icon
5311
+ });
5312
+ });
5313
+ var createBlocks = (state, getView) => {
5314
+ const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
5315
+ const blockGroupAction = createBlockGroupAction(value);
5316
+ const blockActions = createBlockActions(value, getView, state.blankLine);
5317
+ return {
5318
+ nodes: [
5319
+ blockGroupAction,
5320
+ ...blockActions
5321
+ ],
5322
+ edges: [
5323
+ {
5324
+ source: "root",
5325
+ target: "block"
5326
+ },
5327
+ ...blockActions.map(({ id }) => ({
5328
+ source: blockGroupAction.id,
5329
+ target: id
5330
+ }))
5331
+ ]
5332
+ };
5333
+ };
5334
+
5335
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
5336
+ var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
5337
+ var createCommentAction = (label, getView) => createEditorAction("comment", () => createComment(getView()), {
5338
+ testId: "editor.toolbar.comment",
5339
+ icon: "ph--chat-text--regular",
5340
+ label
5341
+ });
5342
+ var createComment2 = (state, getView) => ({
5343
+ nodes: [
5344
+ createCommentAction([
5345
+ commentLabel(state.comment, state.selection),
5346
+ {
5347
+ ns: translationKey
5319
5348
  }
5320
- };
5349
+ ], getView)
5350
+ ],
5351
+ edges: [
5352
+ {
5353
+ source: "root",
5354
+ target: "comment"
5355
+ }
5356
+ ]
5357
+ });
5358
+
5359
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
5360
+ var formats = {
5361
+ strong: "ph--text-b--regular",
5362
+ emphasis: "ph--text-italic--regular",
5363
+ strikethrough: "ph--text-strikethrough--regular",
5364
+ code: "ph--code--regular",
5365
+ link: "ph--link--regular"
5366
+ };
5367
+ var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
5368
+ variant: "toggleGroup",
5369
+ selectCardinality: "multiple",
5370
+ value: Object.keys(formats).filter((key) => !!formatting[key])
5371
+ });
5372
+ var createFormattingActions = (formatting, getView) => Object.entries(formats).map(([type, icon]) => {
5373
+ const checked = !!formatting[type];
5374
+ return createEditorAction(type, () => {
5375
+ const view = getView();
5376
+ if (!view) {
5377
+ return;
5378
+ }
5379
+ if (type === "link") {
5380
+ checked ? removeLink(view) : addLink()(view);
5381
+ return;
5382
+ }
5383
+ const inlineType = type === "strong" ? Inline.Strong : type === "emphasis" ? Inline.Emphasis : type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
5384
+ setStyle(inlineType, !checked)(view);
5321
5385
  }, {
5322
- // NOTE: 0 = default of 300ms.
5323
- hoverTime: 1
5386
+ checked,
5387
+ icon
5324
5388
  });
5389
+ });
5390
+ var createFormatting = (state, getView) => {
5391
+ const formattingGroupAction = createFormattingGroup(state);
5392
+ const formattingActions = createFormattingActions(state, getView);
5393
+ return {
5394
+ nodes: [
5395
+ formattingGroupAction,
5396
+ ...formattingActions
5397
+ ],
5398
+ edges: [
5399
+ {
5400
+ source: "root",
5401
+ target: "formatting"
5402
+ },
5403
+ ...formattingActions.map(({ id }) => ({
5404
+ source: formattingGroupAction.id,
5405
+ target: id
5406
+ }))
5407
+ ]
5408
+ };
5325
5409
  };
5326
5410
 
5327
- // packages/ui/react-ui-editor/src/extensions/mention.ts
5328
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5329
- import { log as log6 } from "@dxos/log";
5330
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5331
- var mention = ({ debug, onSearch }) => {
5332
- return autocompletion2({
5333
- // TODO(burdon): Not working.
5334
- activateOnTyping: true,
5335
- // activateOnTypingDelay: 100,
5336
- // selectOnOpen: true,
5337
- closeOnBlur: !debug,
5338
- // defaultKeymap: false,
5339
- icons: false,
5340
- override: [
5341
- (context) => {
5342
- log6.info("completion context", {
5343
- context
5344
- }, {
5345
- F: __dxlog_file10,
5346
- L: 27,
5347
- S: void 0,
5348
- C: (f, a) => f(...a)
5349
- });
5350
- const match = context.matchBefore(/@(\w+)?/);
5351
- if (!match || match.from === match.to && !context.explicit) {
5352
- return null;
5353
- }
5354
- return {
5355
- from: match.from,
5356
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5357
- label: `@${value}`
5358
- }))
5359
- };
5411
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
5412
+ var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
5413
+ variant: "dropdownMenu",
5414
+ applyActive: true,
5415
+ selectCardinality: "single",
5416
+ value
5417
+ }, "ph--text-h--regular");
5418
+ var createHeadingActions = (getView) => Object.entries({
5419
+ "0": "ph--paragraph--regular",
5420
+ "1": "ph--text-h-one--regular",
5421
+ "2": "ph--text-h-two--regular",
5422
+ "3": "ph--text-h-three--regular",
5423
+ "4": "ph--text-h-four--regular",
5424
+ "5": "ph--text-h-five--regular",
5425
+ "6": "ph--text-h-six--regular"
5426
+ }).map(([levelStr, icon]) => {
5427
+ const level = parseInt(levelStr);
5428
+ return createEditorAction(`heading--${levelStr}`, () => setHeading(level)(getView()), {
5429
+ label: [
5430
+ "heading level label",
5431
+ {
5432
+ count: level,
5433
+ ns: translationKey
5360
5434
  }
5361
- ]
5435
+ ],
5436
+ icon
5362
5437
  });
5438
+ });
5439
+ var computeHeadingValue = (state) => {
5440
+ const blockType = state ? state.blockType : "paragraph";
5441
+ const header = blockType && /heading(\d)/.exec(blockType);
5442
+ return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
5443
+ };
5444
+ var createHeadings = (state, getView) => {
5445
+ const headingValue = computeHeadingValue(state);
5446
+ const headingGroupAction = createHeadingGroupAction(headingValue);
5447
+ const headingActions = createHeadingActions(getView);
5448
+ return {
5449
+ nodes: [
5450
+ headingGroupAction,
5451
+ ...headingActions
5452
+ ],
5453
+ edges: [
5454
+ {
5455
+ source: "root",
5456
+ target: "heading"
5457
+ },
5458
+ ...headingActions.map(({ id }) => ({
5459
+ source: headingGroupAction.id,
5460
+ target: id
5461
+ }))
5462
+ ]
5463
+ };
5363
5464
  };
5364
5465
 
5365
- // packages/ui/react-ui-editor/src/extensions/modes.ts
5366
- import { keymap as keymap9 } from "@codemirror/view";
5367
- import { vim } from "@replit/codemirror-vim";
5368
- import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5369
- import { Schema } from "effect";
5370
- var EditorViewModes = [
5371
- "preview",
5372
- "readonly",
5373
- "source"
5374
- ];
5375
- var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5376
- var EditorInputModes = [
5377
- "default",
5378
- "vim",
5379
- "vscode"
5380
- ];
5381
- var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5382
- var editorInputMode = singleValueFacet({});
5383
- var InputModeExtensions = {
5384
- default: [],
5385
- vscode: [
5386
- // https://github.com/replit/codemirror-vscode-keymap
5387
- editorInputMode.of({
5388
- type: "vscode"
5389
- }),
5390
- keymap9.of(vscodeKeymap)
5466
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/image.ts
5467
+ var createImageUploadAction = (onImageUpload) => createEditorAction("image", onImageUpload, {
5468
+ testId: "editor.toolbar.image",
5469
+ icon: "ph--image-square--regular"
5470
+ });
5471
+ var createImageUpload = (onImageUpload) => ({
5472
+ nodes: [
5473
+ createImageUploadAction(onImageUpload)
5391
5474
  ],
5392
- vim: [
5393
- // https://github.com/replit/codemirror-vim
5394
- vim(),
5395
- editorInputMode.of({
5396
- type: "vim",
5397
- noTabster: true
5398
- }),
5399
- keymap9.of([
5400
- {
5401
- key: "Alt-Escape",
5402
- run: (view) => {
5403
- view.dom.parentElement?.focus();
5404
- return true;
5405
- }
5406
- }
5407
- ])
5475
+ edges: [
5476
+ {
5477
+ source: "root",
5478
+ target: "image"
5479
+ }
5408
5480
  ]
5409
- };
5481
+ });
5410
5482
 
5411
- // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5412
- import "@dxos/lit-ui/dx-ref-tag.pcss";
5413
- import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5414
- import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField10 } from "@codemirror/state";
5415
- import { Decoration as Decoration8, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5416
- var preview = (options = {}) => {
5417
- return [
5418
- // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5419
- // "Block decorations may not be specified via plugins"
5420
- StateField10.define({
5421
- create: (state) => buildDecorations3(state, options),
5422
- update: (_, tr) => buildDecorations3(tr.state, options),
5423
- provide: (field) => [
5424
- EditorView20.decorations.from(field),
5425
- EditorView20.atomicRanges.of((view) => view.state.field(field))
5426
- ]
5427
- }),
5428
- EditorView20.theme({
5429
- ".cm-preview-block": {
5430
- marginLeft: "-1rem",
5431
- marginRight: "-1rem",
5432
- padding: "1rem",
5433
- borderRadius: "0.5rem",
5434
- background: "var(--dx-modalSurface)",
5435
- border: "1px solid var(--dx-separator)"
5436
- }
5437
- })
5438
- ];
5439
- };
5440
- var getLinkRef = (state, node) => {
5441
- const mark = node.getChild("LinkMark");
5442
- const label = node.getChild("LinkLabel");
5443
- if (mark && label) {
5444
- const ref = state.sliceDoc(label.from + 1, label.to - 1);
5445
- return {
5446
- suggest: ref.startsWith("?"),
5447
- block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5448
- label: state.sliceDoc(mark.to, label.from - 1),
5449
- ref: ref.startsWith("?") ? ref.slice(1) : ref
5450
- };
5451
- }
5483
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
5484
+ var listStyles = {
5485
+ bullet: "ph--list-bullets--regular",
5486
+ ordered: "ph--list-numbers--regular",
5487
+ task: "ph--list-checks--regular"
5452
5488
  };
5453
- var buildDecorations3 = (state, options) => {
5454
- const builder = new RangeSetBuilder4();
5455
- syntaxTree9(state).iterate({
5456
- enter: (node) => {
5457
- switch (node.name) {
5458
- //
5459
- // Decoration.
5460
- // [Label][dxn:echo:123]
5461
- //
5462
- case "Link": {
5463
- const link = getLinkRef(state, node.node);
5464
- if (link) {
5465
- builder.add(node.from, node.to, Decoration8.replace({
5466
- widget: new PreviewInlineWidget(options, link)
5467
- }));
5468
- }
5469
- break;
5470
- }
5471
- //
5472
- // Block widget.
5473
- // ![Label][dxn:echo:123]
5474
- //
5475
- case "Image": {
5476
- const link = getLinkRef(state, node.node);
5477
- if (options.renderBlock && link) {
5478
- builder.add(node.from, node.to, Decoration8.replace({
5479
- block: true,
5480
- // atomic: true,
5481
- widget: new PreviewBlockWidget(options, link)
5482
- }));
5483
- }
5484
- break;
5485
- }
5486
- }
5489
+ var createListGroupAction = (value) => createEditorActionGroup("list", {
5490
+ variant: "toggleGroup",
5491
+ selectCardinality: "single",
5492
+ value
5493
+ });
5494
+ var createListActions = (value, getView) => Object.entries(listStyles).map(([listStyle, icon]) => {
5495
+ const checked = value === listStyle;
5496
+ return createEditorAction(`list-${listStyle}`, () => {
5497
+ const view = getView();
5498
+ if (!view) {
5499
+ return;
5500
+ }
5501
+ const listType = listStyle === "ordered" ? List.Ordered : listStyle === "bullet" ? List.Bullet : List.Task;
5502
+ if (checked) {
5503
+ removeList(listType)(view);
5504
+ } else {
5505
+ addList(listType)(view);
5506
+ }
5507
+ }, {
5508
+ checked,
5509
+ icon
5510
+ });
5511
+ });
5512
+ var createLists = (state, getView) => {
5513
+ const value = state.listStyle ?? "";
5514
+ const listGroupAction = createListGroupAction(value);
5515
+ const listActionsMap = createListActions(value, getView);
5516
+ return {
5517
+ nodes: [
5518
+ listGroupAction,
5519
+ ...listActionsMap
5520
+ ],
5521
+ edges: [
5522
+ {
5523
+ source: "root",
5524
+ target: "list"
5525
+ },
5526
+ ...listActionsMap.map(({ id }) => ({
5527
+ source: listGroupAction.id,
5528
+ target: id
5529
+ }))
5530
+ ]
5531
+ };
5532
+ };
5533
+
5534
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/search.ts
5535
+ import { openSearchPanel } from "@codemirror/search";
5536
+ var createSearchAction = (getView) => createEditorAction("search", () => openSearchPanel(getView()), {
5537
+ testId: "editor.toolbar.search",
5538
+ icon: "ph--magnifying-glass--regular"
5539
+ });
5540
+ var createSearch = (getView) => ({
5541
+ nodes: [
5542
+ createSearchAction(getView)
5543
+ ],
5544
+ edges: [
5545
+ {
5546
+ source: "root",
5547
+ target: "search"
5487
5548
  }
5549
+ ]
5550
+ });
5551
+
5552
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
5553
+ var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
5554
+ variant: "dropdownMenu",
5555
+ applyActive: true,
5556
+ selectCardinality: "single",
5557
+ value
5558
+ }, "ph--eye--regular");
5559
+ var createViewModeActions = (value, onViewModeChange) => Object.entries({
5560
+ preview: "ph--eye--regular",
5561
+ source: "ph--pencil-simple--regular",
5562
+ readonly: "ph--pencil-slash--regular"
5563
+ }).map(([viewMode, icon]) => {
5564
+ const checked = viewMode === value;
5565
+ return createEditorAction(`view-mode--${viewMode}`, () => onViewModeChange(viewMode), {
5566
+ label: [
5567
+ `${viewMode} mode label`,
5568
+ {
5569
+ ns: translationKey
5570
+ }
5571
+ ],
5572
+ checked,
5573
+ icon
5488
5574
  });
5489
- return builder.finish();
5575
+ });
5576
+ var createViewMode = (state, onViewModeChange) => {
5577
+ const value = state.viewMode ?? "source";
5578
+ const viewModeGroupAction = createViewModeGroupAction(value);
5579
+ const viewModeActions = createViewModeActions(value, onViewModeChange);
5580
+ return {
5581
+ nodes: [
5582
+ viewModeGroupAction,
5583
+ ...viewModeActions
5584
+ ],
5585
+ edges: [
5586
+ {
5587
+ source: "root",
5588
+ target: "viewMode"
5589
+ },
5590
+ ...viewModeActions.map(({ id }) => ({
5591
+ source: viewModeGroupAction.id,
5592
+ target: id
5593
+ }))
5594
+ ]
5595
+ };
5490
5596
  };
5491
- var PreviewInlineWidget = class extends WidgetType6 {
5492
- constructor(_options, _link) {
5493
- super();
5494
- this._options = _options;
5495
- this._link = _link;
5597
+
5598
+ // packages/ui/react-ui-editor/src/defaults.ts
5599
+ import { EditorView as EditorView20 } from "@codemirror/view";
5600
+ import { mx as mx3 } from "@dxos/react-ui-theme";
5601
+ var margin = "!mt-[1rem]";
5602
+ var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
5603
+ var editorContent = mx3(margin, editorWidth);
5604
+ var editorFullWidth = mx3(margin);
5605
+ var editorGutter = EditorView20.theme({
5606
+ // Match margin from content.
5607
+ // Gutter = 2rem + 1rem margin.
5608
+ ".cm-gutters": {
5609
+ marginTop: "1rem",
5610
+ paddingRight: "1rem"
5496
5611
  }
5497
- // override ignoreEvent() {
5498
- // return false;
5499
- // }
5500
- eq(other) {
5501
- return this._link.ref === other._link.ref && this._link.label === other._link.label;
5612
+ });
5613
+ var editorMonospace = EditorView20.theme({
5614
+ ".cm-content": {
5615
+ fontFamily: fontMono
5502
5616
  }
5503
- toDOM(view) {
5504
- const root = document.createElement("dx-ref-tag");
5505
- root.textContent = this._link.label;
5506
- root.setAttribute("ref", this._link.ref);
5507
- return root;
5617
+ });
5618
+ var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5619
+ var stackItemContentEditorClassNames = (role) => mx3("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");
5620
+ var stackItemContentToolbarClassNames = (role) => mx3("attention-surface is-full border-be !border-separator relative z-[1]", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
5621
+
5622
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
5623
+ var createToolbar = ({ getView, state, customActions, ...features }) => {
5624
+ const nodes = [];
5625
+ const edges = [];
5626
+ if (features.headings ?? true) {
5627
+ const headings2 = createHeadings(state, getView);
5628
+ nodes.push(...headings2.nodes);
5629
+ edges.push(...headings2.edges);
5508
5630
  }
5509
- };
5510
- var PreviewBlockWidget = class extends WidgetType6 {
5511
- constructor(_options, _link) {
5512
- super();
5513
- this._options = _options;
5514
- this._link = _link;
5631
+ if (features.formatting ?? true) {
5632
+ const formatting = createFormatting(state, getView);
5633
+ nodes.push(...formatting.nodes);
5634
+ edges.push(...formatting.edges);
5515
5635
  }
5516
- // override ignoreEvent() {
5517
- // return true;
5518
- // }
5519
- eq(other) {
5520
- return this._link.ref === other._link.ref;
5636
+ if (features.lists ?? true) {
5637
+ const lists = createLists(state, getView);
5638
+ nodes.push(...lists.nodes);
5639
+ edges.push(...lists.edges);
5521
5640
  }
5522
- toDOM(view) {
5523
- const root = document.createElement("div");
5524
- root.classList.add("cm-preview-block");
5525
- const handleAction = (action) => {
5526
- const pos = view.posAtDOM(root);
5527
- const node = syntaxTree9(view.state).resolve(pos + 1).node.parent;
5528
- if (!node) {
5529
- return;
5530
- }
5531
- const link = getLinkRef(view.state, node);
5532
- if (link?.ref !== action.link.ref) {
5533
- return;
5534
- }
5535
- switch (action.type) {
5536
- // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5537
- // Insert ref text.
5538
- case "insert": {
5539
- view.dispatch({
5540
- changes: {
5541
- from: node.from,
5542
- to: node.to,
5543
- insert: action.target.text
5544
- }
5545
- });
5546
- break;
5547
- }
5548
- // Remove ref.
5549
- case "delete": {
5550
- view.dispatch({
5551
- changes: {
5552
- from: node.from,
5553
- to: node.to
5554
- }
5555
- });
5556
- break;
5557
- }
5558
- }
5559
- };
5560
- this._options.renderBlock(root, {
5561
- readonly: view.state.readOnly,
5562
- link: this._link,
5563
- onAction: handleAction,
5564
- onLookup: this._options.onLookup
5565
- }, view);
5566
- return root;
5641
+ if (features.blocks ?? true) {
5642
+ const blocks = createBlocks(state, getView);
5643
+ nodes.push(...blocks.nodes);
5644
+ edges.push(...blocks.edges);
5567
5645
  }
5646
+ if (features.image) {
5647
+ const image2 = createImageUpload(features.image);
5648
+ nodes.push(...image2.nodes);
5649
+ edges.push(...image2.edges);
5650
+ }
5651
+ if (customActions) {
5652
+ const custom = customActions();
5653
+ nodes.push(...custom.nodes);
5654
+ edges.push(...custom.edges);
5655
+ }
5656
+ const editorToolbarGap = createGapSeparator();
5657
+ nodes.push(...editorToolbarGap.nodes);
5658
+ edges.push(...editorToolbarGap.edges);
5659
+ if (features.comment) {
5660
+ const comment = createComment2(state, getView);
5661
+ nodes.push(...comment.nodes);
5662
+ edges.push(...comment.edges);
5663
+ }
5664
+ if (features.search ?? true) {
5665
+ const search = createSearch(getView);
5666
+ nodes.push(...search.nodes);
5667
+ edges.push(...search.edges);
5668
+ }
5669
+ if (features.viewMode) {
5670
+ const viewMode = createViewMode(state, features.viewMode);
5671
+ nodes.push(...viewMode.nodes);
5672
+ edges.push(...viewMode.edges);
5673
+ }
5674
+ return {
5675
+ nodes,
5676
+ edges
5677
+ };
5568
5678
  };
5569
-
5570
- // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5571
- import { keymap as keymap10 } from "@codemirror/view";
5572
- var defaultItems = [
5573
- "hello world!",
5574
- "this is a test.",
5575
- "this is [DXOS](https://dxos.org)"
5576
- ];
5577
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5578
- let t;
5579
- let idx = 0;
5580
- return [
5581
- keymap10.of([
5582
- {
5583
- // Reset.
5584
- key: "alt-meta-'",
5585
- run: (view) => {
5586
- clearTimeout(t);
5587
- idx = 0;
5588
- return true;
5589
- }
5590
- },
5591
- {
5592
- // Next prompt.
5593
- // TODO(burdon): Press 1-9 to select prompt?
5594
- key: "shift-meta-'",
5595
- run: (view) => {
5596
- clearTimeout(t);
5597
- const text = items[idx++];
5598
- if (idx === items?.length) {
5599
- idx = 0;
5600
- }
5601
- let i = 0;
5602
- const insert = (d = 0) => {
5603
- t = setTimeout(() => {
5604
- const pos = view.state.selection.main.head;
5605
- view.dispatch({
5606
- changes: {
5607
- from: pos,
5608
- insert: text[i++]
5609
- },
5610
- selection: {
5611
- anchor: pos + 1
5612
- }
5613
- });
5614
- if (i < text.length) {
5615
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5616
- }
5617
- }, d);
5618
- };
5619
- insert();
5620
- return true;
5621
- }
5622
- }
5623
- ])
5624
- ];
5625
- };
5626
-
5627
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
5628
- import { useCallback as useCallback2 } from "react";
5629
- var useActionHandler = (view) => {
5630
- return useCallback2((action) => view && processEditorPayload(view, action.properties), [
5631
- view
5679
+ var useEditorToolbarActionGraph = (props) => {
5680
+ const menuCreator = useCallback(() => createToolbar(props), [
5681
+ props
5632
5682
  ]);
5683
+ return useMenuActions(menuCreator);
5633
5684
  };
5685
+ var EditorToolbar = /* @__PURE__ */ memo(({ classNames, attendableId, role, ...props }) => {
5686
+ const menuProps = useEditorToolbarActionGraph(props);
5687
+ return /* @__PURE__ */ React3.createElement("div", {
5688
+ role: "none",
5689
+ className: stackItemContentToolbarClassNames(role)
5690
+ }, /* @__PURE__ */ React3.createElement(ElevationProvider, {
5691
+ elevation: role === "section" ? "positioned" : "base"
5692
+ }, /* @__PURE__ */ React3.createElement(MenuProvider, {
5693
+ ...menuProps,
5694
+ attendableId
5695
+ }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
5696
+ classNames: [
5697
+ textBlockWidth,
5698
+ "!bg-transparent",
5699
+ classNames
5700
+ ]
5701
+ }))));
5702
+ });
5634
5703
 
5635
5704
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
5636
5705
  import { EditorState as EditorState2 } from "@codemirror/state";
5637
5706
  import { EditorView as EditorView21 } from "@codemirror/view";
5638
5707
  import { useFocusableGroup } from "@fluentui/react-tabster";
5639
- import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5708
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5640
5709
  import { log as log7 } from "@dxos/log";
5641
5710
  import { getProviderValue, isNotFalsy as isNotFalsy4 } from "@dxos/util";
5642
5711
  var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
@@ -5761,7 +5830,7 @@ var useTextEditor = (props = {}, deps = []) => {
5761
5830
  Escape: view?.state.facet(editorInputMode).noTabster
5762
5831
  }
5763
5832
  });
5764
- const handleKeyUp = useCallback3((event) => {
5833
+ const handleKeyUp = useCallback2((event) => {
5765
5834
  const { key, target, currentTarget } = event;
5766
5835
  if (target === currentTarget) {
5767
5836
  switch (key) {
@@ -5820,7 +5889,7 @@ export {
5820
5889
  commentsState,
5821
5890
  convertTreeToJson,
5822
5891
  createBasicExtensions,
5823
- createComment2 as createComment,
5892
+ createComment,
5824
5893
  createDataExtensions,
5825
5894
  createEditorAction,
5826
5895
  createEditorActionGroup,
@@ -5898,7 +5967,6 @@ export {
5898
5967
  toggleStyle,
5899
5968
  translations_default as translations,
5900
5969
  typewriter,
5901
- useActionHandler,
5902
5970
  useCommentClickListener,
5903
5971
  useCommentState,
5904
5972
  useComments,