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