@kopexa/tiptap 17.1.0 → 17.2.0
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-SFG45HEU.mjs → chunk-2U5CQUZH.mjs} +1 -1
- package/dist/{chunk-QCRK7XVV.mjs → chunk-32SUXCAQ.mjs} +1 -1
- package/dist/chunk-5GFFTVMZ.mjs +62 -0
- package/dist/{chunk-B6SP3ADV.mjs → chunk-7SRL3P4B.mjs} +8 -8
- package/dist/{chunk-WKJGDCGA.mjs → chunk-7VGROP26.mjs} +1 -1
- package/dist/{chunk-6YGMTHOY.mjs → chunk-7VW67NVL.mjs} +1 -1
- package/dist/{chunk-SCL22AGK.mjs → chunk-BXHPO3T7.mjs} +14 -4
- package/dist/{chunk-EBFGDPWY.mjs → chunk-E5NW3MJZ.mjs} +4 -4
- package/dist/chunk-LMCQMSW2.mjs +345 -0
- package/dist/{chunk-EH2JHHGJ.mjs → chunk-NEHW62L7.mjs} +1 -3
- package/dist/{chunk-KTIMXK5V.mjs → chunk-NSYSECKW.mjs} +3 -3
- package/dist/{chunk-Z6GJ2GKW.mjs → chunk-QAE2D4KV.mjs} +13 -13
- package/dist/{chunk-IY4SCXZN.mjs → chunk-UU6JK5HX.mjs} +105 -11
- package/dist/{chunk-DSK3HK5D.mjs → chunk-UVHVCION.mjs} +14 -4
- package/dist/{chunk-MYO2CUDR.mjs → chunk-VF3G2URZ.mjs} +10 -2
- package/dist/chunk-VRQ6OSAZ.mjs +76 -0
- package/dist/chunk-WAAH3NLG.mjs +77 -0
- package/dist/context/editor-file-context.d.mts +70 -0
- package/dist/context/editor-file-context.d.ts +70 -0
- package/dist/context/editor-file-context.js +96 -0
- package/dist/context/editor-file-context.mjs +12 -0
- package/dist/extensions/callout/callout-view.js +8 -1
- package/dist/extensions/callout/callout-view.mjs +1 -1
- package/dist/extensions/callout/index.js +8 -1
- package/dist/extensions/callout/index.mjs +2 -2
- package/dist/extensions/image/image-view.d.mts +15 -0
- package/dist/extensions/image/image-view.d.ts +15 -0
- package/dist/extensions/image/image-view.js +423 -0
- package/dist/extensions/image/image-view.mjs +12 -0
- package/dist/extensions/image/index.d.mts +66 -0
- package/dist/extensions/image/index.d.ts +66 -0
- package/dist/extensions/image/index.js +495 -0
- package/dist/extensions/image/index.mjs +16 -0
- package/dist/extensions/image/messages.d.mts +56 -0
- package/dist/extensions/image/messages.d.ts +56 -0
- package/dist/extensions/image/messages.js +85 -0
- package/dist/extensions/image/messages.mjs +7 -0
- package/dist/extensions/math/index.js +20 -6
- package/dist/extensions/math/index.mjs +4 -4
- package/dist/extensions/math/inline-math-view.js +10 -3
- package/dist/extensions/math/inline-math-view.mjs +1 -1
- package/dist/extensions/math/inline-math.js +10 -3
- package/dist/extensions/math/inline-math.mjs +2 -2
- package/dist/extensions/math/math-block-view.js +10 -3
- package/dist/extensions/math/math-block-view.mjs +1 -1
- package/dist/hooks/use-create-editor.d.mts +12 -2
- package/dist/hooks/use-create-editor.d.ts +12 -2
- package/dist/hooks/use-create-editor.js +809 -237
- package/dist/hooks/use-create-editor.mjs +14 -10
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +1261 -630
- package/dist/index.mjs +51 -37
- package/dist/presets/basic/editor-header.mjs +16 -16
- package/dist/presets/basic/index.d.mts +2 -0
- package/dist/presets/basic/index.d.ts +2 -0
- package/dist/presets/basic/index.js +1198 -628
- package/dist/presets/basic/index.mjs +36 -32
- package/dist/ui/bubble-menu/index.mjs +5 -5
- package/dist/ui/color-highlight-popover/color-highlight-popover.mjs +2 -2
- package/dist/ui/color-highlight-popover/index.mjs +2 -2
- package/dist/ui/copy-anchor-link-button/use-scroll-to-hash.mjs +2 -2
- package/dist/ui/link-popover/index.mjs +3 -3
- package/dist/ui/link-popover/link-popover.mjs +3 -3
- package/dist/ui/link-popover/use-link-popover.mjs +2 -2
- package/dist/ui/slash-dropdown-menu/index.js +1 -3
- package/dist/ui/slash-dropdown-menu/index.mjs +6 -6
- package/dist/ui/slash-dropdown-menu/slash-dropdown-menu.js +1 -3
- package/dist/ui/slash-dropdown-menu/slash-dropdown-menu.mjs +4 -4
- package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.js +1 -3
- package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.mjs +1 -1
- package/dist/ui/suggestion-menu/index.mjs +2 -2
- package/dist/ui/suggestion-menu/suggestion-menu.mjs +2 -2
- package/package.json +25 -24
- package/dist/{chunk-XL5FS7LN.mjs → chunk-C5RQWJKE.mjs} +3 -3
- package/dist/{chunk-JNL4KY45.mjs → chunk-DZLGLP7R.mjs} +3 -3
- package/dist/{chunk-LHXRE26G.mjs → chunk-VTKJPVNM.mjs} +3 -3
- package/dist/{chunk-XLSZK3WJ.mjs → chunk-ZYFCSR3E.mjs} +3 -3
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Selection
|
|
4
|
+
} from "./chunk-U5XAL46P.mjs";
|
|
5
|
+
import {
|
|
6
|
+
TocNode
|
|
7
|
+
} from "./chunk-IFXRPGIJ.mjs";
|
|
2
8
|
import {
|
|
3
9
|
TrailingNode
|
|
4
10
|
} from "./chunk-H6LC4LDQ.mjs";
|
|
11
|
+
import {
|
|
12
|
+
ImageNode
|
|
13
|
+
} from "./chunk-WAAH3NLG.mjs";
|
|
5
14
|
import {
|
|
6
15
|
Link
|
|
7
16
|
} from "./chunk-YLDL3VYY.mjs";
|
|
8
17
|
import {
|
|
9
18
|
MathBlock
|
|
10
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2U5CQUZH.mjs";
|
|
11
20
|
import {
|
|
12
21
|
InlineMath
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import {
|
|
15
|
-
Selection
|
|
16
|
-
} from "./chunk-U5XAL46P.mjs";
|
|
17
|
-
import {
|
|
18
|
-
TocNode
|
|
19
|
-
} from "./chunk-IFXRPGIJ.mjs";
|
|
22
|
+
} from "./chunk-7VW67NVL.mjs";
|
|
20
23
|
import {
|
|
21
24
|
UiState
|
|
22
25
|
} from "./chunk-KR42JAVB.mjs";
|
|
@@ -25,12 +28,16 @@ import {
|
|
|
25
28
|
} from "./chunk-3VRQUYYW.mjs";
|
|
26
29
|
import {
|
|
27
30
|
CalloutNode
|
|
28
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-32SUXCAQ.mjs";
|
|
32
|
+
import {
|
|
33
|
+
useEditorFile
|
|
34
|
+
} from "./chunk-VRQ6OSAZ.mjs";
|
|
29
35
|
|
|
30
36
|
// src/hooks/use-create-editor.ts
|
|
31
37
|
import { CodeKit } from "@kopexa/extension-code";
|
|
32
38
|
import { ControlKit } from "@kopexa/extension-controlref";
|
|
33
39
|
import { TableKit } from "@kopexa/extension-table";
|
|
40
|
+
import { FileHandler } from "@tiptap/extension-file-handler";
|
|
34
41
|
import { Highlight } from "@tiptap/extension-highlight";
|
|
35
42
|
import InvisibleCharacters from "@tiptap/extension-invisible-characters";
|
|
36
43
|
import { TaskItem, TaskList } from "@tiptap/extension-list";
|
|
@@ -57,8 +64,11 @@ var useCreateEditor = ({
|
|
|
57
64
|
onChange,
|
|
58
65
|
enableControls = false,
|
|
59
66
|
controlResolver,
|
|
67
|
+
fileHandler: fileHandlerProp,
|
|
60
68
|
...options
|
|
61
69
|
}) => {
|
|
70
|
+
const fileHandlerFromContext = useEditorFile();
|
|
71
|
+
const fileHandler = fileHandlerProp != null ? fileHandlerProp : fileHandlerFromContext;
|
|
62
72
|
const editor = useEditor({
|
|
63
73
|
editorProps: {
|
|
64
74
|
attributes: {
|
|
@@ -74,7 +84,8 @@ var useCreateEditor = ({
|
|
|
74
84
|
editable,
|
|
75
85
|
placeholder,
|
|
76
86
|
enableControls,
|
|
77
|
-
controlResolver
|
|
87
|
+
controlResolver,
|
|
88
|
+
fileHandler
|
|
78
89
|
}),
|
|
79
90
|
editable,
|
|
80
91
|
onUpdate: ({ editor: editor2 }) => {
|
|
@@ -94,7 +105,8 @@ function getExtensions({
|
|
|
94
105
|
editable,
|
|
95
106
|
placeholder,
|
|
96
107
|
enableControls = false,
|
|
97
|
-
controlResolver
|
|
108
|
+
controlResolver,
|
|
109
|
+
fileHandler
|
|
98
110
|
}) {
|
|
99
111
|
const extensions = [
|
|
100
112
|
StarterKit.configure({
|
|
@@ -146,6 +158,8 @@ function getExtensions({
|
|
|
146
158
|
CalloutNode,
|
|
147
159
|
MathBlock,
|
|
148
160
|
InlineMath,
|
|
161
|
+
// Image support - always include for display
|
|
162
|
+
ImageNode,
|
|
149
163
|
Placeholder.configure({
|
|
150
164
|
placeholder,
|
|
151
165
|
emptyNodeClass: "is-empty with-slash"
|
|
@@ -154,8 +168,88 @@ function getExtensions({
|
|
|
154
168
|
if (enableControls) {
|
|
155
169
|
extensions.push(ControlKit.configure({ resolver: controlResolver }));
|
|
156
170
|
}
|
|
171
|
+
if (fileHandler) {
|
|
172
|
+
extensions.push(
|
|
173
|
+
FileHandler.configure({
|
|
174
|
+
allowedMimeTypes: [
|
|
175
|
+
"image/jpeg",
|
|
176
|
+
"image/png",
|
|
177
|
+
"image/gif",
|
|
178
|
+
"image/webp",
|
|
179
|
+
"image/svg+xml"
|
|
180
|
+
],
|
|
181
|
+
onDrop: (editor, files, pos) => {
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
handleFileUpload(editor, file, fileHandler, pos);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
onPaste: (editor, files) => {
|
|
187
|
+
for (const file of files) {
|
|
188
|
+
handleFileUpload(editor, file, fileHandler);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
}
|
|
157
194
|
return extensions;
|
|
158
195
|
}
|
|
196
|
+
async function handleFileUpload(editor, file, fileHandler, pos) {
|
|
197
|
+
if (!editor) return;
|
|
198
|
+
const tempId = `uploading-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
199
|
+
const insertPos = pos != null ? pos : editor.state.selection.anchor;
|
|
200
|
+
editor.chain().focus().insertContentAt(insertPos, {
|
|
201
|
+
type: "image",
|
|
202
|
+
attrs: {
|
|
203
|
+
src: tempId,
|
|
204
|
+
uploadState: "uploading",
|
|
205
|
+
uploadProgress: 0
|
|
206
|
+
}
|
|
207
|
+
}).run();
|
|
208
|
+
try {
|
|
209
|
+
const ref = await fileHandler.upload(file, (percent) => {
|
|
210
|
+
editor.state.doc.descendants((node, nodePos) => {
|
|
211
|
+
if (node.type.name === "image" && node.attrs.src === tempId) {
|
|
212
|
+
editor.view.dispatch(
|
|
213
|
+
editor.state.tr.setNodeMarkup(nodePos, void 0, {
|
|
214
|
+
...node.attrs,
|
|
215
|
+
uploadProgress: percent
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
editor.state.doc.descendants((node, nodePos) => {
|
|
224
|
+
if (node.type.name === "image" && node.attrs.src === tempId) {
|
|
225
|
+
editor.view.dispatch(
|
|
226
|
+
editor.state.tr.setNodeMarkup(nodePos, void 0, {
|
|
227
|
+
src: ref,
|
|
228
|
+
uploadState: null,
|
|
229
|
+
uploadProgress: null
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
});
|
|
236
|
+
} catch (error) {
|
|
237
|
+
editor.state.doc.descendants((node, nodePos) => {
|
|
238
|
+
if (node.type.name === "image" && node.attrs.src === tempId) {
|
|
239
|
+
editor.view.dispatch(
|
|
240
|
+
editor.state.tr.setNodeMarkup(nodePos, void 0, {
|
|
241
|
+
...node.attrs,
|
|
242
|
+
uploadState: "error",
|
|
243
|
+
uploadProgress: null
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
});
|
|
250
|
+
console.error("[FileHandler] Upload failed:", error);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
159
253
|
|
|
160
254
|
export {
|
|
161
255
|
useCreateEditor,
|
|
@@ -8,7 +8,10 @@ import { Button, IconButton } from "@kopexa/button";
|
|
|
8
8
|
import { Dialog } from "@kopexa/dialog";
|
|
9
9
|
import { EditIcon } from "@kopexa/icons";
|
|
10
10
|
import { Label } from "@kopexa/label";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
NodeViewWrapper,
|
|
13
|
+
useEditorState
|
|
14
|
+
} from "@tiptap/react";
|
|
12
15
|
import katex from "katex";
|
|
13
16
|
import "katex/dist/katex.min.css";
|
|
14
17
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
@@ -18,6 +21,13 @@ function MathBlockView({ editor, node, getPos }) {
|
|
|
18
21
|
const intl = useIntl();
|
|
19
22
|
const attrs = node.attrs;
|
|
20
23
|
const { latex = "" } = attrs;
|
|
24
|
+
const isEditable = useEditorState({
|
|
25
|
+
editor,
|
|
26
|
+
selector: ({ editor: e }) => {
|
|
27
|
+
var _a;
|
|
28
|
+
return (_a = e == null ? void 0 : e.isEditable) != null ? _a : false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
21
31
|
const [isOpen, setIsOpen] = useState(false);
|
|
22
32
|
const [localLatex, setLocalLatex] = useState(latex);
|
|
23
33
|
const [error, setError] = useState(null);
|
|
@@ -70,11 +80,11 @@ function MathBlockView({ editor, node, getPos }) {
|
|
|
70
80
|
(e) => {
|
|
71
81
|
e.stopPropagation();
|
|
72
82
|
e.preventDefault();
|
|
73
|
-
if (
|
|
83
|
+
if (isEditable) {
|
|
74
84
|
setIsOpen(true);
|
|
75
85
|
}
|
|
76
86
|
},
|
|
77
|
-
[
|
|
87
|
+
[isEditable]
|
|
78
88
|
);
|
|
79
89
|
const isEmpty = !latex;
|
|
80
90
|
return /* @__PURE__ */ jsxs(
|
|
@@ -102,7 +112,7 @@ function MathBlockView({ editor, node, getPos }) {
|
|
|
102
112
|
) : /* @__PURE__ */ jsx("div", { className: "text-destructive text-sm", children: intl.formatMessage(messages.invalid_latex) })
|
|
103
113
|
}
|
|
104
114
|
),
|
|
105
|
-
|
|
115
|
+
isEditable && !isEmpty && /* @__PURE__ */ jsx(
|
|
106
116
|
IconButton,
|
|
107
117
|
{
|
|
108
118
|
size: "sm",
|
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
import { callout } from "@kopexa/theme";
|
|
15
15
|
import {
|
|
16
16
|
NodeViewContent,
|
|
17
|
-
NodeViewWrapper
|
|
17
|
+
NodeViewWrapper,
|
|
18
|
+
useEditorState
|
|
18
19
|
} from "@tiptap/react";
|
|
19
20
|
import { useMemo } from "react";
|
|
20
21
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -35,6 +36,13 @@ function getVariantIcon(variant, iconClass) {
|
|
|
35
36
|
function CalloutNodeView({ editor, node, getPos }) {
|
|
36
37
|
const attrs = node.attrs;
|
|
37
38
|
const { variant = "info", title } = attrs;
|
|
39
|
+
const isEditable = useEditorState({
|
|
40
|
+
editor,
|
|
41
|
+
selector: ({ editor: e }) => {
|
|
42
|
+
var _a;
|
|
43
|
+
return (_a = e == null ? void 0 : e.isEditable) != null ? _a : false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
38
46
|
const styles = useMemo(
|
|
39
47
|
() => callout({
|
|
40
48
|
variant,
|
|
@@ -55,7 +63,7 @@ function CalloutNodeView({ editor, node, getPos }) {
|
|
|
55
63
|
title && /* @__PURE__ */ jsx("div", { className: styles.title(), children: title }),
|
|
56
64
|
/* @__PURE__ */ jsx(NodeViewContent, { className: "callout-content" })
|
|
57
65
|
] }),
|
|
58
|
-
|
|
66
|
+
isEditable && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2", children: /* @__PURE__ */ jsx(
|
|
59
67
|
CalloutSettings,
|
|
60
68
|
{
|
|
61
69
|
editor,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/context/editor-file-context.tsx
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef
|
|
10
|
+
} from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
var EditorFileContext = createContext(null);
|
|
13
|
+
var DEFAULT_CACHE_BUFFER = 2 * 60 * 1e3;
|
|
14
|
+
var DEFAULT_CACHE_TTL = 10 * 60 * 1e3;
|
|
15
|
+
var defaultIsReference = (src) => {
|
|
16
|
+
if (!src) return false;
|
|
17
|
+
if (src.startsWith("http://") || src.startsWith("https://")) return false;
|
|
18
|
+
if (src.startsWith("data:")) return false;
|
|
19
|
+
if (src.startsWith("blob:")) return false;
|
|
20
|
+
return true;
|
|
21
|
+
};
|
|
22
|
+
function EditorFileProvider({
|
|
23
|
+
children,
|
|
24
|
+
onUpload,
|
|
25
|
+
onResolve,
|
|
26
|
+
isReference = defaultIsReference,
|
|
27
|
+
cacheBuffer = DEFAULT_CACHE_BUFFER,
|
|
28
|
+
defaultCacheTtl = DEFAULT_CACHE_TTL
|
|
29
|
+
}) {
|
|
30
|
+
const cacheRef = useRef(/* @__PURE__ */ new Map());
|
|
31
|
+
const resolve = useCallback(
|
|
32
|
+
async (ref) => {
|
|
33
|
+
const cache = cacheRef.current;
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const cached = cache.get(ref);
|
|
36
|
+
if (cached && cached.expiresAt > now + cacheBuffer) {
|
|
37
|
+
return cached.url;
|
|
38
|
+
}
|
|
39
|
+
const url = await onResolve(ref);
|
|
40
|
+
cache.set(ref, {
|
|
41
|
+
url,
|
|
42
|
+
expiresAt: now + defaultCacheTtl
|
|
43
|
+
});
|
|
44
|
+
return url;
|
|
45
|
+
},
|
|
46
|
+
[onResolve, cacheBuffer, defaultCacheTtl]
|
|
47
|
+
);
|
|
48
|
+
const value = useMemo(
|
|
49
|
+
() => ({
|
|
50
|
+
isAvailable: true,
|
|
51
|
+
upload: onUpload,
|
|
52
|
+
resolve,
|
|
53
|
+
isReference
|
|
54
|
+
}),
|
|
55
|
+
[onUpload, resolve, isReference]
|
|
56
|
+
);
|
|
57
|
+
return /* @__PURE__ */ jsx(EditorFileContext.Provider, { value, children });
|
|
58
|
+
}
|
|
59
|
+
function useEditorFile() {
|
|
60
|
+
return useContext(EditorFileContext);
|
|
61
|
+
}
|
|
62
|
+
function useEditorFileRequired() {
|
|
63
|
+
const context = useContext(EditorFileContext);
|
|
64
|
+
if (!context) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
"useEditorFileRequired must be used within an EditorFileProvider"
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return context;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
EditorFileProvider,
|
|
74
|
+
useEditorFile,
|
|
75
|
+
useEditorFileRequired
|
|
76
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
ImageNodeView
|
|
4
|
+
} from "./chunk-LMCQMSW2.mjs";
|
|
5
|
+
|
|
6
|
+
// src/extensions/image/index.ts
|
|
7
|
+
import { mergeAttributes, Node } from "@tiptap/core";
|
|
8
|
+
import { ReactNodeViewRenderer } from "@tiptap/react";
|
|
9
|
+
var ImageNode = Node.create({
|
|
10
|
+
name: "image",
|
|
11
|
+
group: "block",
|
|
12
|
+
atom: true,
|
|
13
|
+
draggable: true,
|
|
14
|
+
addOptions() {
|
|
15
|
+
return {
|
|
16
|
+
allowBase64: true,
|
|
17
|
+
HTMLAttributes: {}
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
addAttributes() {
|
|
21
|
+
return {
|
|
22
|
+
src: {
|
|
23
|
+
default: null
|
|
24
|
+
},
|
|
25
|
+
alt: {
|
|
26
|
+
default: null
|
|
27
|
+
},
|
|
28
|
+
title: {
|
|
29
|
+
default: null
|
|
30
|
+
},
|
|
31
|
+
width: {
|
|
32
|
+
default: null
|
|
33
|
+
},
|
|
34
|
+
height: {
|
|
35
|
+
default: null
|
|
36
|
+
},
|
|
37
|
+
uploadState: {
|
|
38
|
+
default: null,
|
|
39
|
+
rendered: false
|
|
40
|
+
},
|
|
41
|
+
uploadProgress: {
|
|
42
|
+
default: null,
|
|
43
|
+
rendered: false
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
parseHTML() {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
tag: "img[src]"
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
},
|
|
54
|
+
renderHTML({ HTMLAttributes }) {
|
|
55
|
+
return [
|
|
56
|
+
"img",
|
|
57
|
+
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)
|
|
58
|
+
];
|
|
59
|
+
},
|
|
60
|
+
addNodeView() {
|
|
61
|
+
return ReactNodeViewRenderer(ImageNodeView);
|
|
62
|
+
},
|
|
63
|
+
addCommands() {
|
|
64
|
+
return {
|
|
65
|
+
setImage: (options) => ({ commands }) => {
|
|
66
|
+
return commands.insertContent({
|
|
67
|
+
type: this.name,
|
|
68
|
+
attrs: options
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
ImageNode
|
|
77
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface EditorFileHandler {
|
|
5
|
+
/**
|
|
6
|
+
* Upload a file and return a reference string.
|
|
7
|
+
* The reference format is determined by the backend (e.g., UUID, custom scheme).
|
|
8
|
+
* @param file The file to upload
|
|
9
|
+
* @param onProgress Optional progress callback (0-100)
|
|
10
|
+
* @returns Promise resolving to a reference string
|
|
11
|
+
*/
|
|
12
|
+
upload: (file: File, onProgress?: (percent: number) => void) => Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a reference to a displayable URL.
|
|
15
|
+
* Typically returns a signed URL from your storage backend.
|
|
16
|
+
* @param ref The reference string from upload
|
|
17
|
+
* @returns Promise resolving to a displayable URL
|
|
18
|
+
*/
|
|
19
|
+
resolve: (ref: string) => Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a src string is a reference that needs resolving.
|
|
22
|
+
* Returns false for already-valid URLs (https://, data:, etc.)
|
|
23
|
+
* @param src The src attribute value
|
|
24
|
+
* @returns true if this needs to be resolved
|
|
25
|
+
*/
|
|
26
|
+
isReference: (src: string) => boolean;
|
|
27
|
+
}
|
|
28
|
+
interface EditorFileContextValue extends EditorFileHandler {
|
|
29
|
+
/**
|
|
30
|
+
* Whether file handling is available
|
|
31
|
+
*/
|
|
32
|
+
isAvailable: true;
|
|
33
|
+
}
|
|
34
|
+
interface EditorFileProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Upload handler - called when user pastes/drops a file
|
|
38
|
+
*/
|
|
39
|
+
onUpload: EditorFileHandler["upload"];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve handler - called to get displayable URL from reference
|
|
42
|
+
*/
|
|
43
|
+
onResolve: EditorFileHandler["resolve"];
|
|
44
|
+
/**
|
|
45
|
+
* Check if src needs resolving (default: not starting with http/https/data)
|
|
46
|
+
*/
|
|
47
|
+
isReference?: EditorFileHandler["isReference"];
|
|
48
|
+
/**
|
|
49
|
+
* Cache TTL buffer in ms (default: 2 minutes)
|
|
50
|
+
* URLs are refreshed this much before actual expiry
|
|
51
|
+
*/
|
|
52
|
+
cacheBuffer?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Default cache TTL in ms if not determined from response (default: 10 minutes)
|
|
55
|
+
*/
|
|
56
|
+
defaultCacheTtl?: number;
|
|
57
|
+
}
|
|
58
|
+
declare function EditorFileProvider({ children, onUpload, onResolve, isReference, cacheBuffer, defaultCacheTtl, }: EditorFileProviderProps): react_jsx_runtime.JSX.Element;
|
|
59
|
+
/**
|
|
60
|
+
* Hook to access file handling capabilities.
|
|
61
|
+
* Returns null if no EditorFileProvider is present (files not supported).
|
|
62
|
+
*/
|
|
63
|
+
declare function useEditorFile(): EditorFileContextValue | null;
|
|
64
|
+
/**
|
|
65
|
+
* Hook that throws if file handling is not available.
|
|
66
|
+
* Use this in components that require file support.
|
|
67
|
+
*/
|
|
68
|
+
declare function useEditorFileRequired(): EditorFileContextValue;
|
|
69
|
+
|
|
70
|
+
export { type EditorFileHandler, EditorFileProvider, type EditorFileProviderProps, useEditorFile, useEditorFileRequired };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface EditorFileHandler {
|
|
5
|
+
/**
|
|
6
|
+
* Upload a file and return a reference string.
|
|
7
|
+
* The reference format is determined by the backend (e.g., UUID, custom scheme).
|
|
8
|
+
* @param file The file to upload
|
|
9
|
+
* @param onProgress Optional progress callback (0-100)
|
|
10
|
+
* @returns Promise resolving to a reference string
|
|
11
|
+
*/
|
|
12
|
+
upload: (file: File, onProgress?: (percent: number) => void) => Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a reference to a displayable URL.
|
|
15
|
+
* Typically returns a signed URL from your storage backend.
|
|
16
|
+
* @param ref The reference string from upload
|
|
17
|
+
* @returns Promise resolving to a displayable URL
|
|
18
|
+
*/
|
|
19
|
+
resolve: (ref: string) => Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a src string is a reference that needs resolving.
|
|
22
|
+
* Returns false for already-valid URLs (https://, data:, etc.)
|
|
23
|
+
* @param src The src attribute value
|
|
24
|
+
* @returns true if this needs to be resolved
|
|
25
|
+
*/
|
|
26
|
+
isReference: (src: string) => boolean;
|
|
27
|
+
}
|
|
28
|
+
interface EditorFileContextValue extends EditorFileHandler {
|
|
29
|
+
/**
|
|
30
|
+
* Whether file handling is available
|
|
31
|
+
*/
|
|
32
|
+
isAvailable: true;
|
|
33
|
+
}
|
|
34
|
+
interface EditorFileProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Upload handler - called when user pastes/drops a file
|
|
38
|
+
*/
|
|
39
|
+
onUpload: EditorFileHandler["upload"];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve handler - called to get displayable URL from reference
|
|
42
|
+
*/
|
|
43
|
+
onResolve: EditorFileHandler["resolve"];
|
|
44
|
+
/**
|
|
45
|
+
* Check if src needs resolving (default: not starting with http/https/data)
|
|
46
|
+
*/
|
|
47
|
+
isReference?: EditorFileHandler["isReference"];
|
|
48
|
+
/**
|
|
49
|
+
* Cache TTL buffer in ms (default: 2 minutes)
|
|
50
|
+
* URLs are refreshed this much before actual expiry
|
|
51
|
+
*/
|
|
52
|
+
cacheBuffer?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Default cache TTL in ms if not determined from response (default: 10 minutes)
|
|
55
|
+
*/
|
|
56
|
+
defaultCacheTtl?: number;
|
|
57
|
+
}
|
|
58
|
+
declare function EditorFileProvider({ children, onUpload, onResolve, isReference, cacheBuffer, defaultCacheTtl, }: EditorFileProviderProps): react_jsx_runtime.JSX.Element;
|
|
59
|
+
/**
|
|
60
|
+
* Hook to access file handling capabilities.
|
|
61
|
+
* Returns null if no EditorFileProvider is present (files not supported).
|
|
62
|
+
*/
|
|
63
|
+
declare function useEditorFile(): EditorFileContextValue | null;
|
|
64
|
+
/**
|
|
65
|
+
* Hook that throws if file handling is not available.
|
|
66
|
+
* Use this in components that require file support.
|
|
67
|
+
*/
|
|
68
|
+
declare function useEditorFileRequired(): EditorFileContextValue;
|
|
69
|
+
|
|
70
|
+
export { type EditorFileHandler, EditorFileProvider, type EditorFileProviderProps, useEditorFile, useEditorFileRequired };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/context/editor-file-context.tsx
|
|
23
|
+
var editor_file_context_exports = {};
|
|
24
|
+
__export(editor_file_context_exports, {
|
|
25
|
+
EditorFileProvider: () => EditorFileProvider,
|
|
26
|
+
useEditorFile: () => useEditorFile,
|
|
27
|
+
useEditorFileRequired: () => useEditorFileRequired
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(editor_file_context_exports);
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
var EditorFileContext = (0, import_react.createContext)(null);
|
|
33
|
+
var DEFAULT_CACHE_BUFFER = 2 * 60 * 1e3;
|
|
34
|
+
var DEFAULT_CACHE_TTL = 10 * 60 * 1e3;
|
|
35
|
+
var defaultIsReference = (src) => {
|
|
36
|
+
if (!src) return false;
|
|
37
|
+
if (src.startsWith("http://") || src.startsWith("https://")) return false;
|
|
38
|
+
if (src.startsWith("data:")) return false;
|
|
39
|
+
if (src.startsWith("blob:")) return false;
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
function EditorFileProvider({
|
|
43
|
+
children,
|
|
44
|
+
onUpload,
|
|
45
|
+
onResolve,
|
|
46
|
+
isReference = defaultIsReference,
|
|
47
|
+
cacheBuffer = DEFAULT_CACHE_BUFFER,
|
|
48
|
+
defaultCacheTtl = DEFAULT_CACHE_TTL
|
|
49
|
+
}) {
|
|
50
|
+
const cacheRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
51
|
+
const resolve = (0, import_react.useCallback)(
|
|
52
|
+
async (ref) => {
|
|
53
|
+
const cache = cacheRef.current;
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const cached = cache.get(ref);
|
|
56
|
+
if (cached && cached.expiresAt > now + cacheBuffer) {
|
|
57
|
+
return cached.url;
|
|
58
|
+
}
|
|
59
|
+
const url = await onResolve(ref);
|
|
60
|
+
cache.set(ref, {
|
|
61
|
+
url,
|
|
62
|
+
expiresAt: now + defaultCacheTtl
|
|
63
|
+
});
|
|
64
|
+
return url;
|
|
65
|
+
},
|
|
66
|
+
[onResolve, cacheBuffer, defaultCacheTtl]
|
|
67
|
+
);
|
|
68
|
+
const value = (0, import_react.useMemo)(
|
|
69
|
+
() => ({
|
|
70
|
+
isAvailable: true,
|
|
71
|
+
upload: onUpload,
|
|
72
|
+
resolve,
|
|
73
|
+
isReference
|
|
74
|
+
}),
|
|
75
|
+
[onUpload, resolve, isReference]
|
|
76
|
+
);
|
|
77
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditorFileContext.Provider, { value, children });
|
|
78
|
+
}
|
|
79
|
+
function useEditorFile() {
|
|
80
|
+
return (0, import_react.useContext)(EditorFileContext);
|
|
81
|
+
}
|
|
82
|
+
function useEditorFileRequired() {
|
|
83
|
+
const context = (0, import_react.useContext)(EditorFileContext);
|
|
84
|
+
if (!context) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"useEditorFileRequired must be used within an EditorFileProvider"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return context;
|
|
90
|
+
}
|
|
91
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
92
|
+
0 && (module.exports = {
|
|
93
|
+
EditorFileProvider,
|
|
94
|
+
useEditorFile,
|
|
95
|
+
useEditorFileRequired
|
|
96
|
+
});
|
|
@@ -227,6 +227,13 @@ function getVariantIcon(variant, iconClass) {
|
|
|
227
227
|
function CalloutNodeView({ editor, node, getPos }) {
|
|
228
228
|
const attrs = node.attrs;
|
|
229
229
|
const { variant = "info", title } = attrs;
|
|
230
|
+
const isEditable = (0, import_react2.useEditorState)({
|
|
231
|
+
editor,
|
|
232
|
+
selector: ({ editor: e }) => {
|
|
233
|
+
var _a;
|
|
234
|
+
return (_a = e == null ? void 0 : e.isEditable) != null ? _a : false;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
230
237
|
const styles = (0, import_react3.useMemo)(
|
|
231
238
|
() => (0, import_theme.callout)({
|
|
232
239
|
variant,
|
|
@@ -247,7 +254,7 @@ function CalloutNodeView({ editor, node, getPos }) {
|
|
|
247
254
|
title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.title(), children: title }),
|
|
248
255
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.NodeViewContent, { className: "callout-content" })
|
|
249
256
|
] }),
|
|
250
|
-
|
|
257
|
+
isEditable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute top-2 right-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
251
258
|
CalloutSettings,
|
|
252
259
|
{
|
|
253
260
|
editor,
|