@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.
- package/dist/lib/browser/index.mjs +548 -423
- 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 +548 -423
- 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 +30 -13
- 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 +50 -41
- package/src/components/Editor/Editor.stories.tsx +10 -15
- package/src/components/Editor/Editor.tsx +51 -34
- package/src/components/Editor/EditorView.tsx +103 -0
- package/src/components/EditorMenuProvider/popover.ts +2 -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 +2 -1
- 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 +1 -1
- 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,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
|
|
342
|
-
import { modalStateField
|
|
343
|
-
import { isNonNullable, isTruthy
|
|
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
|
-
|
|
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(
|
|
64
|
+
].filter(isTruthy);
|
|
361
65
|
};
|
|
362
|
-
var popoverTriggerListener = (options) =>
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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] =
|
|
731
|
-
|
|
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 =
|
|
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__ */
|
|
463
|
+
return /* @__PURE__ */ React.createElement(Popover.Root, {
|
|
776
464
|
modal: false,
|
|
777
465
|
open,
|
|
778
466
|
onOpenChange: setOpen
|
|
779
|
-
}, /* @__PURE__ */
|
|
467
|
+
}, /* @__PURE__ */ React.createElement(Popover.VirtualTrigger, {
|
|
780
468
|
virtualRef: triggerRef
|
|
781
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
488
|
+
}, /* @__PURE__ */ React.createElement(Popover.Viewport, {
|
|
801
489
|
asChild: true,
|
|
802
490
|
classNames: "dx-container"
|
|
803
|
-
}, /* @__PURE__ */
|
|
491
|
+
}, /* @__PURE__ */ React.createElement(ScrollArea.Root, {
|
|
804
492
|
thin: true
|
|
805
|
-
}, /* @__PURE__ */
|
|
493
|
+
}, /* @__PURE__ */ React.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React.createElement(Menu, {
|
|
806
494
|
groups: menuGroups,
|
|
807
495
|
currentItem,
|
|
808
496
|
onSelect: handleSelect
|
|
809
|
-
})))), /* @__PURE__ */
|
|
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__ */
|
|
505
|
+
return /* @__PURE__ */ React.createElement("ul", null, groups.map((group, index) => /* @__PURE__ */ React.createElement(Fragment, {
|
|
818
506
|
key: group.id
|
|
819
|
-
}, /* @__PURE__ */
|
|
507
|
+
}, /* @__PURE__ */ React.createElement(MenuGroup, {
|
|
820
508
|
group,
|
|
821
509
|
currentItem,
|
|
822
510
|
onSelect
|
|
823
|
-
}), index < groups.length - 1 && /* @__PURE__ */
|
|
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__ */
|
|
518
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, group.label && /* @__PURE__ */ React.createElement("div", {
|
|
831
519
|
className: tx("menu.groupLabel", {})
|
|
832
|
-
}, /* @__PURE__ */
|
|
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 =
|
|
843
|
-
|
|
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 =
|
|
541
|
+
const handleSelect = useCallback(() => onSelect?.(item), [
|
|
854
542
|
item,
|
|
855
543
|
onSelect
|
|
856
544
|
]);
|
|
857
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
551
|
+
}, item.icon && /* @__PURE__ */ React.createElement(Icon, {
|
|
864
552
|
icon: item.icon
|
|
865
|
-
}), /* @__PURE__ */
|
|
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
|
|
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
|
|
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 =
|
|
877
|
-
const currentRef =
|
|
878
|
-
const [currentItem, setCurrentItem] =
|
|
879
|
-
const [open, setOpen] =
|
|
880
|
-
const [_, refresh] =
|
|
881
|
-
const getMenuOptions =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
1309
|
-
return /* @__PURE__ */
|
|
995
|
+
const menuActions = useMarkdownMenuActions(props);
|
|
996
|
+
return /* @__PURE__ */ React2.createElement(ElevationProvider, {
|
|
1310
997
|
elevation: role === "section" ? "positioned" : "base"
|
|
1311
|
-
}, /* @__PURE__ */
|
|
998
|
+
}, /* @__PURE__ */ React2.createElement(Menu2.Root, {
|
|
1312
999
|
...menuActions,
|
|
1313
1000
|
attendableId,
|
|
1314
1001
|
onAction
|
|
1315
|
-
}, /* @__PURE__ */
|
|
1002
|
+
}, /* @__PURE__ */ React2.createElement(Menu2.Toolbar, {
|
|
1316
1003
|
classNames
|
|
1317
1004
|
})));
|
|
1318
1005
|
});
|
|
1319
|
-
var
|
|
1320
|
-
const menuCreator =
|
|
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
|
|
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/
|
|
1347
|
-
import {
|
|
1348
|
-
import {
|
|
1349
|
-
|
|
1350
|
-
|
|
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 =
|
|
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
|
|
1385
|
-
var
|
|
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
|
-
|
|
1392
|
-
var
|
|
1393
|
-
var
|
|
1394
|
-
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);
|
|
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(
|
|
1365
|
+
return /* @__PURE__ */ React4.createElement(EditorView3, {
|
|
1403
1366
|
...props,
|
|
1404
1367
|
extensions,
|
|
1405
1368
|
ref: setController
|
|
1406
1369
|
});
|
|
1407
1370
|
};
|
|
1408
|
-
|
|
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
|
-
|
|
1436
|
-
Content:
|
|
1437
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1644
|
+
useBasicMarkdownExtensions,
|
|
1519
1645
|
useEditorContext,
|
|
1520
1646
|
useEditorMenu,
|
|
1521
1647
|
useEditorPreview,
|
|
1522
|
-
useEditorToolbar,
|
|
1523
1648
|
useTextEditor
|
|
1524
1649
|
};
|
|
1525
1650
|
//# sourceMappingURL=index.mjs.map
|