@dxos/react-ui-editor 0.8.4-main.c85a9c8dae → 0.8.4-main.d05539e30a

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