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

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 (143) hide show
  1. package/dist/lib/browser/index.mjs +754 -728
  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 +754 -728
  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 -19
  29. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -19
  31. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -19
  33. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -9
  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 -9
  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 -20
  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 +24 -24
  62. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  63. package/dist/types/src/stories/Comments.stories.d.ts +1 -1
  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 +2 -2
  68. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
  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 +1 -1
  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 +1 -1
  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 +2 -2
  82. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  83. package/dist/types/src/stories/components/util.d.ts +2 -1
  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 +1 -1
  88. package/dist/types/src/util/react.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +56 -47
  91. package/src/components/Editor/Editor.stories.tsx +15 -21
  92. package/src/components/Editor/Editor.tsx +53 -48
  93. package/src/components/Editor/EditorView.tsx +103 -0
  94. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +2 -4
  95. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  96. package/src/components/EditorMenuProvider/popover.ts +2 -1
  97. package/src/components/EditorMenuProvider/useEditorMenu.ts +2 -1
  98. package/src/components/EditorToolbar/EditorToolbar.tsx +29 -48
  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 +8 -8
  116. package/src/stories/Comments.stories.tsx +2 -2
  117. package/src/stories/EditorToolbar.stories.tsx +36 -64
  118. package/src/stories/Experimental.stories.tsx +10 -10
  119. package/src/stories/Outliner.stories.tsx +3 -4
  120. package/src/stories/Popover.stories.tsx +6 -7
  121. package/src/stories/Preview.stories.tsx +6 -7
  122. package/src/stories/Theme.stories.tsx +2 -2
  123. package/src/stories/components/EditorStory.tsx +17 -8
  124. package/src/stories/components/util.tsx +37 -35
  125. package/src/translations.ts +29 -24
  126. package/src/util/react.tsx +1 -1
  127. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  128. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  129. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  130. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  131. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -25
  132. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  133. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  134. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  135. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  136. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  137. package/src/components/EditorContent/EditorContent.tsx +0 -83
  138. package/src/components/EditorContent/index.ts +0 -6
  139. package/src/components/EditorToolbar/actions.ts +0 -87
  140. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  141. package/src/stories/CommandDialog.stories.tsx +0 -81
  142. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  143. /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,15 @@ 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`,
360
64
  ...options.placeholder
361
65
  })
362
- ].filter(isTruthy2);
66
+ ].filter(isTruthy);
363
67
  };
364
- var popoverTriggerListener = (options) => EditorView3.updateListener.of(({ view, docChanged }) => {
68
+ var popoverTriggerListener = (options) => EditorView.updateListener.of(({ view, docChanged }) => {
365
69
  const { range: activeRange, trigger } = view.state.field(popoverStateField) ?? {};
366
70
  if (!activeRange) {
367
71
  return;
@@ -506,7 +210,7 @@ var popoverKeymap = (options) => {
506
210
  return false;
507
211
  }
508
212
  }
509
- ].filter(isTruthy2));
213
+ ].filter(isTruthy));
510
214
  };
511
215
  var popoverAnchorDecoration = (options) => {
512
216
  return ViewPlugin.fromClass(class {
@@ -701,36 +405,28 @@ var linkSlashCommands = {
701
405
 
702
406
  // src/components/EditorMenuProvider/EditorMenuProvider.tsx
703
407
  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";
408
+ import React, { Fragment, useCallback, useEffect, useRef, useState } from "react";
705
409
  import { addEventListener } from "@dxos/async";
706
410
  import { invariant } from "@dxos/invariant";
707
411
  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";
412
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
709
413
  var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp, defaultOpen, numItems = 8, onOpenChange, onActivate, onSelect, onCancel }) => {
710
414
  const { tx } = useThemeContext();
711
- const triggerRef = useRef2(null);
415
+ const triggerRef = useRef(null);
712
416
  const viewRef = useDynamicRef(view);
713
417
  const [open, setOpen] = useControllableState({
714
418
  prop: openProp,
715
419
  defaultProp: defaultOpen,
716
420
  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
- });
421
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 21, S: void 0, A: ["viewRef.current", ""] });
726
422
  onOpenChange?.({
727
423
  view: viewRef.current,
728
424
  open: open2
729
425
  });
730
426
  }
731
427
  });
732
- const [root, setRoot] = useState2(null);
733
- useEffect3(() => {
428
+ const [root, setRoot] = useState(null);
429
+ useEffect(() => {
734
430
  if (!root) {
735
431
  return;
736
432
  }
@@ -755,16 +451,8 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
755
451
  root,
756
452
  onActivate
757
453
  ]);
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
- });
454
+ const handleSelect = useCallback((item) => {
455
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: void 0, A: ["viewRef.current", ""] });
768
456
  onSelect?.({
769
457
  view: viewRef.current,
770
458
  item
@@ -774,13 +462,13 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
774
462
  onSelect
775
463
  ]);
776
464
  const menuGroups = groups?.filter((group) => group.items.length > 0) ?? [];
777
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
465
+ return /* @__PURE__ */ React.createElement(Popover.Root, {
778
466
  modal: false,
779
467
  open,
780
468
  onOpenChange: setOpen
781
- }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
469
+ }, /* @__PURE__ */ React.createElement(Popover.VirtualTrigger, {
782
470
  virtualRef: triggerRef
783
- }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
471
+ }), /* @__PURE__ */ React.createElement(Popover.Portal, null, /* @__PURE__ */ React.createElement(Popover.Content, {
784
472
  align: "start",
785
473
  classNames: [
786
474
  "flex flex-col",
@@ -799,16 +487,16 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
799
487
  }
800
488
  },
801
489
  onOpenAutoFocus: (event) => event.preventDefault()
802
- }, /* @__PURE__ */ React2.createElement(Popover.Viewport, {
490
+ }, /* @__PURE__ */ React.createElement(Popover.Viewport, {
803
491
  asChild: true,
804
492
  classNames: "dx-container"
805
- }, /* @__PURE__ */ React2.createElement(ScrollArea.Root, {
493
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Root, {
806
494
  thin: true
807
- }, /* @__PURE__ */ React2.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React2.createElement(Menu, {
495
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React.createElement(Menu, {
808
496
  groups: menuGroups,
809
497
  currentItem,
810
498
  onSelect: handleSelect
811
- })))), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))), /* @__PURE__ */ React2.createElement("div", {
499
+ })))), /* @__PURE__ */ React.createElement(Popover.Arrow, null))), /* @__PURE__ */ React.createElement("div", {
812
500
  role: "none",
813
501
  className: "contents",
814
502
  ref: setRoot
@@ -816,22 +504,22 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
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,301 @@ 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
+ role: "none",
1309
+ className: mx("w-full outline-hidden focus:border-accent-surface focus-within:border-neutral-focus-indicator", classNames),
1310
+ ...focusable ? focusAttributes : {},
1311
+ ref: parentRef
1312
+ });
1313
+ });
1314
+
1455
1315
  // src/components/Editor/Editor.tsx
1456
1316
  var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/Editor/Editor.tsx";
1457
1317
  var [EditorContextProvider, useEditorContext] = createContext("Editor");
1458
- var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
1459
- const state = useEditorToolbar({
1318
+ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
1319
+ const state = useMemo5(() => Atom2.make({
1320
+ viewMode
1321
+ }), [
1460
1322
  viewMode
1461
- });
1462
- const [controller, setController] = useState4();
1463
- useImperativeHandle2(forwardedRef, () => controller ?? noopController, [
1464
- controller
1465
1323
  ]);
1466
1324
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
1467
1325
  const extensions = useMemo5(() => [
@@ -1471,6 +1329,10 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1471
1329
  extension,
1472
1330
  extensionsProp
1473
1331
  ]);
1332
+ const [controller, setController] = useState4(noopController);
1333
+ useImperativeHandle2(forwardedRef, () => controller, [
1334
+ controller
1335
+ ]);
1474
1336
  return /* @__PURE__ */ React4.createElement(EditorContextProvider, {
1475
1337
  controller,
1476
1338
  setController,
@@ -1479,21 +1341,22 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1479
1341
  }, /* @__PURE__ */ React4.createElement(EditorMenuProvider, {
1480
1342
  view: controller?.view,
1481
1343
  groups: groupsRef.current,
1344
+ numItems,
1482
1345
  ...menuProps
1483
1346
  }, children));
1484
1347
  });
1485
1348
  EditorRoot.displayName = "Editor.Root";
1486
- var EDITOR_VIEWPORT_NAME = "Editor.Viewport";
1487
- var EditorViewport = ({ classNames, children }) => {
1349
+ var EDITOR_CONTENT_NAME = "Editor.Content";
1350
+ var EditorContent = ({ classNames, children }) => {
1488
1351
  return /* @__PURE__ */ React4.createElement("div", {
1489
1352
  role: "none",
1490
1353
  className: mx2("grid grid-rows-[min-content_1fr] h-full overflow-hidden", classNames)
1491
1354
  }, children);
1492
1355
  };
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);
1356
+ EditorContent.displayName = EDITOR_CONTENT_NAME;
1357
+ var EDITOR_VIEW_NAME = "Editor.View";
1358
+ var EditorView4 = ({ extensions: providedExtensions, ...props }) => {
1359
+ const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
1497
1360
  const extensions = useMemo5(() => [
1498
1361
  additionalExtensions,
1499
1362
  providedExtensions
@@ -1501,26 +1364,18 @@ var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1501
1364
  providedExtensions,
1502
1365
  additionalExtensions
1503
1366
  ]);
1504
- return /* @__PURE__ */ React4.createElement(EditorContent, {
1367
+ return /* @__PURE__ */ React4.createElement(EditorView3, {
1505
1368
  ...props,
1506
1369
  extensions,
1507
1370
  ref: setController
1508
1371
  });
1509
1372
  };
1510
- EditorContent2.displayName = EDITOR_CONTENT_NAME;
1373
+ EditorView4.displayName = EDITOR_VIEW_NAME;
1511
1374
  var EDITOR_TOOLBAR_NAME = "Editor.Toolbar";
1512
1375
  var EditorToolbar2 = (props) => {
1513
1376
  const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
1514
1377
  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
- });
1378
+ invariant3(controller?.view, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 100, S: void 0, A: ["controller?.view", ""] });
1524
1379
  return controller?.view;
1525
1380
  }, [
1526
1381
  controller
@@ -1534,19 +1389,19 @@ var EditorToolbar2 = (props) => {
1534
1389
  EditorToolbar2.displayName = EDITOR_TOOLBAR_NAME;
1535
1390
  var Editor = {
1536
1391
  Root: EditorRoot,
1537
- Viewport: EditorViewport,
1538
- Content: EditorContent2,
1539
- Toolbar: EditorToolbar2
1392
+ Toolbar: EditorToolbar2,
1393
+ Content: EditorContent,
1394
+ View: EditorView4
1540
1395
  };
1541
1396
 
1542
1397
  // src/components/EditorPreviewProvider/EditorPreviewProvider.tsx
1543
1398
  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";
1399
+ import React5, { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
1545
1400
  import { addEventListener as addEventListener2 } from "@dxos/async";
1546
1401
  import { DX_ANCHOR_ACTIVATE as DX_ANCHOR_ACTIVATE2, Popover as Popover2 } from "@dxos/react-ui";
1547
1402
  var [EditorPreviewContextProvider, useEditorPreview] = createContext2("PreviewPopover", {});
1548
1403
  var EditorPreviewProvider = ({ children, onLookup }) => {
1549
- const triggerRef = useRef4(null);
1404
+ const triggerRef = useRef5(null);
1550
1405
  const [value, setValue] = useState5({});
1551
1406
  const [open, setOpen] = useState5(false);
1552
1407
  const handleActivate = useCallback5((event) => {
@@ -1600,12 +1455,183 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1600
1455
  ref: setRoot
1601
1456
  }, children)));
1602
1457
  };
1458
+
1459
+ // src/extensions/assistant-extension.tsx
1460
+ import { forEachDiagnostic, linter, setDiagnostics } from "@codemirror/lint";
1461
+ import { ChangeSet } from "@codemirror/state";
1462
+ import { EditorView as EditorView5 } from "@codemirror/view";
1463
+ import { log as log2 } from "@dxos/log";
1464
+ import { safeParseJson, trim } from "@dxos/util";
1465
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/extensions/assistant-extension.tsx";
1466
+ var DEFAULT_INSTRUCTIONS = trim`
1467
+ Proofread the input text below.
1468
+ Identify typos and grammatical errors.
1469
+ Return ONLY a valid JSON array of objects with fields: "original" (string), "replacement" (string), "context" (string, 3-5 words around match).
1470
+ --
1471
+ `;
1472
+ var underline = (color) => {
1473
+ const svg = trim`
1474
+ <svg xmlns="http://www.w3.org/2000/svg" width="6" height="3">
1475
+ <path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width="1"/>
1476
+ </svg>
1477
+ `;
1478
+ return `url('data:image/svg+xml;base64,${btoa(svg)}') !important`;
1479
+ };
1480
+ var assistant = (options) => {
1481
+ const styles = getComputedStyle(document.documentElement);
1482
+ const style = {
1483
+ info: styles.getPropertyValue("--color-green-500").trim(),
1484
+ warning: styles.getPropertyValue("--color-orange-500").trim(),
1485
+ error: styles.getPropertyValue("--color-rose-500").trim()
1486
+ };
1487
+ return [
1488
+ assistantLinter(options),
1489
+ EditorView5.baseTheme({
1490
+ ".cm-lintRange-info": {
1491
+ backgroundImage: underline(style.info)
1492
+ },
1493
+ ".cm-lintRange-warning": {
1494
+ backgroundImage: underline(style.warning)
1495
+ },
1496
+ ".cm-lintRange-error": {
1497
+ backgroundImage: underline(style.error)
1498
+ },
1499
+ ".cm-panels-bottom": {
1500
+ borderTop: "1px solid var(--color-separator) !important"
1501
+ },
1502
+ ".cm-panel-lint .cm-panel": {
1503
+ outline: "none !important"
1504
+ },
1505
+ /** @apply dx-button */
1506
+ ".cm-panel button": {
1507
+ color: "var(--color-base-surface-text) !important"
1508
+ },
1509
+ ".cm-panel.cm-panel-lint ul": {
1510
+ color: "var(--color-base-surface-text) !important",
1511
+ backgroundColor: "var(--color-base-surface) !important",
1512
+ marginRight: "2rem !important"
1513
+ },
1514
+ ".cm-panel.cm-panel-lint ul [aria-selected]": {
1515
+ color: "var(--color-base-surface-text) !important",
1516
+ backgroundColor: "var(--color-base-surface) !important"
1517
+ },
1518
+ ".cm-panel.cm-panel-lint ul li": {
1519
+ display: "grid",
1520
+ gridTemplateColumns: "1fr auto",
1521
+ alignItems: "center"
1522
+ },
1523
+ ".cm-panel.cm-panel-lint ul li .cm-diagnosticText": {
1524
+ paddingRight: "8px !important"
1525
+ },
1526
+ ".cm-panel.cm-panel-lint ul li button.cm-diagnosticAction": {
1527
+ margin: "none !important"
1528
+ },
1529
+ ".cm-diagnostic": {
1530
+ padding: "0px 8px !important",
1531
+ whiteSpace: "pre-wrap !important"
1532
+ },
1533
+ ".cm-diagnostic-info": {
1534
+ border: "none !important"
1535
+ }
1536
+ })
1537
+ ];
1538
+ };
1539
+ var isSuggestion = (value) => typeof value === "object" && value !== null && typeof value.original === "string" && typeof value.replacement === "string" && typeof value.context === "string";
1540
+ var findSuggestionIndex = (content, suggestion) => {
1541
+ const firstIdx = content.indexOf(suggestion.original);
1542
+ if (firstIdx === -1) {
1543
+ return -1;
1544
+ }
1545
+ const secondIdx = content.indexOf(suggestion.original, firstIdx + 1);
1546
+ if (secondIdx === -1) {
1547
+ return firstIdx;
1548
+ }
1549
+ const contextIdx = content.indexOf(suggestion.context);
1550
+ if (contextIdx !== -1) {
1551
+ const contextEnd = contextIdx + suggestion.context.length;
1552
+ if (secondIdx >= contextIdx && secondIdx <= contextEnd) {
1553
+ return secondIdx;
1554
+ }
1555
+ }
1556
+ return firstIdx;
1557
+ };
1558
+ var replaceTextAndDropLintAtRange = (view, from, to, insert) => {
1559
+ const kept = [];
1560
+ forEachDiagnostic(view.state, (diagnostic, diagnosticFrom, diagnosticTo) => {
1561
+ if (diagnosticFrom < to && diagnosticTo > from) {
1562
+ return;
1563
+ }
1564
+ kept.push({
1565
+ ...diagnostic,
1566
+ from: diagnosticFrom,
1567
+ to: diagnosticTo
1568
+ });
1569
+ });
1570
+ const changeSet = ChangeSet.of({
1571
+ from,
1572
+ to,
1573
+ insert
1574
+ }, view.state.doc.length);
1575
+ const next = kept.map((d) => ({
1576
+ ...d,
1577
+ from: changeSet.mapPos(d.from, 1),
1578
+ to: changeSet.mapPos(d.to, -1)
1579
+ }));
1580
+ view.dispatch({
1581
+ changes: {
1582
+ from,
1583
+ to,
1584
+ insert
1585
+ },
1586
+ ...setDiagnostics(view.state, next)
1587
+ });
1588
+ };
1589
+ var assistantLinter = ({ generate, instructions = DEFAULT_INSTRUCTIONS, autoPanel = true, delay = 2e3 }) => linter(async (view) => {
1590
+ try {
1591
+ const content = view.state.doc.toString();
1592
+ const result = await generate({
1593
+ instructions,
1594
+ content
1595
+ });
1596
+ const [match] = result.match(/\[.*\]/s) ?? [];
1597
+ const parsed = match ? safeParseJson(match, []) : [];
1598
+ const suggestions = Array.isArray(parsed) ? parsed.filter(isSuggestion) : [];
1599
+ if (suggestions && suggestions.length > 0) {
1600
+ const diagnostics = [];
1601
+ for (const suggestion of suggestions) {
1602
+ const idx = findSuggestionIndex(content, suggestion);
1603
+ if (idx !== -1) {
1604
+ diagnostics.push({
1605
+ from: idx,
1606
+ to: idx + suggestion.original.length,
1607
+ severity: "info",
1608
+ message: `Suggestion: ${suggestion.replacement}`,
1609
+ actions: [
1610
+ {
1611
+ name: "Apply",
1612
+ apply: (view2, from, to) => {
1613
+ replaceTextAndDropLintAtRange(view2, from, to, suggestion.replacement);
1614
+ }
1615
+ }
1616
+ ]
1617
+ });
1618
+ }
1619
+ }
1620
+ return diagnostics;
1621
+ }
1622
+ } catch (err) {
1623
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 169, S: void 0 });
1624
+ }
1625
+ return [];
1626
+ }, {
1627
+ delay,
1628
+ autoPanel
1629
+ });
1603
1630
  export {
1604
1631
  Editor,
1605
- EditorContent,
1606
1632
  EditorMenuProvider,
1607
1633
  EditorPreviewProvider,
1608
- EditorToolbar,
1634
+ assistant,
1609
1635
  createEditorController,
1610
1636
  createMenuGroup,
1611
1637
  filterMenuGroups,
@@ -1617,10 +1643,10 @@ export {
1617
1643
  popover,
1618
1644
  popoverRangeEffect,
1619
1645
  popoverStateField,
1620
- translations,
1646
+ useBasicMarkdownExtensions,
1647
+ useEditorContext,
1621
1648
  useEditorMenu,
1622
1649
  useEditorPreview,
1623
- useEditorToolbar,
1624
1650
  useTextEditor
1625
1651
  };
1626
1652
  //# sourceMappingURL=index.mjs.map