@dxos/react-ui-editor 0.8.4-main.1068cf700f → 0.8.4-main.1c7ec43d41

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 (146) hide show
  1. package/dist/lib/browser/index.mjs +794 -749
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/translations.mjs +39 -0
  5. package/dist/lib/browser/translations.mjs.map +7 -0
  6. package/dist/lib/node-esm/index.mjs +794 -749
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/translations.mjs +41 -0
  10. package/dist/lib/node-esm/translations.mjs.map +7 -0
  11. package/dist/types/src/components/Editor/Editor.d.ts +36 -25
  12. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  13. package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
  14. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
  16. package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
  17. package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
  18. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
  19. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
  23. package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
  27. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -18
  29. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -18
  31. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -18
  33. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
  35. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  36. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -2
  37. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  38. package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
  39. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
  40. package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
  41. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  42. package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
  43. package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
  44. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +5 -19
  45. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  46. package/dist/types/src/components/index.d.ts +0 -2
  47. package/dist/types/src/components/index.d.ts.map +1 -1
  48. package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
  49. package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
  50. package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
  51. package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
  52. package/dist/types/src/extensions/index.d.ts +2 -0
  53. package/dist/types/src/extensions/index.d.ts.map +1 -0
  54. package/dist/types/src/hooks/index.d.ts +1 -0
  55. package/dist/types/src/hooks/index.d.ts.map +1 -1
  56. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
  57. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
  58. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  59. package/dist/types/src/index.d.ts +1 -2
  60. package/dist/types/src/index.d.ts.map +1 -1
  61. package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
  62. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  63. package/dist/types/src/stories/Comments.stories.d.ts +2 -2
  64. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  65. package/dist/types/src/stories/EditorToolbar.stories.d.ts +28 -26
  66. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
  68. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Markdown.stories.d.ts +2 -2
  70. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
  72. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  73. package/dist/types/src/stories/Popover.stories.d.ts +2 -2
  74. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  75. package/dist/types/src/stories/Preview.stories.d.ts +2 -2
  76. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  77. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  78. package/dist/types/src/stories/TextEditor.stories.d.ts +2 -2
  79. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  80. package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
  81. package/dist/types/src/stories/components/EditorStory.d.ts +4 -4
  82. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  83. package/dist/types/src/stories/components/util.d.ts +3 -2
  84. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  85. package/dist/types/src/translations.d.ts +24 -24
  86. package/dist/types/src/translations.d.ts.map +1 -1
  87. package/dist/types/src/util/react.d.ts +2 -5
  88. package/dist/types/src/util/react.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +59 -50
  91. package/src/components/Editor/Editor.stories.tsx +15 -21
  92. package/src/components/Editor/Editor.tsx +54 -49
  93. package/src/components/Editor/EditorView.tsx +103 -0
  94. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +17 -18
  95. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  96. package/src/components/EditorMenuProvider/popover.ts +3 -1
  97. package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
  98. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -65
  99. package/src/components/EditorToolbar/blocks.ts +54 -46
  100. package/src/components/EditorToolbar/formatting.ts +44 -45
  101. package/src/components/EditorToolbar/headings.ts +44 -50
  102. package/src/components/EditorToolbar/image.ts +16 -21
  103. package/src/components/EditorToolbar/index.ts +2 -3
  104. package/src/components/EditorToolbar/lists.ts +58 -0
  105. package/src/components/EditorToolbar/search.ts +16 -21
  106. package/src/components/EditorToolbar/types.ts +8 -0
  107. package/src/components/EditorToolbar/view-mode.ts +37 -43
  108. package/src/components/index.ts +0 -3
  109. package/src/extensions/Assistant.stories.tsx +112 -0
  110. package/src/extensions/assistant-extension.tsx +223 -0
  111. package/src/extensions/index.ts +5 -0
  112. package/src/hooks/index.ts +1 -0
  113. package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
  114. package/src/index.ts +1 -4
  115. package/src/stories/Automerge.stories.tsx +12 -13
  116. package/src/stories/Comments.stories.tsx +6 -6
  117. package/src/stories/EditorToolbar.stories.tsx +37 -65
  118. package/src/stories/Experimental.stories.tsx +12 -12
  119. package/src/stories/Markdown.stories.tsx +2 -2
  120. package/src/stories/Outliner.stories.tsx +4 -5
  121. package/src/stories/Popover.stories.tsx +9 -10
  122. package/src/stories/Preview.stories.tsx +49 -41
  123. package/src/stories/Tags.stories.tsx +5 -5
  124. package/src/stories/TextEditor.stories.tsx +2 -2
  125. package/src/stories/Theme.stories.tsx +4 -4
  126. package/src/stories/components/EditorStory.tsx +19 -12
  127. package/src/stories/components/util.tsx +49 -50
  128. package/src/translations.ts +29 -24
  129. package/src/util/react.tsx +3 -12
  130. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  131. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  132. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  133. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  134. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
  135. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  136. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  137. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  138. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  139. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  140. package/src/components/EditorContent/EditorContent.tsx +0 -83
  141. package/src/components/EditorContent/index.ts +0 -6
  142. package/src/components/EditorToolbar/actions.ts +0 -87
  143. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  144. package/src/stories/CommandDialog.stories.tsx +0 -81
  145. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  146. /package/src/components/{EditorContent → Editor}/controller.ts +0 -0
@@ -1,309 +1,13 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
- // src/translations.ts
4
- var translationKey = "@dxos/react-ui-editor";
5
- var translations = [
6
- {
7
- "en-US": {
8
- [translationKey]: {
9
- "strong label": "Bold",
10
- "emphasis label": "Italics",
11
- "strikethrough label": "Strikethrough",
12
- "code label": "Code",
13
- "link label": "Link",
14
- "list-bullet label": "Bullet list",
15
- "list-ordered label": "Numbered list",
16
- "list-task label": "Task list",
17
- "blockquote label": "Block quote",
18
- "codeblock label": "Code block",
19
- "comment label": "Create comment",
20
- "selection overlaps existing comment label": "Selection overlaps existing comment",
21
- "select text to comment label": "Select text to comment",
22
- "image label": "Insert image",
23
- "table label": "Create table",
24
- "heading label": "Heading level",
25
- "heading level label_zero": "Paragraph",
26
- "heading level label_one": "Heading level {{count}}",
27
- "heading level label_other": "Heading level {{count}}",
28
- "search label": "Search",
29
- "view mode label": "Editor view",
30
- "preview mode label": "Markdown",
31
- "readonly mode label": "Read only",
32
- "source mode label": "Plain text"
33
- }
34
- }
35
- }
36
- ];
37
-
38
3
  // src/components/Editor/Editor.tsx
4
+ import { Atom as Atom2 } from "@effect-atom/atom-react";
39
5
  import { createContext } from "@radix-ui/react-context";
40
6
  import React4, { forwardRef as forwardRef2, useCallback as useCallback4, useImperativeHandle as useImperativeHandle2, useMemo as useMemo5, useState as useState4 } from "react";
41
7
  import { invariant as invariant3 } from "@dxos/invariant";
42
8
  import { mx as mx2 } from "@dxos/ui-theme";
43
9
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
44
10
 
