@dxos/react-ui-editor 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef

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 (124) hide show
  1. package/dist/lib/browser/index.mjs +548 -423
  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 +548 -423
  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 +30 -13
  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.map +1 -1
  19. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
  21. package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
  25. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/blocks.d.ts +1 -1
  27. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/formatting.d.ts +1 -1
  29. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/headings.d.ts +1 -1
  31. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  33. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
  34. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  35. package/dist/types/src/components/EditorToolbar/lists.d.ts +1 -1
  36. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  37. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  38. package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
  39. package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
  40. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +2 -2
  41. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  42. package/dist/types/src/components/index.d.ts +0 -2
  43. package/dist/types/src/components/index.d.ts.map +1 -1
  44. package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
  45. package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
  46. package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
  47. package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
  48. package/dist/types/src/extensions/index.d.ts +2 -0
  49. package/dist/types/src/extensions/index.d.ts.map +1 -0
  50. package/dist/types/src/hooks/index.d.ts +1 -0
  51. package/dist/types/src/hooks/index.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
  53. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
  54. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  55. package/dist/types/src/index.d.ts +1 -2
  56. package/dist/types/src/index.d.ts.map +1 -1
  57. package/dist/types/src/stories/Automerge.stories.d.ts +24 -24
  58. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  59. package/dist/types/src/stories/Comments.stories.d.ts +1 -1
  60. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  61. package/dist/types/src/stories/EditorToolbar.stories.d.ts +27 -25
  62. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  63. package/dist/types/src/stories/Experimental.stories.d.ts +2 -2
  64. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  65. package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
  66. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  68. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Preview.stories.d.ts +1 -1
  70. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  72. package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
  73. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  74. package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
  75. package/dist/types/src/stories/components/EditorStory.d.ts +1 -1
  76. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  77. package/dist/types/src/stories/components/util.d.ts +2 -1
  78. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  79. package/dist/types/src/translations.d.ts +24 -24
  80. package/dist/types/src/translations.d.ts.map +1 -1
  81. package/dist/types/src/util/react.d.ts +1 -1
  82. package/dist/types/src/util/react.d.ts.map +1 -1
  83. package/dist/types/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +50 -41
  85. package/src/components/Editor/Editor.stories.tsx +10 -15
  86. package/src/components/Editor/Editor.tsx +51 -34
  87. package/src/components/Editor/EditorView.tsx +103 -0
  88. package/src/components/EditorMenuProvider/popover.ts +2 -1
  89. package/src/components/EditorToolbar/EditorToolbar.tsx +9 -7
  90. package/src/components/EditorToolbar/blocks.ts +3 -2
  91. package/src/components/EditorToolbar/formatting.ts +3 -2
  92. package/src/components/EditorToolbar/headings.ts +3 -2
  93. package/src/components/EditorToolbar/image.ts +1 -1
  94. package/src/components/EditorToolbar/index.ts +2 -2
  95. package/src/components/EditorToolbar/lists.ts +3 -2
  96. package/src/components/EditorToolbar/search.ts +1 -1
  97. package/src/components/EditorToolbar/types.ts +8 -0
  98. package/src/components/EditorToolbar/view-mode.ts +4 -3
  99. package/src/components/index.ts +0 -3
  100. package/src/extensions/Assistant.stories.tsx +112 -0
  101. package/src/extensions/assistant-extension.tsx +223 -0
  102. package/src/extensions/index.ts +5 -0
  103. package/src/hooks/index.ts +1 -0
  104. package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
  105. package/src/index.ts +1 -4
  106. package/src/stories/Automerge.stories.tsx +2 -1
  107. package/src/stories/Comments.stories.tsx +2 -1
  108. package/src/stories/EditorToolbar.stories.tsx +35 -78
  109. package/src/stories/Experimental.stories.tsx +7 -7
  110. package/src/stories/Theme.stories.tsx +2 -2
  111. package/src/stories/components/EditorStory.tsx +9 -7
  112. package/src/stories/components/util.tsx +1 -1
  113. package/src/util/react.tsx +1 -1
  114. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  115. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  116. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  117. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  118. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  119. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  120. package/src/components/EditorContent/EditorContent.tsx +0 -82
  121. package/src/components/EditorContent/index.ts +0 -6
  122. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  123. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  124. /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
