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