45
- // src/components/EditorContent/controller.ts
46
- var noopController = {
47
- get view() {
48
- return null;
49
- },
50
- getText: () => "",
51
- setText: () => {
52
- },
53
- focus: () => {
54
- }
55
- };
56
- var createEditorController = (view) => {
57
- return {
58
- get view() {
59
- return view;
60
- },
61
- getText: () => {
62
- return view?.state.doc.toString() ?? "";
63
- },
64
- setText: (text, focus) => {
65
- view?.dispatch({
66
- changes: {
67
- from: 0,
68
- to: view?.state.doc.length ?? 0,
69
- insert: text
70
- },
71
- selection: {
72
- anchor: text.length,
73
- head: text.length
74
- }
75
- });
76
- if (focus) {
77
- view?.focus();
78
- }
79
- },
80
- focus: () => view?.focus()
81
- };
82
- };
83
-
84
- // src/components/EditorContent/EditorContent.tsx
85
- import { Transaction } from "@codemirror/state";
86
- import { EditorView as EditorView2 } from "@codemirror/view";
87
- import React, { forwardRef, useEffect as useEffect2, useImperativeHandle } from "react";
88
- import { initialSync } from "@dxos/ui-editor";
89
- import { mx } from "@dxos/ui-theme";
90
-
91
- // src/hooks/useTextEditor.ts
92
- import { EditorState } from "@codemirror/state";
93
- import { EditorView } from "@codemirror/view";
94
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
95
- import { log } from "@dxos/log";
96
- import { createEditorStateTransaction, debugDispatcher, documentId, modalStateField } from "@dxos/ui-editor";
97
- import { getProviderValue, isTruthy } from "@dxos/util";
98
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
99
- var instanceCount = 0;
100
- var useTextEditor = (props = {}, deps = []) => {
101
- const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, selectionEnd, debug } = useMemo(() => getProviderValue(props), deps ?? []);
102
- const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
103
- const [view, setView] = useState(null);
104
- const parentRef = useRef(null);
105
- useEffect(() => {
106
- let view2 = null;
107
- if (parentRef.current) {
108
- log("create", {
109
- id,
110
- instanceId,
111
- doc: initialValue?.length ?? 0
112
- }, {
113
- F: __dxlog_file,
114
- L: 75,
115
- S: void 0,
116
- C: (f, a) => f(...a)
117
- });
118
- let initialSelection;
119
- if (selection?.anchor && initialValue?.length) {
120
- if (selection.anchor <= initialValue.length && (selection?.head ?? 0) <= initialValue.length) {
121
- initialSelection = selection;
122
- }
123
- } else if (selectionEnd && selection === void 0) {
124
- const index = initialValue?.indexOf("\n");
125
- const anchor = !index || index === -1 ? 0 : index;
126
- initialSelection = {
127
- anchor
128
- };
129
- }
130
- const state = EditorState.create({
131
- doc: doc ?? initialValue,
132
- // selection: initialSelection,
133
- extensions: [
134
- id && documentId.of(id),
135
- extensions,
136
- // NOTE: This doesn't catch errors in keymap functions.
137
- EditorView.exceptionSink.of((err) => {
138
- log.catch(err, void 0, {
139
- F: __dxlog_file,
140
- L: 97,
141
- S: void 0,
142
- C: (f, a) => f(...a)
143
- });
144
- })
145
- ].filter(isTruthy)
146
- });
147
- view2 = new EditorView({
148
- parent: parentRef.current,
149
- state,
150
- scrollTo: scrollTo ? EditorView.scrollIntoView(scrollTo, {
151
- yMargin: 96
152
- }) : void 0,
153
- dispatchTransactions: debug ? debugDispatcher : void 0
154
- });
155
- if (selectionEnd && !initialSelection) {
156
- const { to } = view2.state.doc.lineAt(0);
157
- if (to) {
158
- view2.dispatch({
159
- selection: {
160
- anchor: to
161
- }
162
- });
163
- }
164
- }
165
- setView(view2);
166
- }
167
- return () => {
168
- log("destroy", {
169
- id
170
- }, {
171
- F: __dxlog_file,
172
- L: 122,
173
- S: void 0,
174
- C: (f, a) => f(...a)
175
- });
176
- view2?.destroy();
177
- };
178
- }, deps);
179
- useEffect(() => {
180
- if (view) {
181
- if (scrollTo || selection) {
182
- if (selection && selection.anchor > view.state.doc.length) {
183
- log.warn("invalid selection", {
184
- length: view.state.doc.length,
185
- scrollTo,
186
- selection
187
- }, {
188
- F: __dxlog_file,
189
- L: 131,
190
- S: void 0,
191
- C: (f, a) => f(...a)
192
- });
193
- return;
194
- }
195
- view.dispatch(createEditorStateTransaction({
196
- scrollTo,
197
- selection
198
- }));
199
- }
200
- }
201
- }, [
202
- view,
203
- scrollTo,
204
- selection
205
- ]);
206
- useEffect(() => {
207
- if (view && autoFocus) {
208
- view.focus();
209
- }
210
- }, [
211
- autoFocus,
212
- view
213
- ]);
214
- const handleKeyDown = useCallback((event) => {
215
- const { key, target, currentTarget } = event;
216
- switch (key) {
217
- case "Escape": {
218
- const modal = view?.state.field(modalStateField, false);
219
- if (modal) {
220
- return;
221
- }
222
- const element = view?.contentDOM.closest('[tabindex="0"]');
223
- element?.focus();
224
- break;
225
- }
226
- case "Enter": {
227
- event.preventDefault();
228
- if (target === currentTarget) {
229
- view?.focus();
230
- }
231
- break;
232
- }
233
- }
234
- }, [
235
- view
236
- ]);
237
- return {
238
- parentRef,
239
- view,
240
- focusAttributes: {
241
- tabIndex: 0,
242
- onKeyDown: handleKeyDown
243
- }
244
- };
245
- };
246
-
247
- // src/components/EditorContent/EditorContent.tsx
248
- var EditorContent = /* @__PURE__ */ forwardRef(({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
249
- const { parentRef, focusAttributes, view } = useTextEditor(() => ({
250
- id,
251
- initialValue: value,
252
- selectionEnd,
253
- extensions: [
254
- extensions ?? [],
255
- EditorView2.updateListener.of(({ view: view2, docChanged, transactions }) => {
256
- const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
257
- if (!isInitialSync && docChanged) {
258
- onChange?.(view2.state.doc.toString());
259
- }
260
- })
261
- ],
262
- ...props
263
- }), [
264
- id,
265
- extensions,
266
- selectionEnd,
267
- onChange
268
- ]);
269
- useImperativeHandle(forwardedRef, () => {
270
- return createEditorController(view);
271
- }, [
272
- id,
273
- view
274
- ]);
275
- useEffect2(() => {
276
- requestAnimationFrame(() => {
277
- view?.dispatch({
278
- annotations: initialSync,
279
- changes: value ? [
280
- {
281
- from: 0,
282
- to: view?.state.doc.length ?? 0,
283
- insert: value ?? ""
284
- }
285
- ] : [],
286
- selection: selectionEnd ? {
287
- anchor: view?.state.doc.length ?? 0
288
- } : void 0
289
- });
290
- if (selectionEnd) {
291
- view?.focus();
292
- }
293
- });
294
- }, [
295
- view,
296
- value,
297
- selectionEnd
298
- ]);
299
- return /* @__PURE__ */ React.createElement("div", {
300
- role: "none",
301
- className: mx("is-full outline-none focus:border-accentSurface focus-within:border-neutralFocusIndicator", classNames),
302
- ref: parentRef,
303
- ...focusable ? focusAttributes : {}
304
- });
305
- });
306
-
307
11
  // src/components/EditorMenuProvider/menu.ts
308
12
  import { insertAtCursor } from "@dxos/ui-editor";
309
13
  var getMenuItem = (groups, id) => {
@@ -340,9 +44,9 @@ import { insertAtLineStart } from "@dxos/ui-editor";
340
44
 
341
45
  // src/components/EditorMenuProvider/popover.ts
342
46
  import { Prec, RangeSetBuilder, StateEffect, StateField } from "@codemirror/state";
343
- import { Decoration, EditorView as EditorView3, ViewPlugin, keymap } from "@codemirror/view";
344
- import { modalStateField as modalStateField2, placeholder } from "@dxos/ui-editor";
345
- import { isNonNullable, isTruthy as isTruthy2 } from "@dxos/util";
47
+ import { Decoration, EditorView, ViewPlugin, keymap } from "@codemirror/view";
48
+ import { modalStateField, placeholder } from "@dxos/ui-editor";
49
+ import { isNonNullable, isTruthy } from "@dxos/util";
346
50
  var DELIMITERS = [
347
51
  " ",
348
52
  ":"
@@ -353,15 +57,16 @@ var popover = (options = {}) => {
353
57
  popoverStateField,
354
58
  popoverTriggerListener(options),
355
59
  popoverAnchorDecoration(options),
356
- modalStateField2,
60
+ modalStateField,
357
61
  options.trigger && placeholder({
358
62
  // TODO(burdon): Translations.
359
63
  content: `Press '${Array.isArray(options.trigger) ? options.trigger[0] : options.trigger}' for commands`,
64
+ focusOnly: true,
360
65
  ...options.placeholder
361
66
  })
362
- ].filter(isTruthy2);
67
+ ].filter(isTruthy);
363
68
  };
364
- var popoverTriggerListener = (options) => EditorView3.updateListener.of(({ view, docChanged }) => {
69
+ var popoverTriggerListener = (options) => EditorView.updateListener.of(({ view, docChanged }) => {
365
70
  const { range: activeRange, trigger } = view.state.field(popoverStateField) ?? {};
366
71
  if (!activeRange) {
367
72
  return;
@@ -506,7 +211,7 @@ var popoverKeymap = (options) => {
506
211
  return false;
507
212
  }
508
213
  }
509
- ].filter(isTruthy2));
214
+ ].filter(isTruthy));
510
215
  };