- "comment.label": "Create comment",
10
- "image.label": "Insert image",
11
- "search.label": "Search",
12
- "block.label": "Block",
13
- "block.blockquote.label": "Block quote",
14
- "block.codeblock.label": "Code block",
15
- "block.table.label": "Create table",
16
- "formatting.label": "Formatting",
17
- "formatting.strong.label": "Bold",
18
- "formatting.emphasis.label": "Italics",
19
- "formatting.strikethrough.label": "Strikethrough",
20
- "formatting.code.label": "Code",
21
- "formatting.link.label": "Link",
22
- "list.bullet.label": "Bullet list",
23
- "list.ordered.label": "Numbered list",
24
- "list.task.label": "Task list",
25
- "heading.label": "Heading level",
26
- "heading-level.label_zero": "Paragraph",
27
- "heading-level.label_one": "Heading level {{count}}",
28
- "heading-level.label_other": "Heading level {{count}}",
29
- "view-mode.label": "Editor view",
30
- "view-mode.preview.label": "Markdown",
31
- "view-mode.source.label": "Plain text",
32
- "view-mode.readonly.label": "Read only"
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: 64,
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: 99,
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,35 +540,35 @@ 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, {
553
+ }, item.icon && /* @__PURE__ */ React.createElement(Icon, {
866
554
  icon: item.icon
867
- }), /* @__PURE__ */ React2.createElement("span", {
555
+ }), /* @__PURE__ */ React.createElement("span", {
868
556
  className: "grow truncate"
869
557
  }, toLocalizedString(item.label, t)));
870
558
  };
871
559
 
872
560
  // src/components/EditorMenuProvider/useEditorMenu.ts
873
- 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";
874
562
  import { invariant as invariant2 } from "@dxos/invariant";
875
563
  import { modalStateEffect } from "@dxos/ui-editor";
