@dxos/react-ui-editor 0.8.4-main.74a063c4e0 → 0.8.4-main.765dc60934

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 (128) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +549 -426
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/translations.mjs +39 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +549 -426
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/translations.mjs +41 -0
  12. package/dist/lib/node-esm/translations.mjs.map +7 -0
  13. package/dist/types/src/components/Editor/Editor.d.ts +36 -25
  14. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  15. package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
  16. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
  18. package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
  19. package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
  20. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.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 +1 -1
  29. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/formatting.d.ts +1 -1
  31. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/headings.d.ts +1 -1
  33. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  35. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
  36. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  37. package/dist/types/src/components/EditorToolbar/lists.d.ts +1 -1
  38. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  39. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  40. package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
  41. package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
  42. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +2 -2
  43. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  44. package/dist/types/src/components/index.d.ts +0 -2
  45. package/dist/types/src/components/index.d.ts.map +1 -1
  46. package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
  47. package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
  48. package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
  49. package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
  50. package/dist/types/src/extensions/index.d.ts +2 -0
  51. package/dist/types/src/extensions/index.d.ts.map +1 -0
  52. package/dist/types/src/hooks/index.d.ts +1 -0
  53. package/dist/types/src/hooks/index.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
  55. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
  56. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  57. package/dist/types/src/index.d.ts +1 -2
  58. package/dist/types/src/index.d.ts.map +1 -1
  59. package/dist/types/src/stories/Automerge.stories.d.ts +24 -24
  60. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  61. package/dist/types/src/stories/Comments.stories.d.ts +1 -1
  62. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  63. package/dist/types/src/stories/EditorToolbar.stories.d.ts +27 -25
  64. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  65. package/dist/types/src/stories/Experimental.stories.d.ts +2 -2
  66. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
  68. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  70. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Preview.stories.d.ts +1 -1
  72. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  73. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  74. package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
  75. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  76. package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
  77. package/dist/types/src/stories/components/EditorStory.d.ts +1 -1
  78. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  79. package/dist/types/src/stories/components/util.d.ts +2 -1
  80. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  81. package/dist/types/src/translations.d.ts +24 -24
  82. package/dist/types/src/translations.d.ts.map +1 -1
  83. package/dist/types/src/util/react.d.ts +1 -1
  84. package/dist/types/src/util/react.d.ts.map +1 -1
  85. package/dist/types/tsconfig.tsbuildinfo +1 -1
  86. package/package.json +56 -47
  87. package/src/components/Editor/Editor.stories.tsx +10 -15
  88. package/src/components/Editor/Editor.tsx +54 -53
  89. package/src/components/Editor/EditorView.tsx +102 -0
  90. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +1 -1
  91. package/src/components/EditorMenuProvider/popover.ts +3 -1
  92. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +1 -1
  93. package/src/components/EditorToolbar/EditorToolbar.tsx +9 -7
  94. package/src/components/EditorToolbar/blocks.ts +3 -2
  95. package/src/components/EditorToolbar/formatting.ts +3 -2
  96. package/src/components/EditorToolbar/headings.ts +3 -2
  97. package/src/components/EditorToolbar/image.ts +1 -1
  98. package/src/components/EditorToolbar/index.ts +2 -2
  99. package/src/components/EditorToolbar/lists.ts +3 -2
  100. package/src/components/EditorToolbar/search.ts +1 -1
  101. package/src/components/EditorToolbar/types.ts +8 -0
  102. package/src/components/EditorToolbar/view-mode.ts +4 -3
  103. package/src/components/index.ts +0 -3
  104. package/src/extensions/Assistant.stories.tsx +112 -0
  105. package/src/extensions/assistant-extension.tsx +223 -0
  106. package/src/extensions/index.ts +5 -0
  107. package/src/hooks/index.ts +1 -0
  108. package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
  109. package/src/index.ts +1 -4
  110. package/src/stories/Automerge.stories.tsx +9 -5
  111. package/src/stories/Comments.stories.tsx +2 -1
  112. package/src/stories/EditorToolbar.stories.tsx +35 -78
  113. package/src/stories/Experimental.stories.tsx +7 -7
  114. package/src/stories/Theme.stories.tsx +2 -2
  115. package/src/stories/components/EditorStory.tsx +9 -7
  116. package/src/stories/components/util.tsx +2 -2
  117. package/src/util/react.tsx +1 -1
  118. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  119. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  120. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  121. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  122. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  123. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  124. package/src/components/EditorContent/EditorContent.tsx +0 -82
  125. package/src/components/EditorContent/index.ts +0 -6
  126. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  127. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  128. /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,16 @@ var popover = (options = {}) => {
353
57
  popoverStateField,
354
58
  popoverTriggerListener(options),
355
59
  popoverAnchorDecoration(options),
356
- modalStateField2,
60
+ modalStateField,
357
61
  options.trigger && placeholder({
358
62
  // TODO(burdon): Translations.
359
63
  content: `Press '${Array.isArray(options.trigger) ? options.trigger[0] : options.trigger}' for commands`,
64
+ focusOnly: true,
360
65
  ...options.placeholder
361
66
  })
362
- ].filter(isTruthy2);
67
+ ].filter(isTruthy);
363
68
  };
