neuphlo-editor 1.6.1 → 1.6.3
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/chunk-I3CL5OVL.js +977 -0
- package/dist/chunk-I3CL5OVL.js.map +1 -0
- package/dist/headless/index.cjs +4 -0
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.js +1 -1
- package/dist/react/index.cjs +12 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +9 -3
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all2) => {
|
|
6
|
+
for (var name in all2)
|
|
7
|
+
__defProp(target, name, { get: all2[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
|
+
|
|
19
|
+
// src/headless/index.ts
|
|
20
|
+
import { useCurrentEditor as useCurrentEditor4 } from "@tiptap/react";
|
|
21
|
+
|
|
22
|
+
// src/headless/components/editor.tsx
|
|
23
|
+
import { EditorProvider } from "@tiptap/react";
|
|
24
|
+
import { forwardRef } from "react";
|
|
25
|
+
import { Provider } from "jotai";
|
|
26
|
+
|
|
27
|
+
// src/headless/utils/store.ts
|
|
28
|
+
var store_exports = {};
|
|
29
|
+
__export(store_exports, {
|
|
30
|
+
novelStore: () => novelStore
|
|
31
|
+
});
|
|
32
|
+
__reExport(store_exports, jotai_star);
|
|
33
|
+
import { createStore } from "jotai";
|
|
34
|
+
import * as jotai_star from "jotai";
|
|
35
|
+
var novelStore = createStore();
|
|
36
|
+
|
|
37
|
+
// src/headless/components/editor.tsx
|
|
38
|
+
import { jsx } from "react/jsx-runtime";
|
|
39
|
+
var EditorRoot = ({ children }) => {
|
|
40
|
+
return /* @__PURE__ */ jsx(Provider, { store: novelStore, children });
|
|
41
|
+
};
|
|
42
|
+
var EditorContent = forwardRef(
|
|
43
|
+
({ className, children, initialContent, content, ...rest }, ref) => {
|
|
44
|
+
const effectiveContent = content ?? initialContent;
|
|
45
|
+
return /* @__PURE__ */ jsx("div", { ref, className, children: /* @__PURE__ */ jsx(EditorProvider, { ...rest, content: effectiveContent, children }) });
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
EditorContent.displayName = "EditorContent";
|
|
49
|
+
|
|
50
|
+
// src/headless/components/editor-bubble.tsx
|
|
51
|
+
import { useCurrentEditor } from "@tiptap/react";
|
|
52
|
+
import { BubbleMenu as BubbleMenuReact } from "@tiptap/react/menus";
|
|
53
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
54
|
+
function EditorBubble({ className, children, ...rest }) {
|
|
55
|
+
const { editor } = useCurrentEditor();
|
|
56
|
+
if (!editor) return null;
|
|
57
|
+
return /* @__PURE__ */ jsx2(BubbleMenuReact, { editor, ...rest, children: /* @__PURE__ */ jsx2("div", { className, children }) });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/headless/components/editor-bubble-item.tsx
|
|
61
|
+
import { forwardRef as forwardRef2, isValidElement, cloneElement } from "react";
|
|
62
|
+
import { useCurrentEditor as useCurrentEditor2 } from "@tiptap/react";
|
|
63
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
64
|
+
var EditorBubbleItem = forwardRef2(({ children, asChild, onSelect, ...rest }, ref) => {
|
|
65
|
+
const { editor } = useCurrentEditor2();
|
|
66
|
+
if (!editor) return null;
|
|
67
|
+
const handleClick = (e) => {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
onSelect?.(editor);
|
|
70
|
+
};
|
|
71
|
+
if (asChild && isValidElement(children)) {
|
|
72
|
+
const child = children;
|
|
73
|
+
const childOnClick = child.props?.onClick;
|
|
74
|
+
const mergedOnClick = (e) => {
|
|
75
|
+
childOnClick?.(e);
|
|
76
|
+
if (!e?.defaultPrevented) onSelect?.(editor);
|
|
77
|
+
};
|
|
78
|
+
return cloneElement(child, {
|
|
79
|
+
...rest,
|
|
80
|
+
ref: child.ref ?? ref,
|
|
81
|
+
onClick: mergedOnClick
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return /* @__PURE__ */ jsx3("div", { ref, ...rest, onClick: handleClick, children });
|
|
85
|
+
});
|
|
86
|
+
EditorBubbleItem.displayName = "EditorBubbleItem";
|
|
87
|
+
|
|
88
|
+
// src/headless/components/editor-command.tsx
|
|
89
|
+
import { useAtom, useSetAtom } from "jotai";
|
|
90
|
+
import { useEffect, forwardRef as forwardRef3 } from "react";
|
|
91
|
+
import { Command } from "cmdk";
|
|
92
|
+
|
|
93
|
+
// src/headless/utils/atoms.ts
|
|
94
|
+
import { atom } from "jotai";
|
|
95
|
+
var queryAtom = atom("");
|
|
96
|
+
var rangeAtom = atom(null);
|
|
97
|
+
|
|
98
|
+
// src/headless/components/editor-command.tsx
|
|
99
|
+
import tunnel from "tunnel-rat";
|
|
100
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
101
|
+
var commandTunnel = tunnel();
|
|
102
|
+
var EditorCommandOut = ({
|
|
103
|
+
query,
|
|
104
|
+
range
|
|
105
|
+
}) => {
|
|
106
|
+
const setQuery = useSetAtom(queryAtom, { store: novelStore });
|
|
107
|
+
const setRange = useSetAtom(rangeAtom, { store: novelStore });
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
setQuery(query);
|
|
110
|
+
}, [query, setQuery]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
setRange(range);
|
|
113
|
+
}, [range, setRange]);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
|
|
116
|
+
const onKeyDown = (e) => {
|
|
117
|
+
if (navigationKeys.includes(e.key)) {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
const commandRef = document.querySelector("#slash-command");
|
|
120
|
+
if (commandRef)
|
|
121
|
+
commandRef.dispatchEvent(
|
|
122
|
+
new KeyboardEvent("keydown", {
|
|
123
|
+
key: e.key,
|
|
124
|
+
cancelable: true,
|
|
125
|
+
bubbles: true
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
document.addEventListener("keydown", onKeyDown);
|
|
132
|
+
return () => {
|
|
133
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
134
|
+
};
|
|
135
|
+
}, []);
|
|
136
|
+
return /* @__PURE__ */ jsx4(commandTunnel.Out, {});
|
|
137
|
+
};
|
|
138
|
+
var CommandAny = Command;
|
|
139
|
+
var EditorCommand = forwardRef3(
|
|
140
|
+
({ children, className, ...rest }, ref) => {
|
|
141
|
+
const [query, setQuery] = useAtom(queryAtom);
|
|
142
|
+
return /* @__PURE__ */ jsx4(commandTunnel.In, { children: /* @__PURE__ */ jsxs(
|
|
143
|
+
CommandAny,
|
|
144
|
+
{
|
|
145
|
+
ref,
|
|
146
|
+
onKeyDown: (e) => {
|
|
147
|
+
e.stopPropagation();
|
|
148
|
+
},
|
|
149
|
+
id: "slash-command",
|
|
150
|
+
className,
|
|
151
|
+
...rest,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsx4(
|
|
154
|
+
CommandAny.Input,
|
|
155
|
+
{
|
|
156
|
+
value: query,
|
|
157
|
+
onValueChange: setQuery,
|
|
158
|
+
style: { display: "none" }
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
children
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
) });
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
var EditorCommandList = Command.List;
|
|
168
|
+
EditorCommand.displayName = "EditorCommand";
|
|
169
|
+
|
|
170
|
+
// src/headless/components/editor-command-item.tsx
|
|
171
|
+
import { forwardRef as forwardRef4 } from "react";
|
|
172
|
+
import { CommandEmpty, CommandItem } from "cmdk";
|
|
173
|
+
import { useCurrentEditor as useCurrentEditor3 } from "@tiptap/react";
|
|
174
|
+
import { useAtomValue } from "jotai";
|
|
175
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
176
|
+
var CommandItemAny = CommandItem;
|
|
177
|
+
var CommandEmptyAny = CommandEmpty;
|
|
178
|
+
var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
|
|
179
|
+
const { editor } = useCurrentEditor3();
|
|
180
|
+
const range = useAtomValue(rangeAtom);
|
|
181
|
+
if (!editor || !range) return null;
|
|
182
|
+
return /* @__PURE__ */ jsx5(
|
|
183
|
+
CommandItemAny,
|
|
184
|
+
{
|
|
185
|
+
ref,
|
|
186
|
+
...rest,
|
|
187
|
+
onSelect: () => onCommand({ editor, range }),
|
|
188
|
+
children
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
EditorCommandItem.displayName = "EditorCommandItem";
|
|
193
|
+
var EditorCommandEmpty = CommandEmptyAny;
|
|
194
|
+
|
|
195
|
+
// src/headless/extensions/index.ts
|
|
196
|
+
import { StarterKit } from "@tiptap/starter-kit";
|
|
197
|
+
import { Placeholder } from "@tiptap/extension-placeholder";
|
|
198
|
+
|
|
199
|
+
// src/headless/extensions/CodeBlock/CodeBlock.ts
|
|
200
|
+
import { CodeBlockLowlight } from "@tiptap/extension-code-block-lowlight";
|
|
201
|
+
import { all, createLowlight } from "lowlight";
|
|
202
|
+
var lowlight = createLowlight(all);
|
|
203
|
+
var CodeBlock = CodeBlockLowlight.configure({
|
|
204
|
+
lowlight
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// src/headless/extensions/Link/Link.ts
|
|
208
|
+
import { mergeAttributes } from "@tiptap/core";
|
|
209
|
+
import TiptapLink from "@tiptap/extension-link";
|
|
210
|
+
import { Plugin } from "@tiptap/pm/state";
|
|
211
|
+
var Link = TiptapLink.extend({
|
|
212
|
+
inclusive: false,
|
|
213
|
+
parseHTML() {
|
|
214
|
+
return [
|
|
215
|
+
{
|
|
216
|
+
tag: 'a[href]:not([data-type="button"]):not([href *= "javascript:" i])'
|
|
217
|
+
}
|
|
218
|
+
];
|
|
219
|
+
},
|
|
220
|
+
renderHTML({ HTMLAttributes }) {
|
|
221
|
+
return [
|
|
222
|
+
"a",
|
|
223
|
+
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
224
|
+
class: "link"
|
|
225
|
+
}),
|
|
226
|
+
0
|
|
227
|
+
];
|
|
228
|
+
},
|
|
229
|
+
addProseMirrorPlugins() {
|
|
230
|
+
const { editor } = this;
|
|
231
|
+
return [
|
|
232
|
+
...this.parent?.() || [],
|
|
233
|
+
new Plugin({
|
|
234
|
+
props: {
|
|
235
|
+
handleKeyDown: (view, event) => {
|
|
236
|
+
const { selection } = editor.state;
|
|
237
|
+
if (event.key === "Escape" && selection.empty !== true) {
|
|
238
|
+
editor.commands.focus(selection.to, { scrollIntoView: false });
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
}).configure({
|
|
247
|
+
openOnClick: false,
|
|
248
|
+
autolink: true,
|
|
249
|
+
enableClickSelection: true
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// src/headless/extensions/ImageBlock/ImageBlock.ts
|
|
253
|
+
import { mergeAttributes as mergeAttributes2 } from "@tiptap/core";
|
|
254
|
+
import { Image as TiptapImage } from "@tiptap/extension-image";
|
|
255
|
+
import { ReactNodeViewRenderer } from "@tiptap/react";
|
|
256
|
+
import { Plugin as Plugin2, PluginKey } from "@tiptap/pm/state";
|
|
257
|
+
var ImageBlock = TiptapImage.extend({
|
|
258
|
+
name: "imageBlock",
|
|
259
|
+
group: "block",
|
|
260
|
+
defining: true,
|
|
261
|
+
isolating: true,
|
|
262
|
+
addOptions() {
|
|
263
|
+
return {
|
|
264
|
+
...this.parent?.(),
|
|
265
|
+
uploadImage: void 0,
|
|
266
|
+
inline: false
|
|
267
|
+
};
|
|
268
|
+
},
|
|
269
|
+
addAttributes() {
|
|
270
|
+
return {
|
|
271
|
+
src: {
|
|
272
|
+
default: "",
|
|
273
|
+
parseHTML: (element) => element.getAttribute("src"),
|
|
274
|
+
renderHTML: (attributes) => ({
|
|
275
|
+
src: attributes.src
|
|
276
|
+
})
|
|
277
|
+
},
|
|
278
|
+
width: {
|
|
279
|
+
default: "100%",
|
|
280
|
+
parseHTML: (element) => element.getAttribute("data-width"),
|
|
281
|
+
renderHTML: (attributes) => ({
|
|
282
|
+
"data-width": attributes.width
|
|
283
|
+
})
|
|
284
|
+
},
|
|
285
|
+
align: {
|
|
286
|
+
default: "center",
|
|
287
|
+
parseHTML: (element) => element.getAttribute("data-align"),
|
|
288
|
+
renderHTML: (attributes) => ({
|
|
289
|
+
"data-align": attributes.align
|
|
290
|
+
})
|
|
291
|
+
},
|
|
292
|
+
alt: {
|
|
293
|
+
default: void 0,
|
|
294
|
+
parseHTML: (element) => element.getAttribute("alt"),
|
|
295
|
+
renderHTML: (attributes) => ({
|
|
296
|
+
alt: attributes.alt
|
|
297
|
+
})
|
|
298
|
+
},
|
|
299
|
+
loading: {
|
|
300
|
+
default: false,
|
|
301
|
+
parseHTML: () => false,
|
|
302
|
+
renderHTML: () => ({})
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
parseHTML() {
|
|
307
|
+
return [
|
|
308
|
+
{
|
|
309
|
+
tag: 'img[src]:not([src^="data:"])',
|
|
310
|
+
getAttrs: (element) => {
|
|
311
|
+
const el = element;
|
|
312
|
+
return {
|
|
313
|
+
src: el.getAttribute("src"),
|
|
314
|
+
alt: el.getAttribute("alt"),
|
|
315
|
+
width: el.getAttribute("data-width") || "100%",
|
|
316
|
+
align: el.getAttribute("data-align") || "center"
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
];
|
|
321
|
+
},
|
|
322
|
+
renderHTML({ HTMLAttributes }) {
|
|
323
|
+
return ["img", mergeAttributes2(HTMLAttributes)];
|
|
324
|
+
},
|
|
325
|
+
addCommands() {
|
|
326
|
+
return {
|
|
327
|
+
setImageBlock: (attrs) => ({ commands }) => {
|
|
328
|
+
return commands.insertContent({
|
|
329
|
+
type: "imageBlock",
|
|
330
|
+
attrs: { src: attrs.src }
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
setImageBlockAt: (attrs) => ({ commands }) => {
|
|
334
|
+
return commands.insertContentAt(attrs.pos, {
|
|
335
|
+
type: "imageBlock",
|
|
336
|
+
attrs: { src: attrs.src }
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
setImageBlockAlign: (align) => ({ commands }) => commands.updateAttributes("imageBlock", { align }),
|
|
340
|
+
setImageBlockWidth: (width) => ({ commands }) => commands.updateAttributes("imageBlock", {
|
|
341
|
+
width: `${Math.max(0, Math.min(100, width))}%`
|
|
342
|
+
})
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
addNodeView() {
|
|
346
|
+
if (this.options.nodeView) {
|
|
347
|
+
return ReactNodeViewRenderer(this.options.nodeView);
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
},
|
|
351
|
+
addProseMirrorPlugins() {
|
|
352
|
+
return [
|
|
353
|
+
new Plugin2({
|
|
354
|
+
key: new PluginKey("imageBlockDrop"),
|
|
355
|
+
props: {
|
|
356
|
+
handleDOMEvents: {
|
|
357
|
+
drop: (view, event) => {
|
|
358
|
+
const hasFiles = event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length;
|
|
359
|
+
if (!hasFiles) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
const images = Array.from(event.dataTransfer.files).filter(
|
|
363
|
+
(file) => /image/i.test(file.type)
|
|
364
|
+
);
|
|
365
|
+
if (images.length === 0) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
event.preventDefault();
|
|
369
|
+
const { schema } = view.state;
|
|
370
|
+
const coordinates = view.posAtCoords({
|
|
371
|
+
left: event.clientX,
|
|
372
|
+
top: event.clientY
|
|
373
|
+
});
|
|
374
|
+
if (!coordinates) return false;
|
|
375
|
+
images.forEach(async (image) => {
|
|
376
|
+
if (this.options.uploadImage) {
|
|
377
|
+
try {
|
|
378
|
+
const placeholderNode = schema.nodes.imageBlock.create({
|
|
379
|
+
src: "",
|
|
380
|
+
loading: true
|
|
381
|
+
});
|
|
382
|
+
const placeholderTr = view.state.tr.insert(
|
|
383
|
+
coordinates.pos,
|
|
384
|
+
placeholderNode
|
|
385
|
+
);
|
|
386
|
+
view.dispatch(placeholderTr);
|
|
387
|
+
const url = await this.options.uploadImage(image);
|
|
388
|
+
const node = schema.nodes.imageBlock.create({ src: url });
|
|
389
|
+
const currentState = view.state;
|
|
390
|
+
let foundPos = -1;
|
|
391
|
+
currentState.doc.descendants((node2, pos) => {
|
|
392
|
+
if (node2.type.name === "imageBlock" && node2.attrs.loading) {
|
|
393
|
+
foundPos = pos;
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
if (foundPos !== -1) {
|
|
398
|
+
const transaction = view.state.tr.replaceWith(
|
|
399
|
+
foundPos,
|
|
400
|
+
foundPos + 1,
|
|
401
|
+
node
|
|
402
|
+
);
|
|
403
|
+
view.dispatch(transaction);
|
|
404
|
+
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error("Failed to upload image:", error);
|
|
407
|
+
const currentState = view.state;
|
|
408
|
+
let foundPos = -1;
|
|
409
|
+
currentState.doc.descendants((node, pos) => {
|
|
410
|
+
if (node.type.name === "imageBlock" && node.attrs.loading) {
|
|
411
|
+
foundPos = pos;
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
if (foundPos !== -1) {
|
|
416
|
+
const transaction = view.state.tr.delete(
|
|
417
|
+
foundPos,
|
|
418
|
+
foundPos + 1
|
|
419
|
+
);
|
|
420
|
+
view.dispatch(transaction);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return true;
|
|
426
|
+
},
|
|
427
|
+
paste: (view, event) => {
|
|
428
|
+
const hasFiles = event.clipboardData && event.clipboardData.files && event.clipboardData.files.length;
|
|
429
|
+
if (!hasFiles) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const images = Array.from(event.clipboardData.files).filter(
|
|
433
|
+
(file) => /image/i.test(file.type)
|
|
434
|
+
);
|
|
435
|
+
if (images.length === 0) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
event.preventDefault();
|
|
439
|
+
images.forEach(async (image) => {
|
|
440
|
+
if (this.options.uploadImage) {
|
|
441
|
+
try {
|
|
442
|
+
const placeholderNode = view.state.schema.nodes.imageBlock.create({
|
|
443
|
+
src: "",
|
|
444
|
+
loading: true
|
|
445
|
+
});
|
|
446
|
+
view.dispatch(
|
|
447
|
+
view.state.tr.replaceSelectionWith(placeholderNode)
|
|
448
|
+
);
|
|
449
|
+
const url = await this.options.uploadImage(image);
|
|
450
|
+
const node = view.state.schema.nodes.imageBlock.create({
|
|
451
|
+
src: url
|
|
452
|
+
});
|
|
453
|
+
const currentState = view.state;
|
|
454
|
+
let foundPos = -1;
|
|
455
|
+
currentState.doc.descendants((node2, pos) => {
|
|
456
|
+
if (node2.type.name === "imageBlock" && node2.attrs.loading) {
|
|
457
|
+
foundPos = pos;
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
if (foundPos !== -1) {
|
|
462
|
+
const transaction = view.state.tr.replaceWith(
|
|
463
|
+
foundPos,
|
|
464
|
+
foundPos + 1,
|
|
465
|
+
node
|
|
466
|
+
);
|
|
467
|
+
view.dispatch(transaction);
|
|
468
|
+
}
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.error("Failed to upload image:", error);
|
|
471
|
+
const currentState = view.state;
|
|
472
|
+
let foundPos = -1;
|
|
473
|
+
currentState.doc.descendants((node, pos) => {
|
|
474
|
+
if (node.type.name === "imageBlock" && node.attrs.loading) {
|
|
475
|
+
foundPos = pos;
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
if (foundPos !== -1) {
|
|
480
|
+
const transaction = view.state.tr.delete(
|
|
481
|
+
foundPos,
|
|
482
|
+
foundPos + 1
|
|
483
|
+
);
|
|
484
|
+
view.dispatch(transaction);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
];
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// src/headless/extensions/Mention/mention.tsx
|
|
499
|
+
import { ReactRenderer, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
|
|
500
|
+
import Mention from "@tiptap/extension-mention";
|
|
501
|
+
|
|
502
|
+
// src/headless/extensions/Mention/mention-command.tsx
|
|
503
|
+
import { forwardRef as forwardRef5, useEffect as useEffect2, useImperativeHandle, useState } from "react";
|
|
504
|
+
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
505
|
+
function Avatar({ src, fallback }) {
|
|
506
|
+
return /* @__PURE__ */ jsx6("div", { style: {
|
|
507
|
+
position: "relative",
|
|
508
|
+
display: "flex",
|
|
509
|
+
height: "24px",
|
|
510
|
+
width: "24px",
|
|
511
|
+
flexShrink: 0,
|
|
512
|
+
overflow: "hidden",
|
|
513
|
+
borderRadius: "9999px"
|
|
514
|
+
}, children: src ? /* @__PURE__ */ jsx6(
|
|
515
|
+
"img",
|
|
516
|
+
{
|
|
517
|
+
style: {
|
|
518
|
+
aspectRatio: "1",
|
|
519
|
+
height: "100%",
|
|
520
|
+
width: "100%"
|
|
521
|
+
},
|
|
522
|
+
src,
|
|
523
|
+
alt: fallback
|
|
524
|
+
}
|
|
525
|
+
) : /* @__PURE__ */ jsx6("div", { style: {
|
|
526
|
+
display: "flex",
|
|
527
|
+
height: "100%",
|
|
528
|
+
width: "100%",
|
|
529
|
+
alignItems: "center",
|
|
530
|
+
justifyContent: "center",
|
|
531
|
+
borderRadius: "9999px",
|
|
532
|
+
backgroundColor: "var(--muted)",
|
|
533
|
+
color: "var(--muted-foreground)",
|
|
534
|
+
fontSize: "10px",
|
|
535
|
+
fontWeight: 600
|
|
536
|
+
}, children: fallback }) });
|
|
537
|
+
}
|
|
538
|
+
var MentionCommand = forwardRef5((props, ref) => {
|
|
539
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
540
|
+
const selectItem = (index) => {
|
|
541
|
+
const item = props.items[index];
|
|
542
|
+
if (item) {
|
|
543
|
+
props.command(item);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const upHandler = () => {
|
|
547
|
+
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length);
|
|
548
|
+
};
|
|
549
|
+
const downHandler = () => {
|
|
550
|
+
setSelectedIndex((selectedIndex + 1) % props.items.length);
|
|
551
|
+
};
|
|
552
|
+
const enterHandler = () => {
|
|
553
|
+
selectItem(selectedIndex);
|
|
554
|
+
};
|
|
555
|
+
useEffect2(() => setSelectedIndex(0), [props.items]);
|
|
556
|
+
useImperativeHandle(ref, () => ({
|
|
557
|
+
onKeyDown: ({ event }) => {
|
|
558
|
+
if (event.key === "ArrowUp") {
|
|
559
|
+
upHandler();
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
if (event.key === "ArrowDown") {
|
|
563
|
+
downHandler();
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
if (event.key === "Enter") {
|
|
567
|
+
enterHandler();
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
}));
|
|
573
|
+
return /* @__PURE__ */ jsx6(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
id: "mention-list",
|
|
577
|
+
style: {
|
|
578
|
+
backgroundColor: "var(--popover)",
|
|
579
|
+
color: "var(--foreground)",
|
|
580
|
+
maxHeight: "300px",
|
|
581
|
+
minWidth: "280px",
|
|
582
|
+
overflow: "hidden",
|
|
583
|
+
overflowY: "auto",
|
|
584
|
+
borderRadius: "12px",
|
|
585
|
+
border: "1px solid var(--border)",
|
|
586
|
+
padding: "4px",
|
|
587
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
588
|
+
},
|
|
589
|
+
children: props.items.length ? props.items.map((item, index) => /* @__PURE__ */ jsxs2(
|
|
590
|
+
"button",
|
|
591
|
+
{
|
|
592
|
+
type: "button",
|
|
593
|
+
onClick: () => selectItem(index),
|
|
594
|
+
onMouseEnter: () => setSelectedIndex(index),
|
|
595
|
+
style: {
|
|
596
|
+
position: "relative",
|
|
597
|
+
display: "flex",
|
|
598
|
+
width: "100%",
|
|
599
|
+
cursor: "default",
|
|
600
|
+
userSelect: "none",
|
|
601
|
+
alignItems: "center",
|
|
602
|
+
gap: "8px",
|
|
603
|
+
borderRadius: "4px",
|
|
604
|
+
padding: "10px 8px",
|
|
605
|
+
fontSize: "14px",
|
|
606
|
+
outline: "none",
|
|
607
|
+
backgroundColor: index === selectedIndex ? "var(--accent)" : "transparent",
|
|
608
|
+
color: index === selectedIndex ? "var(--accent-foreground)" : "inherit",
|
|
609
|
+
border: "none",
|
|
610
|
+
textAlign: "left"
|
|
611
|
+
},
|
|
612
|
+
children: [
|
|
613
|
+
/* @__PURE__ */ jsx6(
|
|
614
|
+
Avatar,
|
|
615
|
+
{
|
|
616
|
+
src: item.avatar,
|
|
617
|
+
fallback: item.label.slice(0, 2).toUpperCase()
|
|
618
|
+
}
|
|
619
|
+
),
|
|
620
|
+
/* @__PURE__ */ jsx6("span", { style: {
|
|
621
|
+
flex: 1,
|
|
622
|
+
overflow: "hidden",
|
|
623
|
+
textOverflow: "ellipsis",
|
|
624
|
+
whiteSpace: "nowrap"
|
|
625
|
+
}, children: item.label })
|
|
626
|
+
]
|
|
627
|
+
},
|
|
628
|
+
item.id
|
|
629
|
+
)) : /* @__PURE__ */ jsx6("div", { style: {
|
|
630
|
+
padding: "24px 0",
|
|
631
|
+
textAlign: "center",
|
|
632
|
+
fontSize: "14px",
|
|
633
|
+
color: "var(--muted-foreground)"
|
|
634
|
+
}, children: "No users found" })
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
});
|
|
638
|
+
MentionCommand.displayName = "MentionCommand";
|
|
639
|
+
|
|
640
|
+
// src/headless/extensions/Mention/mention-node-view.tsx
|
|
641
|
+
import { NodeViewWrapper } from "@tiptap/react";
|
|
642
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
643
|
+
var MentionNodeView = (props) => {
|
|
644
|
+
const { node } = props;
|
|
645
|
+
const { id, label, avatar } = node.attrs;
|
|
646
|
+
return /* @__PURE__ */ jsxs3(
|
|
647
|
+
NodeViewWrapper,
|
|
648
|
+
{
|
|
649
|
+
as: "span",
|
|
650
|
+
style: {
|
|
651
|
+
display: "inline-flex",
|
|
652
|
+
alignItems: "center",
|
|
653
|
+
gap: "4px",
|
|
654
|
+
backgroundColor: "var(--accent)",
|
|
655
|
+
color: "var(--accent-foreground)",
|
|
656
|
+
padding: "2px 8px 2px 2px",
|
|
657
|
+
borderRadius: "12px",
|
|
658
|
+
fontSize: "14px",
|
|
659
|
+
fontWeight: 500,
|
|
660
|
+
verticalAlign: "middle"
|
|
661
|
+
},
|
|
662
|
+
children: [
|
|
663
|
+
avatar && /* @__PURE__ */ jsx7(
|
|
664
|
+
"img",
|
|
665
|
+
{
|
|
666
|
+
src: avatar,
|
|
667
|
+
alt: label || id,
|
|
668
|
+
style: {
|
|
669
|
+
width: "20px",
|
|
670
|
+
height: "20px",
|
|
671
|
+
borderRadius: "50%",
|
|
672
|
+
flexShrink: 0
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
),
|
|
676
|
+
!avatar && /* @__PURE__ */ jsx7(
|
|
677
|
+
"div",
|
|
678
|
+
{
|
|
679
|
+
style: {
|
|
680
|
+
width: "20px",
|
|
681
|
+
height: "20px",
|
|
682
|
+
borderRadius: "50%",
|
|
683
|
+
backgroundColor: "var(--muted)",
|
|
684
|
+
color: "var(--muted-foreground)",
|
|
685
|
+
display: "flex",
|
|
686
|
+
alignItems: "center",
|
|
687
|
+
justifyContent: "center",
|
|
688
|
+
fontSize: "10px",
|
|
689
|
+
fontWeight: 600,
|
|
690
|
+
flexShrink: 0
|
|
691
|
+
},
|
|
692
|
+
children: (label || id).slice(0, 2).toUpperCase()
|
|
693
|
+
}
|
|
694
|
+
),
|
|
695
|
+
/* @__PURE__ */ jsx7("span", { children: label || id })
|
|
696
|
+
]
|
|
697
|
+
}
|
|
698
|
+
);
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// src/headless/extensions/Mention/mention.tsx
|
|
702
|
+
var createMentionExtension = (options) => {
|
|
703
|
+
return Mention.extend({
|
|
704
|
+
addAttributes() {
|
|
705
|
+
return {
|
|
706
|
+
id: {
|
|
707
|
+
default: null,
|
|
708
|
+
parseHTML: (element) => element.getAttribute("data-id"),
|
|
709
|
+
renderHTML: (attributes) => {
|
|
710
|
+
if (!attributes.id) {
|
|
711
|
+
return {};
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
"data-id": attributes.id
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
label: {
|
|
719
|
+
default: null,
|
|
720
|
+
parseHTML: (element) => element.getAttribute("data-label"),
|
|
721
|
+
renderHTML: (attributes) => {
|
|
722
|
+
if (!attributes.label) {
|
|
723
|
+
return {};
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
"data-label": attributes.label
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
avatar: {
|
|
731
|
+
default: null,
|
|
732
|
+
parseHTML: (element) => element.getAttribute("data-avatar"),
|
|
733
|
+
renderHTML: (attributes) => {
|
|
734
|
+
if (!attributes.avatar) {
|
|
735
|
+
return {};
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
"data-avatar": attributes.avatar
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
},
|
|
744
|
+
addNodeView() {
|
|
745
|
+
return ReactNodeViewRenderer2(MentionNodeView);
|
|
746
|
+
}
|
|
747
|
+
}).configure({
|
|
748
|
+
HTMLAttributes: {
|
|
749
|
+
class: "mention"
|
|
750
|
+
},
|
|
751
|
+
suggestion: {
|
|
752
|
+
char: options?.char ?? "@",
|
|
753
|
+
items: async ({ query }) => {
|
|
754
|
+
if (!options?.items) return [];
|
|
755
|
+
const items = await options.items(query);
|
|
756
|
+
return items;
|
|
757
|
+
},
|
|
758
|
+
command: ({ editor, range, props: item }) => {
|
|
759
|
+
editor.chain().focus().insertContentAt(range, [
|
|
760
|
+
{
|
|
761
|
+
type: "mention",
|
|
762
|
+
attrs: {
|
|
763
|
+
id: item.id,
|
|
764
|
+
label: item.label,
|
|
765
|
+
avatar: item.avatar
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
]).run();
|
|
769
|
+
},
|
|
770
|
+
render: renderMentionSuggestion
|
|
771
|
+
},
|
|
772
|
+
renderLabel: options?.renderLabel ?? ((props) => {
|
|
773
|
+
return `@${props.node.attrs.label ?? props.node.attrs.id}`;
|
|
774
|
+
})
|
|
775
|
+
});
|
|
776
|
+
};
|
|
777
|
+
var renderMentionSuggestion = () => {
|
|
778
|
+
let component = null;
|
|
779
|
+
let container = null;
|
|
780
|
+
const destroy = () => {
|
|
781
|
+
component?.destroy();
|
|
782
|
+
component = null;
|
|
783
|
+
if (container) {
|
|
784
|
+
container.remove();
|
|
785
|
+
container = null;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
const updatePosition = (clientRect) => {
|
|
789
|
+
if (!container || !clientRect) return;
|
|
790
|
+
const top = Math.round(clientRect.bottom + 8);
|
|
791
|
+
const left = Math.round(clientRect.left);
|
|
792
|
+
container.style.top = `${top}px`;
|
|
793
|
+
container.style.left = `${left}px`;
|
|
794
|
+
};
|
|
795
|
+
return {
|
|
796
|
+
onStart: (props) => {
|
|
797
|
+
component = new ReactRenderer(MentionCommand, {
|
|
798
|
+
props: {
|
|
799
|
+
items: props.items ?? [],
|
|
800
|
+
command: props.command ?? (() => {
|
|
801
|
+
}),
|
|
802
|
+
query: props.query ?? ""
|
|
803
|
+
},
|
|
804
|
+
editor: props.editor
|
|
805
|
+
});
|
|
806
|
+
container = document.createElement("div");
|
|
807
|
+
container.style.position = "fixed";
|
|
808
|
+
container.style.zIndex = "9999";
|
|
809
|
+
document.body.appendChild(container);
|
|
810
|
+
container.appendChild(component.element);
|
|
811
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
812
|
+
if (rect) updatePosition(rect);
|
|
813
|
+
},
|
|
814
|
+
onUpdate: (props) => {
|
|
815
|
+
component?.updateProps({
|
|
816
|
+
items: props.items ?? [],
|
|
817
|
+
command: props.command ?? (() => {
|
|
818
|
+
}),
|
|
819
|
+
query: props.query ?? ""
|
|
820
|
+
});
|
|
821
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
822
|
+
if (rect) updatePosition(rect);
|
|
823
|
+
},
|
|
824
|
+
onKeyDown: ({ event }) => {
|
|
825
|
+
if (!component) return false;
|
|
826
|
+
if (event.key === "Escape") {
|
|
827
|
+
event.preventDefault();
|
|
828
|
+
event.stopPropagation();
|
|
829
|
+
destroy();
|
|
830
|
+
return true;
|
|
831
|
+
}
|
|
832
|
+
if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
|
|
833
|
+
event.preventDefault();
|
|
834
|
+
event.stopPropagation();
|
|
835
|
+
return component.ref?.onKeyDown?.({ event }) ?? false;
|
|
836
|
+
}
|
|
837
|
+
return false;
|
|
838
|
+
},
|
|
839
|
+
onExit: () => {
|
|
840
|
+
destroy();
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// src/headless/extensions/slash-command.tsx
|
|
846
|
+
import { ReactRenderer as ReactRenderer2 } from "@tiptap/react";
|
|
847
|
+
import Suggestion from "@tiptap/suggestion";
|
|
848
|
+
import { Extension } from "@tiptap/core";
|
|
849
|
+
var Command2 = Extension.create({
|
|
850
|
+
name: "slash-command",
|
|
851
|
+
addOptions() {
|
|
852
|
+
return {
|
|
853
|
+
suggestion: {
|
|
854
|
+
char: "/",
|
|
855
|
+
command: (ctx) => {
|
|
856
|
+
ctx.props.command({ editor: ctx.editor, range: ctx.range });
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
},
|
|
861
|
+
addProseMirrorPlugins() {
|
|
862
|
+
const base = this.options.suggestion ?? {};
|
|
863
|
+
return [
|
|
864
|
+
Suggestion({
|
|
865
|
+
editor: this.editor,
|
|
866
|
+
char: base.char ?? "/",
|
|
867
|
+
// Only trigger slash command at start of line or after whitespace
|
|
868
|
+
startOfLine: base.startOfLine ?? true,
|
|
869
|
+
items: base.items ?? (() => ["/"]),
|
|
870
|
+
command: (ctx) => {
|
|
871
|
+
if (typeof ctx?.props?.command === "function") {
|
|
872
|
+
ctx.props.command({ editor: ctx.editor, range: ctx.range });
|
|
873
|
+
}
|
|
874
|
+
},
|
|
875
|
+
...base
|
|
876
|
+
})
|
|
877
|
+
];
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
var renderItems = (elementRef) => {
|
|
881
|
+
let component = null;
|
|
882
|
+
let container = null;
|
|
883
|
+
const destroy = () => {
|
|
884
|
+
component?.destroy();
|
|
885
|
+
component = null;
|
|
886
|
+
if (container) {
|
|
887
|
+
container.remove();
|
|
888
|
+
container = null;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
const updatePosition = (clientRect) => {
|
|
892
|
+
if (!container || !clientRect) return;
|
|
893
|
+
const top = Math.round(clientRect.bottom + 8);
|
|
894
|
+
const left = Math.round(clientRect.left);
|
|
895
|
+
container.style.top = `${top}px`;
|
|
896
|
+
container.style.left = `${left}px`;
|
|
897
|
+
};
|
|
898
|
+
return {
|
|
899
|
+
onStart: (props) => {
|
|
900
|
+
const { selection } = props.editor.state;
|
|
901
|
+
const parentNode = selection.$from.node(selection.$from.depth);
|
|
902
|
+
const blockType = parentNode.type.name;
|
|
903
|
+
if (blockType === "codeBlock") return false;
|
|
904
|
+
const { $from } = selection;
|
|
905
|
+
const marks = $from.marks();
|
|
906
|
+
if (marks.some((mark) => mark.type.name === "code" || mark.type.name === "link")) {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
component = new ReactRenderer2(EditorCommandOut, {
|
|
910
|
+
props: {
|
|
911
|
+
query: props.query ?? "",
|
|
912
|
+
range: props.range
|
|
913
|
+
},
|
|
914
|
+
editor: props.editor
|
|
915
|
+
});
|
|
916
|
+
container = document.createElement("div");
|
|
917
|
+
container.style.position = "fixed";
|
|
918
|
+
container.style.zIndex = "9999";
|
|
919
|
+
container.style.minWidth = "240px";
|
|
920
|
+
(elementRef?.current ?? document.body).appendChild(container);
|
|
921
|
+
container.appendChild(component.element);
|
|
922
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
923
|
+
if (rect) updatePosition(rect);
|
|
924
|
+
},
|
|
925
|
+
onUpdate: (props) => {
|
|
926
|
+
component?.updateProps({
|
|
927
|
+
query: props.query ?? "",
|
|
928
|
+
range: props.range
|
|
929
|
+
});
|
|
930
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
931
|
+
if (rect) updatePosition(rect);
|
|
932
|
+
},
|
|
933
|
+
onKeyDown: ({ event }) => {
|
|
934
|
+
if (event.key === "Escape") {
|
|
935
|
+
destroy();
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
return false;
|
|
939
|
+
},
|
|
940
|
+
onExit: () => {
|
|
941
|
+
destroy();
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
};
|
|
945
|
+
var createSuggestionItems = (items) => items;
|
|
946
|
+
var handleCommandNavigation = (event) => {
|
|
947
|
+
if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
|
|
948
|
+
const slashCommand = document.querySelector("#slash-command");
|
|
949
|
+
if (slashCommand) return true;
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
export {
|
|
954
|
+
EditorRoot,
|
|
955
|
+
EditorContent,
|
|
956
|
+
EditorBubble,
|
|
957
|
+
EditorBubbleItem,
|
|
958
|
+
EditorCommandOut,
|
|
959
|
+
EditorCommand,
|
|
960
|
+
EditorCommandList,
|
|
961
|
+
EditorCommandItem,
|
|
962
|
+
EditorCommandEmpty,
|
|
963
|
+
CodeBlock,
|
|
964
|
+
Link,
|
|
965
|
+
ImageBlock,
|
|
966
|
+
MentionCommand,
|
|
967
|
+
createMentionExtension,
|
|
968
|
+
renderMentionSuggestion,
|
|
969
|
+
StarterKit,
|
|
970
|
+
Placeholder,
|
|
971
|
+
Command2 as Command,
|
|
972
|
+
renderItems,
|
|
973
|
+
createSuggestionItems,
|
|
974
|
+
handleCommandNavigation,
|
|
975
|
+
useCurrentEditor4 as useCurrentEditor
|
|
976
|
+
};
|
|
977
|
+
//# sourceMappingURL=chunk-I3CL5OVL.js.map
|