@dxos/react-ui-editor 0.8.2-main.f081794 → 0.8.2-main.fbd8ed0

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 (131) hide show
  1. package/dist/lib/browser/index.mjs +1664 -1359
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs.map +2 -2
  5. package/dist/lib/node/index.cjs +2122 -1819
  6. package/dist/lib/node/index.cjs.map +4 -4
  7. package/dist/lib/node/meta.json +1 -1
  8. package/dist/lib/node/testing/index.cjs.map +2 -2
  9. package/dist/lib/node-esm/index.mjs +1664 -1359
  10. package/dist/lib/node-esm/index.mjs.map +4 -4
  11. package/dist/lib/node-esm/meta.json +1 -1
  12. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  13. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  14. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  15. package/dist/types/src/{stories/InputMode.stories.d.ts → components/EditorToolbar/EditorToolbar.stories.d.ts} +3 -7
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  18. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/comment.d.ts +4 -3
  20. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  22. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  24. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorToolbar/image.d.ts +16 -0
  26. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  27. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  28. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  29. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  30. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  31. package/dist/types/src/components/EditorToolbar/util.d.ts +11 -17
  32. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  33. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
  34. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  35. package/dist/types/src/defaults.d.ts.map +1 -1
  36. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  37. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  38. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  39. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  40. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  41. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  42. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  43. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  44. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  45. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  46. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  47. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  48. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  49. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  50. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  51. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  52. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  53. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  54. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  55. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  56. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  57. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  58. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  59. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  60. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  61. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  62. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -0
  63. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  64. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  65. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  66. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  69. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  71. package/dist/types/src/extensions/markdown/outliner.d.ts +12 -0
  72. package/dist/types/src/extensions/markdown/outliner.d.ts.map +1 -0
  73. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  74. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  75. package/dist/types/src/extensions/modes.d.ts +5 -5
  76. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  77. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  78. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  79. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  80. package/dist/types/src/hooks/index.d.ts +0 -1
  81. package/dist/types/src/hooks/index.d.ts.map +1 -1
  82. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  83. package/dist/types/src/stories/TextEditorBasic.stories.d.ts +3 -0
  84. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +1 -1
  85. package/dist/types/src/stories/story-utils.d.ts.map +1 -1
  86. package/dist/types/src/styles/theme.d.ts.map +1 -1
  87. package/dist/types/src/testing/RefPopover.d.ts.map +1 -1
  88. package/dist/types/src/util/cursor.d.ts.map +1 -1
  89. package/dist/types/src/util/debug.d.ts.map +1 -1
  90. package/dist/types/src/util/dom.d.ts.map +1 -1
  91. package/dist/types/src/util/facet.d.ts.map +1 -1
  92. package/dist/types/src/util/react.d.ts.map +1 -1
  93. package/dist/types/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +32 -28
  95. package/src/components/EditorToolbar/EditorToolbar.stories.tsx +90 -0
  96. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -32
  97. package/src/components/EditorToolbar/blocks.ts +27 -6
  98. package/src/components/EditorToolbar/comment.ts +11 -4
  99. package/src/components/EditorToolbar/formatting.ts +34 -7
  100. package/src/components/EditorToolbar/headings.ts +9 -8
  101. package/src/components/EditorToolbar/image.ts +16 -0
  102. package/src/components/EditorToolbar/lists.ts +26 -7
  103. package/src/components/EditorToolbar/search.ts +19 -0
  104. package/src/components/EditorToolbar/util.ts +14 -14
  105. package/src/components/EditorToolbar/view-mode.ts +9 -8
  106. package/src/defaults.ts +1 -1
  107. package/src/extensions/automerge/automerge.stories.tsx +9 -7
  108. package/src/extensions/automerge/automerge.test.tsx +4 -4
  109. package/src/extensions/automerge/automerge.ts +2 -2
  110. package/src/extensions/automerge/defs.ts +1 -2
  111. package/src/extensions/automerge/sync.ts +4 -4
  112. package/src/extensions/automerge/update-automerge.ts +1 -1
  113. package/src/extensions/automerge/update-codemirror.ts +3 -4
  114. package/src/extensions/markdown/changes.ts +3 -2
  115. package/src/extensions/markdown/decorate.ts +8 -7
  116. package/src/extensions/markdown/formatting.ts +4 -4
  117. package/src/extensions/markdown/index.ts +1 -0
  118. package/src/extensions/markdown/outliner.ts +235 -0
  119. package/src/extensions/markdown/styles.ts +2 -2
  120. package/src/extensions/modes.ts +5 -6
  121. package/src/extensions/preview/preview.ts +1 -1
  122. package/src/hooks/index.ts +0 -1
  123. package/src/stories/TextEditorBasic.stories.tsx +44 -0
  124. package/src/stories/story-utils.tsx +7 -9
  125. package/src/styles/theme.ts +3 -0
  126. package/src/testing/RefPopover.tsx +4 -4
  127. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  128. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  129. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  130. package/src/hooks/useActionHandler.ts +0 -12
  131. package/src/stories/InputMode.stories.tsx +0 -124
@@ -36,13 +36,13 @@ var translations_default = [
36
36
  ];
37
37
 
38
38
  // packages/ui/react-ui-editor/src/index.ts
39
- import { EditorState as EditorState3 } from "@codemirror/state";
40
- import { EditorView as EditorView22, keymap as keymap11 } from "@codemirror/view";
39
+ import { EditorState as EditorState4 } from "@codemirror/state";
40
+ import { EditorView as EditorView23, keymap as keymap11 } from "@codemirror/view";
41
41
  import { tags as tags2 } from "@lezer/highlight";
42
42
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
43
43
 
44
44
  // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
45
- import React, { useCallback } from "react";
45
+ import React3, { memo, useCallback } from "react";
46
46
  import { ElevationProvider } from "@dxos/react-ui";
47
47
  import { MenuProvider, ToolbarMenu, createGapSeparator, useMenuActions } from "@dxos/react-ui-menu";
48
48
  import { textBlockWidth } from "@dxos/react-ui-theme";
@@ -54,692 +54,110 @@ import { createMenuAction, createMenuItemGroup } from "@dxos/react-ui-menu";
54
54
  var useEditorToolbarState = (initialState = {}) => {
55
55
  return useMemo(() => live(initialState), []);
56
56
  };
57
- var createEditorAction = (payload, icon, label = [
58
- `${payload.type} label`,
59
- {
60
- ns: translationKey
61
- }
62
- ], id = payload.type) => createMenuAction(id, {
63
- icon,
64
- label,
65
- ...payload
66
- });
57
+ var createEditorAction = (id, invoke, properties) => {
58
+ const { label = [
59
+ `${id} label`,
60
+ {
61
+ ns: translationKey
62
+ }
63
+ ], ...rest } = properties;
64
+ return createMenuAction(id, invoke, {
65
+ label,
66
+ ...rest
67
+ });
68
+ };
67
69
  var createEditorActionGroup = (id, props, icon) => createMenuItemGroup(id, {
68
70
  icon,
69
71
  iconOnly: true,
70
72
  ...props
71
73
  });
72
- var editorToolbarSearch = createEditorAction({
73
- type: "search"
74
- }, "ph--magnifying-glass--regular");
75
74
 
76
- // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
77
- var createBlockGroupAction = (value) => createEditorActionGroup("block", {
78
- variant: "toggleGroup",
79
- selectCardinality: "single",
80
- value
81
- });
82
- var createBlockActions = (value, blankLine) => Object.entries({
83
- blockquote: "ph--quotes--regular",
84
- codeblock: "ph--code-block--regular",
85
- table: "ph--table--regular"
86
- }).map(([type, icon]) => {
87
- return createEditorAction({
88
- type,
89
- checked: type === value,
90
- ...type === "table" && {
91
- disabled: !!blankLine
92
- }
93
- }, icon);
94
- });
95
- var createBlocks = (state) => {
96
- const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
97
- const blockGroupAction = createBlockGroupAction(value);
98
- const blockActions = createBlockActions(value, state.blankLine);
99
- return {
100
- nodes: [
101
- blockGroupAction,
102
- ...blockActions
103
- ],
104
- edges: [
105
- {
106
- source: "root",
107
- target: "block"
108
- },
109
- ...blockActions.map(({ id }) => ({
110
- source: blockGroupAction.id,
111
- target: id
112
- }))
113
- ]
114
- };
115
- };
75
+ // packages/ui/react-ui-editor/src/extensions/annotations.ts
76
+ import { StateField } from "@codemirror/state";
77
+ import { Decoration, EditorView } from "@codemirror/view";
78
+ import { isNotFalsy } from "@dxos/util";
116
79
 