511
216
  var popoverAnchorDecoration = (options) => {
512
217
  return ViewPlugin.fromClass(class {
@@ -701,36 +406,28 @@ var linkSlashCommands = {
701
406
 
702
407
  // src/components/EditorMenuProvider/EditorMenuProvider.tsx
703
408
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
704
- import React2, { Fragment, useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
409
+ import React, { Fragment, useCallback, useEffect, useRef, useState } from "react";
705
410
  import { addEventListener } from "@dxos/async";
706
411
  import { invariant } from "@dxos/invariant";
707
- import { DX_ANCHOR_ACTIVATE, Icon, Popover, toLocalizedString, useDynamicRef, useThemeContext, useTranslation } from "@dxos/react-ui";
708
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
412
+ import { DX_ANCHOR_ACTIVATE, Icon, Popover, ScrollArea, toLocalizedString, useDynamicRef, useThemeContext, useTranslation } from "@dxos/react-ui";
413
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
709
414
  var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp, defaultOpen, numItems = 8, onOpenChange, onActivate, onSelect, onCancel }) => {
710
415
  const { tx } = useThemeContext();
711
- const triggerRef = useRef2(null);
416
+ const triggerRef = useRef(null);
712
417
  const viewRef = useDynamicRef(view);
713
418
  const [open, setOpen] = useControllableState({
714
419
  prop: openProp,
715
420
  defaultProp: defaultOpen,
716
421
  onChange: (open2) => {
717
- invariant(viewRef.current, void 0, {
718
- F: __dxlog_file2,
719
- L: 65,
720
- S: void 0,
721
- A: [
722
- "viewRef.current",
723
- ""
724
- ]
725
- });
422
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 21, S: void 0, A: ["viewRef.current", ""] });
726
423
  onOpenChange?.({
727
424
  view: viewRef.current,
728
425
  open: open2
729
426
  });
730
427
  }
731
428
  });
732
- const [root, setRoot] = useState2(null);
733
- useEffect3(() => {
429
+ const [root, setRoot] = useState(null);
430
+ useEffect(() => {
734
431
  if (!root) {
735
432
  return;
736
433
  }
@@ -755,16 +452,8 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
755
452
  root,
756
453
  onActivate
757
454
  ]);
758
- const handleSelect = useCallback2((item) => {
759
- invariant(viewRef.current, void 0, {
760
- F: __dxlog_file2,
761
- L: 100,
762
- S: void 0,
763
- A: [
764
- "viewRef.current",
765
- ""
766
- ]
767
- });
455
+ const handleSelect = useCallback((item) => {
456
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: void 0, A: ["viewRef.current", ""] });
768
457
  onSelect?.({
769
458
  view: viewRef.current,
770
459
  item
@@ -774,62 +463,64 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
774
463
  onSelect
775
464
  ]);
776
465
  const menuGroups = groups?.filter((group) => group.items.length > 0) ?? [];
777
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
466
+ return /* @__PURE__ */ React.createElement(Popover.Root, {
778
467
  modal: false,
779
468
  open,
780
469
  onOpenChange: setOpen
781
- }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
470
+ }, /* @__PURE__ */ React.createElement(Popover.VirtualTrigger, {
782
471
  virtualRef: triggerRef
783
- }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
472
+ }), /* @__PURE__ */ React.createElement(Popover.Portal, null, /* @__PURE__ */ React.createElement(Popover.Content, {
784
473
  align: "start",
785
- classNames: tx("menu.content", {
786
- elevation: "positioned"
787
- }, [
788
- "overflow-y-auto",
474
+ classNames: [
475
+ "flex flex-col",
789
476
  !menuGroups.length && "hidden"
790
- ]),
477
+ ],
791
478
  style: {
792
479
  maxBlockSize: 36 * numItems + 10
793
480
  },
794
- /**
795
- * NOTE: We keep the focus in the editor, but Radix routes escape key.
796
- */
481
+ // NOTE: We keep the focus in the editor, but Radix routes escape key.
797
482
  onEscapeKeyDown: () => {
798
- onCancel?.({
799
- view
800
- });
483
+ const currentView = viewRef.current;
484
+ if (currentView) {
485
+ onCancel?.({
486
+ view: currentView
487
+ });
488
+ }
801
489
  },
802
490
  onOpenAutoFocus: (event) => event.preventDefault()
803
- }, /* @__PURE__ */ React2.createElement(Popover.Viewport, {
804
- classNames: tx("menu.viewport", {})
805
- }, /* @__PURE__ */ React2.createElement(Menu, {
491
+ }, /* @__PURE__ */ React.createElement(Popover.Viewport, {
492
+ asChild: true,
493
+ classNames: "dx-container"
494
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Root, {
495
+ thin: true
496
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React.createElement(Menu, {
806
497
  groups: menuGroups,
807
498
  currentItem,
808
499
  onSelect: handleSelect
809
- })), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))), /* @__PURE__ */ React2.createElement("div", {
810
- ref: setRoot,
500
+ })))), /* @__PURE__ */ React.createElement(Popover.Arrow, null))), /* @__PURE__ */ React.createElement("div", {
811
501
  role: "none",
812
- className: "contents"
502
+ className: "contents",
503
+ ref: setRoot
813
504
  }, children));
814
505
  };
815
506
  var Menu = ({ groups, currentItem, onSelect }) => {
816
507
  const { tx } = useThemeContext();
817
- return /* @__PURE__ */ React2.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React2.createElement(Fragment, {
508
+ return /* @__PURE__ */ React.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React.createElement(Fragment, {
818
509
  key: group.id
819
- }, /* @__PURE__ */ React2.createElement(MenuGroup, {
510
+ }, /* @__PURE__ */ React.createElement(MenuGroup, {
820
511
  group,
821
512
  currentItem,
822
513
  onSelect
823
- }), index < groups.length - 1 && /* @__PURE__ */ React2.createElement("div", {
514
+ }), index < groups.length - 1 && /* @__PURE__ */ React.createElement("div", {
824
515
  className: tx("menu.separator", {})
825
516
  }))));
826
517
  };
827
518
  var MenuGroup = ({ group, currentItem, onSelect }) => {
828
519
  const { tx } = useThemeContext();
829
520
  const { t } = useTranslation();
830
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, group.label && /* @__PURE__ */ React2.createElement("div", {
521
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, group.label && /* @__PURE__ */ React.createElement("div", {
831
522
  className: tx("menu.groupLabel", {})
832
- }, /* @__PURE__ */ React2.createElement("span", null, toLocalizedString(group.label, t))), group.items.map((item) => /* @__PURE__ */ React2.createElement(MenuItem, {
523
+ }, /* @__PURE__ */ React.createElement("span", null, toLocalizedString(group.label, t))), group.items.map((item) => /* @__PURE__ */ React.createElement(MenuItem, {
833
524
  key: item.id,
834
525
  item,
835
526
  current: currentItem === item.id,
@@ -839,8 +530,8 @@ var MenuGroup = ({ group, currentItem, onSelect }) => {
839
530
  var MenuItem = ({ item, current, onSelect }) => {
840
531
  const { tx } = useThemeContext();
841
532
  const { t } = useTranslation();
842
- const listRef = useRef2(null);
843
- useEffect3(() => {
533
+ const listRef = useRef(null);
534
+ useEffect(() => {
844
535
  if (current && listRef.current) {
845
536
  listRef.current.scrollIntoView({
846
537
  behavior: "smooth",
@@ -850,56 +541,47 @@ var MenuItem = ({ item, current, onSelect }) => {
850
541
  }, [
851
542
  current
852
543
  ]);
853
- const handleSelect = useCallback2(() => onSelect?.(item), [
544
+ const handleSelect = useCallback(() => onSelect?.(item), [
854
545
  item,
855
546
  onSelect
856
547
  ]);
857
- return /* @__PURE__ */ React2.createElement("li", {
548
+ return /* @__PURE__ */ React.createElement("li", {
858
549
  ref: listRef,
859
550
  className: tx("menu.item", {}, [
860
- current && "bg-hoverSurface"
551
+ current && "bg-hover-surface"
861
552
  ]),
862
553
  onClick: handleSelect
863
- }, item.icon && /* @__PURE__ */ React2.createElement(Icon, {
864
- icon: item.icon,
865
- size: 5
866
- }), /* @__PURE__ */ React2.createElement("span", {
554
+ }, item.icon && /* @__PURE__ */ React.createElement(Icon, {
555
+ icon: item.icon
556
+ }), /* @__PURE__ */ React.createElement("span", {
867
557
  className: "grow truncate"
868
558
  }, toLocalizedString(item.label, t)));
869
559
  };
870
560
 
871
561
  // src/components/EditorMenuProvider/useEditorMenu.ts
872
- import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
562
+ import { useCallback as useCallback2, useMemo, useRef as useRef2, useState as useState2 } from "react";
873
563
  import { invariant as invariant2 } from "@dxos/invariant";
874
564
  import { modalStateEffect } from "@dxos/ui-editor";
875
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/useEditorMenu.ts";
565
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/useEditorMenu.ts";
876
566
  var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter = true, getMenu }) => {
877
- const groupsRef = useRef3([]);
878
- const currentRef = useRef3(null);
879
- const [currentItem, setCurrentItem] = useState3();
880
- const [open, setOpen] = useState3(false);
881
- const [_, refresh] = useState3({});
882
- const getMenuOptions = useCallback3(async ({ text, trigger: trigger2, ...props }) => {
567
+ const groupsRef = useRef2([]);
568
+ const currentRef = useRef2(null);
569
+ const [currentItem, setCurrentItem] = useState2();
570
+ const [open, setOpen] = useState2(false);
571
+ const [_, refresh] = useState2({});
572
+ const getMenuOptions = useCallback2(async ({ text, trigger: trigger2, ...props }) => {
883
573
  const groups = await getMenu?.({
884
574
  text,
885
575
  trigger: trigger2,
886
576
  ...props
887
577
  }) ?? [];
888
- return filter ? filterMenuGroups(groups, (item) => text ? item.label.toLowerCase().startsWith(text.toLowerCase()) : true) : groups;
578
+ return filter && trigger2 !== "@" ? filterMenuGroups(groups, (item) => text ? item.label.toLowerCase().startsWith(text.toLowerCase()) : true) : groups;
889
579
  }, [
890
580
  getMenu,
891
581
  filter
892
582
  ]);
893
- const handleOpenChange = useCallback3(async ({ view, open: open2 }) => {
894
- invariant2(view, void 0, {
895
- F: __dxlog_file3,
896
- L: 76,
897
- S: void 0,
898
- A: [
899
- "view",
900
- ""
901
- ]
902
- });
583
+ const handleOpenChange = useCallback2(async ({ view, open: open2 }) => {
584
+ invariant2(view, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 40, S: void 0, A: ["view", ""] });
903
585
  setOpen(open2);
904
586
  if (!open2) {
905
587
  setCurrentItem(void 0);
@@ -919,7 +601,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
919
601
  }, [
920
602
  getMenuOptions
921
603
  ]);
922
- const handleActivate = useCallback3(async ({ view, trigger: trigger2 }) => {
604
+ const handleActivate = useCallback2(async ({ view, trigger: trigger2 }) => {
923
605
  const item = getMenuItem(groupsRef.current, currentItem);
924
606
  if (item) {
925
607
  currentRef.current = item;
@@ -935,13 +617,24 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
935
617
  open,
936
618
  handleOpenChange
937
619
  ]);
938
- const handleSelect = useCallback3(({ view, item }) => {
620
+ const handleSelect = useCallback2(({ view, item }) => {
621
+ const { range } = view.state.field(popoverStateField) ?? {};
622
+ if (range) {
623
+ view.dispatch({
624
+ changes: {
625
+ from: range.from,
626
+ to: range.to,
627
+ insert: ""
628
+ }
629
+ });
630
+ }
939
631
  void item.onSelect?.({
940
632
  view,
941
633
  head: view.state.selection.main.head
942
634
  });
635
+ view.focus();
943
636
  }, []);
944
- const handleCancel = useCallback3(({ view }) => {
637
+ const handleCancel = useCallback2(({ view }) => {
945
638
  const { range, trigger: trigger2 } = view.state.field(popoverStateField) ?? {};
946
639
  if (range && trigger2) {
947
640
  view.dispatch({
@@ -953,7 +646,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
953
646
  }
954
647
  }, []);
955
648
  const serializedTrigger = Array.isArray(trigger) ? trigger.join(",") : trigger;
956
- const extension = useMemo2(() => {
649
+ const extension = useMemo(() => {
957
650
  return popover({
958
651
  trigger,
959
652
  triggerKey,
@@ -1019,150 +712,70 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
1019
712
 
1020
713
  // src/components/EditorToolbar/EditorToolbar.tsx
1021
714
  import { Atom } from "@effect-atom/atom-react";
1022
- import React3, { memo, useMemo as useMemo3 } from "react";
715
+ import React2, { memo, useMemo as useMemo2 } from "react";
1023
716
  import { ElevationProvider } from "@dxos/react-ui";
1024
- import { MenuProvider, ToolbarMenu, createGapSeparator, useMenuActions } from "@dxos/react-ui-menu";
717
+ import { Menu as Menu2, MenuBuilder, useMenuActions } from "@dxos/react-ui-menu";
1025
718
 
1026
- // src/components/EditorToolbar/actions.ts
1027
- import { createMenuAction, createMenuItemGroup } from "@dxos/react-ui-menu";
1028
- import { List, addList, removeList } from "@dxos/ui-editor";
1029
- var listStyles = {
1030
- bullet: "ph--list-bullets--regular",
1031
- ordered: "ph--list-numbers--regular",
1032
- task: "ph--list-checks--regular"
719
+ // src/components/EditorToolbar/blocks.ts
720
+ import { addBlockquote, addCodeblock, insertTable, removeBlockquote, removeCodeblock } from "@dxos/ui-editor";
721
+ import { translationKey } from "#translations";
722
+ var blockTypes = {
723
+ blockquote: "ph--quotes--regular",
724
+ codeblock: "ph--code-block--regular",
725
+ table: "ph--table--regular"
1033
726
  };
1034
- var createLists = (state, getView) => {
1035
- const value = state.listStyle ?? "";
1036
- const listGroupAction = createListGroupAction(value);
1037
- const listActionsMap = createListActions(value, getView);
1038
- return {
1039
- nodes: [
1040
- listGroupAction,
1041
- ...listActionsMap
1042
- ],
1043
- edges: [
727
+ var addBlocks = (state, getView) => (builder) => {
728
+ const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
729
+ builder.group("block", {
730
+ label: [
731
+ "block.label",
1044
732
  {
1045
- source: "root",
1046
- target: "list"
1047
- },
1048
- ...listActionsMap.map(({ id }) => ({
1049
- source: listGroupAction.id,
1050
- target: id
1051
- }))
1052
- ]
1053
- };
1054
- };
1055
- var createEditorAction = (id, props, invoke) => {
1056
- const { label = [
1057
- `${id} label`,
1058
- {
1059
- ns: translationKey
1060
- }
1061
- ], ...rest } = props;
1062
- return createMenuAction(id, invoke, {
1063
- label,
1064
- ...rest
1065
- });
1066
- };
1067
- var createEditorActionGroup = (id, props, icon) => {
1068
- const { label = [
1069
- `${id} label`,
1070
- {
1071
- ns: translationKey
1072
- }
1073
- ], ...rest } = props;
1074
- return createMenuItemGroup(id, {
1075
- label,
1076
- icon,
733
+ ns: translationKey
734
+ }
735
+ ],
1077
736
  iconOnly: true,
1078
- ...rest
1079
- });
1080
- };
1081
- var createListGroupAction = (value) => createEditorActionGroup("list", {
1082
- variant: "toggleGroup",
1083
- selectCardinality: "single",
1084
- value
1085
- });
1086
- var createListActions = (value, getView) => Object.entries(listStyles).map(([listStyle, icon]) => {
1087
- const checked = value === listStyle;
1088
- return createEditorAction(`list-${listStyle}`, {
1089
- checked,
1090
- icon
1091
- }, () => {
1092
- const view = getView();
1093
- if (!view) {
1094
- return;
1095
- }
1096
- const listType = listStyle === "ordered" ? List.Ordered : listStyle === "bullet" ? List.Bullet : List.Task;
1097
- if (checked) {
1098
- removeList(listType)(view);
1099
- } else {
1100
- addList(listType)(view);
1101
- }
1102
- });
1103
- });
1104
-
1105
- // src/components/EditorToolbar/blocks.ts
1106
- import { addBlockquote, addCodeblock, insertTable, removeBlockquote, removeCodeblock } from "@dxos/ui-editor";
1107
- var createBlockGroupAction = (value) => createEditorActionGroup("block", {
1108
- variant: "toggleGroup",
1109
- selectCardinality: "single",
1110
- value
1111
- });
1112
- var createBlockActions = (value, getView, blankLine) => Object.entries({
1113
- blockquote: "ph--quotes--regular",
1114
- codeblock: "ph--code-block--regular",
1115
- table: "ph--table--regular"
1116
- }).map(([type, icon]) => {
1117
- const checked = type === value;
1118
- return createEditorAction(type, {
1119
- checked,
1120
- ...type === "table" && {
1121
- disabled: !!blankLine
1122
- },
1123
- icon
1124
- }, () => {
1125
- const view = getView();
1126
- if (!view) {
1127
- return;
1128
- }
1129
- switch (type) {
1130
- case "blockquote":
1131
- checked ? removeBlockquote(view) : addBlockquote(view);
1132
- break;
1133
- case "codeblock":
1134
- checked ? removeCodeblock(view) : addCodeblock(view);
1135
- break;
1136
- case "table":
1137
- insertTable(view);
1138
- break;
737
+ variant: "toggleGroup",
738
+ selectCardinality: "single",
739
+ value
740
+ }, (group) => {
741
+ for (const [type, icon] of Object.entries(blockTypes)) {
742
+ const checked = type === value;
743
+ group.action(type, {
744
+ label: [
745
+ `block.${type}.label`,
746
+ {
747
+ ns: translationKey
748
+ }
749
+ ],
750
+ checked,
751
+ ...type === "table" && {
752
+ disabled: !!state.blankLine
753
+ },
754
+ icon
755
+ }, () => {
756
+ const view = getView();
757
+ if (!view) {
758
+ return;
759
+ }
760
+ switch (type) {
761
+ case "blockquote":
762
+ checked ? removeBlockquote(view) : addBlockquote(view);
763
+ break;
764
+ case "codeblock":
765
+ checked ? removeCodeblock(view) : addCodeblock(view);
766
+ break;
767
+ case "table":
768
+ insertTable(view);
769
+ break;
770
+ }
771
+ });
1139
772
  }
1140
773
  });
1141
- });
1142
- var createBlocks = (state, getView) => {
1143
- const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
1144
- const blockGroupAction = createBlockGroupAction(value);
1145
- const blockActions = createBlockActions(value, getView, state.blankLine);
1146
- return {
1147
- nodes: [
1148
- blockGroupAction,
1149
- ...blockActions
1150
- ],
1151
- edges: [
1152
- {
1153
- source: "root",
1154
- target: "block"
1155
- },
1156
- ...blockActions.map(({ id }) => ({
1157
- source: blockGroupAction.id,
1158
- target: id
1159
- }))
1160
- ]
1161
- };
1162
774
  };
1163
775
 
1164
776
  // src/components/EditorToolbar/formatting.ts
1165
777
  import { Inline, addLink, removeLink, setStyle } from "@dxos/ui-editor";
778
+ import { translationKey as translationKey2 } from "#translations";
1166
779
  var formats = {
1167
780
  strong: "ph--text-b--regular",
1168
781
  emphasis: "ph--text-italic--regular",
@@ -1170,205 +783,231 @@ var formats = {
1170
783
  code: "ph--code--regular",
1171
784
  link: "ph--link--regular"
1172
785
  };
1173
- var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
1174
- variant: "toggleGroup",
1175
- selectCardinality: "multiple",
1176
- value: Object.keys(formats).filter((key) => !!formatting[key])
1177
- });
1178
- var createFormattingActions = (formatting, getView) => Object.entries(formats).map(([type, icon]) => {
1179
- const checked = !!formatting[type];
1180
- return createEditorAction(type, {
1181
- checked,
1182
- icon
1183
- }, () => {
1184
- const view = getView();
1185
- if (!view) {
1186
- return;
1187
- }
1188
- if (type === "link") {
1189
- checked ? removeLink(view) : addLink()(view);
1190
- return;
786
+ var addFormatting = (state, getView) => (builder) => {
787
+ const formatting = state;
788
+ builder.group("formatting", {
789
+ label: [
790
+ "formatting.label",
791
+ {
792
+ ns: translationKey2
793
+ }
794
+ ],
795
+ iconOnly: true,
796
+ variant: "toggleGroup",
797
+ selectCardinality: "multiple",
798
+ value: Object.keys(formats).filter((key) => !!formatting[key])
799
+ }, (group) => {
800
+ for (const [type, icon] of Object.entries(formats)) {
801
+ const checked = !!formatting[type];
802
+ group.action(type, {
803
+ label: [
804
+ `formatting.${type}.label`,
805
+ {
806
+ ns: translationKey2
807
+ }
808
+ ],
809
+ checked,
810
+ icon
811
+ }, () => {
812
+ const view = getView();
813
+ if (!view) {
814
+ return;
815
+ }
816
+ if (type === "link") {
817
+ checked ? removeLink(view) : addLink()(view);
818
+ return;
819
+ }
820
+ setStyle(type === "strong" ? Inline.Strong : type === "emphasis" ? Inline.Emphasis : type === "strikethrough" ? Inline.Strikethrough : Inline.Code, !checked)(view);
821
+ });
1191
822
  }
1192
- const inlineType = type === "strong" ? Inline.Strong : type === "emphasis" ? Inline.Emphasis : type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
1193
- setStyle(inlineType, !checked)(view);
1194
823
  });
1195
- });
1196
- var createFormatting = (state, getView) => {
1197
- const formattingGroupAction = createFormattingGroup(state);
1198
- const formattingActions = createFormattingActions(state, getView);
1199
- return {
1200
- nodes: [
1201
- formattingGroupAction,
1202
- ...formattingActions
1203
- ],
1204
- edges: [
1205
- {
1206
- source: "root",
1207
- target: "formatting"
1208
- },
1209
- ...formattingActions.map(({ id }) => ({
1210
- source: formattingGroupAction.id,
1211
- target: id
1212
- }))
1213
- ]
1214
- };
1215
824
  };
1216
825
 
1217
826
  // src/components/EditorToolbar/headings.ts
1218
827
  import { setHeading } from "@dxos/ui-editor";
1219
- var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
1220
- variant: "dropdownMenu",
1221
- applyActive: true,
1222
- selectCardinality: "single",
1223
- // TODO(wittjosiah): Remove? Not sure this does anything.
1224
- value
1225
- }, "ph--text-h--regular");
1226
- var createHeadingActions = (currentLevel, getView) => Object.entries({
1227
- "0": "ph--paragraph--regular",
1228
- "1": "ph--text-h-one--regular",
1229
- "2": "ph--text-h-two--regular",
1230
- "3": "ph--text-h-three--regular",
1231
- "4": "ph--text-h-four--regular",
1232
- "5": "ph--text-h-five--regular",
1233
- "6": "ph--text-h-six--regular"
1234
- }).map(([levelStr, icon]) => {
1235
- const level = parseInt(levelStr);
1236
- return createEditorAction(`heading--${levelStr}`, {
1237
- label: [
1238
- "heading level label",
1239
- {
1240
- count: level,
1241
- ns: translationKey
1242
- }
1243
- ],
1244
- icon,
1245
- checked: levelStr === currentLevel
1246
- }, () => setHeading(level)(getView()));
1247
- });
828
+ import { translationKey as translationKey3 } from "#translations";
829
+ var headingIcons = {
830
+ 0: "ph--paragraph--regular",
831
+ 1: "ph--text-h-one--regular",
832
+ 2: "ph--text-h-two--regular",
833
+ 3: "ph--text-h-three--regular",
834
+ 4: "ph--text-h-four--regular",
835
+ 5: "ph--text-h-five--regular",
836
+ 6: "ph--text-h-six--regular"
837
+ };
1248
838
  var computeHeadingValue = (state) => {
1249
839
  const blockType = state ? state.blockType : "paragraph";
1250
840
  const heading = blockType && /heading(\d)/.exec(blockType);
1251
841
  return heading ? heading[1] : blockType === "paragraph" || !blockType ? "0" : "";
1252
842
  };
1253
- var createHeadings = (state, getView) => {
843
+ var addHeadings = (state, getView) => (builder) => {
1254
844
  const headingValue = computeHeadingValue(state);
1255
- const headingGroupAction = createHeadingGroupAction(headingValue);
1256
- const headingActions = createHeadingActions(headingValue, getView);
1257
- return {
1258
- nodes: [
1259
- headingGroupAction,
1260
- ...headingActions
1261
- ],
1262
- edges: [
845
+ builder.group("heading", {
846
+ label: [
847
+ "heading.label",
1263
848
  {
1264
- source: "root",
1265
- target: "heading"
1266
- },
1267
- ...headingActions.map(({ id }) => ({
1268
- source: headingGroupAction.id,
1269
- target: id
1270
- }))
1271
- ]
1272
- };
849
+ ns: translationKey3
850
+ }
851
+ ],
852
+ icon: "ph--text-h--regular",
853
+ iconOnly: true,
854
+ variant: "dropdownMenu",
855
+ applyActive: true,
856
+ selectCardinality: "single",
857
+ // TODO(wittjosiah): Remove? Not sure this does anything.
858
+ value: headingValue
859
+ }, (group) => {
860
+ for (const [levelStr, icon] of Object.entries(headingIcons)) {
861
+ const level = parseInt(levelStr);
862
+ group.action(`heading--${levelStr}`, {
863
+ label: [
864
+ "heading-level.label",
865
+ {
866
+ count: level,
867
+ ns: translationKey3
868
+ }
869
+ ],
870
+ icon,
871
+ checked: levelStr === headingValue
872
+ }, () => setHeading(level)(getView()));
873
+ }
874
+ });
1273
875
  };
1274
876
 
1275
877
  // src/components/EditorToolbar/image.ts
1276
- var createImageUploadAction = (onImageUpload) => createEditorAction("image", {
1277
- testId: "editor.toolbar.image",
1278
- icon: "ph--image-square--regular"
1279
- }, onImageUpload);
1280
- var createImageUpload = (onImageUpload) => ({
1281
- nodes: [
1282
- createImageUploadAction(onImageUpload)
1283
- ],
1284
- edges: [
1285
- {
1286
- source: "root",
1287
- target: "image"
878
+ import { translationKey as translationKey4 } from "#translations";
879
+ var addImageUpload = (onImageUpload) => (builder) => {
880
+ builder.action("image", {
881
+ label: [
882
+ "image.label",
883
+ {
884
+ ns: translationKey4
885
+ }
886
+ ],
887
+ testId: "editor.toolbar.image",
888
+ icon: "ph--image-square--regular"
889
+ }, onImageUpload);
890
+ };
891
+
892
+ // src/components/EditorToolbar/lists.ts
893
+ import { List, addList, removeList } from "@dxos/ui-editor";
894
+ import { translationKey as translationKey5 } from "#translations";
895
+ var listStyles = {
896
+ bullet: "ph--list-bullets--regular",
897
+ ordered: "ph--list-numbers--regular",
898
+ task: "ph--list-checks--regular"
899
+ };
900
+ var addLists = (state, getView) => (builder) => {
901
+ const value = state.listStyle ?? "";
902
+ builder.group("list", {
903
+ label: [
904
+ "list.label",
905
+ {
906
+ ns: translationKey5
907
+ }
908
+ ],
909
+ iconOnly: true,
910
+ variant: "toggleGroup",
911
+ selectCardinality: "single",
912
+ value
913
+ }, (group) => {
914
+ for (const [listStyle, icon] of Object.entries(listStyles)) {
915
+ const checked = value === listStyle;
916
+ group.action(`list-${listStyle}`, {
917
+ label: [
918
+ `list.${listStyle}.label`,
919
+ {
920
+ ns: translationKey5
921
+ }
922
+ ],
923
+ checked,
924
+ icon
925
+ }, () => {
926
+ const view = getView();
927
+ if (!view) {
928
+ return;
929
+ }
930
+ const listType = listStyle === "ordered" ? List.Ordered : listStyle === "bullet" ? List.Bullet : List.Task;
931
+ if (checked) {
932
+ removeList(listType)(view);
933
+ } else {
934
+ addList(listType)(view);
935
+ }
936
+ });
1288
937
  }
1289
- ]
1290
- });
938
+ });
939
+ };
1291
940
 
1292
941
  // src/components/EditorToolbar/search.ts
1293
942
  import { openSearchPanel } from "@codemirror/search";
1294
- var createSearchAction = (getView) => createEditorAction("search", {
1295
- testId: "editor.toolbar.search",
1296
- icon: "ph--magnifying-glass--regular"
1297
- }, () => openSearchPanel(getView()));
1298
- var createSearch = (getView) => ({
1299
- nodes: [
1300
- createSearchAction(getView)
1301
- ],
1302
- edges: [
1303
- {
1304
- source: "root",
1305
- target: "search"
1306
- }
1307
- ]
1308
- });
943
+ import { translationKey as translationKey6 } from "#translations";
944
+ var addSearch = (getView) => (builder) => {
945
+ builder.action("search", {
946
+ label: [
947
+ "search.label",
948
+ {
949
+ ns: translationKey6
950
+ }
951
+ ],
952
+ testId: "editor.toolbar.search",
953
+ icon: "ph--magnifying-glass--regular"
954
+ }, () => openSearchPanel(getView()));
955
+ };
1309
956
 
1310
957
  // src/components/EditorToolbar/view-mode.ts
1311
- var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
1312
- variant: "dropdownMenu",
1313
- applyActive: true,
1314
- selectCardinality: "single",
1315
- value
1316
- }, "ph--eye--regular");
1317
- var createViewModeActions = (value, onViewModeChange) => Object.entries({
958
+ import { translationKey as translationKey7 } from "#translations";
959
+ var viewModes = {
1318
960
  preview: "ph--eye--regular",
1319
961
  source: "ph--pencil-simple--regular",
1320
962
  readonly: "ph--pencil-slash--regular"
1321
- }).map(([viewMode, icon]) => {
1322
- const checked = viewMode === value;
1323
- return createEditorAction(`view-mode--${viewMode}`, {
963
+ };
964
+ var addViewMode = (state, onViewModeChange) => (builder) => {
965
+ const value = state.viewMode ?? "source";
966
+ builder.group("viewMode", {
1324
967
  label: [
1325
- `${viewMode} mode label`,
968
+ "view-mode.label",
1326
969
  {
1327
- ns: translationKey
970
+ ns: translationKey7
1328
971
  }
1329
972
  ],
1330
- checked,
1331
- icon
1332
- }, () => onViewModeChange(viewMode));
1333
- });
1334
- var createViewMode = (state, onViewModeChange) => {
1335
- const value = state.viewMode ?? "source";
1336
- const viewModeGroupAction = createViewModeGroupAction(value);
1337
- const viewModeActions = createViewModeActions(value, onViewModeChange);
1338
- return {
1339
- nodes: [
1340
- viewModeGroupAction,
1341
- ...viewModeActions
1342
- ],
1343
- edges: [
1344
- {
1345
- source: "root",
1346
- target: "viewMode"
1347
- },
1348
- ...viewModeActions.map(({ id }) => ({
1349
- source: viewModeGroupAction.id,
1350
- target: id
1351
- }))
1352
- ]
1353
- };
973
+ icon: "ph--eye--regular",
974
+ iconOnly: true,
975
+ variant: "dropdownMenu",
976
+ applyActive: true,
977
+ selectCardinality: "single",
978
+ value
979
+ }, (group) => {
980
+ for (const [viewMode, icon] of Object.entries(viewModes)) {
981
+ const checked = viewMode === value;
982
+ group.action(`view-mode--${viewMode}`, {
983
+ label: [
984
+ `view-mode.${viewMode}.label`,
985
+ {
986
+ ns: translationKey7
987
+ }
988
+ ],
989
+ checked,
990
+ icon
991
+ }, () => onViewModeChange(viewMode));
992
+ }
993
+ });
1354
994
  };
1355
995
 
1356
996
  // src/components/EditorToolbar/EditorToolbar.tsx
1357
997
  var EditorToolbar = /* @__PURE__ */ memo(({ classNames, role, attendableId, onAction, ...props }) => {
1358
- const menuProps = useEditorToolbarActionGraph(props);
1359
- return /* @__PURE__ */ React3.createElement(ElevationProvider, {
998
+ const menuActions = useMarkdownMenuActions(props);
999
+ return /* @__PURE__ */ React2.createElement(ElevationProvider, {
1360
1000
  elevation: role === "section" ? "positioned" : "base"
1361
- }, /* @__PURE__ */ React3.createElement(MenuProvider, {
1362
- ...menuProps,
1001
+ }, /* @__PURE__ */ React2.createElement(Menu2.Root, {
1002
+ ...menuActions,
1363
1003
  attendableId,
1364
1004
  onAction
1365
- }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
1366
- classNames,
1367
- textBlockWidth: true
1005
+ }, /* @__PURE__ */ React2.createElement(Menu2.Toolbar, {
1006
+ classNames
1368
1007
  })));
1369
1008
  });
1370
- var useEditorToolbarActionGraph = ({ state, getView, customActions, ...features }) => {
1371
- const menuCreator = useMemo3(() => createToolbarActions({
1009
+ var useMarkdownMenuActions = ({ state, getView, customActions, ...features }) => {
1010
+ const menuCreator = useMemo2(() => createMarkdownActions({
1372
1011
  state,
1373
1012
  getView,
1374
1013
  customActions,
@@ -1387,63 +1026,301 @@ var useEditorToolbarActionGraph = ({ state, getView, customActions, ...features
1387
1026
  ]);
1388
1027
  return useMenuActions(menuCreator);
1389
1028
  };
1390
- var createToolbarActions = ({ state, getView, customActions, ...features }) => {
1029
+ var createMarkdownActions = ({ state, getView, customActions, ...features }) => {
1391
1030
  return Atom.make((get) => {
1392
- const graph = {
1393
- nodes: [],
1394
- edges: []
1395
- };
1396
- const addSubGraph = (graph2, subGraph) => {
1397
- graph2.nodes.push(...subGraph.nodes);
1398
- graph2.edges.push(...subGraph.edges);
1399
- };
1400
1031
  const stateSnapshot = get(state);
1401
- if (features?.showHeadings ?? true) {
1402
- addSubGraph(graph, createHeadings(stateSnapshot, getView));
1403
- }
1404
- if (features?.showFormatting ?? true) {
1405
- addSubGraph(graph, createFormatting(stateSnapshot, getView));
1406
- }
1407
- if (features?.showLists ?? true) {
1408
- addSubGraph(graph, createLists(stateSnapshot, getView));
1409
- }
1410
- if (features?.showBlocks ?? true) {
1411
- addSubGraph(graph, createBlocks(stateSnapshot, getView));
1032
+ return MenuBuilder.make().subgraph(features?.showHeadings !== false && addHeadings(stateSnapshot, getView)).subgraph(features?.showFormatting !== false && addFormatting(stateSnapshot, getView)).subgraph(features?.showLists !== false && addLists(stateSnapshot, getView)).subgraph(features?.showBlocks !== false && addBlocks(stateSnapshot, getView)).subgraph(features?.onImageUpload && addImageUpload(features.onImageUpload)).separator("gap").subgraph(customActions && get(customActions)).subgraph(features?.showSearch !== false && addSearch(getView)).subgraph(features?.onViewModeChange && addViewMode(stateSnapshot, features.onViewModeChange)).build();
1033
+ });
1034
+ };
1035
+
1036
+ // src/components/Editor/EditorView.tsx
1037
+ import { Transaction } from "@codemirror/state";
1038
+ import { EditorView as NaturalEditorView } from "@codemirror/view";
1039
+ import React3, { forwardRef, useEffect as useEffect3, useImperativeHandle, useRef as useRef4 } from "react";
1040
+ import { initialSync } from "@dxos/ui-editor";
1041
+ import { mx } from "@dxos/ui-theme";
1042
+
1043
+ // src/hooks/useBasicMarkdownExtensions.ts
1044
+ import { useMemo as useMemo3 } from "react";
1045
+ import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
1046
+ import { createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown } from "@dxos/ui-editor";
1047
+ var useBasicMarkdownExtensions = ({ placeholder: placeholder2, decorate = true, extensions } = {}) => {
1048
+ const { themeMode } = useThemeContext2();
1049
+ return useMemo3(() => [
1050
+ createBasicExtensions({
1051
+ placeholder: placeholder2
1052
+ }),
1053
+ createThemeExtensions({
1054
+ syntaxHighlighting: true,
1055
+ themeMode
1056
+ }),
1057
+ createMarkdownExtensions(),
1058
+ ...decorate ? [
1059
+ decorateMarkdown()
1060
+ ] : [],
1061
+ ...extensions ?? []
1062
+ ], [
1063
+ placeholder2,
1064
+ themeMode,
1065
+ decorate,
1066
+ extensions
1067
+ ]);
1068
+ };
1069
+
1070
+ // src/hooks/useTextEditor.ts
1071
+ import { EditorState } from "@codemirror/state";
1072
+ import { EditorView as EditorView2 } from "@codemirror/view";
1073
+ import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
1074
+ import { log } from "@dxos/log";
1075
+ import { createEditorStateTransaction, debugDispatcher, documentId, modalStateField as modalStateField2 } from "@dxos/ui-editor";
1076
+ import { getProviderValue, isTruthy as isTruthy2 } from "@dxos/util";
1077
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
1078
+ var instanceCount = 0;
1079
+ var useTextEditor = (props = {}, deps = []) => {
1080
+ const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, selectionEnd, debug } = useMemo4(() => getProviderValue(props), deps ?? []);
1081
+ const [instanceId] = useState3(() => `text-editor-${++instanceCount}`);
1082
+ const [view, setView] = useState3(null);
1083
+ const parentRef = useRef3(null);
1084
+ useEffect2(() => {
1085
+ let view2 = null;
1086
+ if (parentRef.current) {
1087
+ log("create", {
1088
+ id,
1089
+ instanceId,
1090
+ doc: initialValue?.length ?? 0
1091
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 22, S: void 0 });
1092
+ let initialSelection;
1093
+ if (selection?.anchor && initialValue?.length) {
1094
+ if (selection.anchor <= initialValue.length && (selection?.head ?? 0) <= initialValue.length) {
1095
+ initialSelection = selection;
1096
+ }
1097
+ } else if (selectionEnd && selection === void 0) {
1098
+ const index = initialValue?.indexOf("\n");
1099
+ const anchor = !index || index === -1 ? 0 : index;
1100
+ initialSelection = {
1101
+ anchor
1102
+ };
1103
+ }
1104
+ const state = EditorState.create({
1105
+ doc: doc ?? initialValue,
1106
+ // selection: initialSelection,
1107
+ extensions: [
1108
+ id && documentId.of(id),
1109
+ extensions,
1110
+ // NOTE: This doesn't catch errors in keymap functions.
1111
+ EditorView2.exceptionSink.of((err) => {
1112
+ log.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 48, S: void 0 });
1113
+ })
1114
+ ].filter(isTruthy2)
1115
+ });
1116
+ view2 = new EditorView2({
1117
+ parent: parentRef.current,
1118
+ state,
1119
+ scrollTo: scrollTo ? EditorView2.scrollIntoView(scrollTo, {
1120
+ yMargin: 96
1121
+ }) : void 0,
1122
+ dispatchTransactions: debug ? debugDispatcher : void 0
1123
+ });
1124
+ if (selectionEnd && !initialSelection) {
1125
+ const { to } = view2.state.doc.lineAt(0);
1126
+ if (to) {
1127
+ view2.dispatch({
1128
+ selection: {
1129
+ anchor: to
1130
+ }
1131
+ });
1132
+ }
1133
+ }
1134
+ setView(view2);
1412
1135
  }
1413
- if (features?.onImageUpload) {
1414
- addSubGraph(graph, createImageUpload(features.onImageUpload));
1136
+ return () => {
1137
+ log("destroy", {
1138
+ id
1139
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 75, S: void 0 });
1140
+ view2?.destroy();
1141
+ };
1142
+ }, deps);
1143
+ useEffect2(() => {
1144
+ if (view) {
1145
+ if (scrollTo || selection) {
1146
+ if (selection && selection.anchor > view.state.doc.length) {
1147
+ log.warn("invalid selection", {
1148
+ length: view.state.doc.length,
1149
+ scrollTo,
1150
+ selection
1151
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 85, S: void 0 });
1152
+ return;
1153
+ }
1154
+ view.dispatch(createEditorStateTransaction({
1155
+ scrollTo,
1156
+ selection
1157
+ }));
1158
+ }
1415
1159
  }
1416
- addSubGraph(graph, createGapSeparator());
1417
- if (customActions) {
1418
- addSubGraph(graph, get(customActions));
1160
+ }, [
1161
+ view,
1162
+ scrollTo,
1163
+ selection
1164
+ ]);
1165
+ useEffect2(() => {
1166
+ if (view && autoFocus) {
1167
+ view.focus();
1419
1168
  }
1420
- if (features?.showSearch ?? true) {
1421
- addSubGraph(graph, createSearch(getView));
1169
+ }, [
1170
+ autoFocus,
1171
+ view
1172
+ ]);
1173
+ const handleKeyDown = useCallback3((event) => {
1174
+ const { key, target, currentTarget } = event;
1175
+ switch (key) {
1176
+ case "Escape": {
1177
+ const modal = view?.state.field(modalStateField2, false);
1178
+ if (modal) {
1179
+ return;
1180
+ }
1181
+ const element = view?.contentDOM.closest('[tabindex="0"]');
1182
+ element?.focus();
1183
+ break;
1184
+ }
1185
+ case "Enter": {
1186
+ event.preventDefault();
1187
+ if (target === currentTarget) {
1188
+ view?.focus();
1189
+ }
1190
+ break;
1191
+ }
1422
1192
  }
1423
- if (features?.onViewModeChange) {
1424
- addSubGraph(graph, createViewMode(stateSnapshot, features.onViewModeChange));
1193
+ }, [
1194
+ view
1195
+ ]);
1196
+ return {
1197
+ parentRef,
1198
+ view,
1199
+ focusAttributes: {
1200
+ tabIndex: 0,
1201
+ onKeyDown: handleKeyDown
1425
1202
  }
1426
- return graph;
1427
- });
1203
+ };
1428
1204
  };
1429
1205
 
1430
- // src/components/EditorToolbar/useEditorToolbar.ts
1431
- import { Atom as Atom2 } from "@effect-atom/atom-react";
1432
- import { useMemo as useMemo4 } from "react";
1433
- var useEditorToolbar = (initialState = {}) => {
1434
- return useMemo4(() => Atom2.make(initialState), []);
1206
+ // src/components/Editor/controller.ts
1207
+ var noopController = {
1208
+ get view() {
1209
+ return null;
1210
+ },
1211
+ getText: () => "",
1212
+ setText: () => {
1213
+ },
1214
+ focus: () => {
1215
+ }
1216
+ };
1217
+ var createEditorController = (view) => {
1218
+ return {
1219
+ get view() {
1220
+ return view;
1221
+ },
1222
+ getText: () => {
1223
+ return view?.state.doc.toString() ?? "";
1224
+ },
1225
+ setText: (text, focus) => {
1226
+ view?.dispatch({
1227
+ changes: {
1228
+ from: 0,
1229
+ to: view?.state.doc.length ?? 0,
1230
+ insert: text
1231
+ },
1232
+ selection: {
1233
+ anchor: text.length,
1234
+ head: text.length
1235
+ }
1236
+ });
1237
+ if (focus) {
1238
+ view?.focus();
1239
+ }
1240
+ },
1241
+ focus: () => view?.focus()
1242
+ };
1435
1243
  };
1436
1244
 
1245
+ // src/components/Editor/EditorView.tsx
1246
+ var EditorView3 = /* @__PURE__ */ forwardRef(({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
1247
+ const onChangeRef = useRef4(onChange);
1248
+ onChangeRef.current = onChange;
1249
+ const { parentRef, focusAttributes, view } = useTextEditor(() => ({
1250
+ id,
1251
+ initialValue: value,
1252
+ selectionEnd,
1253
+ extensions: [
1254
+ extensions ?? [],
1255
+ NaturalEditorView.updateListener.of(({ view: view2, docChanged, transactions }) => {
1256
+ const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
1257
+ if (!isInitialSync && docChanged) {
1258
+ onChangeRef.current?.(view2.state.doc.toString());
1259
+ }
1260
+ })
1261
+ ],
1262
+ ...props
1263
+ }), [
1264
+ id,
1265
+ extensions,
1266
+ selectionEnd
1267
+ ]);
1268
+ useImperativeHandle(forwardedRef, () => {
1269
+ return createEditorController(view);
1270
+ }, [
1271
+ id,
1272
+ view
1273
+ ]);
1274
+ useEffect3(() => {
1275
+ if (!view) {
1276
+ return;
1277
+ }
1278
+ const next = value ?? "";
1279
+ if (view.state.doc.toString() === next) {
1280
+ return;
1281
+ }
1282
+ requestAnimationFrame(() => {
1283
+ if (view.state.doc.toString() === next) {
1284
+ return;
1285
+ }
1286
+ view.dispatch({
1287
+ annotations: initialSync,
1288
+ changes: [
1289
+ {
1290
+ from: 0,
1291
+ to: view.state.doc.length,
1292
+ insert: next
1293
+ }
1294
+ ],
1295
+ selection: selectionEnd ? {
1296
+ anchor: next.length
1297
+ } : void 0
1298
+ });
1299
+ if (selectionEnd) {
1300
+ view.focus();
1301
+ }
1302
+ });
1303
+ }, [
1304
+ view,
1305
+ value,
1306
+ selectionEnd
1307
+ ]);
1308
+ return /* @__PURE__ */ React3.createElement("div", {
1309
+ role: "none",
1310
+ className: mx("w-full outline-hidden focus:border-accent-surface focus-within:border-neutral-focus-indicator", classNames),
1311
+ ...focusable ? focusAttributes : {},
1312
+ ref: parentRef
1313
+ });
1314
+ });
1315
+
1437
1316
  // src/components/Editor/Editor.tsx
1438
1317
  var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/Editor/Editor.tsx";
1439
1318
  var [EditorContextProvider, useEditorContext] = createContext("Editor");
1440
- var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
1441
- const state = useEditorToolbar({
1319
+ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
1320
+ const state = useMemo5(() => Atom2.make({
1321
+ viewMode
1322
+ }), [
1442
1323
  viewMode
1443
- });
1444
- const [controller, setController] = useState4();
1445
- useImperativeHandle2(forwardedRef, () => controller ?? noopController, [
1446
- controller
1447
1324
  ]);
1448
1325
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
1449
1326
  const extensions = useMemo5(() => [
@@ -1453,6 +1330,10 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1453
1330
  extension,
1454
1331
  extensionsProp
1455
1332
  ]);
1333
+ const [controller, setController] = useState4(noopController);
1334
+ useImperativeHandle2(forwardedRef, () => controller, [
1335
+ controller
1336
+ ]);
1456
1337
  return /* @__PURE__ */ React4.createElement(EditorContextProvider, {
1457
1338
  controller,
1458
1339
  setController,
@@ -1461,21 +1342,22 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1461
1342
  }, /* @__PURE__ */ React4.createElement(EditorMenuProvider, {
1462
1343
  view: controller?.view,
1463
1344
  groups: groupsRef.current,
1345
+ numItems,
1464
1346
  ...menuProps
1465
1347
  }, children));
1466
1348
  });
1467
1349
  EditorRoot.displayName = "Editor.Root";
1468
- var EDITOR_VIEWPORT_NAME = "Editor.Viewport";
1469
- var EditorViewport = ({ classNames, children }) => {
1350
+ var EDITOR_CONTENT_NAME = "Editor.Content";
1351
+ var EditorContent = ({ classNames, children }) => {
1470
1352
  return /* @__PURE__ */ React4.createElement("div", {
1471
1353
  role: "none",
1472
- className: mx2("grid grid-rows-[min-content_1fr] bs-full overflow-hidden", classNames)
1354
+ className: mx2("grid grid-rows-[min-content_1fr] h-full overflow-hidden", classNames)
1473
1355
  }, children);
1474
1356
  };
1475
- EditorViewport.displayName = EDITOR_VIEWPORT_NAME;
1476
- var EDITOR_CONTENT_NAME = "Editor.Content";
1477
- var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1478
- const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_CONTENT_NAME);
1357
+ EditorContent.displayName = EDITOR_CONTENT_NAME;
1358
+ var EDITOR_VIEW_NAME = "Editor.View";
1359
+ var EditorView4 = ({ extensions: providedExtensions, ...props }) => {
1360
+ const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
1479
1361
  const extensions = useMemo5(() => [
1480
1362
  additionalExtensions,
1481
1363
  providedExtensions
@@ -1483,26 +1365,18 @@ var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1483
1365
  providedExtensions,
1484
1366
  additionalExtensions
1485
1367
  ]);
1486
- return /* @__PURE__ */ React4.createElement(EditorContent, {
1368
+ return /* @__PURE__ */ React4.createElement(EditorView3, {
1487
1369
  ...props,
1488
1370
  extensions,
1489
1371
  ref: setController
1490
1372
  });
1491
1373
  };
1492
- EditorContent2.displayName = EDITOR_CONTENT_NAME;
1374
+ EditorView4.displayName = EDITOR_VIEW_NAME;
1493
1375
  var EDITOR_TOOLBAR_NAME = "Editor.Toolbar";
1494
1376
  var EditorToolbar2 = (props) => {
1495
1377
  const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
1496
1378
  const getView = useCallback4(() => {
1497
- invariant3(controller?.view, void 0, {
1498
- F: __dxlog_file4,
1499
- L: 146,
1500
- S: void 0,
1501
- A: [
1502
- "controller?.view",
1503
- ""
1504
- ]
1505
- });
1379
+ invariant3(controller?.view, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 100, S: void 0, A: ["controller?.view", ""] });
1506
1380
  return controller?.view;
1507
1381
  }, [
1508
1382
  controller
@@ -1516,19 +1390,19 @@ var EditorToolbar2 = (props) => {
1516
1390
  EditorToolbar2.displayName = EDITOR_TOOLBAR_NAME;
1517
1391
  var Editor = {
1518
1392
  Root: EditorRoot,
1519
- Viewport: EditorViewport,
1520
- Content: EditorContent2,
1521
- Toolbar: EditorToolbar2
1393
+ Toolbar: EditorToolbar2,
1394
+ Content: EditorContent,
1395
+ View: EditorView4
1522
1396
  };
1523
1397
 
1524
1398
  // src/components/EditorPreviewProvider/EditorPreviewProvider.tsx
1525
1399
  import { createContext as createContext2 } from "@radix-ui/react-context";
1526
- import React5, { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
1400
+ import React5, { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
1527
1401
  import { addEventListener as addEventListener2 } from "@dxos/async";
1528
1402
  import { DX_ANCHOR_ACTIVATE as DX_ANCHOR_ACTIVATE2, Popover as Popover2 } from "@dxos/react-ui";
1529
1403
  var [EditorPreviewContextProvider, useEditorPreview] = createContext2("PreviewPopover", {});
1530
1404
  var EditorPreviewProvider = ({ children, onLookup }) => {
1531
- const triggerRef = useRef4(null);
1405
+ const triggerRef = useRef5(null);
1532
1406
  const [value, setValue] = useState5({});
1533
1407
  const [open, setOpen] = useState5(false);
1534
1408
  const handleActivate = useCallback5((event) => {
@@ -1582,12 +1456,183 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1582
1456
  ref: setRoot
1583
1457
  }, children)));
1584
1458
  };
1459
+
1460
+ // src/extensions/assistant-extension.tsx
1461
+ import { forEachDiagnostic, linter, setDiagnostics } from "@codemirror/lint";
1462
+ import { ChangeSet } from "@codemirror/state";
1463
+ import { EditorView as EditorView5 } from "@codemirror/view";
1464
+ import { log as log2 } from "@dxos/log";
1465
+ import { safeParseJson, trim } from "@dxos/util";
1466
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/extensions/assistant-extension.tsx";
1467
+ var DEFAULT_INSTRUCTIONS = trim`
1468
+ Proofread the input text below.
1469
+ Identify typos and grammatical errors.
1470
+ Return ONLY a valid JSON array of objects with fields: "original" (string), "replacement" (string), "context" (string, 3-5 words around match).
1471
+ --
1472
+ `;
1473
+ var underline = (color) => {
1474
+ const svg = trim`
1475
+ <svg xmlns="http://www.w3.org/2000/svg" width="6" height="3">
1476
+ <path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width="1"/>
1477
+ </svg>
1478
+ `;
1479
+ return `url('data:image/svg+xml;base64,${btoa(svg)}') !important`;
1480
+ };
1481
+ var assistant = (options) => {
1482
+ const styles = getComputedStyle(document.documentElement);
1483
+ const style = {
1484
+ info: styles.getPropertyValue("--color-green-500").trim(),
1485
+ warning: styles.getPropertyValue("--color-orange-500").trim(),
1486
+ error: styles.getPropertyValue("--color-rose-500").trim()
1487
+ };
1488
+ return [
1489
+ assistantLinter(options),
1490
+ EditorView5.baseTheme({
1491
+ ".cm-lintRange-info": {
1492
+ backgroundImage: underline(style.info)
1493
+ },
1494
+ ".cm-lintRange-warning": {
1495
+ backgroundImage: underline(style.warning)
1496
+ },
1497
+ ".cm-lintRange-error": {
1498
+ backgroundImage: underline(style.error)
1499
+ },
1500
+ ".cm-panels-bottom": {
1501
+ borderTop: "1px solid var(--color-separator) !important"
1502
+ },
1503
+ ".cm-panel-lint .cm-panel": {
1504
+ outline: "none !important"
1505
+ },
1506
+ /** @apply dx-button */
1507
+ ".cm-panel button": {
1508
+ color: "var(--color-base-surface-text) !important"
1509
+ },
1510
+ ".cm-panel.cm-panel-lint ul": {
1511
+ color: "var(--color-base-surface-text) !important",
1512
+ backgroundColor: "var(--color-base-surface) !important",
1513
+ marginRight: "2rem !important"
1514
+ },
1515
+ ".cm-panel.cm-panel-lint ul [aria-selected]": {
1516
+ color: "var(--color-base-surface-text) !important",
1517
+ backgroundColor: "var(--color-base-surface) !important"
1518
+ },
1519
+ ".cm-panel.cm-panel-lint ul li": {
1520
+ display: "grid",
1521
+ gridTemplateColumns: "1fr auto",
1522
+ alignItems: "center"
1523
+ },
1524
+ ".cm-panel.cm-panel-lint ul li .cm-diagnosticText": {
1525
+ paddingRight: "8px !important"
1526
+ },
1527
+ ".cm-panel.cm-panel-lint ul li button.cm-diagnosticAction": {
1528
+ margin: "none !important"
1529
+ },
1530
+ ".cm-diagnostic": {
1531
+ padding: "0px 8px !important",
1532
+ whiteSpace: "pre-wrap !important"
1533
+ },
1534
+ ".cm-diagnostic-info": {
1535
+ border: "none !important"
1536
+ }
1537
+ })
1538
+ ];
1539
+ };
1540
+ var isSuggestion = (value) => typeof value === "object" && value !== null && typeof value.original === "string" && typeof value.replacement === "string" && typeof value.context === "string";
1541
+ var findSuggestionIndex = (content, suggestion) => {
1542
+ const firstIdx = content.indexOf(suggestion.original);
1543
+ if (firstIdx === -1) {
1544
+ return -1;
1545
+ }
1546
+ const secondIdx = content.indexOf(suggestion.original, firstIdx + 1);
1547
+ if (secondIdx === -1) {
1548
+ return firstIdx;
1549
+ }
1550
+ const contextIdx = content.indexOf(suggestion.context);
1551
+ if (contextIdx !== -1) {
1552
+ const contextEnd = contextIdx + suggestion.context.length;
1553
+ if (secondIdx >= contextIdx && secondIdx <= contextEnd) {
1554
+ return secondIdx;
1555
+ }
1556
+ }
1557
+ return firstIdx;
1558
+ };
1559
+ var replaceTextAndDropLintAtRange = (view, from, to, insert) => {
1560
+ const kept = [];
1561
+ forEachDiagnostic(view.state, (diagnostic, diagnosticFrom, diagnosticTo) => {
1562
+ if (diagnosticFrom < to && diagnosticTo > from) {
1563
+ return;
1564
+ }
1565
+ kept.push({
1566
+ ...diagnostic,
1567
+ from: diagnosticFrom,
1568
+ to: diagnosticTo
1569
+ });
1570
+ });
1571
+ const changeSet = ChangeSet.of({
1572
+ from,
1573
+ to,
1574
+ insert
1575
+ }, view.state.doc.length);
1576
+ const next = kept.map((d) => ({
1577
+ ...d,
1578
+ from: changeSet.mapPos(d.from, 1),
1579
+ to: changeSet.mapPos(d.to, -1)
1580
+ }));
1581
+ view.dispatch({
1582
+ changes: {
1583
+ from,
1584
+ to,
1585
+ insert
1586
+ },
1587
+ ...setDiagnostics(view.state, next)
1588
+ });
1589
+ };
1590
+ var assistantLinter = ({ generate, instructions = DEFAULT_INSTRUCTIONS, autoPanel = true, delay = 2e3 }) => linter(async (view) => {
1591
+ try {
1592
+ const content = view.state.doc.toString();
1593
+ const result = await generate({
1594
+ instructions,
1595
+ content
1596
+ });
1597
+ const [match] = result.match(/\[.*\]/s) ?? [];
1598
+ const parsed = match ? safeParseJson(match, []) : [];
1599
+ const suggestions = Array.isArray(parsed) ? parsed.filter(isSuggestion) : [];
1600
+ if (suggestions && suggestions.length > 0) {
1601
+ const diagnostics = [];
1602
+ for (const suggestion of suggestions) {
1603
+ const idx = findSuggestionIndex(content, suggestion);
1604
+ if (idx !== -1) {
1605
+ diagnostics.push({
1606
+ from: idx,
1607
+ to: idx + suggestion.original.length,
1608
+ severity: "info",
1609
+ message: `Suggestion: ${suggestion.replacement}`,
1610
+ actions: [
1611
+ {
1612
+ name: "Apply",
1613
+ apply: (view2, from, to) => {
1614
+ replaceTextAndDropLintAtRange(view2, from, to, suggestion.replacement);
1615
+ }
1616
+ }
1617
+ ]
1618
+ });
1619
+ }
1620
+ }
1621
+ return diagnostics;
1622
+ }
1623
+ } catch (err) {
1624
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 169, S: void 0 });
1625
+ }
1626
+ return [];
1627
+ }, {
1628
+ delay,
1629
+ autoPanel
1630
+ });
1585
1631
  export {
1586
1632
  Editor,
1587
- EditorContent,
1588
1633
  EditorMenuProvider,
1589
1634
  EditorPreviewProvider,
1590
- EditorToolbar,
1635
+ assistant,
1591
1636
  createEditorController,
1592
1637
  createMenuGroup,
1593
1638
  filterMenuGroups,
@@ -1599,10 +1644,10 @@ export {
1599
1644
  popover,
1600
1645
  popoverRangeEffect,
1601
1646
  popoverStateField,
1602
- translations,
1647
+ useBasicMarkdownExtensions,
1648
+ useEditorContext,
1603
1649
  useEditorMenu,
1604
1650
  useEditorPreview,
1605
- useEditorToolbar,
1606
1651
  useTextEditor
1607
1652
  };
1608
1653
  //# sourceMappingURL=index.mjs.map