@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.
- package/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/browser/index.mjs +549 -426
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +39 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +549 -426
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +41 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/Editor/Editor.d.ts +36 -25
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
- package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
- package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
- package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
- package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
- package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
- package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +2 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
- package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/stories/Automerge.stories.d.ts +24 -24
- package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +27 -25
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +2 -2
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/stories/components/util.d.ts +2 -1
- package/dist/types/src/stories/components/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +24 -24
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +56 -47
- package/src/components/Editor/Editor.stories.tsx +10 -15
- package/src/components/Editor/Editor.tsx +54 -53
- package/src/components/Editor/EditorView.tsx +102 -0
- package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +1 -1
- package/src/components/EditorMenuProvider/popover.ts +3 -1
- package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +1 -1
- package/src/components/EditorToolbar/EditorToolbar.tsx +9 -7
- package/src/components/EditorToolbar/blocks.ts +3 -2
- package/src/components/EditorToolbar/formatting.ts +3 -2
- package/src/components/EditorToolbar/headings.ts +3 -2
- package/src/components/EditorToolbar/image.ts +1 -1
- package/src/components/EditorToolbar/index.ts +2 -2
- package/src/components/EditorToolbar/lists.ts +3 -2
- package/src/components/EditorToolbar/search.ts +1 -1
- package/src/components/EditorToolbar/types.ts +8 -0
- package/src/components/EditorToolbar/view-mode.ts +4 -3
- package/src/components/index.ts +0 -3
- package/src/extensions/Assistant.stories.tsx +112 -0
- package/src/extensions/assistant-extension.tsx +223 -0
- package/src/extensions/index.ts +5 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
- package/src/index.ts +1 -4
- package/src/stories/Automerge.stories.tsx +9 -5
- package/src/stories/Comments.stories.tsx +2 -1
- package/src/stories/EditorToolbar.stories.tsx +35 -78
- package/src/stories/Experimental.stories.tsx +7 -7
- package/src/stories/Theme.stories.tsx +2 -2
- package/src/stories/components/EditorStory.tsx +9 -7
- package/src/stories/components/util.tsx +2 -2
- package/src/util/react.tsx +1 -1
- package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/index.d.ts +0 -3
- package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
- package/src/components/EditorContent/EditorContent.tsx +0 -82
- package/src/components/EditorContent/index.ts +0 -6
- package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
- /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
- /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
|
|
344
|
-
import { modalStateField
|
|
345
|
-
import { isNonNullable, isTruthy
|
|
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
|
-
|
|
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(
|
|
67
|
+
].filter(isTruthy);
|
|
363
68
|
};
|
|
364
|
-
var popoverTriggerListener = (options) =>
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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] =
|
|
733
|
-
|
|
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 =
|
|
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__ */
|
|
466
|
+
return /* @__PURE__ */ React.createElement(Popover.Root, {
|
|
778
467
|
modal: false,
|
|
779
468
|
open,
|
|
780
469
|
onOpenChange: setOpen
|
|
781
|
-
}, /* @__PURE__ */
|
|
470
|
+
}, /* @__PURE__ */ React.createElement(Popover.VirtualTrigger, {
|
|
782
471
|
virtualRef: triggerRef
|
|
783
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
491
|
+
}, /* @__PURE__ */ React.createElement(Popover.Viewport, {
|
|
803
492
|
asChild: true,
|
|
804
493
|
classNames: "dx-container"
|
|
805
|
-
}, /* @__PURE__ */
|
|
494
|
+
}, /* @__PURE__ */ React.createElement(ScrollArea.Root, {
|
|
806
495
|
thin: true
|
|
807
|
-
}, /* @__PURE__ */
|
|
496
|
+
}, /* @__PURE__ */ React.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React.createElement(Menu, {
|
|
808
497
|
groups: menuGroups,
|
|
809
498
|
currentItem,
|
|
810
499
|
onSelect: handleSelect
|
|
811
|
-
})))), /* @__PURE__ */
|
|
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__ */
|
|
507
|
+
return /* @__PURE__ */ React.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React.createElement(Fragment, {
|
|
820
508
|
key: group.id
|
|
821
|
-
}, /* @__PURE__ */
|
|
509
|
+
}, /* @__PURE__ */ React.createElement(MenuGroup, {
|
|
822
510
|
group,
|
|
823
511
|
currentItem,
|
|
824
512
|
onSelect
|
|
825
|
-
}), index < groups.length - 1 && /* @__PURE__ */
|
|
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__ */
|
|
520
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, group.label && /* @__PURE__ */ React.createElement("div", {
|
|
833
521
|
className: tx("menu.groupLabel", {})
|
|
834
|
-
}, /* @__PURE__ */
|
|
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 =
|
|
845
|
-
|
|
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 =
|
|
543
|
+
const handleSelect = useCallback(() => onSelect?.(item), [
|
|
856
544
|
item,
|
|
857
545
|
onSelect
|
|
858
546
|
]);
|
|
859
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
553
|
+
}, item.icon && /* @__PURE__ */ React.createElement(Icon, {
|
|
866
554
|
icon: item.icon
|
|
867
|
-
}), /* @__PURE__ */
|
|
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
|
|
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
|
|
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 =
|
|
879
|
-
const currentRef =
|
|
880
|
-
const [currentItem, setCurrentItem] =
|
|
881
|
-
const [open, setOpen] =
|
|
882
|
-
const [_, refresh] =
|
|
883
|
-
const getMenuOptions =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
1311
|
-
return /* @__PURE__ */
|
|
997
|
+
const menuActions = useMarkdownMenuActions(props);
|
|
998
|
+
return /* @__PURE__ */ React2.createElement(ElevationProvider, {
|
|
1312
999
|
elevation: role === "section" ? "positioned" : "base"
|
|
1313
|
-
}, /* @__PURE__ */
|
|
1000
|
+
}, /* @__PURE__ */ React2.createElement(Menu2.Root, {
|
|
1314
1001
|
...menuActions,
|
|
1315
1002
|
attendableId,
|
|
1316
1003
|
onAction
|
|
1317
|
-
}, /* @__PURE__ */
|
|
1004
|
+
}, /* @__PURE__ */ React2.createElement(Menu2.Toolbar, {
|
|
1318
1005
|
classNames
|
|
1319
1006
|
})));
|
|
1320
1007
|
});
|
|
1321
|
-
var
|
|
1322
|
-
const menuCreator =
|
|
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
|
|
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/
|
|
1349
|
-
import {
|
|
1350
|
-
import {
|
|
1351
|
-
|
|
1352
|
-
|
|
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 =
|
|
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
|
|
1387
|
-
var
|
|
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
|
-
|
|
1394
|
-
var
|
|
1395
|
-
var
|
|
1396
|
-
const { extensions: additionalExtensions = [], setController } = useEditorContext(
|
|
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(
|
|
1365
|
+
return /* @__PURE__ */ React4.createElement(EditorView3, {
|
|
1405
1366
|
...props,
|
|
1406
1367
|
extensions,
|
|
1407
1368
|
ref: setController
|
|
1408
1369
|
});
|
|
1409
1370
|
};
|
|
1410
|
-
|
|
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
|
-
|
|
1438
|
-
Content:
|
|
1439
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1643
|
+
useBasicMarkdownExtensions,
|
|
1644
|
+
useEditorContext,
|
|
1521
1645
|
useEditorMenu,
|
|
1522
1646
|
useEditorPreview,
|
|
1523
|
-
useEditorToolbar,
|
|
1524
1647
|
useTextEditor
|
|
1525
1648
|
};
|
|
1526
1649
|
//# sourceMappingURL=index.mjs.map
|