117
- // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
118
- var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
119
- var createCommentAction = (label) => createEditorAction({
120
- type: "comment",
121
- testId: "editor.toolbar.comment"
122
- }, "ph--chat-text--regular", label);
123
- var createComment = (state) => ({
124
- nodes: [
125
- createCommentAction([
126
- commentLabel(state.comment, state.selection),
127
- {
128
- ns: translationKey
129
- }
130
- ])
131
- ],
132
- edges: [
133
- {
134
- source: "root",
135
- target: "comment"
136
- }
137
- ]
80
+ // packages/ui/react-ui-editor/src/util/facet.ts
81
+ import { Facet } from "@codemirror/state";
82
+ var singleValueFacet = (defaultValue) => Facet.define({
83
+ // Called immediately.
84
+ combine: (providers) => {
85
+ return providers[0] ?? defaultValue;
86
+ }
138
87
  });
139
88
 
140
- // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
141
- var formats = {
142
- strong: "ph--text-b--regular",
143
- emphasis: "ph--text-italic--regular",
144
- strikethrough: "ph--text-strikethrough--regular",
145
- code: "ph--code--regular",
146
- link: "ph--link--regular"
89
+ // packages/ui/react-ui-editor/src/util/cursor.ts
90
+ var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
91
+ var defaultCursorConverter = {
92
+ toCursor: (position) => position.toString(),
93
+ fromCursor: (cursor) => parseInt(cursor)
147
94
  };
148
- var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
149
- variant: "toggleGroup",
150
- selectCardinality: "multiple",
151
- value: Object.keys(formats).filter((key) => !!formatting[key])
152
- });
153
- var createFormattingActions = (formatting) => Object.entries(formats).map(([type, icon]) => createEditorAction({
154
- type,
155
- checked: !!formatting[type]
156
- }, icon));
157
- var createFormatting = (state) => {
158
- const formattingGroupAction = createFormattingGroup(state);
159
- const formattingActions = createFormattingActions(state);
160
- return {
161
- nodes: [
162
- formattingGroupAction,
163
- ...formattingActions
164
- ],
165
- edges: [
166
- {
167
- source: "root",
168
- target: "formatting"
169
- },
170
- ...formattingActions.map(({ id }) => ({
171
- source: formattingGroupAction.id,
172
- target: id
173
- }))
174
- ]
175
- };
95
+ var Cursor = class _Cursor {
96
+ static {
97
+ this.converter = singleValueFacet(defaultCursorConverter);
98
+ }
99
+ static {
100
+ this.getCursorFromRange = (state, range) => {
101
+ const cursorConverter2 = state.facet(_Cursor.converter);
102
+ const from = cursorConverter2.toCursor(range.from);
103
+ const to = cursorConverter2.toCursor(range.to, -1);
104
+ return [
105
+ from,
106
+ to
107
+ ].join(":");
108
+ };
109
+ }
110
+ static {
111
+ this.getRangeFromCursor = (state, cursor) => {
112
+ const cursorConverter2 = state.facet(_Cursor.converter);
113
+ const parts = cursor.split(":");
114
+ const from = cursorConverter2.fromCursor(parts[0]);
115
+ const to = cursorConverter2.fromCursor(parts[1]);
116
+ return from !== void 0 && to !== void 0 ? {
117
+ from,
118
+ to
119
+ } : void 0;
120
+ };
121
+ }
176
122
  };
177
123
 
178
- // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
179
- var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
180
- variant: "dropdownMenu",
181
- applyActive: true,
182
- selectCardinality: "single",
183
- value
184
- }, "ph--text-h--regular");
185
- var createHeadingActions = (value) => Object.entries({
186
- "0": "ph--paragraph--regular",
187
- "1": "ph--text-h-one--regular",
188
- "2": "ph--text-h-two--regular",
189
- "3": "ph--text-h-three--regular",
190
- "4": "ph--text-h-four--regular",
191
- "5": "ph--text-h-five--regular",
192
- "6": "ph--text-h-six--regular"
193
- }).map(([levelStr, icon]) => {
194
- const level = parseInt(levelStr);
195
- return createEditorAction({
196
- type: "heading",
197
- data: level,
198
- checked: value === levelStr
199
- }, icon, [
200
- "heading level label",
201
- {
202
- count: level,
203
- ns: translationKey
124
+ // packages/ui/react-ui-editor/src/util/debug.ts
125
+ import { log } from "@dxos/log";
126
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
127
+ var wrapWithCatch = (fn) => {
128
+ return (...args) => {
129
+ try {
130
+ return fn(...args);
131
+ } catch (err) {
132
+ log.catch(err, void 0, {
133
+ F: __dxlog_file,
134
+ L: 15,
135
+ S: void 0,
136
+ C: (f, a) => f(...a)
137
+ });
204
138
  }
205
- ], `heading--${levelStr}`);
206
- });
207
- var computeHeadingValue = (state) => {
208
- const blockType = state ? state.blockType : "paragraph";
209
- const header = blockType && /heading(\d)/.exec(blockType);
210
- return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
211
- };
212
- var createHeadings = (state) => {
213
- const headingValue = computeHeadingValue(state);
214
- const headingGroupAction = createHeadingGroupAction(headingValue);
215
- const headingActions = createHeadingActions(headingValue);
216
- return {
217
- nodes: [
218
- headingGroupAction,
219
- ...headingActions
220
- ],
221
- edges: [
222
- {
223
- source: "root",
224
- target: "heading"
225
- },
226
- ...headingActions.map(({ id }) => ({
227
- source: headingGroupAction.id,
228
- target: id
229
- }))
230
- ]
231
139
  };
232
140
  };
233
-
234
- // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
235
- var listStyles = {
236
- bullet: "ph--list-bullets--regular",
237
- ordered: "ph--list-numbers--regular",
238
- task: "ph--list-checks--regular"
141
+ var callbackWrapper = (fn) => (...args) => {
142
+ try {
143
+ return fn(...args);
144
+ } catch (err) {
145
+ log.catch(err, void 0, {
146
+ F: __dxlog_file,
147
+ L: 29,
148
+ S: void 0,
149
+ C: (f, a) => f(...a)
150
+ });
151
+ }
239
152
  };
240
- var createListGroupAction = (value) => createEditorActionGroup("list", {
241
- variant: "toggleGroup",
242
- selectCardinality: "single",
243
- value
244
- });
245
- var createListActions = (value) => Object.entries(listStyles).map(([listStyle, icon]) => createEditorAction({
246
- type: `list-${listStyle}`,
247
- checked: value === listStyle
248
- }, icon));
249
- var createLists = (state) => {
250
- const value = state.listStyle ?? "";
251
- const listGroupAction = createListGroupAction(value);
252
- const listActionsMap = createListActions(value);
253
- return {
254
- nodes: [
255
- listGroupAction,
256
- ...listActionsMap
257
- ],
258
- edges: [
259
- {
260
- source: "root",
261
- target: "list"
262
- },
263
- ...listActionsMap.map(({ id }) => ({
264
- source: listGroupAction.id,
265
- target: id
266
- }))
267
- ]
268
- };
153
+ var debugDispatcher = (trs, view) => {
154
+ logChanges(trs);
155
+ view.update(trs);
269
156
  };
270
-
271
- // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
272
- var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
273
- variant: "dropdownMenu",
274
- applyActive: true,
275
- selectCardinality: "single",
276
- value
277
- }, "ph--eye--regular");
278
- var createViewModeActions = (value) => Object.entries({
279
- preview: "ph--eye--regular",
280
- source: "ph--pencil-simple--regular",
281
- readonly: "ph--pencil-slash--regular"
282
- }).map(([viewMode, icon]) => {
283
- return createEditorAction({
284
- type: "view-mode",
285
- data: viewMode,
286
- checked: viewMode === value
287
- }, icon, [
288
- `${viewMode} mode label`,
289
- {
290
- ns: translationKey
291
- }
292
- ], `view-mode--${viewMode}`);
293
- });
294
- var createViewMode = (state) => {
295
- const value = state.viewMode ?? "source";
296
- const viewModeGroupAction = createViewModeGroupAction(value);
297
- const viewModeActions = createViewModeActions(value);
298
- return {
299
- nodes: [
300
- viewModeGroupAction,
301
- ...viewModeActions
302
- ],
303
- edges: [
304
- {
305
- source: "root",
306
- target: "viewMode"
307
- },
308
- ...viewModeActions.map(({ id }) => ({
309
- source: viewModeGroupAction.id,
310
- target: id
311
- }))
312
- ]
313
- };
314
- };
315
-
316
- // packages/ui/react-ui-editor/src/defaults.ts
317
- import { EditorView } from "@codemirror/view";
318
- import { mx as mx2 } from "@dxos/react-ui-theme";
319
-
320
- // packages/ui/react-ui-editor/src/styles/markdown.ts
321
- import { mx } from "@dxos/react-ui-theme";
322
- var headings = {
323
- 1: "text-4xl",
324
- 2: "text-3xl",
325
- 3: "text-2xl",
326
- 4: "text-xl",
327
- 5: "text-lg",
328
- 6: ""
329
- };
330
- var theme = {
331
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
332
- codeMark: "font-mono text-primary-500",
333
- mark: "opacity-50",
334
- heading: (level) => {
335
- return mx(headings[level], "dark:text-primary-400");
336
- }
337
- };
338
-
339
- // packages/ui/react-ui-editor/src/styles/tokens.ts
340
- import get from "lodash.get";
341
- import { tokens } from "@dxos/react-ui-theme";
342
- var getToken = (path, defaultValue) => {
343
- const value = get(tokens, path, defaultValue);
344
- return value?.toString() ?? "";
345
- };
346
- var fontBody = getToken("fontFamily.body");
347
- var fontMono = getToken("fontFamily.mono");
348
-
349
- // packages/ui/react-ui-editor/src/styles/theme.ts
350
- var defaultTheme = {
351
- "&": {},
352
- "&.cm-focused": {
353
- outline: "none"
354
- },
355
- /**
356
- * Scroller
357
- */
358
- ".cm-scroller": {
359
- overflowY: "auto"
360
- },
361
- /**
362
- * Content
363
- * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
364
- */
365
- ".cm-content": {
366
- padding: "unset",
367
- fontFamily: fontBody,
368
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
369
- fontSize: "16px",
370
- lineHeight: 1.5,
371
- color: "unset"
372
- },
373
- /**
374
- * Gutters
375
- * NOTE: Gutters should have the same top margin as the content.
376
- */
377
- ".cm-gutters": {
378
- borderRight: "none",
379
- background: "transparent"
380
- },
381
- ".cm-gutter": {},
382
- ".cm-gutter.cm-lineNumbers": {
383
- paddingRight: "4px",
384
- borderRight: "1px solid var(--dx-separator)"
385
- },
386
- ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
387
- minWidth: "40px",
388
- alignContent: "center"
389
- },
390
- /**
391
- * Height is set to match the corresponding line.
392
- */
393
- ".cm-gutterElement": {
394
- alignItems: "center",
395
- fontSize: "12px"
396
- },
397
- /**
398
- * Line.
399
- */
400
- ".cm-line": {
401
- paddingInline: 0
402
- },
403
- ".cm-activeLine": {
404
- background: "var(--dx-cmActiveLine)"
405
- },
406
- /**
407
- * Cursor (layer).
408
- */
409
- ".cm-cursor, .cm-dropCursor": {
410
- borderLeft: "2px solid var(--dx-cmCursor)"
411
- },
412
- ".cm-placeholder": {
413
- color: "var(--dx-subdued)"
414
- },
415
- /**
416
- * Selection (layer).
417
- */
418
- ".cm-selectionBackground": {
419
- background: "var(--dx-cmSelection)"
420
- },
421
- /**
422
- * Search.
423
- * NOTE: Matches comment.
424
- */
425
- ".cm-searchMatch": {
426
- margin: "0 -3px",
427
- padding: "3px",
428
- borderRadius: "3px",
429
- background: "var(--dx-cmHighlightSurface)",
430
- color: "var(--dx-cmHighlight)"
431
- },
432
- ".cm-searchMatch-selected": {
433
- textDecoration: "underline"
434
- },
435
- /**
436
- * Link.
437
- */
438
- ".cm-link": {
439
- textDecorationLine: "underline",
440
- textDecorationThickness: "1px",
441
- textDecorationColor: "var(--dx-separator)",
442
- textUnderlineOffset: "2px",
443
- borderRadius: ".125rem"
444
- },
445
- ".cm-link > span": {
446
- color: "var(--dx-accentText)"
447
- },
448
- /**
449
- * Tooltip.
450
- */
451
- ".cm-tooltip": {
452
- background: "var(--dx-baseSurface)"
453
- },
454
- ".cm-tooltip-below": {},
455
- /**
456
- * Autocomplete.
457
- * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
458
- */
459
- ".cm-tooltip.cm-tooltip-autocomplete": {
460
- marginTop: "4px",
461
- marginLeft: "-3px"
462
- },
463
- ".cm-tooltip.cm-tooltip-autocomplete > ul": {
464
- maxHeight: "20em"
465
- },
466
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
467
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
468
- ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
469
- paddingLeft: "4px !important",
470
- borderBottom: "none !important",
471
- color: "var(--dx-accentText)"
472
- },
473
- ".cm-tooltip.cm-completionInfo": {
474
- width: "360px !important",
475
- margin: "-10px 1px 0 1px",
476
- padding: "8px !important",
477
- borderColor: "var(--dx-separator)"
478
- },
479
- ".cm-completionIcon": {
480
- display: "none"
481
- },
482
- ".cm-completionLabel": {
483
- fontFamily: fontBody
484
- },
485
- ".cm-completionMatchedText": {
486
- textDecoration: "none !important",
487
- opacity: 0.5
488
- },
489
- /**
490
- * Panels
491
- * https://github.com/codemirror/search/blob/main/src/search.ts#L745
492
- *
493
- * Find/replace panel.
494
- * <div class="cm-announced">...</div>
495
- * <div class="cm-scroller">...</div>
496
- * <div class="cm-panels cm-panels-bottom">
497
- * <div class="cm-search cm-panel">
498
- * <input class="cm-textfield" />
499
- * <button class="cm-button">...</button>
500
- * <label><input type="checkbox" />...</label>
501
- * </div>
502
- * </div
503
- */
504
- // TODO(burdon): Implement custom panel (with icon buttons).
505
- ".cm-panels": {},
506
- ".cm-panel": {
507
- fontFamily: fontBody,
508
- backgroundColor: "var(--surface-bg)"
509
- },
510
- ".cm-panel input, .cm-panel button, .cm-panel label": {
511
- color: "var(--dx-subdued)",
512
- fontFamily: fontBody,
513
- fontSize: "14px",
514
- all: "unset",
515
- margin: "3px !important",
516
- padding: "2px 6px !important",
517
- outline: "1px solid transparent"
518
- },
519
- ".cm-panel input, .cm-panel button": {
520
- backgroundColor: "var(--dx-input)"
521
- },
522
- ".cm-panel input:focus, .cm-panel button:focus": {
523
- outline: "1px solid var(--dx-accentFocusIndicator)"
524
- },
525
- ".cm-panel label": {
526
- display: "inline-flex",
527
- alignItems: "center",
528
- cursor: "pointer"
529
- },
530
- ".cm-panel input.cm-textfield": {},
531
- ".cm-panel input[type=checkbox]": {
532
- width: "8px",
533
- height: "8px",
534
- marginRight: "6px !important",
535
- padding: "2px !important",
536
- color: "var(--dx-accentFocusIndicator)"
537
- },
538
- ".cm-panel button": {
539
- "&:hover": {
540
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
541
- },
542
- "&:active": {
543
- backgroundColor: "var(--dx-accentSurfaceHover)"
544
- }
545
- },
546
- ".cm-panel.cm-search": {
547
- padding: "4px",
548
- borderTop: "1px solid var(--dx-separator)"
549
- }
550
- };
551
-
552
- // packages/ui/react-ui-editor/src/defaults.ts
553
- var margin = "!mt-[1rem]";
554
- var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
555
- var editorContent = mx2(margin, editorWidth);
556
- var editorFullWidth = mx2(margin);
557
- var editorGutter = EditorView.theme({
558
- // Match margin from content.
559
- // Gutter = 2rem + 1rem margin.
560
- ".cm-gutters": {
561
- marginTop: "1rem",
562
- paddingRight: "1rem"
563
- }
564
- });
565
- var editorMonospace = EditorView.theme({
566
- ".cm-content": {
567
- fontFamily: fontMono
568
- }
569
- });
570
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
571
- 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");
572
- 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");
573
-
574
- // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
575
- var createToolbar = ({ state, customActions, ...features }) => {
576
- const nodes = [];
577
- const edges = [];
578
- if (features.headings ?? true) {
579
- const headings2 = createHeadings(state);
580
- nodes.push(...headings2.nodes);
581
- edges.push(...headings2.edges);
582
- }
583
- if (features.formatting ?? true) {
584
- const formatting = createFormatting(state);
585
- nodes.push(...formatting.nodes);
586
- edges.push(...formatting.edges);
587
- }
588
- if (features.lists ?? true) {
589
- const lists = createLists(state);
590
- nodes.push(...lists.nodes);
591
- edges.push(...lists.edges);
592
- }
593
- if (features.blocks ?? true) {
594
- const blocks = createBlocks(state);
595
- nodes.push(...blocks.nodes);
596
- edges.push(...blocks.edges);
597
- }
598
- if (customActions) {
599
- const custom = customActions();
600
- nodes.push(...custom.nodes);
601
- edges.push(...custom.edges);
602
- }
603
- const editorToolbarGap = createGapSeparator();
604
- nodes.push(...editorToolbarGap.nodes);
605
- edges.push(...editorToolbarGap.edges);
606
- if (features.comment ?? true) {
607
- const comment = createComment(state);
608
- nodes.push(...comment.nodes);
609
- edges.push(...comment.edges);
610
- }
611
- if (features.search ?? true) {
612
- nodes.push(editorToolbarSearch);
613
- edges.push({
614
- source: "root",
615
- target: editorToolbarSearch.id
616
- });
617
- }
618
- if (features.viewMode ?? true) {
619
- const viewMode = createViewMode(state);
620
- nodes.push(...viewMode.nodes);
621
- edges.push(...viewMode.edges);
622
- }
623
- return {
624
- nodes,
625
- edges
626
- };
627
- };
628
- var useEditorToolbarActionGraph = ({ onAction, ...props }) => {
629
- const menuCreator = useCallback(() => createToolbar(props), [
630
- props
631
- ]);
632
- const { resolveGroupItems } = useMenuActions(menuCreator);
633
- return {
634
- resolveGroupItems,
635
- onAction
636
- };
637
- };
638
- var EditorToolbar = ({ classNames, attendableId, role, ...props }) => {
639
- const menuProps = useEditorToolbarActionGraph(props);
640
- return /* @__PURE__ */ React.createElement("div", {
641
- role: "none",
642
- className: stackItemContentToolbarClassNames(role)
643
- }, /* @__PURE__ */ React.createElement(ElevationProvider, {
644
- elevation: role === "section" ? "positioned" : "base"
645
- }, /* @__PURE__ */ React.createElement(MenuProvider, {
646
- ...menuProps,
647
- attendableId
648
- }, /* @__PURE__ */ React.createElement(ToolbarMenu, {
649
- classNames: [
650
- textBlockWidth,
651
- "!bg-transparent",
652
- classNames
653
- ]
654
- }))));
655
- };
656
-
657
- // packages/ui/react-ui-editor/src/extensions/annotations.ts
658
- import { StateField } from "@codemirror/state";
659
- import { Decoration, EditorView as EditorView2 } from "@codemirror/view";
660
- import { isNotFalsy } from "@dxos/util";
661
-
662
- // packages/ui/react-ui-editor/src/util/facet.ts
663
- import { Facet } from "@codemirror/state";
664
- var singleValueFacet = (defaultValue) => Facet.define({
665
- // Called immediately.
666
- combine: (providers) => {
667
- return providers[0] ?? defaultValue;
668
- }
669
- });
670
-
671
- // packages/ui/react-ui-editor/src/util/cursor.ts
672
- var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
673
- var defaultCursorConverter = {
674
- toCursor: (position) => position.toString(),
675
- fromCursor: (cursor) => parseInt(cursor)
676
- };
677
- var Cursor = class _Cursor {
678
- static {
679
- this.converter = singleValueFacet(defaultCursorConverter);
680
- }
681
- static {
682
- this.getCursorFromRange = (state, range) => {
683
- const cursorConverter2 = state.facet(_Cursor.converter);
684
- const from = cursorConverter2.toCursor(range.from);
685
- const to = cursorConverter2.toCursor(range.to, -1);
686
- return [
687
- from,
688
- to
689
- ].join(":");
690
- };
691
- }
692
- static {
693
- this.getRangeFromCursor = (state, cursor) => {
694
- const cursorConverter2 = state.facet(_Cursor.converter);
695
- const parts = cursor.split(":");
696
- const from = cursorConverter2.fromCursor(parts[0]);
697
- const to = cursorConverter2.fromCursor(parts[1]);
698
- return from !== void 0 && to !== void 0 ? {
699
- from,
700
- to
701
- } : void 0;
702
- };
703
- }
704
- };
705
-
706
- // packages/ui/react-ui-editor/src/util/debug.ts
707
- import { log } from "@dxos/log";
708
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
709
- var wrapWithCatch = (fn) => {
710
- return (...args) => {
711
- try {
712
- return fn(...args);
713
- } catch (err) {
714
- log.catch(err, void 0, {
715
- F: __dxlog_file,
716
- L: 15,
717
- S: void 0,
718
- C: (f, a) => f(...a)
719
- });
720
- }
721
- };
722
- };
723
- var callbackWrapper = (fn) => (...args) => {
724
- try {
725
- return fn(...args);
726
- } catch (err) {
727
- log.catch(err, void 0, {
728
- F: __dxlog_file,
729
- L: 29,
730
- S: void 0,
731
- C: (f, a) => f(...a)
732
- });
733
- }
734
- };
735
- var debugDispatcher = (trs, view) => {
736
- logChanges(trs);
737
- view.update(trs);
738
- };
739
- var logChanges = (trs) => {
740
- const changes = trs.flatMap((tr) => {
741
- if (tr.changes.empty) {
742
- return void 0;
157
+ var logChanges = (trs) => {
158
+ const changes = trs.flatMap((tr) => {
159
+ if (tr.changes.empty) {
160
+ return void 0;
743
161
  }
744
162
  const changes2 = [];
745
163
  tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
@@ -791,7 +209,7 @@ var clientRectsFor = (dom) => {
791
209
  };
792
210
 
793
211
  // packages/ui/react-ui-editor/src/util/react.tsx
794
- import React2 from "react";
212
+ import React from "react";
795
213
  import { createRoot } from "react-dom/client";
796
214
  import { ThemeProvider, Tooltip } from "@dxos/react-ui";
797
215
  import { defaultTx } from "@dxos/react-ui-theme";
@@ -808,15 +226,15 @@ var createElement = (tag, options, children) => {
808
226
  return el;
809
227
  };
810
228
  var renderRoot = (root, node) => {
811
- createRoot(root).render(/* @__PURE__ */ React2.createElement(ThemeProvider, {
229
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
812
230
  tx: defaultTx
813
231
  }, node));
814
232
  return root;
815
233
  };
816
234
  var createRenderer = (Component) => (el, props) => {
817
- renderRoot(el, /* @__PURE__ */ React2.createElement(ThemeProvider, {
235
+ renderRoot(el, /* @__PURE__ */ React.createElement(ThemeProvider, {
818
236
  tx: defaultTx
819
- }, /* @__PURE__ */ React2.createElement(Tooltip.Provider, null, /* @__PURE__ */ React2.createElement(Component, props))));
237
+ }, /* @__PURE__ */ React.createElement(Tooltip.Provider, null, /* @__PURE__ */ React.createElement(Component, props))));
820
238
  };
821
239
 
822
240
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
@@ -856,7 +274,7 @@ var annotations = (options = {}) => {
856
274
  });
857
275
  return [
858
276
  annotationsState,
859
- EditorView2.decorations.compute([
277
+ EditorView.decorations.compute([
860
278
  annotationsState
861
279
  ], (state) => {
862
280
  const annotations2 = state.field(annotationsState);
@@ -869,7 +287,7 @@ var annotations = (options = {}) => {
869
287
  styles
870
288
  ];
871
289
  };
872
- var styles = EditorView2.theme({
290
+ var styles = EditorView.theme({
873
291
  ".cm-annotation": {
874
292
  textDecoration: "underline",
875
293
  textDecorationStyle: "wavy",
@@ -917,9 +335,9 @@ var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
917
335
  };
918
336
 
919
337
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
338
+ import { next as A3 } from "@automerge/automerge";
920
339
  import { StateField as StateField2 } from "@codemirror/state";
921
- import { EditorView as EditorView3, ViewPlugin } from "@codemirror/view";
922
- import { next as A3 } from "@dxos/automerge/automerge";
340
+ import { EditorView as EditorView2, ViewPlugin } from "@codemirror/view";
923
341
 
924
342
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
925
343
  import { log as log2 } from "@dxos/log";
@@ -968,10 +386,10 @@ var isReconcile = (tr) => {
968
386
  };
969
387
 
970
388
  // packages/ui/react-ui-editor/src/extensions/automerge/sync.ts
971
- import { next as A2 } from "@dxos/automerge/automerge";
389
+ import { next as A2 } from "@automerge/automerge";
972
390
 
973
391
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
974
- import { next as A } from "@dxos/automerge/automerge";
392
+ import { next as A } from "@automerge/automerge";
975
393
  var updateAutomerge = (field, handle, transactions, state) => {
976
394
  const { lastHeads, path } = state.field(field);
977
395
  let hasChanges = false;
@@ -1140,8 +558,8 @@ var Syncer = class {
1140
558
  }
1141
559
  onAutomergeChange(view) {
1142
560
  const oldHeads = getLastHeads(view.state, this._state);
1143
- const newHeads = A2.getHeads(this._handle.docSync());
1144
- const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.docSync(), oldHeads, newHeads);
561
+ const newHeads = A2.getHeads(this._handle.doc());
562
+ const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
1145
563
  const selection = view.state.selection;
1146
564
  const path = getPath(view.state, this._state);
1147
565
  updateCodeMirror(view, selection, path, diff);
@@ -1157,7 +575,7 @@ var automerge = (accessor) => {
1157
575
  const syncState = StateField2.define({
1158
576
  create: () => ({
1159
577
  path: accessor.path.slice(),
1160
- lastHeads: A3.getHeads(accessor.handle.docSync()),
578
+ lastHeads: A3.getHeads(accessor.handle.doc()),
1161
579
  unreconciledTransactions: []
1162
580
  }),
1163
581
  update: (value, tr) => {
@@ -1202,7 +620,7 @@ var automerge = (accessor) => {
1202
620
  }
1203
621
  }),
1204
622
  // Reconcile local updates.
1205
- EditorView3.updateListener.of(({ view, changes }) => {
623
+ EditorView2.updateListener.of(({ view, changes }) => {
1206
624
  if (!changes.empty) {
1207
625
  syncer.reconcile(view, true);
1208
626
  }
@@ -1212,7 +630,7 @@ var automerge = (accessor) => {
1212
630
 
1213
631
  // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
1214
632
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1215
- import { Decoration as Decoration2, EditorView as EditorView4, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
633
+ import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
1216
634
  import { Event } from "@dxos/async";
1217
635
  import { Context } from "@dxos/context";
1218
636
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts";
@@ -1386,7 +804,7 @@ var RemoteCaretWidget = class extends WidgetType {
1386
804
  return true;
1387
805
  }
1388
806
  };
1389
- var styles2 = EditorView4.theme({
807
+ var styles2 = EditorView3.theme({
1390
808
  ".cm-collab-selection": {},
1391
809
  ".cm-collab-selectionLine": {
1392
810
  padding: 0,
@@ -1552,7 +970,7 @@ var SpaceAwarenessProvider = class {
1552
970
  };
1553
971
 
1554
972
  // packages/ui/react-ui-editor/src/extensions/blast.ts
1555
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
973
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
1556
974
  import defaultsDeep from "lodash.defaultsdeep";
1557
975
  import { invariant as invariant2 } from "@dxos/invariant";
1558
976
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -1600,7 +1018,7 @@ var blast = (options = defaultOptions) => {
1600
1018
  };
1601
1019
  return [
1602
1020
  // Cursor moved.
1603
- EditorView5.updateListener.of((update2) => {
1021
+ EditorView4.updateListener.of((update2) => {
1604
1022
  if (blaster?.node !== update2.view.scrollDOM) {
1605
1023
  if (blaster) {
1606
1024
  blaster.destroy();
@@ -1974,11 +1392,11 @@ var commandKeyBindings = [
1974
1392
  ];
1975
1393
 
1976
1394
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1977
- import { EditorView as EditorView7, keymap as keymap3 } from "@codemirror/view";
1395
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1978
1396
 
1979
1397
  // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1980
1398
  import { RangeSetBuilder } from "@codemirror/state";
1981
- import { Decoration as Decoration3, EditorView as EditorView6, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1399
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1982
1400
  var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1983
1401
  constructor() {
1984
1402
  this.deco = Decoration3.none;
@@ -2002,7 +1420,7 @@ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
2002
1420
  }
2003
1421
  }, {
2004
1422
  provide: (plugin) => [
2005
- EditorView6.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1423
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
2006
1424
  ]
2007
1425
  });
2008
1426
  var CommandHint = class extends WidgetType2 {
@@ -2121,10 +1539,10 @@ var command = (options = {}) => {
2121
1539
  options.onHint ? hintViewPlugin({
2122
1540
  onHint: options.onHint
2123
1541
  }) : [],
2124
- EditorView7.focusChangeEffect.of((_, focusing) => {
1542
+ EditorView6.focusChangeEffect.of((_, focusing) => {
2125
1543
  return focusing ? closeEffect.of(null) : null;
2126
1544
  }),
2127
- EditorView7.theme({
1545
+ EditorView6.theme({
2128
1546
  ".cm-tooltip": {
2129
1547
  background: "transparent"
2130
1548
  }
@@ -2135,7 +1553,7 @@ var command = (options = {}) => {
2135
1553
  // packages/ui/react-ui-editor/src/extensions/comments.ts
2136
1554
  import { invertedEffects } from "@codemirror/commands";
2137
1555
  import { StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
2138
- import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView9, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1556
+ import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView8, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2139
1557
  import sortBy from "lodash.sortby";
2140
1558
  import { useEffect, useMemo as useMemo2 } from "react";
2141
1559
  import { debounce as debounce2 } from "@dxos/async";
@@ -2144,7 +1562,7 @@ import { isNonNullable } from "@dxos/util";
2144
1562
 
2145
1563
  // packages/ui/react-ui-editor/src/extensions/selection.ts
2146
1564
  import { Transaction } from "@codemirror/state";
2147
- import { EditorView as EditorView8, keymap as keymap4 } from "@codemirror/view";
1565
+ import { EditorView as EditorView7, keymap as keymap4 } from "@codemirror/view";
2148
1566
  import { debounce } from "@dxos/async";
2149
1567
  import { invariant as invariant3 } from "@dxos/invariant";
2150
1568
  import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
@@ -2155,7 +1573,7 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
2155
1573
  return {
2156
1574
  selection,
2157
1575
  scrollIntoView: !scrollTo,
2158
- effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1576
+ effects: scrollTo ? EditorView7.scrollIntoView(scrollTo, {
2159
1577
  yMargin: 96
2160
1578
  }) : void 0,
2161
1579
  annotations: Transaction.userEvent.of(stateRestoreAnnotation)
@@ -2197,7 +1615,7 @@ var selectionState = ({ getState, setState } = {}) => {
2197
1615
  // setStateDebounced(id, {});
2198
1616
  // },
2199
1617
  // }),
2200
- EditorView8.updateListener.of(({ view, transactions }) => {
1618
+ EditorView7.updateListener.of(({ view, transactions }) => {
2201
1619
  const id = view.state.facet(documentId);
2202
1620
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
2203
1621
  return;
@@ -2278,7 +1696,7 @@ var commentsState = StateField4.define({
2278
1696
  return value;
2279
1697
  }
2280
1698
  });
2281
- var styles3 = EditorView9.theme({
1699
+ var styles3 = EditorView8.theme({
2282
1700
  ".cm-comment, .cm-comment-current": {
2283
1701
  margin: "0 -3px",
2284
1702
  padding: "3px",
@@ -2298,7 +1716,7 @@ var createCommentMark = (id, isCurrent) => Decoration4.mark({
2298
1716
  "data-comment-id": id
2299
1717
  }
2300
1718
  });
2301
- var commentsDecorations = EditorView9.decorations.compute([
1719
+ var commentsDecorations = EditorView8.decorations.compute([
2302
1720
  commentsState
2303
1721
  ], (state) => {
2304
1722
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -2321,7 +1739,7 @@ var commentsDecorations = EditorView9.decorations.compute([
2321
1739
  return Decoration4.set(decorations);
2322
1740
  });
2323
1741
  var commentClickedEffect = StateEffect3.define();
2324
- var handleCommentClick = EditorView9.domEventHandlers({
1742
+ var handleCommentClick = EditorView8.domEventHandlers({
2325
1743
  click: (event, view) => {
2326
1744
  let target = event.target;
2327
1745
  const editorRoot = view.dom;
@@ -2360,7 +1778,7 @@ var trackPastedComments = (onUpdate) => {
2360
1778
  }
2361
1779
  };
2362
1780
  return [
2363
- EditorView9.domEventHandlers({
1781
+ EditorView8.domEventHandlers({
2364
1782
  cut: handleTrack,
2365
1783
  copy: handleTrack
2366
1784
  }),
@@ -2382,7 +1800,7 @@ var trackPastedComments = (onUpdate) => {
2382
1800
  return effects;
2383
1801
  }),
2384
1802
  // Handle paste or the undo of comment deletion.
2385
- EditorView9.updateListener.of((update2) => {
1803
+ EditorView8.updateListener.of((update2) => {
2386
1804
  const restore = [];
2387
1805
  for (let i = 0; i < update2.transactions.length; i++) {
2388
1806
  const tr = update2.transactions[i];
@@ -2441,7 +1859,7 @@ var mapTrackedComment = (comment, changes) => ({
2441
1859
  var restoreCommentEffect = StateEffect3.define({
2442
1860
  map: mapTrackedComment
2443
1861
  });
2444
- var createComment2 = (view) => {
1862
+ var createComment = (view) => {
2445
1863
  const options = view.state.facet(optionsFacet);
2446
1864
  const { from, to } = view.state.selection.main;
2447
1865
  if (from === to) {
@@ -2486,7 +1904,7 @@ var comments = (options = {}) => {
2486
1904
  options.onCreate && keymap5.of([
2487
1905
  {
2488
1906
  key: shortcut,
2489
- run: callbackWrapper(createComment2)
1907
+ run: callbackWrapper(createComment)
2490
1908
  }
2491
1909
  ]),
2492
1910
  //
@@ -2524,7 +1942,7 @@ var comments = (options = {}) => {
2524
1942
  //
2525
1943
  // Track deleted ranges and update ranges for decorations.
2526
1944
  //
2527
- EditorView9.updateListener.of(({ view, state, changes }) => {
1945
+ EditorView8.updateListener.of(({ view, state, changes }) => {
2528
1946
  let mod = false;
2529
1947
  const { comments: comments2, ...value } = state.field(commentsState);
2530
1948
  changes.iterChanges((from, to, from2, to2) => {
@@ -2556,7 +1974,7 @@ var comments = (options = {}) => {
2556
1974
  //
2557
1975
  // Track selection/proximity.
2558
1976
  //
2559
- EditorView9.updateListener.of(({ view, state }) => {
1977
+ EditorView8.updateListener.of(({ view, state }) => {
2560
1978
  let min = Infinity;
2561
1979
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2562
1980
  const { head } = state.selection.main;
@@ -2610,7 +2028,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2610
2028
  anchor: range.from
2611
2029
  } : void 0,
2612
2030
  effects: [
2613
- needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2031
+ needsScroll ? EditorView8.scrollIntoView(range.from, center ? {
2614
2032
  y: "center"
2615
2033
  } : void 0) : [],
2616
2034
  needsSelectionUpdate ? setSelection.of({
@@ -2662,7 +2080,7 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin5.from
2662
2080
  }
2663
2081
  });
2664
2082
  var useCommentState = (state) => {
2665
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2083
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2666
2084
  if (update2.docChanged || update2.selectionSet) {
2667
2085
  state.comment = selectionOverlapsComment(update2.state);
2668
2086
  state.selection = hasActiveSelection(update2.state);
@@ -2686,7 +2104,7 @@ var useComments = (view, id, comments2) => {
2686
2104
  });
2687
2105
  };
2688
2106
  var useCommentClickListener = (onCommentClick) => {
2689
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2107
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2690
2108
  update2.transactions.forEach((transaction) => {
2691
2109
  transaction.effects.forEach((effect) => {
2692
2110
  if (effect.is(commentClickedEffect)) {
@@ -2702,9 +2120,9 @@ var useCommentClickListener = (onCommentClick) => {
2702
2120
  // packages/ui/react-ui-editor/src/extensions/debug.ts
2703
2121
  import { syntaxTree } from "@codemirror/language";
2704
2122
  import { StateField as StateField5 } from "@codemirror/state";
2705
- var debugNodeLogger = (log8 = console.log) => {
2123
+ var debugNodeLogger = (log9 = console.log) => {
2706
2124
  const logTokens = (state) => syntaxTree(state).iterate({
2707
- enter: (node) => log8(node.type)
2125
+ enter: (node) => log9(node.type)
2708
2126
  });
2709
2127
  return StateField5.define({
2710
2128
  create: (state) => logTokens(state),
@@ -2713,8 +2131,8 @@ var debugNodeLogger = (log8 = console.log) => {
2713
2131
  };
2714
2132
 
2715
2133
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
2716
- import { dropCursor, EditorView as EditorView10 } from "@codemirror/view";
2717
- var styles4 = EditorView10.theme({
2134
+ import { dropCursor, EditorView as EditorView9 } from "@codemirror/view";
2135
+ var styles4 = EditorView9.theme({
2718
2136
  ".cm-dropCursor": {
2719
2137
  borderLeft: "2px solid var(--dx-accentText)",
2720
2138
  color: "var(--dx-accentText)",
@@ -2728,7 +2146,7 @@ var dropFile = (options = {}) => {
2728
2146
  return [
2729
2147
  styles4,
2730
2148
  dropCursor(),
2731
- EditorView10.domEventHandlers({
2149
+ EditorView9.domEventHandlers({
2732
2150
  drop: (event, view) => {
2733
2151
  event.preventDefault();
2734
2152
  const files = event.dataTransfer?.files;
@@ -2755,7 +2173,7 @@ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@cod
2755
2173
  import { searchKeymap } from "@codemirror/search";
2756
2174
  import { EditorState } from "@codemirror/state";
2757
2175
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2758
- import { EditorView as EditorView12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2176
+ import { EditorView as EditorView11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2759
2177
  import defaultsDeep2 from "lodash.defaultsdeep";
2760
2178
  import merge from "lodash.merge";
2761
2179
  import { generateName } from "@dxos/display-name";
@@ -2764,7 +2182,7 @@ import { hexToHue, isNotFalsy as isNotFalsy3 } from "@dxos/util";
2764
2182
 
2765
2183
  // packages/ui/react-ui-editor/src/extensions/focus.ts
2766
2184
  import { StateEffect as StateEffect4, StateField as StateField6 } from "@codemirror/state";
2767
- import { EditorView as EditorView11 } from "@codemirror/view";
2185
+ import { EditorView as EditorView10 } from "@codemirror/view";
2768
2186
  var focusEffect = StateEffect4.define();
2769
2187
  var focusField = StateField6.define({
2770
2188
  create: () => false,
@@ -2779,7 +2197,7 @@ var focusField = StateField6.define({
2779
2197
  });
2780
2198
  var focus = [
2781
2199
  focusField,
2782
- EditorView11.domEventHandlers({
2200
+ EditorView10.domEventHandlers({
2783
2201
  focus: (event, view) => {
2784
2202
  setTimeout(() => view.dispatch({
2785
2203
  effects: focusEffect.of(true)
@@ -2793,6 +2211,241 @@ var focus = [
2793
2211
  })
2794
2212
  ];
2795
2213
 
2214
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
2215
+ import { mx } from "@dxos/react-ui-theme";
2216
+ var headings = {
2217
+ 1: "text-4xl",
2218
+ 2: "text-3xl",
2219
+ 3: "text-2xl",
2220
+ 4: "text-xl",
2221
+ 5: "text-lg",
2222
+ 6: ""
2223
+ };
2224
+ var theme = {
2225
+ code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2226
+ codeMark: "font-mono text-primary-500",
2227
+ mark: "opacity-50",
2228
+ heading: (level) => {
2229
+ return mx(headings[level], "dark:text-primary-400");
2230
+ }
2231
+ };
2232
+
2233
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
2234
+ import get from "lodash.get";
2235
+ import { tokens } from "@dxos/react-ui-theme";
2236
+ var getToken = (path, defaultValue) => {
2237
+ const value = get(tokens, path, defaultValue);
2238
+ return value?.toString() ?? "";
2239
+ };
2240
+ var fontBody = getToken("fontFamily.body");
2241
+ var fontMono = getToken("fontFamily.mono");
2242
+
2243
+ // packages/ui/react-ui-editor/src/styles/theme.ts
2244
+ var defaultTheme = {
2245
+ "&": {},
2246
+ "&.cm-focused": {
2247
+ outline: "none"
2248
+ },
2249
+ /**
2250
+ * Scroller
2251
+ */
2252
+ ".cm-scroller": {
2253
+ overflowY: "auto"
2254
+ },
2255
+ /**
2256
+ * Content
2257
+ * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
2258
+ */
2259
+ ".cm-content": {
2260
+ padding: "unset",
2261
+ fontFamily: fontBody,
2262
+ // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
2263
+ fontSize: "16px",
2264
+ lineHeight: 1.5,
2265
+ color: "unset"
2266
+ },
2267
+ /**
2268
+ * Gutters
2269
+ * NOTE: Gutters should have the same top margin as the content.
2270
+ */
2271
+ ".cm-gutters": {
2272
+ borderRight: "none",
2273
+ background: "transparent"
2274
+ },
2275
+ ".cm-gutter": {},
2276
+ ".cm-gutter.cm-lineNumbers": {
2277
+ paddingRight: "4px",
2278
+ borderRight: "1px solid var(--dx-separator)"
2279
+ },
2280
+ ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2281
+ minWidth: "40px",
2282
+ alignContent: "center"
2283
+ },
2284
+ /**
2285
+ * Height is set to match the corresponding line.
2286
+ */
2287
+ ".cm-gutterElement": {
2288
+ alignItems: "center",
2289
+ fontSize: "12px"
2290
+ },
2291
+ /**
2292
+ * Line.
2293
+ */
2294
+ ".cm-line": {
2295
+ paddingInline: 0
2296
+ },
2297
+ ".cm-activeLine": {
2298
+ background: "var(--dx-cmActiveLine)"
2299
+ },
2300
+ /**
2301
+ * Cursor (layer).
2302
+ */
2303
+ ".cm-cursor, .cm-dropCursor": {
2304
+ borderLeft: "2px solid var(--dx-cmCursor)"
2305
+ },
2306
+ ".cm-placeholder": {
2307
+ color: "var(--dx-subdued)"
2308
+ },
2309
+ /**
2310
+ * Selection (layer).
2311
+ */
2312
+ ".cm-selectionBackground": {
2313
+ background: "var(--dx-cmSelection)"
2314
+ },
2315
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2316
+ background: "var(--dx-cmFocusedSelection)"
2317
+ },
2318
+ /**
2319
+ * Search.
2320
+ * NOTE: Matches comment.
2321
+ */
2322
+ ".cm-searchMatch": {
2323
+ margin: "0 -3px",
2324
+ padding: "3px",
2325
+ borderRadius: "3px",
2326
+ background: "var(--dx-cmHighlightSurface)",
2327
+ color: "var(--dx-cmHighlight)"
2328
+ },
2329
+ ".cm-searchMatch-selected": {
2330
+ textDecoration: "underline"
2331
+ },
2332
+ /**
2333
+ * Link.
2334
+ */
2335
+ ".cm-link": {
2336
+ textDecorationLine: "underline",
2337
+ textDecorationThickness: "1px",
2338
+ textDecorationColor: "var(--dx-separator)",
2339
+ textUnderlineOffset: "2px",
2340
+ borderRadius: ".125rem"
2341
+ },
2342
+ ".cm-link > span": {
2343
+ color: "var(--dx-accentText)"
2344
+ },
2345
+ /**
2346
+ * Tooltip.
2347
+ */
2348
+ ".cm-tooltip": {
2349
+ background: "var(--dx-baseSurface)"
2350
+ },
2351
+ ".cm-tooltip-below": {},
2352
+ /**
2353
+ * Autocomplete.
2354
+ * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
2355
+ */
2356
+ ".cm-tooltip.cm-tooltip-autocomplete": {
2357
+ marginTop: "4px",
2358
+ marginLeft: "-3px"
2359
+ },
2360
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
2361
+ maxHeight: "20em"
2362
+ },
2363
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
2364
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
2365
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2366
+ paddingLeft: "4px !important",
2367
+ borderBottom: "none !important",
2368
+ color: "var(--dx-accentText)"
2369
+ },
2370
+ ".cm-tooltip.cm-completionInfo": {
2371
+ width: "360px !important",
2372
+ margin: "-10px 1px 0 1px",
2373
+ padding: "8px !important",
2374
+ borderColor: "var(--dx-separator)"
2375
+ },
2376
+ ".cm-completionIcon": {
2377
+ display: "none"
2378
+ },
2379
+ ".cm-completionLabel": {
2380
+ fontFamily: fontBody
2381
+ },
2382
+ ".cm-completionMatchedText": {
2383
+ textDecoration: "none !important",
2384
+ opacity: 0.5
2385
+ },
2386
+ /**
2387
+ * Panels
2388
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
2389
+ *
2390
+ * Find/replace panel.
2391
+ * <div class="cm-announced">...</div>
2392
+ * <div class="cm-scroller">...</div>
2393
+ * <div class="cm-panels cm-panels-bottom">
2394
+ * <div class="cm-search cm-panel">
2395
+ * <input class="cm-textfield" />
2396
+ * <button class="cm-button">...</button>
2397
+ * <label><input type="checkbox" />...</label>
2398
+ * </div>
2399
+ * </div
2400
+ */
2401
+ // TODO(burdon): Implement custom panel (with icon buttons).
2402
+ ".cm-panels": {},
2403
+ ".cm-panel": {
2404
+ fontFamily: fontBody,
2405
+ backgroundColor: "var(--surface-bg)"
2406
+ },
2407
+ ".cm-panel input, .cm-panel button, .cm-panel label": {
2408
+ color: "var(--dx-subdued)",
2409
+ fontFamily: fontBody,
2410
+ fontSize: "14px",
2411
+ all: "unset",
2412
+ margin: "3px !important",
2413
+ padding: "2px 6px !important",
2414
+ outline: "1px solid transparent"
2415
+ },
2416
+ ".cm-panel input, .cm-panel button": {
2417
+ backgroundColor: "var(--dx-input)"
2418
+ },
2419
+ ".cm-panel input:focus, .cm-panel button:focus": {
2420
+ outline: "1px solid var(--dx-accentFocusIndicator)"
2421
+ },
2422
+ ".cm-panel label": {
2423
+ display: "inline-flex",
2424
+ alignItems: "center",
2425
+ cursor: "pointer"
2426
+ },
2427
+ ".cm-panel input.cm-textfield": {},
2428
+ ".cm-panel input[type=checkbox]": {
2429
+ width: "8px",
2430
+ height: "8px",
2431
+ marginRight: "6px !important",
2432
+ padding: "2px !important",
2433
+ color: "var(--dx-accentFocusIndicator)"
2434
+ },
2435
+ ".cm-panel button": {
2436
+ "&:hover": {
2437
+ backgroundColor: "var(--dx-accentSurfaceHover) !important"
2438
+ },
2439
+ "&:active": {
2440
+ backgroundColor: "var(--dx-accentSurfaceHover)"
2441
+ }
2442
+ },
2443
+ ".cm-panel.cm-search": {
2444
+ padding: "4px",
2445
+ borderTop: "1px solid var(--dx-separator)"
2446
+ }
2447
+ };
2448
+
2796
2449
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2797
2450
  var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2798
2451
  var preventNewline = EditorState.transactionFilter.of((tr) => tr.newDoc.lines > 1 ? [] : tr);
@@ -2817,7 +2470,7 @@ var createBasicExtensions = (_props) => {
2817
2470
  const props = defaultsDeep2({}, _props, defaultBasicOptions);
2818
2471
  return [
2819
2472
  // NOTE: Doesn't catch errors in keymap functions.
2820
- EditorView12.exceptionSink.of((err) => {
2473
+ EditorView11.exceptionSink.of((err) => {
2821
2474
  log5.catch(err, void 0, {
2822
2475
  F: __dxlog_file8,
2823
2476
  L: 96,
@@ -2832,12 +2485,12 @@ var createBasicExtensions = (_props) => {
2832
2485
  props.drawSelection && drawSelection({
2833
2486
  cursorBlinkRate: 1200
2834
2487
  }),
2835
- props.editable !== void 0 && EditorView12.editable.of(props.editable),
2488
+ props.editable !== void 0 && EditorView11.editable.of(props.editable),
2836
2489
  props.focus && focus,
2837
2490
  props.highlightActiveLine && highlightActiveLine(),
2838
2491
  props.history && history(),
2839
2492
  props.lineNumbers && lineNumbers(),
2840
- props.lineWrapping && EditorView12.lineWrapping,
2493
+ props.lineWrapping && EditorView11.lineWrapping,
2841
2494
  props.placeholder && placeholder(props.placeholder),
2842
2495
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2843
2496
  props.scrollPastEnd && scrollPastEnd(),
@@ -2874,14 +2527,14 @@ var defaultThemeSlots = {
2874
2527
  var createThemeExtensions = ({ themeMode, styles: styles5, syntaxHighlighting: _syntaxHighlighting, slots: _slots } = {}) => {
2875
2528
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2876
2529
  return [
2877
- EditorView12.darkTheme.of(themeMode === "dark"),
2878
- EditorView12.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2530
+ EditorView11.darkTheme.of(themeMode === "dark"),
2531
+ EditorView11.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2879
2532
  // https://github.com/codemirror/theme-one-dark
2880
2533
  _syntaxHighlighting && (themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
2881
- slots.editor?.className && EditorView12.editorAttributes.of({
2534
+ slots.editor?.className && EditorView11.editorAttributes.of({
2882
2535
  class: slots.editor.className
2883
2536
  }),
2884
- slots.content?.className && EditorView12.contentAttributes.of({
2537
+ slots.content?.className && EditorView11.contentAttributes.of({
2885
2538
  class: slots.content.className
2886
2539
  })
2887
2540
  ].filter(isNotFalsy3);
@@ -2910,8 +2563,8 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2910
2563
 
2911
2564
  // packages/ui/react-ui-editor/src/extensions/folding.tsx
2912
2565
  import { codeFolding, foldGutter } from "@codemirror/language";
2913
- import { EditorView as EditorView13 } from "@codemirror/view";
2914
- import React3 from "react";
2566
+ import { EditorView as EditorView12 } from "@codemirror/view";
2567
+ import React2 from "react";
2915
2568
  import { Icon } from "@dxos/react-ui";
2916
2569
  var folding = (_props = {}) => [
2917
2570
  codeFolding({
@@ -2924,7 +2577,7 @@ var folding = (_props = {}) => [
2924
2577
  const el = createElement("div", {
2925
2578
  className: "flex h-full items-center"
2926
2579
  });
2927
- return renderRoot(el, /* @__PURE__ */ React3.createElement(Icon, {
2580
+ return renderRoot(el, /* @__PURE__ */ React2.createElement(Icon, {
2928
2581
  icon: "ph--caret-right--bold",
2929
2582
  size: 3,
2930
2583
  classNames: [
@@ -2934,7 +2587,7 @@ var folding = (_props = {}) => [
2934
2587
  }));
2935
2588
  }
2936
2589
  }),
2937
- EditorView13.theme({
2590
+ EditorView12.theme({
2938
2591
  ".cm-foldGutter": {
2939
2592
  opacity: 0.3,
2940
2593
  transition: "opacity 0.3s",
@@ -2947,14 +2600,14 @@ var folding = (_props = {}) => [
2947
2600
  ];
2948
2601
 
2949
2602
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2950
- import { EditorView as EditorView14 } from "@codemirror/view";
2603
+ import { EditorView as EditorView13 } from "@codemirror/view";
2951
2604
  var listener = ({ onFocus, onChange }) => {
2952
2605
  const extensions = [];
2953
- onFocus && extensions.push(EditorView14.focusChangeEffect.of((_, focusing) => {
2606
+ onFocus && extensions.push(EditorView13.focusChangeEffect.of((_, focusing) => {
2954
2607
  onFocus(focusing);
2955
2608
  return null;
2956
2609
  }));
2957
- onChange && extensions.push(EditorView14.updateListener.of((update2) => {
2610
+ onChange && extensions.push(EditorView13.updateListener.of((update2) => {
2958
2611
  onChange(update2.state.doc.toString(), update2.state.facet(documentId));
2959
2612
  }));
2960
2613
  return extensions;
@@ -2964,7 +2617,7 @@ var listener = ({ onFocus, onChange }) => {
2964
2617
  import { snippet } from "@codemirror/autocomplete";
2965
2618
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2966
2619
  import { EditorSelection } from "@codemirror/state";
2967
- import { EditorView as EditorView15, keymap as keymap7 } from "@codemirror/view";
2620
+ import { EditorView as EditorView14, keymap as keymap7 } from "@codemirror/view";
2968
2621
  import { useMemo as useMemo3 } from "react";
2969
2622
  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;
2970
2623
  var Inline;
@@ -4053,7 +3706,7 @@ var getFormatting = (state) => {
4053
3706
  };
4054
3707
  };
4055
3708
  var useFormattingState = (state) => {
4056
- return useMemo3(() => EditorView15.updateListener.of((update2) => {
3709
+ return useMemo3(() => EditorView14.updateListener.of((update2) => {
4057
3710
  if (update2.docChanged || update2.selectionSet) {
4058
3711
  Object.entries(getFormatting(update2.state)).forEach(([key, active]) => {
4059
3712
  state[key] = active;
@@ -4101,7 +3754,7 @@ var processEditorPayload = (view, { type, data }) => {
4101
3754
  })(view);
4102
3755
  break;
4103
3756
  case "comment":
4104
- createComment2(view);
3757
+ createComment(view);
4105
3758
  break;
4106
3759
  }
4107
3760
  requestAnimationFrame(() => {
@@ -4365,9 +4018,9 @@ var convertTreeToJson = (state) => {
4365
4018
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
4366
4019
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4367
4020
  import { RangeSetBuilder as RangeSetBuilder3, StateEffect as StateEffect5 } from "@codemirror/state";
4368
- import { EditorView as EditorView19, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4021
+ import { EditorView as EditorView18, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4369
4022
  import { invariant as invariant4 } from "@dxos/invariant";
4370
- import { mx as mx3 } from "@dxos/react-ui-theme";
4023
+ import { mx as mx2 } from "@dxos/react-ui-theme";
4371
4024
 
4372
4025
  // packages/ui/react-ui-editor/src/extensions/markdown/changes.ts
4373
4026
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -4417,11 +4070,11 @@ var adjustChanges = () => {
4417
4070
  if (url) {
4418
4071
  const node = tree.resolveInner(fromA, -1);
4419
4072
  const invalidPositions = /* @__PURE__ */ new Set([
4420
- "Link",
4421
- "LinkMark",
4422
4073
  "Code",
4423
4074
  "CodeText",
4424
4075
  "FencedCode",
4076
+ "Link",
4077
+ "LinkMark",
4425
4078
  "URL"
4426
4079
  ]);
4427
4080
  if (!invalidPositions.has(node?.name)) {
@@ -4516,7 +4169,7 @@ var getValidUrl = (str) => {
4516
4169
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
4517
4170
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4518
4171
  import { StateField as StateField8 } from "@codemirror/state";
4519
- import { Decoration as Decoration5, EditorView as EditorView16, WidgetType as WidgetType3 } from "@codemirror/view";
4172
+ import { Decoration as Decoration5, EditorView as EditorView15, WidgetType as WidgetType3 } from "@codemirror/view";
4520
4173
  var image = (_options = {}) => {
4521
4174
  return [
4522
4175
  StateField8.define({
@@ -4544,7 +4197,7 @@ var image = (_options = {}) => {
4544
4197
  add: buildDecorations(from, to, tr.state)
4545
4198
  });
4546
4199
  },
4547
- provide: (field) => EditorView16.decorations.from(field)
4200
+ provide: (field) => EditorView15.decorations.from(field)
4548
4201
  })
4549
4202
  ];
4550
4203
  };
@@ -4604,10 +4257,10 @@ var ImageWidget = class extends WidgetType3 {
4604
4257
  };
4605
4258
 
4606
4259
  // packages/ui/react-ui-editor/src/extensions/markdown/styles.ts
4607
- import { EditorView as EditorView17 } from "@codemirror/view";
4260
+ import { EditorView as EditorView16 } from "@codemirror/view";
4608
4261
  var bulletListIndentationWidth = 24;
4609
4262
  var orderedListIndentationWidth = 36;
4610
- var formattingStyles = EditorView17.theme({
4263
+ var formattingStyles = EditorView16.theme({
4611
4264
  /**
4612
4265
  * Horizontal rule.
4613
4266
  */
@@ -4654,11 +4307,11 @@ var formattingStyles = EditorView17.theme({
4654
4307
  background: "var(--dx-cmCodeblock)",
4655
4308
  paddingInline: "1rem !important"
4656
4309
  },
4657
- "& .cm-codeblock-first": {
4310
+ "& .cm-codeblock-start": {
4658
4311
  borderTopLeftRadius: ".25rem",
4659
4312
  borderTopRightRadius: ".25rem"
4660
4313
  },
4661
- "& .cm-codeblock-last": {
4314
+ "& .cm-codeblock-end": {
4662
4315
  borderBottomLeftRadius: ".25rem",
4663
4316
  borderBottomRightRadius: ".25rem"
4664
4317
  },
@@ -4728,12 +4381,12 @@ var formattingStyles = EditorView17.theme({
4728
4381
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4729
4382
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4730
4383
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField9 } from "@codemirror/state";
4731
- import { Decoration as Decoration6, EditorView as EditorView18, WidgetType as WidgetType4 } from "@codemirror/view";
4384
+ import { Decoration as Decoration6, EditorView as EditorView17, WidgetType as WidgetType4 } from "@codemirror/view";
4732
4385
  var table = (options = {}) => {
4733
4386
  return StateField9.define({
4734
4387
  create: (state) => update(state, options),
4735
4388
  update: (_, tr) => update(tr.state, options),
4736
- provide: (field) => EditorView18.decorations.from(field)
4389
+ provide: (field) => EditorView17.decorations.from(field)
4737
4390
  });
4738
4391
  };
4739
4392
  var update = (state, _options) => {
@@ -4919,16 +4572,16 @@ var TextWidget = class extends WidgetType5 {
4919
4572
  };
4920
4573
  var hide = Decoration7.replace({});
4921
4574
  var blockQuote = Decoration7.line({
4922
- class: mx3("cm-blockquote")
4575
+ class: "cm-blockquote"
4923
4576
  });
4924
4577
  var fencedCodeLine = Decoration7.line({
4925
- class: mx3("cm-code cm-codeblock-line")
4578
+ class: "cm-code cm-codeblock-line"
4926
4579
  });
4927
4580
  var fencedCodeLineFirst = Decoration7.line({
4928
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-first")
4581
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-start")
4929
4582
  });
4930
4583
  var fencedCodeLineLast = Decoration7.line({
4931
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-last")
4584
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-end")
4932
4585
  });
4933
4586
  var commentBlockLine = fencedCodeLine;
4934
4587
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -4983,665 +4636,1317 @@ var buildDecorations2 = (view, options, focus2) => {
4983
4636
  } else {
4984
4637
  headerLevels.splice(level);
4985
4638
  }
4986
- return headerLevels.slice(0, level);
4987
- };
4988
- const listLevels = [];
4989
- const enterList = (node) => {
4990
- listLevels.push({
4991
- type: node.name,
4992
- from: node.from,
4993
- to: node.to,
4994
- level: listLevels.length,
4995
- number: 0
4639
+ return headerLevels.slice(0, level);
4640
+ };
4641
+ const listLevels = [];
4642
+ const enterList = (node) => {
4643
+ listLevels.push({
4644
+ type: node.name,
4645
+ from: node.from,
4646
+ to: node.to,
4647
+ level: listLevels.length,
4648
+ number: 0
4649
+ });
4650
+ };
4651
+ const leaveList = () => {
4652
+ listLevels.pop();
4653
+ };
4654
+ const getCurrentListLevel = () => {
4655
+ invariant4(listLevels.length, void 0, {
4656
+ F: __dxlog_file9,
4657
+ L: 201,
4658
+ S: void 0,
4659
+ A: [
4660
+ "listLevels.length",
4661
+ ""
4662
+ ]
4663
+ });
4664
+ return listLevels[listLevels.length - 1];
4665
+ };
4666
+ const enterNode = (node) => {
4667
+ switch (node.name) {
4668
+ // ATXHeading > HeaderMark > Paragraph
4669
+ // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
4670
+ // processed and the numbering will be incorrect.
4671
+ case "ATXHeading1":
4672
+ case "ATXHeading2":
4673
+ case "ATXHeading3":
4674
+ case "ATXHeading4":
4675
+ case "ATXHeading5":
4676
+ case "ATXHeading6": {
4677
+ const level = parseInt(node.name["ATXHeading".length]);
4678
+ const headers = getHeaderLevels(node, level);
4679
+ if (options.numberedHeadings?.from !== void 0) {
4680
+ const header = headers[level - 1];
4681
+ if (header) {
4682
+ header.number++;
4683
+ }
4684
+ }
4685
+ const editing = editingRange(state, node, focus2);
4686
+ if (editing) {
4687
+ break;
4688
+ }
4689
+ const mark = node.node.firstChild;
4690
+ if (mark?.name === "HeaderMark") {
4691
+ const { from, to = 6 } = options.numberedHeadings ?? {};
4692
+ const text = view.state.sliceDoc(node.from, node.to);
4693
+ const len = text.match(/[#\s]+/)[0].length;
4694
+ if (!from || level < from || level > to) {
4695
+ atomicDeco.add(mark.from, mark.from + len, hide);
4696
+ } else {
4697
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4698
+ if (num.length) {
4699
+ atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4700
+ widget: new TextWidget(num, theme.heading(level))
4701
+ }));
4702
+ }
4703
+ }
4704
+ }
4705
+ return false;
4706
+ }
4707
+ //
4708
+ // Lists.
4709
+ // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
4710
+ //
4711
+ case "BulletList":
4712
+ case "OrderedList": {
4713
+ enterList(node);
4714
+ break;
4715
+ }
4716
+ case "ListItem": {
4717
+ const line = state.doc.lineAt(node.from);
4718
+ const list = getCurrentListLevel();
4719
+ const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
4720
+ const offset = (options?.listPaddingLeft ?? 0) + ((list.level ?? 0) + 1) * width;
4721
+ if (node.from === line.to - 1) {
4722
+ return false;
4723
+ }
4724
+ deco.add(line.from, line.from, Decoration7.line({
4725
+ class: "cm-list-item",
4726
+ attributes: {
4727
+ style: `padding-left: ${offset}px; text-indent: -${width}px;`
4728
+ }
4729
+ }));
4730
+ break;
4731
+ }
4732
+ case "ListMark": {
4733
+ const list = getCurrentListLevel();
4734
+ const next = tree.resolve(node.to + 1, 1);
4735
+ if (next?.name === "TaskMarker") {
4736
+ break;
4737
+ }
4738
+ const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
4739
+ const line = state.doc.lineAt(node.from);
4740
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4741
+ atomicDeco.add(line.from, to, Decoration7.replace({
4742
+ widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
4743
+ }));
4744
+ break;
4745
+ }
4746
+ case "TaskMarker": {
4747
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
4748
+ const line = state.doc.lineAt(node.from);
4749
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4750
+ atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
4751
+ break;
4752
+ }
4753
+ //
4754
+ // Blockquote > QuoteMark > Paragraph
4755
+ //
4756
+ case "Blockquote": {
4757
+ const editing = editingRange(state, node, focus2);
4758
+ const quoteMark = node.node.getChild("QuoteMark");
4759
+ const paragraph = node.node.getChild("Paragraph");
4760
+ if (!editing && quoteMark && paragraph) {
4761
+ atomicDeco.add(quoteMark.from, paragraph.from, hide);
4762
+ }
4763
+ for (const block of view.viewportLineBlocks) {
4764
+ if (block.to < node.from) {
4765
+ continue;
4766
+ }
4767
+ if (block.from > node.to) {
4768
+ break;
4769
+ }
4770
+ deco.add(block.from, block.from, blockQuote);
4771
+ }
4772
+ break;
4773
+ }
4774
+ //
4775
+ // CommentBlock
4776
+ //
4777
+ case "CommentBlock": {
4778
+ const editing = editingRange(state, node, focus2);
4779
+ for (const block of view.viewportLineBlocks) {
4780
+ if (block.to < node.from) {
4781
+ continue;
4782
+ }
4783
+ if (block.from > node.to) {
4784
+ break;
4785
+ }
4786
+ const isFirst = block.from <= node.from;
4787
+ const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
4788
+ deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
4789
+ if (!editing && (isFirst || isLast)) {
4790
+ atomicDeco.add(block.from, block.to, hide);
4791
+ }
4792
+ }
4793
+ break;
4794
+ }
4795
+ //
4796
+ // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
4797
+ //
4798
+ case "FencedCode": {
4799
+ for (const block of view.viewportLineBlocks) {
4800
+ if (block.to < node.from) {
4801
+ continue;
4802
+ }
4803
+ if (block.from > node.to) {
4804
+ break;
4805
+ }
4806
+ const first = block.from <= node.from;
4807
+ const last = block.to >= node.to && /```$/.test(state.doc.sliceString(block.from, block.to));
4808
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
4809
+ const editing = editingRange(state, node, focus2);
4810
+ if (!editing && (first || last)) {
4811
+ atomicDeco.add(block.from, block.to, hide);
4812
+ }
4813
+ }
4814
+ return false;
4815
+ }
4816
+ //
4817
+ // Link > [LinkMark, URL]
4818
+ //
4819
+ case "Link": {
4820
+ const marks = node.node.getChildren("LinkMark");
4821
+ const urlNode = node.node.getChild("URL");
4822
+ const editing = editingRange(state, node, focus2);
4823
+ if (urlNode && marks.length >= 2) {
4824
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
4825
+ if (!editing) {
4826
+ atomicDeco.add(node.from, marks[0].to, hide);
4827
+ }
4828
+ deco.add(marks[0].to, marks[1].from, Decoration7.mark({
4829
+ tagName: "a",
4830
+ attributes: {
4831
+ class: "cm-link",
4832
+ href: url,
4833
+ rel: "noreferrer",
4834
+ target: "_blank"
4835
+ }
4836
+ }));
4837
+ if (!editing) {
4838
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
4839
+ widget: new LinkButton(url, options.renderLinkButton)
4840
+ }) : hide);
4841
+ }
4842
+ }
4843
+ break;
4844
+ }
4845
+ //
4846
+ // HR
4847
+ //
4848
+ case "HorizontalRule": {
4849
+ if (!editingRange(state, node, focus2)) {
4850
+ deco.add(node.from, node.to, horizontalRule);
4851
+ }
4852
+ break;
4853
+ }
4854
+ default: {
4855
+ if (autoHideTags.has(node.name)) {
4856
+ if (!editingRange(state, node.node.parent, focus2)) {
4857
+ atomicDeco.add(node.from, node.to, hide);
4858
+ }
4859
+ }
4860
+ }
4861
+ }
4862
+ };
4863
+ const leaveNode = (node) => {
4864
+ switch (node.name) {
4865
+ case "BulletList":
4866
+ case "OrderedList": {
4867
+ leaveList();
4868
+ break;
4869
+ }
4870
+ }
4871
+ };
4872
+ const tree = syntaxTree7(state);
4873
+ if (options.numberedHeadings?.from === void 0) {
4874
+ for (const { from, to } of view.visibleRanges) {
4875
+ tree.iterate({
4876
+ from,
4877
+ to,
4878
+ enter: wrapWithCatch(enterNode),
4879
+ leave: wrapWithCatch(leaveNode)
4880
+ });
4881
+ }
4882
+ } else {
4883
+ tree.iterate({
4884
+ enter: wrapWithCatch(enterNode),
4885
+ leave: wrapWithCatch(leaveNode)
4996
4886
  });
4887
+ }
4888
+ return {
4889
+ deco: deco.finish(),
4890
+ atomicDeco: atomicDeco.finish()
4997
4891
  };
4998
- const leaveList = () => {
4999
- listLevels.pop();
5000
- };
5001
- const getCurrentListLevel = () => {
5002
- invariant4(listLevels.length, void 0, {
5003
- F: __dxlog_file9,
5004
- L: 201,
5005
- S: void 0,
5006
- A: [
5007
- "listLevels.length",
5008
- ""
4892
+ };
4893
+ var forceUpdate = StateEffect5.define();
4894
+ var decorateMarkdown = (options = {}) => {
4895
+ return [
4896
+ ViewPlugin7.fromClass(class {
4897
+ constructor(view) {
4898
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
4899
+ }
4900
+ update(update2) {
4901
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
4902
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
4903
+ this.clearUpdate();
4904
+ } else if (update2.selectionSet) {
4905
+ this.scheduleUpdate(update2.view);
4906
+ }
4907
+ }
4908
+ // Defer update in case moving through the document.
4909
+ scheduleUpdate(view) {
4910
+ this.clearUpdate();
4911
+ this.pendingUpdate = setTimeout(() => {
4912
+ view.dispatch({
4913
+ effects: forceUpdate.of(null)
4914
+ });
4915
+ }, options.selectionChangeDelay);
4916
+ }
4917
+ clearUpdate() {
4918
+ if (this.pendingUpdate) {
4919
+ clearTimeout(this.pendingUpdate);
4920
+ this.pendingUpdate = void 0;
4921
+ }
4922
+ }
4923
+ destroy() {
4924
+ this.clearUpdate();
4925
+ }
4926
+ }, {
4927
+ provide: (plugin) => [
4928
+ EditorView18.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4929
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4930
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
5009
4931
  ]
5010
- });
5011
- return listLevels[listLevels.length - 1];
4932
+ }),
4933
+ image(),
4934
+ table(),
4935
+ adjustChanges(),
4936
+ formattingStyles
4937
+ ];
4938
+ };
4939
+
4940
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
4941
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
4942
+ import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
4943
+ import { tooltipContent } from "@dxos/react-ui-theme";
4944
+ var linkTooltip = (renderTooltip) => {
4945
+ return hoverTooltip2((view, pos, side) => {
4946
+ const syntax = syntaxTree8(view.state).resolveInner(pos, side);
4947
+ let link = null;
4948
+ for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
4949
+ link = node.name === "Link" ? node : null;
4950
+ }
4951
+ const url = link && link.getChild("URL");
4952
+ if (!url || !link) {
4953
+ return null;
4954
+ }
4955
+ const urlText = view.state.sliceDoc(url.from, url.to);
4956
+ return {
4957
+ pos: link.from,
4958
+ end: link.to,
4959
+ // NOTE: Forcing above causes the tooltip to flicker.
4960
+ // above: true,
4961
+ create: () => {
4962
+ const el = document.createElement("div");
4963
+ el.className = tooltipContent({});
4964
+ renderTooltip(el, {
4965
+ url: urlText
4966
+ }, view);
4967
+ return {
4968
+ dom: el,
4969
+ offset: {
4970
+ x: 0,
4971
+ y: 4
4972
+ }
4973
+ };
4974
+ }
4975
+ };
4976
+ }, {
4977
+ // NOTE: 0 = default of 300ms.
4978
+ hoverTime: 1
4979
+ });
4980
+ };
4981
+
4982
+ // packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts
4983
+ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
4984
+ import { StateField as StateField10, EditorState as EditorState2 } from "@codemirror/state";
4985
+ import { Decoration as Decoration8, EditorView as EditorView19 } from "@codemirror/view";
4986
+ import { log as log6 } from "@dxos/log";
4987
+ import { mx as mx3 } from "@dxos/react-ui-theme";
4988
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts";
4989
+ var indentLevel = 2;
4990
+ var matchTaskMarker = /^\s*- (\[ \]|\[x\])? /;
4991
+ var getLineInfo = (line) => {
4992
+ const match = line.text.match(matchTaskMarker);
4993
+ const start = line.from + (match?.[0]?.length ?? 0);
4994
+ return {
4995
+ match,
4996
+ start
5012
4997
  };
5013
- const enterNode = (node) => {
5014
- switch (node.name) {
5015
- // ATXHeading > HeaderMark > Paragraph
5016
- // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
5017
- // processed and the numbering will be incorrect.
5018
- case "ATXHeading1":
5019
- case "ATXHeading2":
5020
- case "ATXHeading3":
5021
- case "ATXHeading4":
5022
- case "ATXHeading5":
5023
- case "ATXHeading6": {
5024
- const level = parseInt(node.name["ATXHeading".length]);
5025
- const headers = getHeaderLevels(node, level);
5026
- if (options.numberedHeadings?.from !== void 0) {
5027
- const header = headers[level - 1];
5028
- if (header) {
5029
- header.number++;
4998
+ };
4999
+ var outliner = () => [
5000
+ EditorState2.transactionFilter.of((tr) => {
5001
+ if (!tr.docChanged) {
5002
+ const pos = tr.selection?.ranges[tr.selection?.mainIndex]?.from;
5003
+ if (pos != null) {
5004
+ const { match, start } = getLineInfo(tr.startState.doc.lineAt(pos));
5005
+ if (match) {
5006
+ if (pos < start) {
5007
+ return [
5008
+ {
5009
+ selection: {
5010
+ anchor: start,
5011
+ head: start
5012
+ }
5013
+ }
5014
+ ];
5030
5015
  }
5031
5016
  }
5032
- const editing = editingRange(state, node, focus2);
5033
- if (editing) {
5034
- break;
5017
+ }
5018
+ return tr;
5019
+ }
5020
+ const changes = [];
5021
+ tr.changes.iterChanges((fromA, toA, fromB, toB, insert) => {
5022
+ const line = tr.startState.doc.lineAt(fromA);
5023
+ const isTaskMarker = line.text.match(matchTaskMarker);
5024
+ if (isTaskMarker) {
5025
+ const { start } = getLineInfo(line);
5026
+ const replace = start === toA && toA - fromA === insert.length;
5027
+ if (replace) {
5028
+ log6.info("delete line", void 0, {
5029
+ F: __dxlog_file10,
5030
+ L: 82,
5031
+ S: void 0,
5032
+ C: (f, a) => f(...a)
5033
+ });
5034
+ changes.push({
5035
+ from: line.from - 1,
5036
+ to: toA
5037
+ });
5038
+ return;
5035
5039
  }
5036
- const mark = node.node.firstChild;
5037
- if (mark?.name === "HeaderMark") {
5038
- const { from, to = 6 } = options.numberedHeadings ?? {};
5039
- const text = view.state.sliceDoc(node.from, node.to);
5040
- const len = text.match(/[#\s]+/)[0].length;
5041
- if (!from || level < from || level > to) {
5042
- atomicDeco.add(mark.from, mark.from + len, hide);
5040
+ if (fromB === toB) {
5041
+ if (toA === line.to) {
5042
+ const line2 = tr.state.doc.lineAt(fromA);
5043
+ if (line2.text.match(/^\s*$/)) {
5044
+ if (line2.from === 0) {
5045
+ log6.info("skip", void 0, {
5046
+ F: __dxlog_file10,
5047
+ L: 94,
5048
+ S: void 0,
5049
+ C: (f, a) => f(...a)
5050
+ });
5051
+ changes.push({
5052
+ from: 0,
5053
+ to: 0
5054
+ });
5055
+ return;
5056
+ } else {
5057
+ log6.info("delete line", void 0, {
5058
+ F: __dxlog_file10,
5059
+ L: 99,
5060
+ S: void 0,
5061
+ C: (f, a) => f(...a)
5062
+ });
5063
+ changes.push({
5064
+ from: line2.from - 1,
5065
+ to: toA
5066
+ });
5067
+ return;
5068
+ }
5069
+ }
5070
+ }
5071
+ return;
5072
+ }
5073
+ if (insert.length === indentLevel) {
5074
+ if (line.number === 1) {
5075
+ log6.info("skip", void 0, {
5076
+ F: __dxlog_file10,
5077
+ L: 111,
5078
+ S: void 0,
5079
+ C: (f, a) => f(...a)
5080
+ });
5081
+ changes.push({
5082
+ from: 0,
5083
+ to: 0
5084
+ });
5085
+ return;
5043
5086
  } else {
5044
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5045
- if (num.length) {
5046
- atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
5047
- widget: new TextWidget(num, theme.heading(level))
5048
- }));
5087
+ const getIndent = (text) => (text.match(/^\s*/)?.[0]?.length ?? 0) / indentLevel;
5088
+ const currentIndent = getIndent(line.text);
5089
+ const indentPrevious = getIndent(tr.state.doc.lineAt(fromA - 1).text);
5090
+ if (currentIndent > indentPrevious) {
5091
+ log6.info("skip", void 0, {
5092
+ F: __dxlog_file10,
5093
+ L: 119,
5094
+ S: void 0,
5095
+ C: (f, a) => f(...a)
5096
+ });
5097
+ changes.push({
5098
+ from: 0,
5099
+ to: 0
5100
+ });
5101
+ return;
5049
5102
  }
5050
5103
  }
5051
5104
  }
5052
- return false;
5105
+ log6.info("change", {
5106
+ line: {
5107
+ from: line.from,
5108
+ to: line.to
5109
+ },
5110
+ start,
5111
+ a: [
5112
+ fromA,
5113
+ toA
5114
+ ],
5115
+ b: [
5116
+ fromB,
5117
+ toB
5118
+ ],
5119
+ insert: {
5120
+ text: insert.toString(),
5121
+ length: insert.length
5122
+ }
5123
+ }, {
5124
+ F: __dxlog_file10,
5125
+ L: 134,
5126
+ S: void 0,
5127
+ C: (f, a) => f(...a)
5128
+ });
5053
5129
  }
5054
- //
5055
- // Lists.
5056
- // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
5057
- //
5058
- case "BulletList":
5059
- case "OrderedList": {
5060
- enterList(node);
5061
- break;
5130
+ });
5131
+ if (changes.length > 0) {
5132
+ return [
5133
+ {
5134
+ changes
5135
+ }
5136
+ ];
5137
+ }
5138
+ return tr;
5139
+ }),
5140
+ StateField10.define({
5141
+ create: (state) => {
5142
+ return Decoration8.set(buildDecorations3(0, state.doc.length, state));
5143
+ },
5144
+ update: (value, tr) => {
5145
+ const from = 0;
5146
+ const to = tr.state.doc.length;
5147
+ return value.map(tr.changes).update({
5148
+ filterFrom: 0,
5149
+ filterTo: tr.state.doc.length,
5150
+ filter: () => false,
5151
+ add: buildDecorations3(from, to, tr.state)
5152
+ });
5153
+ },
5154
+ provide: (field) => EditorView19.decorations.from(field)
5155
+ }),
5156
+ // TODO(burdon): Increase indent padding by configuring decorate extension.
5157
+ // TODO(burdon): Hover to select entire group.
5158
+ EditorView19.theme({
5159
+ ".cm-list-item-start": {
5160
+ borderTop: "1px solid var(--dx-separator)",
5161
+ borderLeft: "1px solid var(--dx-separator)",
5162
+ borderRight: "1px solid var(--dx-separator)",
5163
+ borderTopLeftRadius: "4px",
5164
+ borderTopRightRadius: "4px",
5165
+ paddingTop: "4px",
5166
+ marginTop: "8px"
5167
+ },
5168
+ ".cm-list-item-end": {
5169
+ borderLeft: "1px solid var(--dx-separator)",
5170
+ borderRight: "1px solid var(--dx-separator)",
5171
+ borderBottom: "1px solid var(--dx-separator)",
5172
+ borderBottomLeftRadius: "4px",
5173
+ borderBottomRightRadius: "4px",
5174
+ paddingBottom: "4px",
5175
+ marginBottom: "8px"
5176
+ },
5177
+ ".cm-list-item-continuation": {
5178
+ borderLeft: "1px solid var(--dx-separator)",
5179
+ borderRight: "1px solid var(--dx-separator)",
5180
+ // TODO(burdon): Should match parent indentation.
5181
+ paddingLeft: "24px"
5182
+ },
5183
+ // TODO(burdon): Set via options to decorate extension.
5184
+ ".cm-list-item-continuation.cm-codeblock-start": {
5185
+ borderRadius: "0"
5186
+ }
5187
+ })
5188
+ ];
5189
+ var buildDecorations3 = (from, to, state) => {
5190
+ const decorations = [];
5191
+ syntaxTree9(state).iterate({
5192
+ enter: (node) => {
5193
+ if (node.name === "ListItem") {
5194
+ const sub = node.node.getChild("BulletList");
5195
+ const lineStart = state.doc.lineAt(node.from);
5196
+ const lineEnd = sub ? state.doc.lineAt(state.doc.lineAt(sub.from).from - 1) : state.doc.lineAt(node.to);
5197
+ decorations.push(Decoration8.line({
5198
+ class: mx3("cm-list-item-start", lineStart.number === lineEnd.number && "cm-list-item-end")
5199
+ }).range(lineStart.from, lineStart.from));
5200
+ for (let i = lineStart.from + 1; i < lineEnd.from; i++) {
5201
+ decorations.push(Decoration8.line({
5202
+ class: mx3("cm-list-item-continuation")
5203
+ }).range(i, i));
5204
+ }
5205
+ if (lineStart.number !== lineEnd.number) {
5206
+ decorations.push(Decoration8.line({
5207
+ class: mx3("cm-list-item-end")
5208
+ }).range(lineEnd.from, lineEnd.from));
5209
+ }
5062
5210
  }
5063
- case "ListItem": {
5064
- const line = state.doc.lineAt(node.from);
5065
- const list = getCurrentListLevel();
5066
- const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
5067
- const offset = ((list.level ?? 0) + 1) * width;
5068
- if (node.from === line.to - 1) {
5069
- return false;
5211
+ }
5212
+ });
5213
+ return decorations;
5214
+ };
5215
+
5216
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
5217
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5218
+ import { log as log7 } from "@dxos/log";
5219
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5220
+ var mention = ({ debug, onSearch }) => {
5221
+ return autocompletion2({
5222
+ // TODO(burdon): Not working.
5223
+ activateOnTyping: true,
5224
+ // activateOnTypingDelay: 100,
5225
+ // selectOnOpen: true,
5226
+ closeOnBlur: !debug,
5227
+ // defaultKeymap: false,
5228
+ icons: false,
5229
+ override: [
5230
+ (context) => {
5231
+ log7.info("completion context", {
5232
+ context
5233
+ }, {
5234
+ F: __dxlog_file11,
5235
+ L: 27,
5236
+ S: void 0,
5237
+ C: (f, a) => f(...a)
5238
+ });
5239
+ const match = context.matchBefore(/@(\w+)?/);
5240
+ if (!match || match.from === match.to && !context.explicit) {
5241
+ return null;
5070
5242
  }
5071
- deco.add(line.from, line.from, Decoration7.line({
5072
- class: "cm-list-item",
5073
- attributes: {
5074
- style: `padding-left: ${offset}px; text-indent: -${width}px;`
5075
- }
5076
- }));
5077
- break;
5243
+ return {
5244
+ from: match.from,
5245
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5246
+ label: `@${value}`
5247
+ }))
5248
+ };
5078
5249
  }
5079
- case "ListMark": {
5080
- const list = getCurrentListLevel();
5081
- const next = tree.resolve(node.to + 1, 1);
5082
- if (next?.name === "TaskMarker") {
5083
- break;
5250
+ ]
5251
+ });
5252
+ };
5253
+
5254
+ // packages/ui/react-ui-editor/src/extensions/modes.ts
5255
+ import { keymap as keymap9 } from "@codemirror/view";
5256
+ import { vim } from "@replit/codemirror-vim";
5257
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5258
+ import { Schema } from "effect";
5259
+ var EditorViewModes = [
5260
+ "preview",
5261
+ "readonly",
5262
+ "source"
5263
+ ];
5264
+ var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5265
+ var EditorInputModes = [
5266
+ "default",
5267
+ "vim",
5268
+ "vscode"
5269
+ ];
5270
+ var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5271
+ var editorInputMode = singleValueFacet({});
5272
+ var InputModeExtensions = {
5273
+ default: [],
5274
+ vscode: [
5275
+ // https://github.com/replit/codemirror-vscode-keymap
5276
+ editorInputMode.of({
5277
+ type: "vscode"
5278
+ }),
5279
+ keymap9.of(vscodeKeymap)
5280
+ ],
5281
+ vim: [
5282
+ // https://github.com/replit/codemirror-vim
5283
+ vim(),
5284
+ editorInputMode.of({
5285
+ type: "vim",
5286
+ noTabster: true
5287
+ }),
5288
+ keymap9.of([
5289
+ {
5290
+ key: "Alt-Escape",
5291
+ run: (view) => {
5292
+ view.dom.parentElement?.focus();
5293
+ return true;
5084
5294
  }
5085
- const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
5086
- const line = state.doc.lineAt(node.from);
5087
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5088
- atomicDeco.add(line.from, to, Decoration7.replace({
5089
- widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
5090
- }));
5091
- break;
5092
5295
  }
5093
- case "TaskMarker": {
5094
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
5095
- const line = state.doc.lineAt(node.from);
5096
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5097
- atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
5098
- break;
5296
+ ])
5297
+ ]
5298
+ };
5299
+
5300
+ // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5301
+ import "@dxos/lit-ui/dx-ref-tag.pcss";
5302
+ import { syntaxTree as syntaxTree10 } from "@codemirror/language";
5303
+ import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField11 } from "@codemirror/state";
5304
+ import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5305
+ var preview = (options = {}) => {
5306
+ return [
5307
+ // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5308
+ // "Block decorations may not be specified via plugins"
5309
+ StateField11.define({
5310
+ create: (state) => buildDecorations4(state, options),
5311
+ update: (_, tr) => buildDecorations4(tr.state, options),
5312
+ provide: (field) => [
5313
+ EditorView20.decorations.from(field),
5314
+ EditorView20.atomicRanges.of((view) => view.state.field(field))
5315
+ ]
5316
+ }),
5317
+ EditorView20.theme({
5318
+ ".cm-preview-block": {
5319
+ marginLeft: "-1rem",
5320
+ marginRight: "-1rem",
5321
+ padding: "1rem",
5322
+ borderRadius: "0.5rem",
5323
+ background: "var(--dx-modalSurface)",
5324
+ border: "1px solid var(--dx-separator)"
5099
5325
  }
5100
- //
5101
- // Blockquote > QuoteMark > Paragraph
5102
- //
5103
- case "Blockquote": {
5104
- const editing = editingRange(state, node, focus2);
5105
- const quoteMark = node.node.getChild("QuoteMark");
5106
- const paragraph = node.node.getChild("Paragraph");
5107
- if (!editing && quoteMark && paragraph) {
5108
- atomicDeco.add(quoteMark.from, paragraph.from, hide);
5109
- }
5110
- for (const block of view.viewportLineBlocks) {
5111
- if (block.to < node.from) {
5112
- continue;
5113
- }
5114
- if (block.from > node.to) {
5115
- break;
5326
+ })
5327
+ ];
5328
+ };
5329
+ var getLinkRef = (state, node) => {
5330
+ const mark = node.getChild("LinkMark");
5331
+ const label = node.getChild("LinkLabel");
5332
+ if (mark && label) {
5333
+ const ref = state.sliceDoc(label.from + 1, label.to - 1);
5334
+ return {
5335
+ suggest: ref.startsWith("?"),
5336
+ block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5337
+ label: state.sliceDoc(mark.to, label.from - 1),
5338
+ ref: ref.startsWith("?") ? ref.slice(1) : ref
5339
+ };
5340
+ }
5341
+ };
5342
+ var buildDecorations4 = (state, options) => {
5343
+ const builder = new RangeSetBuilder4();
5344
+ syntaxTree10(state).iterate({
5345
+ enter: (node) => {
5346
+ switch (node.name) {
5347
+ //
5348
+ // Decoration.
5349
+ // [Label][dxn:echo:123]
5350
+ //
5351
+ case "Link": {
5352
+ const link = getLinkRef(state, node.node);
5353
+ if (link) {
5354
+ builder.add(node.from, node.to, Decoration9.replace({
5355
+ widget: new PreviewInlineWidget(options, link)
5356
+ }));
5116
5357
  }
5117
- deco.add(block.from, block.from, blockQuote);
5358
+ break;
5118
5359
  }
5119
- break;
5120
- }
5121
- //
5122
- // CommentBlock
5123
- //
5124
- case "CommentBlock": {
5125
- const editing = editingRange(state, node, focus2);
5126
- for (const block of view.viewportLineBlocks) {
5127
- if (block.to < node.from) {
5128
- continue;
5129
- }
5130
- if (block.from > node.to) {
5131
- break;
5132
- }
5133
- const isFirst = block.from <= node.from;
5134
- const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
5135
- deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
5136
- if (!editing && (isFirst || isLast)) {
5137
- atomicDeco.add(block.from, block.to, hide);
5360
+ //
5361
+ // Block widget.
5362
+ // ![Label][dxn:echo:123]
5363
+ //
5364
+ case "Image": {
5365
+ const link = getLinkRef(state, node.node);
5366
+ if (options.renderBlock && link) {
5367
+ builder.add(node.from, node.to, Decoration9.replace({
5368
+ block: true,
5369
+ // atomic: true,
5370
+ widget: new PreviewBlockWidget(options, link)
5371
+ }));
5138
5372
  }
5373
+ break;
5139
5374
  }
5140
- break;
5141
5375
  }
5142
- //
5143
- // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
5144
- //
5145
- case "FencedCode": {
5146
- for (const block of view.viewportLineBlocks) {
5147
- if (block.to < node.from) {
5148
- continue;
5149
- }
5150
- if (block.from > node.to) {
5151
- break;
5152
- }
5153
- const first = block.from <= node.from;
5154
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
5155
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
5156
- const editing = editingRange(state, node, focus2);
5157
- if (!editing && (first || last)) {
5158
- atomicDeco.add(block.from, block.to, hide);
5159
- }
5376
+ }
5377
+ });
5378
+ return builder.finish();
5379
+ };
5380
+ var PreviewInlineWidget = class extends WidgetType6 {
5381
+ constructor(_options, _link) {
5382
+ super();
5383
+ this._options = _options;
5384
+ this._link = _link;
5385
+ }
5386
+ // override ignoreEvent() {
5387
+ // return false;
5388
+ // }
5389
+ eq(other) {
5390
+ return this._link.ref === other._link.ref && this._link.label === other._link.label;
5391
+ }
5392
+ toDOM(view) {
5393
+ const root = document.createElement("dx-ref-tag");
5394
+ root.textContent = this._link.label;
5395
+ root.setAttribute("ref", this._link.ref);
5396
+ return root;
5397
+ }
5398
+ };
5399
+ var PreviewBlockWidget = class extends WidgetType6 {
5400
+ constructor(_options, _link) {
5401
+ super();
5402
+ this._options = _options;
5403
+ this._link = _link;
5404
+ }
5405
+ // override ignoreEvent() {
5406
+ // return true;
5407
+ // }
5408
+ eq(other) {
5409
+ return this._link.ref === other._link.ref;
5410
+ }
5411
+ toDOM(view) {
5412
+ const root = document.createElement("div");
5413
+ root.classList.add("cm-preview-block");
5414
+ const handleAction = (action) => {
5415
+ const pos = view.posAtDOM(root);
5416
+ const node = syntaxTree10(view.state).resolve(pos + 1).node.parent;
5417
+ if (!node) {
5418
+ return;
5419
+ }
5420
+ const link = getLinkRef(view.state, node);
5421
+ if (link?.ref !== action.link.ref) {
5422
+ return;
5423
+ }
5424
+ switch (action.type) {
5425
+ // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5426
+ // Insert ref text.
5427
+ case "insert": {
5428
+ view.dispatch({
5429
+ changes: {
5430
+ from: node.from,
5431
+ to: node.to,
5432
+ insert: action.target.text
5433
+ }
5434
+ });
5435
+ break;
5160
5436
  }
5161
- return false;
5162
- }
5163
- //
5164
- // Link > [LinkMark, URL]
5165
- //
5166
- case "Link": {
5167
- const marks = node.node.getChildren("LinkMark");
5168
- const urlNode = node.node.getChild("URL");
5169
- const editing = editingRange(state, node, focus2);
5170
- if (urlNode && marks.length >= 2) {
5171
- const url = state.sliceDoc(urlNode.from, urlNode.to);
5172
- if (!editing) {
5173
- atomicDeco.add(node.from, marks[0].to, hide);
5174
- }
5175
- deco.add(marks[0].to, marks[1].from, Decoration7.mark({
5176
- tagName: "a",
5177
- attributes: {
5178
- class: "cm-link",
5179
- href: url,
5180
- rel: "noreferrer",
5181
- target: "_blank"
5437
+ // Remove ref.
5438
+ case "delete": {
5439
+ view.dispatch({
5440
+ changes: {
5441
+ from: node.from,
5442
+ to: node.to
5182
5443
  }
5183
- }));
5184
- if (!editing) {
5185
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
5186
- widget: new LinkButton(url, options.renderLinkButton)
5187
- }) : hide);
5188
- }
5444
+ });
5445
+ break;
5189
5446
  }
5190
- break;
5191
5447
  }
5192
- //
5193
- // HR
5194
- //
5195
- case "HorizontalRule": {
5196
- if (!editingRange(state, node, focus2)) {
5197
- deco.add(node.from, node.to, horizontalRule);
5448
+ };
5449
+ this._options.renderBlock(root, {
5450
+ readonly: view.state.readOnly,
5451
+ link: this._link,
5452
+ onAction: handleAction,
5453
+ onLookup: this._options.onLookup
5454
+ }, view);
5455
+ return root;
5456
+ }
5457
+ };
5458
+
5459
+ // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5460
+ import { keymap as keymap10 } from "@codemirror/view";
5461
+ var defaultItems = [
5462
+ "hello world!",
5463
+ "this is a test.",
5464
+ "this is [DXOS](https://dxos.org)"
5465
+ ];
5466
+ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5467
+ let t;
5468
+ let idx = 0;
5469
+ return [
5470
+ keymap10.of([
5471
+ {
5472
+ // Reset.
5473
+ key: "alt-meta-'",
5474
+ run: (view) => {
5475
+ clearTimeout(t);
5476
+ idx = 0;
5477
+ return true;
5198
5478
  }
5199
- break;
5200
- }
5201
- default: {
5202
- if (autoHideTags.has(node.name)) {
5203
- if (!editingRange(state, node.node.parent, focus2)) {
5204
- atomicDeco.add(node.from, node.to, hide);
5479
+ },
5480
+ {
5481
+ // Next prompt.
5482
+ // TODO(burdon): Press 1-9 to select prompt?
5483
+ key: "shift-meta-'",
5484
+ run: (view) => {
5485
+ clearTimeout(t);
5486
+ const text = items[idx++];
5487
+ if (idx === items?.length) {
5488
+ idx = 0;
5205
5489
  }
5490
+ let i = 0;
5491
+ const insert = (d = 0) => {
5492
+ t = setTimeout(() => {
5493
+ const pos = view.state.selection.main.head;
5494
+ view.dispatch({
5495
+ changes: {
5496
+ from: pos,
5497
+ insert: text[i++]
5498
+ },
5499
+ selection: {
5500
+ anchor: pos + 1
5501
+ }
5502
+ });
5503
+ if (i < text.length) {
5504
+ insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5505
+ }
5506
+ }, d);
5507
+ };
5508
+ insert();
5509
+ return true;
5206
5510
  }
5207
5511
  }
5512
+ ])
5513
+ ];
5514
+ };
5515
+
5516
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
5517
+ var createBlockGroupAction = (value) => createEditorActionGroup("block", {
5518
+ variant: "toggleGroup",
5519
+ selectCardinality: "single",
5520
+ value
5521
+ });
5522
+ var createBlockActions = (value, getView, blankLine) => Object.entries({
5523
+ blockquote: "ph--quotes--regular",
5524
+ codeblock: "ph--code-block--regular",
5525
+ table: "ph--table--regular"
5526
+ }).map(([type, icon]) => {
5527
+ const checked = type === value;
5528
+ return createEditorAction(type, () => {
5529
+ const view = getView();
5530
+ if (!view) {
5531
+ return;
5208
5532
  }
5209
- };
5210
- const leaveNode = (node) => {
5211
- switch (node.name) {
5212
- case "BulletList":
5213
- case "OrderedList": {
5214
- leaveList();
5533
+ switch (type) {
5534
+ case "blockquote":
5535
+ checked ? removeBlockquote(view) : addBlockquote(view);
5536
+ break;
5537
+ case "codeblock":
5538
+ checked ? removeCodeblock(view) : addCodeblock(view);
5539
+ break;
5540
+ case "table":
5541
+ insertTable(view);
5215
5542
  break;
5216
- }
5217
- }
5218
- };
5219
- const tree = syntaxTree7(state);
5220
- if (options.numberedHeadings?.from === void 0) {
5221
- for (const { from, to } of view.visibleRanges) {
5222
- tree.iterate({
5223
- from,
5224
- to,
5225
- enter: wrapWithCatch(enterNode),
5226
- leave: wrapWithCatch(leaveNode)
5227
- });
5228
5543
  }
5229
- } else {
5230
- tree.iterate({
5231
- enter: wrapWithCatch(enterNode),
5232
- leave: wrapWithCatch(leaveNode)
5233
- });
5234
- }
5544
+ }, {
5545
+ checked,
5546
+ ...type === "table" && {
5547
+ disabled: !!blankLine
5548
+ },
5549
+ icon
5550
+ });
5551
+ });
5552
+ var createBlocks = (state, getView) => {
5553
+ const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
5554
+ const blockGroupAction = createBlockGroupAction(value);
5555
+ const blockActions = createBlockActions(value, getView, state.blankLine);
5235
5556
  return {
5236
- deco: deco.finish(),
5237
- atomicDeco: atomicDeco.finish()
5557
+ nodes: [
5558
+ blockGroupAction,
5559
+ ...blockActions
5560
+ ],
5561
+ edges: [
5562
+ {
5563
+ source: "root",
5564
+ target: "block"
5565
+ },
5566
+ ...blockActions.map(({ id }) => ({
5567
+ source: blockGroupAction.id,
5568
+ target: id
5569
+ }))
5570
+ ]
5238
5571
  };
5239
5572
  };
5240
- var forceUpdate = StateEffect5.define();
5241
- var decorateMarkdown = (options = {}) => {
5242
- return [
5243
- ViewPlugin7.fromClass(class {
5244
- constructor(view) {
5245
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
5246
- }
5247
- update(update2) {
5248
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
5249
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
5250
- this.clearUpdate();
5251
- } else if (update2.selectionSet) {
5252
- this.scheduleUpdate(update2.view);
5253
- }
5254
- }
5255
- // Defer update in case moving through the document.
5256
- scheduleUpdate(view) {
5257
- this.clearUpdate();
5258
- this.pendingUpdate = setTimeout(() => {
5259
- view.dispatch({
5260
- effects: forceUpdate.of(null)
5261
- });
5262
- }, options.selectionChangeDelay);
5263
- }
5264
- clearUpdate() {
5265
- if (this.pendingUpdate) {
5266
- clearTimeout(this.pendingUpdate);
5267
- this.pendingUpdate = void 0;
5268
- }
5269
- }
5270
- destroy() {
5271
- this.clearUpdate();
5573
+
5574
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
5575
+ var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
5576
+ var createCommentAction = (label, getView) => createEditorAction("comment", () => createComment(getView()), {
5577
+ testId: "editor.toolbar.comment",
5578
+ icon: "ph--chat-text--regular",
5579
+ label
5580
+ });
5581
+ var createComment2 = (state, getView) => ({
5582
+ nodes: [
5583
+ createCommentAction([
5584
+ commentLabel(state.comment, state.selection),
5585
+ {
5586
+ ns: translationKey
5272
5587
  }
5273
- }, {
5274
- provide: (plugin) => [
5275
- EditorView19.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5276
- EditorView19.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5277
- EditorView19.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
5278
- ]
5279
- }),
5280
- image(),
5281
- table(),
5282
- adjustChanges(),
5283
- formattingStyles
5284
- ];
5285
- };
5588
+ ], getView)
5589
+ ],
5590
+ edges: [
5591
+ {
5592
+ source: "root",
5593
+ target: "comment"
5594
+ }
5595
+ ]
5596
+ });
5286
5597
 
5287
- // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
5288
- import { syntaxTree as syntaxTree8 } from "@codemirror/language";
5289
- import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
5290
- import { tooltipContent } from "@dxos/react-ui-theme";
5291
- var linkTooltip = (renderTooltip) => {
5292
- return hoverTooltip2((view, pos, side) => {
5293
- const syntax = syntaxTree8(view.state).resolveInner(pos, side);
5294
- let link = null;
5295
- for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
5296
- link = node.name === "Link" ? node : null;
5598
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
5599
+ var formats = {
5600
+ strong: "ph--text-b--regular",
5601
+ emphasis: "ph--text-italic--regular",
5602
+ strikethrough: "ph--text-strikethrough--regular",
5603
+ code: "ph--code--regular",
5604
+ link: "ph--link--regular"
5605
+ };
5606
+ var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
5607
+ variant: "toggleGroup",
5608
+ selectCardinality: "multiple",
5609
+ value: Object.keys(formats).filter((key) => !!formatting[key])
5610
+ });
5611
+ var createFormattingActions = (formatting, getView) => Object.entries(formats).map(([type, icon]) => {
5612
+ const checked = !!formatting[type];
5613
+ return createEditorAction(type, () => {
5614
+ const view = getView();
5615
+ if (!view) {
5616
+ return;
5297
5617
  }
5298
- const url = link && link.getChild("URL");
5299
- if (!url || !link) {
5300
- return null;
5618
+ if (type === "link") {
5619
+ checked ? removeLink(view) : addLink()(view);
5620
+ return;
5301
5621
  }
5302
- const urlText = view.state.sliceDoc(url.from, url.to);
5303
- return {
5304
- pos: link.from,
5305
- end: link.to,
5306
- // NOTE: Forcing above causes the tooltip to flicker.
5307
- // above: true,
5308
- create: () => {
5309
- const el = document.createElement("div");
5310
- el.className = tooltipContent({});
5311
- renderTooltip(el, {
5312
- url: urlText
5313
- }, view);
5314
- return {
5315
- dom: el,
5316
- offset: {
5317
- x: 0,
5318
- y: 4
5319
- }
5320
- };
5321
- }
5322
- };
5622
+ const inlineType = type === "strong" ? Inline.Strong : type === "emphasis" ? Inline.Emphasis : type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
5623
+ setStyle(inlineType, !checked)(view);
5323
5624
  }, {
5324
- // NOTE: 0 = default of 300ms.
5325
- hoverTime: 1
5625
+ checked,
5626
+ icon
5326
5627
  });
5628
+ });
5629
+ var createFormatting = (state, getView) => {
5630
+ const formattingGroupAction = createFormattingGroup(state);
5631
+ const formattingActions = createFormattingActions(state, getView);
5632
+ return {
5633
+ nodes: [
5634
+ formattingGroupAction,
5635
+ ...formattingActions
5636
+ ],
5637
+ edges: [
5638
+ {
5639
+ source: "root",
5640
+ target: "formatting"
5641
+ },
5642
+ ...formattingActions.map(({ id }) => ({
5643
+ source: formattingGroupAction.id,
5644
+ target: id
5645
+ }))
5646
+ ]
5647
+ };
5327
5648
  };
5328
5649
 
5329
- // packages/ui/react-ui-editor/src/extensions/mention.ts
5330
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5331
- import { log as log6 } from "@dxos/log";
5332
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5333
- var mention = ({ debug, onSearch }) => {
5334
- return autocompletion2({
5335
- // TODO(burdon): Not working.
5336
- activateOnTyping: true,
5337
- // activateOnTypingDelay: 100,
5338
- // selectOnOpen: true,
5339
- closeOnBlur: !debug,
5340
- // defaultKeymap: false,
5341
- icons: false,
5342
- override: [
5343
- (context) => {
5344
- log6.info("completion context", {
5345
- context
5346
- }, {
5347
- F: __dxlog_file10,
5348
- L: 27,
5349
- S: void 0,
5350
- C: (f, a) => f(...a)
5351
- });
5352
- const match = context.matchBefore(/@(\w+)?/);
5353
- if (!match || match.from === match.to && !context.explicit) {
5354
- return null;
5355
- }
5356
- return {
5357
- from: match.from,
5358
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5359
- label: `@${value}`
5360
- }))
5361
- };
5650
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
5651
+ var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
5652
+ variant: "dropdownMenu",
5653
+ applyActive: true,
5654
+ selectCardinality: "single",
5655
+ value
5656
+ }, "ph--text-h--regular");
5657
+ var createHeadingActions = (getView) => Object.entries({
5658
+ "0": "ph--paragraph--regular",
5659
+ "1": "ph--text-h-one--regular",
5660
+ "2": "ph--text-h-two--regular",
5661
+ "3": "ph--text-h-three--regular",
5662
+ "4": "ph--text-h-four--regular",
5663
+ "5": "ph--text-h-five--regular",
5664
+ "6": "ph--text-h-six--regular"
5665
+ }).map(([levelStr, icon]) => {
5666
+ const level = parseInt(levelStr);
5667
+ return createEditorAction(`heading--${levelStr}`, () => setHeading(level)(getView()), {
5668
+ label: [
5669
+ "heading level label",
5670
+ {
5671
+ count: level,
5672
+ ns: translationKey
5362
5673
  }
5363
- ]
5674
+ ],
5675
+ icon
5364
5676
  });
5677
+ });
5678
+ var computeHeadingValue = (state) => {
5679
+ const blockType = state ? state.blockType : "paragraph";
5680
+ const header = blockType && /heading(\d)/.exec(blockType);
5681
+ return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
5682
+ };
5683
+ var createHeadings = (state, getView) => {
5684
+ const headingValue = computeHeadingValue(state);
5685
+ const headingGroupAction = createHeadingGroupAction(headingValue);
5686
+ const headingActions = createHeadingActions(getView);
5687
+ return {
5688
+ nodes: [
5689
+ headingGroupAction,
5690
+ ...headingActions
5691
+ ],
5692
+ edges: [
5693
+ {
5694
+ source: "root",
5695
+ target: "heading"
5696
+ },
5697
+ ...headingActions.map(({ id }) => ({
5698
+ source: headingGroupAction.id,
5699
+ target: id
5700
+ }))
5701
+ ]
5702
+ };
5365
5703
  };
5366
5704
 
5367
- // packages/ui/react-ui-editor/src/extensions/modes.ts
5368
- import { keymap as keymap9 } from "@codemirror/view";
5369
- import { vim } from "@replit/codemirror-vim";
5370
- import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5371
- import { S } from "@dxos/echo-schema";
5372
- var EditorViewModes = [
5373
- "preview",
5374
- "readonly",
5375
- "source"
5376
- ];
5377
- var EditorViewMode = S.Union(...EditorViewModes.map((mode) => S.Literal(mode)));
5378
- var EditorInputModes = [
5379
- "default",
5380
- "vim",
5381
- "vscode"
5382
- ];
5383
- var EditorInputMode = S.Union(...EditorInputModes.map((mode) => S.Literal(mode)));
5384
- var editorInputMode = singleValueFacet({});
5385
- var InputModeExtensions = {
5386
- default: [],
5387
- vscode: [
5388
- // https://github.com/replit/codemirror-vscode-keymap
5389
- editorInputMode.of({
5390
- type: "vscode"
5391
- }),
5392
- keymap9.of(vscodeKeymap)
5705
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/image.ts
5706
+ var createImageUploadAction = (onImageUpload) => createEditorAction("image", onImageUpload, {
5707
+ testId: "editor.toolbar.image",
5708
+ icon: "ph--image-square--regular"
5709
+ });
5710
+ var createImageUpload = (onImageUpload) => ({
5711
+ nodes: [
5712
+ createImageUploadAction(onImageUpload)
5393
5713
  ],
5394
- vim: [
5395
- // https://github.com/replit/codemirror-vim
5396
- vim(),
5397
- editorInputMode.of({
5398
- type: "vim",
5399
- noTabster: true
5400
- }),
5401
- keymap9.of([
5402
- {
5403
- key: "Alt-Escape",
5404
- run: (view) => {
5405
- view.dom.parentElement?.focus();
5406
- return true;
5407
- }
5408
- }
5409
- ])
5714
+ edges: [
5715
+ {
5716
+ source: "root",
5717
+ target: "image"
5718
+ }
5410
5719
  ]
5411
- };
5720
+ });
5412
5721
 
5413
- // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5414
- import "@dxos/lit-ui/dx-ref-tag.pcss";
5415
- import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5416
- import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField10 } from "@codemirror/state";
5417
- import { Decoration as Decoration8, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5418
- var preview = (options = {}) => {
5419
- return [
5420
- // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5421
- // "Block decorations may not be specified via plugins"
5422
- StateField10.define({
5423
- create: (state) => buildDecorations3(state, options),
5424
- update: (_, tr) => buildDecorations3(tr.state, options),
5425
- provide: (field) => [
5426
- EditorView20.decorations.from(field),
5427
- EditorView20.atomicRanges.of((view) => view.state.field(field))
5428
- ]
5429
- }),
5430
- EditorView20.theme({
5431
- ".cm-preview-block": {
5432
- marginLeft: "-1rem",
5433
- marginRight: "-1rem",
5434
- padding: "1rem",
5435
- borderRadius: "0.5rem",
5436
- background: "var(--dx-modalSurface)",
5437
- border: "1px solid var(--dx-separator)"
5438
- }
5439
- })
5440
- ];
5441
- };
5442
- var getLinkRef = (state, node) => {
5443
- const mark = node.getChild("LinkMark");
5444
- const label = node.getChild("LinkLabel");
5445
- if (mark && label) {
5446
- const ref = state.sliceDoc(label.from + 1, label.to - 1);
5447
- return {
5448
- suggest: ref.startsWith("?"),
5449
- block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5450
- label: state.sliceDoc(mark.to, label.from - 1),
5451
- ref: ref.startsWith("?") ? ref.slice(1) : ref
5452
- };
5453
- }
5722
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
5723
+ var listStyles = {
5724
+ bullet: "ph--list-bullets--regular",
5725
+ ordered: "ph--list-numbers--regular",
5726
+ task: "ph--list-checks--regular"
5454
5727
  };
5455
- var buildDecorations3 = (state, options) => {
5456
- const builder = new RangeSetBuilder4();
5457
- syntaxTree9(state).iterate({
5458
- enter: (node) => {
5459
- switch (node.name) {
5460
- //
5461
- // Decoration.
5462
- // [Label][dxn:echo:123]
5463
- //
5464
- case "Link": {
5465
- const link = getLinkRef(state, node.node);
5466
- if (link) {
5467
- builder.add(node.from, node.to, Decoration8.replace({
5468
- widget: new PreviewInlineWidget(options, link)
5469
- }));
5470
- }
5471
- break;
5472
- }
5473
- //
5474
- // Block widget.
5475
- // ![Label][dxn:echo:123]
5476
- //
5477
- case "Image": {
5478
- const link = getLinkRef(state, node.node);
5479
- if (options.renderBlock && link) {
5480
- builder.add(node.from, node.to, Decoration8.replace({
5481
- block: true,
5482
- // atomic: true,
5483
- widget: new PreviewBlockWidget(options, link)
5484
- }));
5485
- }
5486
- break;
5487
- }
5488
- }
5728
+ var createListGroupAction = (value) => createEditorActionGroup("list", {
5729
+ variant: "toggleGroup",
5730
+ selectCardinality: "single",
5731
+ value
5732
+ });
5733
+ var createListActions = (value, getView) => Object.entries(listStyles).map(([listStyle, icon]) => {
5734
+ const checked = value === listStyle;
5735
+ return createEditorAction(`list-${listStyle}`, () => {
5736
+ const view = getView();
5737
+ if (!view) {
5738
+ return;
5739
+ }
5740
+ const listType = listStyle === "ordered" ? List.Ordered : listStyle === "bullet" ? List.Bullet : List.Task;
5741
+ if (checked) {
5742
+ removeList(listType)(view);
5743
+ } else {
5744
+ addList(listType)(view);
5745
+ }
5746
+ }, {
5747
+ checked,
5748
+ icon
5749
+ });
5750
+ });
5751
+ var createLists = (state, getView) => {
5752
+ const value = state.listStyle ?? "";
5753
+ const listGroupAction = createListGroupAction(value);
5754
+ const listActionsMap = createListActions(value, getView);
5755
+ return {
5756
+ nodes: [
5757
+ listGroupAction,
5758
+ ...listActionsMap
5759
+ ],
5760
+ edges: [
5761
+ {
5762
+ source: "root",
5763
+ target: "list"
5764
+ },
5765
+ ...listActionsMap.map(({ id }) => ({
5766
+ source: listGroupAction.id,
5767
+ target: id
5768
+ }))
5769
+ ]
5770
+ };
5771
+ };
5772
+
5773
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/search.ts
5774
+ import { openSearchPanel } from "@codemirror/search";
5775
+ var createSearchAction = (getView) => createEditorAction("search", () => openSearchPanel(getView()), {
5776
+ testId: "editor.toolbar.search",
5777
+ icon: "ph--magnifying-glass--regular"
5778
+ });
5779
+ var createSearch = (getView) => ({
5780
+ nodes: [
5781
+ createSearchAction(getView)
5782
+ ],
5783
+ edges: [
5784
+ {
5785
+ source: "root",
5786
+ target: "search"
5489
5787
  }
5788
+ ]
5789
+ });
5790
+
5791
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
5792
+ var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
5793
+ variant: "dropdownMenu",
5794
+ applyActive: true,
5795
+ selectCardinality: "single",
5796
+ value
5797
+ }, "ph--eye--regular");
5798
+ var createViewModeActions = (value, onViewModeChange) => Object.entries({
5799
+ preview: "ph--eye--regular",
5800
+ source: "ph--pencil-simple--regular",
5801
+ readonly: "ph--pencil-slash--regular"
5802
+ }).map(([viewMode, icon]) => {
5803
+ const checked = viewMode === value;
5804
+ return createEditorAction(`view-mode--${viewMode}`, () => onViewModeChange(viewMode), {
5805
+ label: [
5806
+ `${viewMode} mode label`,
5807
+ {
5808
+ ns: translationKey
5809
+ }
5810
+ ],
5811
+ checked,
5812
+ icon
5490
5813
  });
5491
- return builder.finish();
5814
+ });
5815
+ var createViewMode = (state, onViewModeChange) => {
5816
+ const value = state.viewMode ?? "source";
5817
+ const viewModeGroupAction = createViewModeGroupAction(value);
5818
+ const viewModeActions = createViewModeActions(value, onViewModeChange);
5819
+ return {
5820
+ nodes: [
5821
+ viewModeGroupAction,
5822
+ ...viewModeActions
5823
+ ],
5824
+ edges: [
5825
+ {
5826
+ source: "root",
5827
+ target: "viewMode"
5828
+ },
5829
+ ...viewModeActions.map(({ id }) => ({
5830
+ source: viewModeGroupAction.id,
5831
+ target: id
5832
+ }))
5833
+ ]
5834
+ };
5492
5835
  };
5493
- var PreviewInlineWidget = class extends WidgetType6 {
5494
- constructor(_options, _link) {
5495
- super();
5496
- this._options = _options;
5497
- this._link = _link;
5836
+
5837
+ // packages/ui/react-ui-editor/src/defaults.ts
5838
+ import { EditorView as EditorView21 } from "@codemirror/view";
5839
+ import { mx as mx4 } from "@dxos/react-ui-theme";
5840
+ var margin = "!mt-[1rem]";
5841
+ var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
5842
+ var editorContent = mx4(margin, editorWidth);
5843
+ var editorFullWidth = mx4(margin);
5844
+ var editorGutter = EditorView21.theme({
5845
+ // Match margin from content.
5846
+ // Gutter = 2rem + 1rem margin.
5847
+ ".cm-gutters": {
5848
+ marginTop: "1rem",
5849
+ paddingRight: "1rem"
5498
5850
  }
5499
- // override ignoreEvent() {
5500
- // return false;
5501
- // }
5502
- eq(other) {
5503
- return this._link.ref === other._link.ref && this._link.label === other._link.label;
5851
+ });
5852
+ var editorMonospace = EditorView21.theme({
5853
+ ".cm-content": {
5854
+ fontFamily: fontMono
5504
5855
  }
5505
- toDOM(view) {
5506
- const root = document.createElement("dx-ref-tag");
5507
- root.setAttribute("label", this._link.label);
5508
- root.setAttribute("ref", this._link.ref);
5509
- return root;
5856
+ });
5857
+ var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5858
+ var stackItemContentEditorClassNames = (role) => mx4("attention-surface dx-focus-ring-inset data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
5859
+ var stackItemContentToolbarClassNames = (role) => mx4("relative z-[1] flex is-full bg-toolbarSurface border-be border-separator", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
5860
+
5861
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
5862
+ var createToolbar = ({ getView, state, customActions, ...features }) => {
5863
+ const nodes = [];
5864
+ const edges = [];
5865
+ if (features.headings ?? true) {
5866
+ const headings2 = createHeadings(state, getView);
5867
+ nodes.push(...headings2.nodes);
5868
+ edges.push(...headings2.edges);
5510
5869
  }
5511
- };
5512
- var PreviewBlockWidget = class extends WidgetType6 {
5513
- constructor(_options, _link) {
5514
- super();
5515
- this._options = _options;
5516
- this._link = _link;
5870
+ if (features.formatting ?? true) {
5871
+ const formatting = createFormatting(state, getView);
5872
+ nodes.push(...formatting.nodes);
5873
+ edges.push(...formatting.edges);
5517
5874
  }
5518
- // override ignoreEvent() {
5519
- // return true;
5520
- // }
5521
- eq(other) {
5522
- return this._link.ref === other._link.ref;
5875
+ if (features.lists ?? true) {
5876
+ const lists = createLists(state, getView);
5877
+ nodes.push(...lists.nodes);
5878
+ edges.push(...lists.edges);
5523
5879
  }
5524
- toDOM(view) {
5525
- const root = document.createElement("div");
5526
- root.classList.add("cm-preview-block");
5527
- const handleAction = (action) => {
5528
- const pos = view.posAtDOM(root);
5529
- const node = syntaxTree9(view.state).resolve(pos + 1).node.parent;
5530
- if (!node) {
5531
- return;
5532
- }
5533
- const link = getLinkRef(view.state, node);
5534
- if (link?.ref !== action.link.ref) {
5535
- return;
5536
- }
5537
- switch (action.type) {
5538
- // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5539
- // Insert ref text.
5540
- case "insert": {
5541
- view.dispatch({
5542
- changes: {
5543
- from: node.from,
5544
- to: node.to,
5545
- insert: action.target.text
5546
- }
5547
- });
5548
- break;
5549
- }
5550
- // Remove ref.
5551
- case "delete": {
5552
- view.dispatch({
5553
- changes: {
5554
- from: node.from,
5555
- to: node.to
5556
- }
5557
- });
5558
- break;
5559
- }
5560
- }
5561
- };
5562
- this._options.renderBlock(root, {
5563
- readonly: view.state.readOnly,
5564
- link: this._link,
5565
- onAction: handleAction,
5566
- onLookup: this._options.onLookup
5567
- }, view);
5568
- return root;
5880
+ if (features.blocks ?? true) {
5881
+ const blocks = createBlocks(state, getView);
5882
+ nodes.push(...blocks.nodes);
5883
+ edges.push(...blocks.edges);
5569
5884
  }
5885
+ if (features.image) {
5886
+ const image2 = createImageUpload(features.image);
5887
+ nodes.push(...image2.nodes);
5888
+ edges.push(...image2.edges);
5889
+ }
5890
+ if (customActions) {
5891
+ const custom = customActions();
5892
+ nodes.push(...custom.nodes);
5893
+ edges.push(...custom.edges);
5894
+ }
5895
+ const editorToolbarGap = createGapSeparator();
5896
+ nodes.push(...editorToolbarGap.nodes);
5897
+ edges.push(...editorToolbarGap.edges);
5898
+ if (features.comment) {
5899
+ const comment = createComment2(state, getView);
5900
+ nodes.push(...comment.nodes);
5901
+ edges.push(...comment.edges);
5902
+ }
5903
+ if (features.search ?? true) {
5904
+ const search = createSearch(getView);
5905
+ nodes.push(...search.nodes);
5906
+ edges.push(...search.edges);
5907
+ }
5908
+ if (features.viewMode) {
5909
+ const viewMode = createViewMode(state, features.viewMode);
5910
+ nodes.push(...viewMode.nodes);
5911
+ edges.push(...viewMode.edges);
5912
+ }
5913
+ return {
5914
+ nodes,
5915
+ edges
5916
+ };
5570
5917
  };
5571
-
5572
- // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5573
- import { keymap as keymap10 } from "@codemirror/view";
5574
- var defaultItems = [
5575
- "hello world!",
5576
- "this is a test.",
5577
- "this is [DXOS](https://dxos.org)"
5578
- ];
5579
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5580
- let t;
5581
- let idx = 0;
5582
- return [
5583
- keymap10.of([
5584
- {
5585
- // Reset.
5586
- key: "alt-meta-'",
5587
- run: (view) => {
5588
- clearTimeout(t);
5589
- idx = 0;
5590
- return true;
5591
- }
5592
- },
5593
- {
5594
- // Next prompt.
5595
- // TODO(burdon): Press 1-9 to select prompt?
5596
- key: "shift-meta-'",
5597
- run: (view) => {
5598
- clearTimeout(t);
5599
- const text = items[idx++];
5600
- if (idx === items?.length) {
5601
- idx = 0;
5602
- }
5603
- let i = 0;
5604
- const insert = (d = 0) => {
5605
- t = setTimeout(() => {
5606
- const pos = view.state.selection.main.head;
5607
- view.dispatch({
5608
- changes: {
5609
- from: pos,
5610
- insert: text[i++]
5611
- },
5612
- selection: {
5613
- anchor: pos + 1
5614
- }
5615
- });
5616
- if (i < text.length) {
5617
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5618
- }
5619
- }, d);
5620
- };
5621
- insert();
5622
- return true;
5623
- }
5624
- }
5625
- ])
5626
- ];
5627
- };
5628
-
5629
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
5630
- import { useCallback as useCallback2 } from "react";
5631
- var useActionHandler = (view) => {
5632
- return useCallback2((action) => view && processEditorPayload(view, action.properties), [
5633
- view
5918
+ var useEditorToolbarActionGraph = (props) => {
5919
+ const menuCreator = useCallback(() => createToolbar(props), [
5920
+ props
5634
5921
  ]);
5922
+ return useMenuActions(menuCreator);
5635
5923
  };
5924
+ var EditorToolbar = /* @__PURE__ */ memo(({ classNames, attendableId, role, ...props }) => {
5925
+ const menuProps = useEditorToolbarActionGraph(props);
5926
+ return /* @__PURE__ */ React3.createElement("div", {
5927
+ role: "none",
5928
+ className: stackItemContentToolbarClassNames(role)
5929
+ }, /* @__PURE__ */ React3.createElement(ElevationProvider, {
5930
+ elevation: role === "section" ? "positioned" : "base"
5931
+ }, /* @__PURE__ */ React3.createElement(MenuProvider, {
5932
+ ...menuProps,
5933
+ attendableId
5934
+ }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
5935
+ classNames: [
5936
+ textBlockWidth,
5937
+ classNames
5938
+ ]
5939
+ }))));
5940
+ });
5636
5941
 
5637
5942
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
5638
- import { EditorState as EditorState2 } from "@codemirror/state";
5639
- import { EditorView as EditorView21 } from "@codemirror/view";
5943
+ import { EditorState as EditorState3 } from "@codemirror/state";
5944
+ import { EditorView as EditorView22 } from "@codemirror/view";
5640
5945
  import { useFocusableGroup } from "@fluentui/react-tabster";
5641
- import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5642
- import { log as log7 } from "@dxos/log";
5946
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5947
+ import { log as log8 } from "@dxos/log";
5643
5948
  import { getProviderValue, isNotFalsy as isNotFalsy4 } from "@dxos/util";
5644
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5949
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5645
5950
  var instanceCount = 0;
5646
5951
  var useTextEditor = (props = {}, deps = []) => {
5647
5952
  const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } = useMemo4(() => getProviderValue(props), deps ?? []);
@@ -5651,12 +5956,12 @@ var useTextEditor = (props = {}, deps = []) => {
5651
5956
  useEffect2(() => {
5652
5957
  let view2;
5653
5958
  if (parentRef.current) {
5654
- log7("create", {
5959
+ log8("create", {
5655
5960
  id,
5656
5961
  instanceId,
5657
5962
  doc: initialValue?.length ?? 0
5658
5963
  }, {
5659
- F: __dxlog_file11,
5964
+ F: __dxlog_file12,
5660
5965
  L: 76,
5661
5966
  S: void 0,
5662
5967
  C: (f, a) => f(...a)
@@ -5673,16 +5978,16 @@ var useTextEditor = (props = {}, deps = []) => {
5673
5978
  anchor
5674
5979
  };
5675
5980
  }
5676
- const state = EditorState2.create({
5981
+ const state = EditorState3.create({
5677
5982
  doc: doc ?? initialValue,
5678
5983
  // selection: initialSelection,
5679
5984
  extensions: [
5680
5985
  id && documentId.of(id),
5681
5986
  extensions,
5682
5987
  // NOTE: This doesn't catch errors in keymap functions.
5683
- EditorView21.exceptionSink.of((err) => {
5684
- log7.catch(err, void 0, {
5685
- F: __dxlog_file11,
5988
+ EditorView22.exceptionSink.of((err) => {
5989
+ log8.catch(err, void 0, {
5990
+ F: __dxlog_file12,
5686
5991
  L: 98,
5687
5992
  S: void 0,
5688
5993
  C: (f, a) => f(...a)
@@ -5690,10 +5995,10 @@ var useTextEditor = (props = {}, deps = []) => {
5690
5995
  })
5691
5996
  ].filter(isNotFalsy4)
5692
5997
  });
5693
- view2 = new EditorView21({
5998
+ view2 = new EditorView22({
5694
5999
  parent: parentRef.current,
5695
6000
  state,
5696
- scrollTo: scrollTo ? EditorView21.scrollIntoView(scrollTo, {
6001
+ scrollTo: scrollTo ? EditorView22.scrollIntoView(scrollTo, {
5697
6002
  yMargin: 96
5698
6003
  }) : void 0,
5699
6004
  dispatchTransactions: debug ? debugDispatcher : void 0
@@ -5711,10 +6016,10 @@ var useTextEditor = (props = {}, deps = []) => {
5711
6016
  setView(view2);
5712
6017
  }
5713
6018
  return () => {
5714
- log7("destroy", {
6019
+ log8("destroy", {
5715
6020
  id
5716
6021
  }, {
5717
- F: __dxlog_file11,
6022
+ F: __dxlog_file12,
5718
6023
  L: 135,
5719
6024
  S: void 0,
5720
6025
  C: (f, a) => f(...a)
@@ -5726,12 +6031,12 @@ var useTextEditor = (props = {}, deps = []) => {
5726
6031
  if (view) {
5727
6032
  if (scrollTo || selection) {
5728
6033
  if (selection && selection.anchor > view.state.doc.length) {
5729
- log7.warn("invalid selection", {
6034
+ log8.warn("invalid selection", {
5730
6035
  length: view.state.doc.length,
5731
6036
  scrollTo,
5732
6037
  selection
5733
6038
  }, {
5734
- F: __dxlog_file11,
6039
+ F: __dxlog_file12,
5735
6040
  L: 144,
5736
6041
  S: void 0,
5737
6042
  C: (f, a) => f(...a)
@@ -5763,7 +6068,7 @@ var useTextEditor = (props = {}, deps = []) => {
5763
6068
  Escape: view?.state.facet(editorInputMode).noTabster
5764
6069
  }
5765
6070
  });
5766
- const handleKeyUp = useCallback3((event) => {
6071
+ const handleKeyUp = useCallback2((event) => {
5767
6072
  const { key, target, currentTarget } = event;
5768
6073
  if (target === currentTarget) {
5769
6074
  switch (key) {
@@ -5790,9 +6095,9 @@ export {
5790
6095
  Cursor,
5791
6096
  EditorInputMode,
5792
6097
  EditorInputModes,
5793
- EditorState3 as EditorState,
6098
+ EditorState4 as EditorState,
5794
6099
  EditorToolbar,
5795
- EditorView22 as EditorView,
6100
+ EditorView23 as EditorView,
5796
6101
  EditorViewMode,
5797
6102
  EditorViewModes,
5798
6103
  Inline,
@@ -5822,7 +6127,7 @@ export {
5822
6127
  commentsState,
5823
6128
  convertTreeToJson,
5824
6129
  createBasicExtensions,
5825
- createComment2 as createComment,
6130
+ createComment,
5826
6131
  createDataExtensions,
5827
6132
  createEditorAction,
5828
6133
  createEditorActionGroup,
@@ -5866,6 +6171,7 @@ export {
5866
6171
  mention,
5867
6172
  openCommand,
5868
6173
  openEffect,
6174
+ outliner,
5869
6175
  overlap,
5870
6176
  preventNewline,
5871
6177
  preview,
@@ -5900,7 +6206,6 @@ export {
5900
6206
  toggleStyle,
5901
6207
  translations_default as translations,
5902
6208
  typewriter,
5903
- useActionHandler,
5904
6209
  useCommentClickListener,
5905
6210
  useCommentState,
5906
6211
  useComments,