876
- 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";
877
565
  var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter = true, getMenu }) => {
878
- const groupsRef = useRef3([]);
879
- const currentRef = useRef3(null);
880
- const [currentItem, setCurrentItem] = useState3();
881
- const [open, setOpen] = useState3(false);
882
- const [_, refresh] = useState3({});
883
- 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 }) => {
884
572
  const groups = await getMenu?.({
885
573
  text,
886
574
  trigger: trigger2,
@@ -891,16 +579,8 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
891
579
  getMenu,
892
580
  filter
893
581
  ]);
894
- const handleOpenChange = useCallback3(async ({ view, open: open2 }) => {
895
- invariant2(view, void 0, {
896
- F: __dxlog_file3,
897
- L: 77,
898
- S: void 0,
899
- A: [
900
- "view",
901
- ""
902
- ]
903
- });
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", ""] });
904
584
  setOpen(open2);
905
585
  if (!open2) {
906
586
  setCurrentItem(void 0);
@@ -920,7 +600,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
920
600
  }, [
921
601
  getMenuOptions
922
602
  ]);
923
- const handleActivate = useCallback3(async ({ view, trigger: trigger2 }) => {
603
+ const handleActivate = useCallback2(async ({ view, trigger: trigger2 }) => {
924
604
  const item = getMenuItem(groupsRef.current, currentItem);
925
605
  if (item) {
926
606
  currentRef.current = item;
@@ -936,7 +616,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
936
616
  open,
937
617
  handleOpenChange
938
618
  ]);
939
- const handleSelect = useCallback3(({ view, item }) => {
619
+ const handleSelect = useCallback2(({ view, item }) => {
940
620
  const { range } = view.state.field(popoverStateField) ?? {};
941
621
  if (range) {
942
622
  view.dispatch({
@@ -953,7 +633,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
953
633
  });
954
634
  view.focus();
955
635
  }, []);
956
- const handleCancel = useCallback3(({ view }) => {
636
+ const handleCancel = useCallback2(({ view }) => {
957
637
  const { range, trigger: trigger2 } = view.state.field(popoverStateField) ?? {};
958
638
  if (range && trigger2) {
959
639
  view.dispatch({
@@ -965,7 +645,7 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
965
645
  }
966
646
  }, []);
967
647
  const serializedTrigger = Array.isArray(trigger) ? trigger.join(",") : trigger;
968
- const extension = useMemo2(() => {
648
+ const extension = useMemo(() => {
969
649
  return popover({
970
650
  trigger,
971
651
  triggerKey,
@@ -1031,12 +711,13 @@ var useEditorMenu = ({ trigger, triggerKey, placeholder: placeholder2, filter =
1031
711
 
1032
712
  // src/components/EditorToolbar/EditorToolbar.tsx
1033
713
  import { Atom } from "@effect-atom/atom-react";
1034
- import React3, { memo, useMemo as useMemo3 } from "react";
714
+ import React2, { memo, useMemo as useMemo2 } from "react";
1035
715
  import { ElevationProvider } from "@dxos/react-ui";
1036
716
  import { Menu as Menu2, MenuBuilder, useMenuActions } from "@dxos/react-ui-menu";
1037
717
 
1038
718
  // src/components/EditorToolbar/blocks.ts
1039
719
  import { addBlockquote, addCodeblock, insertTable, removeBlockquote, removeCodeblock } from "@dxos/ui-editor";
720
+ import { translationKey } from "#translations";
1040
721
  var blockTypes = {
1041
722
  blockquote: "ph--quotes--regular",
1042
723
  codeblock: "ph--code-block--regular",
@@ -1093,6 +774,7 @@ var addBlocks = (state, getView) => (builder) => {
1093
774
 
1094
775
  // src/components/EditorToolbar/formatting.ts
1095
776
  import { Inline, addLink, removeLink, setStyle } from "@dxos/ui-editor";
777
+ import { translationKey as translationKey2 } from "#translations";
1096
778
  var formats = {
1097
779
  strong: "ph--text-b--regular",
1098
780
  emphasis: "ph--text-italic--regular",
@@ -1106,7 +788,7 @@ var addFormatting = (state, getView) => (builder) => {
1106
788
  label: [
1107
789
  "formatting.label",
1108
790
  {
1109
- ns: translationKey
791
+ ns: translationKey2
1110
792
  }
1111
793
  ],
1112
794
  iconOnly: true,
@@ -1120,7 +802,7 @@ var addFormatting = (state, getView) => (builder) => {
1120
802
  label: [
1121
803
  `formatting.${type}.label`,
1122
804
  {
1123
- ns: translationKey
805
+ ns: translationKey2
1124
806
  }
1125
807
  ],
1126
808
  checked,
@@ -1142,6 +824,7 @@ var addFormatting = (state, getView) => (builder) => {
1142
824
 
1143
825
  // src/components/EditorToolbar/headings.ts
1144
826
  import { setHeading } from "@dxos/ui-editor";
827
+ import { translationKey as translationKey3 } from "#translations";
1145
828
  var headingIcons = {
1146
829
  0: "ph--paragraph--regular",
1147
830
  1: "ph--text-h-one--regular",
@@ -1162,7 +845,7 @@ var addHeadings = (state, getView) => (builder) => {
1162
845
  label: [
1163
846
  "heading.label",
1164
847
  {
1165
- ns: translationKey
848
+ ns: translationKey3
1166
849
  }
1167
850
  ],
1168
851
  icon: "ph--text-h--regular",
@@ -1180,7 +863,7 @@ var addHeadings = (state, getView) => (builder) => {
1180
863
  "heading-level.label",
1181
864
  {
1182
865
  count: level,
1183
- ns: translationKey
866
+ ns: translationKey3
1184
867
  }
1185
868
  ],
1186
869
  icon,
@@ -1191,12 +874,13 @@ var addHeadings = (state, getView) => (builder) => {
1191
874
  };
1192
875
 
1193
876
  // src/components/EditorToolbar/image.ts
877
+ import { translationKey as translationKey4 } from "#translations";
1194
878
  var addImageUpload = (onImageUpload) => (builder) => {
1195
879
  builder.action("image", {
1196
880
  label: [
1197
881
  "image.label",
1198
882
  {
1199
- ns: translationKey
883
+ ns: translationKey4
1200
884
  }
1201
885
  ],
1202
886
  testId: "editor.toolbar.image",
@@ -1206,6 +890,7 @@ var addImageUpload = (onImageUpload) => (builder) => {
1206
890
 
1207
891
  // src/components/EditorToolbar/lists.ts
1208
892
  import { List, addList, removeList } from "@dxos/ui-editor";
893
+ import { translationKey as translationKey5 } from "#translations";
1209
894
  var listStyles = {
1210
895
  bullet: "ph--list-bullets--regular",
1211
896
  ordered: "ph--list-numbers--regular",
@@ -1217,7 +902,7 @@ var addLists = (state, getView) => (builder) => {
1217
902
  label: [
1218
903
  "list.label",
1219
904
  {
1220
- ns: translationKey
905
+ ns: translationKey5
1221
906
  }
1222
907
  ],
1223
908
  iconOnly: true,
@@ -1231,7 +916,7 @@ var addLists = (state, getView) => (builder) => {
1231
916
  label: [
1232
917
  `list.${listStyle}.label`,
1233
918
  {
1234
- ns: translationKey
919
+ ns: translationKey5
1235
920
  }
1236
921
  ],
1237
922
  checked,
@@ -1254,12 +939,13 @@ var addLists = (state, getView) => (builder) => {
1254
939
 
1255
940
  // src/components/EditorToolbar/search.ts
1256
941
  import { openSearchPanel } from "@codemirror/search";
942
+ import { translationKey as translationKey6 } from "#translations";
1257
943
  var addSearch = (getView) => (builder) => {
1258
944
  builder.action("search", {
1259
945
  label: [
1260
946
  "search.label",
1261
947
  {
1262
- ns: translationKey
948
+ ns: translationKey6
1263
949
  }
1264
950
  ],
1265
951
  testId: "editor.toolbar.search",
@@ -1268,6 +954,7 @@ var addSearch = (getView) => (builder) => {
1268
954
  };
1269
955
 
1270
956
  // src/components/EditorToolbar/view-mode.ts
957
+ import { translationKey as translationKey7 } from "#translations";
1271
958
  var viewModes = {
1272
959
  preview: "ph--eye--regular",
1273
960
  source: "ph--pencil-simple--regular",
@@ -1279,7 +966,7 @@ var addViewMode = (state, onViewModeChange) => (builder) => {
1279
966
  label: [
1280
967
  "view-mode.label",
1281
968
  {
1282
- ns: translationKey
969
+ ns: translationKey7
1283
970
  }
1284
971
  ],
1285
972
  icon: "ph--eye--regular",
@@ -1295,7 +982,7 @@ var addViewMode = (state, onViewModeChange) => (builder) => {
1295
982
  label: [
1296
983
  `view-mode.${viewMode}.label`,
1297
984
  {
1298
- ns: translationKey
985
+ ns: translationKey7
1299
986
  }
1300
987
  ],
1301
988
  checked,
@@ -1307,19 +994,19 @@ var addViewMode = (state, onViewModeChange) => (builder) => {
1307
994
 
1308
995
  // src/components/EditorToolbar/EditorToolbar.tsx
1309
996
  var EditorToolbar = /* @__PURE__ */ memo(({ classNames, role, attendableId, onAction, ...props }) => {
1310
- const menuActions = useEditorToolbarActionGraph(props);
1311
- return /* @__PURE__ */ React3.createElement(ElevationProvider, {
997
+ const menuActions = useMarkdownMenuActions(props);
998
+ return /* @__PURE__ */ React2.createElement(ElevationProvider, {
1312
999
  elevation: role === "section" ? "positioned" : "base"
1313
- }, /* @__PURE__ */ React3.createElement(Menu2.Root, {
1000
+ }, /* @__PURE__ */ React2.createElement(Menu2.Root, {
1314
1001
  ...menuActions,
1315
1002
  attendableId,
1316
1003
  onAction
1317
- }, /* @__PURE__ */ React3.createElement(Menu2.Toolbar, {
1004
+ }, /* @__PURE__ */ React2.createElement(Menu2.Toolbar, {
1318
1005
  classNames
1319
1006
  })));
1320
1007
  });
1321
- var useEditorToolbarActionGraph = ({ state, getView, customActions, ...features }) => {
1322
- const menuCreator = useMemo3(() => createToolbarActions({
1008
+ var useMarkdownMenuActions = ({ state, getView, customActions, ...features }) => {
1009
+ const menuCreator = useMemo2(() => createMarkdownActions({
1323
1010
  state,
1324
1011
  getView,
1325
1012
  customActions,
@@ -1338,30 +1025,301 @@ var useEditorToolbarActionGraph = ({ state, getView, customActions, ...features
1338
1025
  ]);
1339
1026
  return useMenuActions(menuCreator);
1340
1027
  };
1341
- var createToolbarActions = ({ state, getView, customActions, ...features }) => {
1028
+ var createMarkdownActions = ({ state, getView, customActions, ...features }) => {
1342
1029
  return Atom.make((get) => {
1343
1030
  const stateSnapshot = get(state);
1344
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();
1345
1032
  });
1346
1033
  };
1347
1034
 
1348
- // src/components/EditorToolbar/useEditorToolbar.ts
1349
- import { Atom as Atom2 } from "@effect-atom/atom-react";
1350
- import { useMemo as useMemo4 } from "react";
1351
- var useEditorToolbar = (initialState = {}) => {
1352
- return useMemo4(() => Atom2.make(initialState), []);
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
+ ]);
1353
1067
  };
1354
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);
1134
+ }
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
+ }
1158
+ }
1159
+ }, [
1160
+ view,
1161
+ scrollTo,
1162
+ selection
1163
+ ]);
1164
+ useEffect2(() => {
1165
+ if (view && autoFocus) {
1166
+ view.focus();
1167
+ }
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
+ }
1191
+ }
1192
+ }, [
1193
+ view
1194
+ ]);
1195
+ return {
1196
+ parentRef,
1197
+ view,
1198
+ focusAttributes: {
1199
+ tabIndex: 0,
1200
+ onKeyDown: handleKeyDown
1201
+ }
1202
+ };
1203
+ };
1204
+
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
+ };
1242
+ };
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
+
1355
1315
  // src/components/Editor/Editor.tsx
1356
1316
  var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/Editor/Editor.tsx";
1357
1317
  var [EditorContextProvider, useEditorContext] = createContext("Editor");
1358
- var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
1359
- const state = useEditorToolbar({
1318
+ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
1319
+ const state = useMemo5(() => Atom2.make({
1320
+ viewMode
1321
+ }), [
1360
1322
  viewMode
1361
- });
1362
- const [controller, setController] = useState4();
1363
- useImperativeHandle2(forwardedRef, () => controller ?? noopController, [
1364
- controller
1365
1323
  ]);
1366
1324
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
1367
1325
  const extensions = useMemo5(() => [
@@ -1371,6 +1329,10 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1371
1329
  extension,
1372
1330
  extensionsProp
1373
1331
  ]);
1332
+ const [controller, setController] = useState4(noopController);
1333
+ useImperativeHandle2(forwardedRef, () => controller, [
1334
+ controller
1335
+ ]);
1374
1336
  return /* @__PURE__ */ React4.createElement(EditorContextProvider, {
1375
1337
  controller,
1376
1338
  setController,
@@ -1379,21 +1341,22 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1379
1341
  }, /* @__PURE__ */ React4.createElement(EditorMenuProvider, {
1380
1342
  view: controller?.view,
1381
1343
  groups: groupsRef.current,
1344
+ numItems,
1382
1345
  ...menuProps
1383
1346
  }, children));
1384
1347
  });
1385
1348
  EditorRoot.displayName = "Editor.Root";
1386
- var EDITOR_VIEWPORT_NAME = "Editor.Viewport";
1387
- var EditorViewport = ({ classNames, children }) => {
1349
+ var EDITOR_CONTENT_NAME = "Editor.Content";
1350
+ var EditorContent = ({ classNames, children }) => {
1388
1351
  return /* @__PURE__ */ React4.createElement("div", {
1389
1352
  role: "none",
1390
1353
  className: mx2("grid grid-rows-[min-content_1fr] h-full overflow-hidden", classNames)
1391
1354
  }, children);
1392
1355
  };
1393
- EditorViewport.displayName = EDITOR_VIEWPORT_NAME;
1394
- var EDITOR_CONTENT_NAME = "Editor.Content";
1395
- var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1396
- 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);
1397
1360
  const extensions = useMemo5(() => [
1398
1361
  additionalExtensions,
1399
1362
  providedExtensions
@@ -1401,26 +1364,18 @@ var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1401
1364
  providedExtensions,
1402
1365
  additionalExtensions
1403
1366
  ]);
1404
- return /* @__PURE__ */ React4.createElement(EditorContent, {
1367
+ return /* @__PURE__ */ React4.createElement(EditorView3, {
1405
1368
  ...props,
1406
1369
  extensions,
1407
1370
  ref: setController
1408
1371
  });
1409
1372
  };
1410
- EditorContent2.displayName = EDITOR_CONTENT_NAME;
1373
+ EditorView4.displayName = EDITOR_VIEW_NAME;
1411
1374
  var EDITOR_TOOLBAR_NAME = "Editor.Toolbar";
1412
1375
  var EditorToolbar2 = (props) => {
1413
1376
  const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
1414
1377
  const getView = useCallback4(() => {
1415
- invariant3(controller?.view, void 0, {
1416
- F: __dxlog_file4,
1417
- L: 153,
1418
- S: void 0,
1419
- A: [
1420
- "controller?.view",
1421
- ""
1422
- ]
1423
- });
1378
+ invariant3(controller?.view, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 100, S: void 0, A: ["controller?.view", ""] });
1424
1379
  return controller?.view;
1425
1380
  }, [
1426
1381
  controller
@@ -1434,19 +1389,19 @@ var EditorToolbar2 = (props) => {
1434
1389
  EditorToolbar2.displayName = EDITOR_TOOLBAR_NAME;
1435
1390
  var Editor = {
1436
1391
  Root: EditorRoot,
1437
- Viewport: EditorViewport,
1438
- Content: EditorContent2,
1439
- Toolbar: EditorToolbar2
1392
+ Toolbar: EditorToolbar2,
1393
+ Content: EditorContent,
1394
+ View: EditorView4
1440
1395
  };
1441
1396
 
1442
1397
  // src/components/EditorPreviewProvider/EditorPreviewProvider.tsx
1443
1398
  import { createContext as createContext2 } from "@radix-ui/react-context";
1444
- 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";
1445
1400
  import { addEventListener as addEventListener2 } from "@dxos/async";
1446
1401
  import { DX_ANCHOR_ACTIVATE as DX_ANCHOR_ACTIVATE2, Popover as Popover2 } from "@dxos/react-ui";
1447
1402
  var [EditorPreviewContextProvider, useEditorPreview] = createContext2("PreviewPopover", {});
1448
1403
  var EditorPreviewProvider = ({ children, onLookup }) => {
1449
- const triggerRef = useRef4(null);
1404
+ const triggerRef = useRef5(null);
1450
1405
  const [value, setValue] = useState5({});
1451
1406
  const [open, setOpen] = useState5(false);
1452
1407
  const handleActivate = useCallback5((event) => {
@@ -1500,12 +1455,183 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1500
1455
  ref: setRoot
1501
1456
  }, children)));
1502
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
+ });
1503
1630
  export {
1504
1631
  Editor,
1505
- EditorContent,
1506
1632
  EditorMenuProvider,
1507
1633
  EditorPreviewProvider,
1508
- EditorToolbar,
1634
+ assistant,
1509
1635
  createEditorController,
1510
1636
  createMenuGroup,
1511
1637
  filterMenuGroups,
@@ -1517,11 +1643,10 @@ export {
1517
1643
  popover,
1518
1644
  popoverRangeEffect,
1519
1645
  popoverStateField,
1520
- translations,
1646
+ useBasicMarkdownExtensions,
1521
1647
  useEditorContext,
1522
1648
  useEditorMenu,
1523
1649
  useEditorPreview,
1524
- useEditorToolbar,
1525
1650
  useTextEditor
1526
1651
  };
1527
1652
  //# sourceMappingURL=index.mjs.map