364
- var popoverTriggerListener = (options) => EditorView3.updateListener.of(({ view, docChanged }) => {
69
+ var popoverTriggerListener = (options) => EditorView.updateListener.of(({ view, docChanged }) => {
365
70
  const { range: activeRange, trigger } = view.state.field(popoverStateField) ?? {};
366
71
  if (!activeRange) {
367
72
  return;
@@ -506,7 +211,7 @@ var popoverKeymap = (options) => {
506
211
  return false;
507
212
  }
508
213
  }
509
- ].filter(isTruthy2));
214
+ ].filter(isTruthy));
510
215
  };
511
216
  var popoverAnchorDecoration = (options) => {
512
217
  return ViewPlugin.fromClass(class {
@@ -701,36 +406,28 @@ var linkSlashCommands = {
701
406
 
702
407
  // src/components/EditorMenuProvider/EditorMenuProvider.tsx
703
408
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
704
- import React2, { Fragment, useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
409
+ import React, { Fragment, useCallback, useEffect, useRef, useState } from "react";
705
410
  import { addEventListener } from "@dxos/async";
706
411
  import { invariant } from "@dxos/invariant";
707
412
  import { DX_ANCHOR_ACTIVATE, Icon, Popover, ScrollArea, toLocalizedString, useDynamicRef, useThemeContext, useTranslation } from "@dxos/react-ui";
708
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
413
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
709
414
  var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp, defaultOpen, numItems = 8, onOpenChange, onActivate, onSelect, onCancel }) => {
710
415
  const { tx } = useThemeContext();
711
- const triggerRef = useRef2(null);
416
+ const triggerRef = useRef(null);
712
417
  const viewRef = useDynamicRef(view);
713
418
  const [open, setOpen] = useControllableState({
714
419
  prop: openProp,
715
420
  defaultProp: defaultOpen,
716
421
  onChange: (open2) => {
717
- invariant(viewRef.current, void 0, {
718
- F: __dxlog_file2,
719
- L: 64,
720
- S: void 0,
721
- A: [
722
- "viewRef.current",
723
- ""
724
- ]
725
- });
422
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 21, S: void 0, A: ["viewRef.current", ""] });
726
423
  onOpenChange?.({
727
424
  view: viewRef.current,
728
425
  open: open2
729
426
  });
730
427
  }
731
428
  });
732
- const [root, setRoot] = useState2(null);
733
- useEffect3(() => {
429
+ const [root, setRoot] = useState(null);
430
+ useEffect(() => {
734
431
  if (!root) {
735
432
  return;
736
433
  }
@@ -755,16 +452,8 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
755
452
  root,
756
453
  onActivate
757
454
  ]);
758
- const handleSelect = useCallback2((item) => {
759
- invariant(viewRef.current, void 0, {
760
- F: __dxlog_file2,
761
- L: 99,
762
- S: void 0,
763
- A: [
764
- "viewRef.current",
765
- ""
766
- ]
767
- });
455
+ const handleSelect = useCallback((item) => {
456
+ invariant(viewRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: void 0, A: ["viewRef.current", ""] });
768
457
  onSelect?.({
769
458
  view: viewRef.current,
770
459
  item
@@ -774,13 +463,13 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
774
463
  onSelect
775
464
  ]);
776
465
  const menuGroups = groups?.filter((group) => group.items.length > 0) ?? [];
777
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
466
+ return /* @__PURE__ */ React.createElement(Popover.Root, {
778
467
  modal: false,
779
468
  open,
780
469
  onOpenChange: setOpen
781
- }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
470
+ }, /* @__PURE__ */ React.createElement(Popover.VirtualTrigger, {
782
471
  virtualRef: triggerRef
783
- }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
472
+ }), /* @__PURE__ */ React.createElement(Popover.Portal, null, /* @__PURE__ */ React.createElement(Popover.Content, {
784
473
  align: "start",
785
474
  classNames: [
786
475
  "flex flex-col",
@@ -799,39 +488,38 @@ var EditorMenuProvider = ({ children, view, groups, currentItem, open: openProp,
799
488
  }
800
489
  },
801
490
  onOpenAutoFocus: (event) => event.preventDefault()
802
- }, /* @__PURE__ */ React2.createElement(Popover.Viewport, {
491
+ }, /* @__PURE__ */ React.createElement(Popover.Viewport, {
803
492
  asChild: true,
804
493
  classNames: "dx-container"
805
- }, /* @__PURE__ */ React2.createElement(ScrollArea.Root, {
494
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Root, {
806
495
  thin: true
807
- }, /* @__PURE__ */ React2.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React2.createElement(Menu, {
496
+ }, /* @__PURE__ */ React.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React.createElement(Menu, {
808
497
  groups: menuGroups,
809
498
  currentItem,
810
499
  onSelect: handleSelect
811
- })))), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))), /* @__PURE__ */ React2.createElement("div", {
812
- role: "none",
500
+ })))), /* @__PURE__ */ React.createElement(Popover.Arrow, null))), /* @__PURE__ */ React.createElement("div", {
813
501
  className: "contents",
814
502
  ref: setRoot
815
503
  }, children));
816
504
  };
817
505
  var Menu = ({ groups, currentItem, onSelect }) => {
818
506
  const { tx } = useThemeContext();
819
- return /* @__PURE__ */ React2.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React2.createElement(Fragment, {
507
+ return /* @__PURE__ */ React.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React.createElement(Fragment, {
820
508
  key: group.id
821
- }, /* @__PURE__ */ React2.createElement(MenuGroup, {
509
+ }, /* @__PURE__ */ React.createElement(MenuGroup, {
822
510
  group,
823
511
  currentItem,
824
512
  onSelect
825
- }), index < groups.length - 1 && /* @__PURE__ */ React2.createElement("div", {
513
+ }), index < groups.length - 1 && /* @__PURE__ */ React.createElement("div", {
826
514
  className: tx("menu.separator", {})
827
515
  }))));
828
516
  };
829
517
  var MenuGroup = ({ group, currentItem, onSelect }) => {
830
518
  const { tx } = useThemeContext();
831
519
  const { t } = useTranslation();
832
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, group.label && /* @__PURE__ */ React2.createElement("div", {
520
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, group.label && /* @__PURE__ */ React.createElement("div", {
833
521
  className: tx("menu.groupLabel", {})
834
- }, /* @__PURE__ */ React2.createElement("span", null, toLocalizedString(group.label, t))), group.items.map((item) => /* @__PURE__ */ React2.createElement(MenuItem, {
522
+ }, /* @__PURE__ */ React.createElement("span", null, toLocalizedString(group.label, t))), group.items.map((item) => /* @__PURE__ */ React.createElement(MenuItem, {
835
523
  key: item.id,
836
524
  item,
837
525
  current: currentItem === item.id,
@@ -841,8 +529,8 @@ var MenuGroup = ({ group, currentItem, onSelect }) => {
841
529
  var MenuItem = ({ item, current, onSelect }) => {
842
530
  const { tx } = useThemeContext();
843
531
  const { t } = useTranslation();
844
- const listRef = useRef2(null);
845
- useEffect3(() => {
532
+ const listRef = useRef(null);
533
+ useEffect(() => {
846
534
  if (current && listRef.current) {
847
535
  listRef.current.scrollIntoView({
848
536
  behavior: "smooth",
@@ -852,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,300 @@ 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
+ className: mx("w-full outline-hidden focus:border-accent-surface focus-within:border-focus-ring-subtle", classNames),
1309
+ ...focusable ? focusAttributes : {},
1310
+ ref: parentRef
1311
+ });
1312
+ });
1313
+
1355
1314
  // src/components/Editor/Editor.tsx
1356
1315
  var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/Editor/Editor.tsx";
1357
1316
  var [EditorContextProvider, useEditorContext] = createContext("Editor");
1358
- var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
1359
- const state = useEditorToolbar({
1317
+ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
1318
+ const state = useMemo5(() => Atom2.make({
1319
+ viewMode
1320
+ }), [
1360
1321
  viewMode
1361
- });
1362
- const [controller, setController] = useState4();
1363
- useImperativeHandle2(forwardedRef, () => controller ?? noopController, [
1364
- controller
1365
1322
  ]);
1366
1323
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
1367
1324
  const extensions = useMemo5(() => [
@@ -1371,6 +1328,10 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1371
1328
  extension,
1372
1329
  extensionsProp
1373
1330
  ]);
1331
+ const [controller, setController] = useState4(noopController);
1332
+ useImperativeHandle2(forwardedRef, () => controller, [
1333
+ controller
1334
+ ]);
1374
1335
  return /* @__PURE__ */ React4.createElement(EditorContextProvider, {
1375
1336
  controller,
1376
1337
  setController,
@@ -1379,21 +1340,21 @@ var EditorRoot = /* @__PURE__ */ forwardRef2(({ children, extensions: extensions
1379
1340
  }, /* @__PURE__ */ React4.createElement(EditorMenuProvider, {
1380
1341
  view: controller?.view,
1381
1342
  groups: groupsRef.current,
1343
+ numItems,
1382
1344
  ...menuProps
1383
1345
  }, children));
1384
1346
  });
1385
1347
  EditorRoot.displayName = "Editor.Root";
1386
- var EDITOR_VIEWPORT_NAME = "Editor.Viewport";
1387
- var EditorViewport = ({ classNames, children }) => {
1348
+ var EDITOR_CONTENT_NAME = "Editor.Content";
1349
+ var EditorContent = ({ classNames, children }) => {
1388
1350
  return /* @__PURE__ */ React4.createElement("div", {
1389
- role: "none",
1390
1351
  className: mx2("grid grid-rows-[min-content_1fr] h-full overflow-hidden", classNames)
1391
1352
  }, children);
1392
1353
  };
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);
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);
1397
1358
  const extensions = useMemo5(() => [
1398
1359
  additionalExtensions,
1399
1360
  providedExtensions
@@ -1401,26 +1362,18 @@ var EditorContent2 = ({ extensions: providedExtensions, ...props }) => {
1401
1362
  providedExtensions,
1402
1363
  additionalExtensions
1403
1364
  ]);
1404
- return /* @__PURE__ */ React4.createElement(EditorContent, {
1365
+ return /* @__PURE__ */ React4.createElement(EditorView3, {
1405
1366
  ...props,
1406
1367
  extensions,
1407
1368
  ref: setController
1408
1369
  });
1409
1370
  };
1410
- EditorContent2.displayName = EDITOR_CONTENT_NAME;
1371
+ EditorView4.displayName = EDITOR_VIEW_NAME;
1411
1372
  var EDITOR_TOOLBAR_NAME = "Editor.Toolbar";
1412
1373
  var EditorToolbar2 = (props) => {
1413
1374
  const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
1414
1375
  const getView = useCallback4(() => {
1415
- invariant3(controller?.view, void 0, {
1416
- F: __dxlog_file4,
1417
- L: 146,
1418
- S: void 0,
1419
- A: [
1420
- "controller?.view",
1421
- ""
1422
- ]
1423
- });
1376
+ invariant3(controller?.view, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 99, S: void 0, A: ["controller?.view", ""] });
1424
1377
  return controller?.view;
1425
1378
  }, [
1426
1379
  controller
@@ -1434,19 +1387,19 @@ var EditorToolbar2 = (props) => {
1434
1387
  EditorToolbar2.displayName = EDITOR_TOOLBAR_NAME;
1435
1388
  var Editor = {
1436
1389
  Root: EditorRoot,
1437
- Viewport: EditorViewport,
1438
- Content: EditorContent2,
1439
- Toolbar: EditorToolbar2
1390
+ Toolbar: EditorToolbar2,
1391
+ Content: EditorContent,
1392
+ View: EditorView4
1440
1393
  };
1441
1394
 
1442
1395
  // src/components/EditorPreviewProvider/EditorPreviewProvider.tsx
1443
1396
  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";
1397
+ import React5, { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
1445
1398
  import { addEventListener as addEventListener2 } from "@dxos/async";
1446
1399
  import { DX_ANCHOR_ACTIVATE as DX_ANCHOR_ACTIVATE2, Popover as Popover2 } from "@dxos/react-ui";
1447
1400
  var [EditorPreviewContextProvider, useEditorPreview] = createContext2("PreviewPopover", {});
1448
1401
  var EditorPreviewProvider = ({ children, onLookup }) => {
1449
- const triggerRef = useRef4(null);
1402
+ const triggerRef = useRef5(null);
1450
1403
  const [value, setValue] = useState5({});
1451
1404
  const [open, setOpen] = useState5(false);
1452
1405
  const handleActivate = useCallback5((event) => {
@@ -1495,17 +1448,187 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1495
1448
  }, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
1496
1449
  virtualRef: triggerRef
1497
1450
  }), /* @__PURE__ */ React5.createElement("div", {
1498
- role: "none",
1499
1451
  className: "contents",
1500
1452
  ref: setRoot
1501
1453
  }, children)));
1502
1454
  };
1455
+
1456
+ // src/extensions/assistant-extension.tsx
1457
+ import { forEachDiagnostic, linter, setDiagnostics } from "@codemirror/lint";
1458
+ import { ChangeSet } from "@codemirror/state";
1459
+ import { EditorView as EditorView5 } from "@codemirror/view";
1460
+ import { log as log2 } from "@dxos/log";
1461
+ import { safeParseJson, trim } from "@dxos/util";
1462
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/extensions/assistant-extension.tsx";
1463
+ var DEFAULT_INSTRUCTIONS = trim`
1464
+ Proofread the input text below.
1465
+ Identify typos and grammatical errors.
1466
+ Return ONLY a valid JSON array of objects with fields: "original" (string), "replacement" (string), "context" (string, 3-5 words around match).
1467
+ --
1468
+ `;
1469
+ var underline = (color) => {
1470
+ const svg = trim`
1471
+ <svg xmlns="http://www.w3.org/2000/svg" width="6" height="3">
1472
+ <path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width="1"/>
1473
+ </svg>
1474
+ `;
1475
+ return `url('data:image/svg+xml;base64,${btoa(svg)}') !important`;
1476
+ };
1477
+ var assistant = (options) => {
1478
+ const styles = getComputedStyle(document.documentElement);
1479
+ const style = {
1480
+ info: styles.getPropertyValue("--color-green-500").trim(),
1481
+ warning: styles.getPropertyValue("--color-orange-500").trim(),
1482
+ error: styles.getPropertyValue("--color-rose-500").trim()
1483
+ };
1484
+ return [
1485
+ assistantLinter(options),
1486
+ EditorView5.baseTheme({
1487
+ ".cm-lintRange-info": {
1488
+ backgroundImage: underline(style.info)
1489
+ },
1490
+ ".cm-lintRange-warning": {
1491
+ backgroundImage: underline(style.warning)
1492
+ },
1493
+ ".cm-lintRange-error": {
1494
+ backgroundImage: underline(style.error)
1495
+ },
1496
+ ".cm-panels-bottom": {
1497
+ borderTop: "1px solid var(--color-separator) !important"
1498
+ },
1499
+ ".cm-panel-lint .cm-panel": {
1500
+ outline: "none !important"
1501
+ },
1502
+ /** @apply dx-button */
1503
+ ".cm-panel button": {
1504
+ color: "var(--color-base-foreground) !important"
1505
+ },
1506
+ ".cm-panel.cm-panel-lint ul": {
1507
+ color: "var(--color-base-foreground) !important",
1508
+ backgroundColor: "var(--color-base-surface) !important",
1509
+ marginRight: "2rem !important"
1510
+ },
1511
+ ".cm-panel.cm-panel-lint ul [aria-selected]": {
1512
+ color: "var(--color-base-foreground) !important",
1513
+ backgroundColor: "var(--color-base-surface) !important"
1514
+ },
1515
+ ".cm-panel.cm-panel-lint ul li": {
1516
+ display: "grid",
1517
+ gridTemplateColumns: "1fr auto",
1518
+ alignItems: "center"
1519
+ },
1520
+ ".cm-panel.cm-panel-lint ul li .cm-diagnosticText": {
1521
+ paddingRight: "8px !important"
1522
+ },
1523
+ ".cm-panel.cm-panel-lint ul li button.cm-diagnosticAction": {
1524
+ margin: "none !important"
1525
+ },
1526
+ ".cm-diagnostic": {
1527
+ padding: "0px 8px !important",
1528
+ whiteSpace: "pre-wrap !important"
1529
+ },
1530
+ ".cm-diagnostic-info": {
1531
+ border: "none !important"
1532
+ }
1533
+ })
1534
+ ];
1535
+ };
1536
+ var isSuggestion = (value) => typeof value === "object" && value !== null && typeof value.original === "string" && typeof value.replacement === "string" && typeof value.context === "string";
1537
+ var findSuggestionIndex = (content, suggestion) => {
1538
+ const firstIdx = content.indexOf(suggestion.original);
1539
+ if (firstIdx === -1) {
1540
+ return -1;
1541
+ }
1542
+ const secondIdx = content.indexOf(suggestion.original, firstIdx + 1);
1543
+ if (secondIdx === -1) {
1544
+ return firstIdx;
1545
+ }
1546
+ const contextIdx = content.indexOf(suggestion.context);
1547
+ if (contextIdx !== -1) {
1548
+ const contextEnd = contextIdx + suggestion.context.length;
1549
+ if (secondIdx >= contextIdx && secondIdx <= contextEnd) {
1550
+ return secondIdx;
1551
+ }
1552
+ }
1553
+ return firstIdx;
1554
+ };
1555
+ var replaceTextAndDropLintAtRange = (view, from, to, insert) => {
1556
+ const kept = [];
1557
+ forEachDiagnostic(view.state, (diagnostic, diagnosticFrom, diagnosticTo) => {
1558
+ if (diagnosticFrom < to && diagnosticTo > from) {
1559
+ return;
1560
+ }
1561
+ kept.push({
1562
+ ...diagnostic,
1563
+ from: diagnosticFrom,
1564
+ to: diagnosticTo
1565
+ });
1566
+ });
1567
+ const changeSet = ChangeSet.of({
1568
+ from,
1569
+ to,
1570
+ insert
1571
+ }, view.state.doc.length);
1572
+ const next = kept.map((d) => ({
1573
+ ...d,
1574
+ from: changeSet.mapPos(d.from, 1),
1575
+ to: changeSet.mapPos(d.to, -1)
1576
+ }));
1577
+ view.dispatch({
1578
+ changes: {
1579
+ from,
1580
+ to,
1581
+ insert
1582
+ },
1583
+ ...setDiagnostics(view.state, next)
1584
+ });
1585
+ };
1586
+ var assistantLinter = ({ generate, instructions = DEFAULT_INSTRUCTIONS, autoPanel = true, delay = 2e3 }) => linter(async (view) => {
1587
+ try {
1588
+ const content = view.state.doc.toString();
1589
+ const result = await generate({
1590
+ instructions,
1591
+ content
1592
+ });
1593
+ const [match] = result.match(/\[.*\]/s) ?? [];
1594
+ const parsed = match ? safeParseJson(match, []) : [];
1595
+ const suggestions = Array.isArray(parsed) ? parsed.filter(isSuggestion) : [];
1596
+ if (suggestions && suggestions.length > 0) {
1597
+ const diagnostics = [];
1598
+ for (const suggestion of suggestions) {
1599
+ const idx = findSuggestionIndex(content, suggestion);
1600
+ if (idx !== -1) {
1601
+ diagnostics.push({
1602
+ from: idx,
1603
+ to: idx + suggestion.original.length,
1604
+ severity: "info",
1605
+ message: `Suggestion: ${suggestion.replacement}`,
1606
+ actions: [
1607
+ {
1608
+ name: "Apply",
1609
+ apply: (view2, from, to) => {
1610
+ replaceTextAndDropLintAtRange(view2, from, to, suggestion.replacement);
1611
+ }
1612
+ }
1613
+ ]
1614
+ });
1615
+ }
1616
+ }
1617
+ return diagnostics;
1618
+ }
1619
+ } catch (err) {
1620
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 169, S: void 0 });
1621
+ }
1622
+ return [];
1623
+ }, {
1624
+ delay,
1625
+ autoPanel
1626
+ });
1503
1627
  export {
1504
1628
  Editor,
1505
- EditorContent,
1506
1629
  EditorMenuProvider,
1507
1630
  EditorPreviewProvider,
1508
- EditorToolbar,
1631
+ assistant,
1509
1632
  createEditorController,
1510
1633
  createMenuGroup,
1511
1634
  filterMenuGroups,
@@ -1517,10 +1640,10 @@ export {
1517
1640
  popover,
1518
1641
  popoverRangeEffect,
1519
1642
  popoverStateField,
1520
- translations,
1643
+ useBasicMarkdownExtensions,
1644
+ useEditorContext,
1521
1645
  useEditorMenu,
1522
1646
  useEditorPreview,
1523
- useEditorToolbar,
1524
1647
  useTextEditor
1525
1648
  };
1526
1649
  //# sourceMappingURL=index.mjs.map