@cyberyozh/text-editor 0.5.0 → 0.5.2
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/editor.d.mts +2 -2
- package/dist/editor.d.ts +2 -2
- package/dist/editor.js +125 -211
- package/dist/editor.js.map +1 -1
- package/dist/editor.mjs +118 -204
- package/dist/editor.mjs.map +1 -1
- package/dist/render.js +128 -220
- package/dist/render.js.map +1 -1
- package/dist/render.mjs +108 -200
- package/dist/render.mjs.map +1 -1
- package/package.json +35 -34
- package/src/editor/commands/items.ts +1 -6
- package/src/editor/handlers/insertion-handlers.test.ts +37 -0
- package/src/editor/handlers/insertion-handlers.ts +14 -9
- package/src/editor/handlers/markup-insertion.ts +9 -0
- package/src/extensions/base/compatibility.ts +3 -1
- package/src/extensions/base/myth-reality.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyberyozh/text-editor",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Rich text editor library based on Tiptap for CyberYozh",
|
|
5
5
|
"author": "CyberYozh",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,38 +50,38 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@floating-ui/dom": "1.7.6",
|
|
53
|
-
"@tiptap/core": "3.
|
|
54
|
-
"@tiptap/extension-blockquote": "3.
|
|
55
|
-
"@tiptap/extension-bubble-menu": "3.
|
|
56
|
-
"@tiptap/extension-code": "3.
|
|
57
|
-
"@tiptap/extension-code-block-lowlight": "3.
|
|
58
|
-
"@tiptap/extension-collaboration": "3.
|
|
59
|
-
"@tiptap/extension-details": "3.
|
|
60
|
-
"@tiptap/extension-drag-handle": "3.
|
|
61
|
-
"@tiptap/extension-drag-handle-react": "3.
|
|
62
|
-
"@tiptap/extension-floating-menu": "3.
|
|
63
|
-
"@tiptap/extension-hard-break": "3.
|
|
64
|
-
"@tiptap/extension-highlight": "3.
|
|
65
|
-
"@tiptap/extension-horizontal-rule": "3.
|
|
66
|
-
"@tiptap/extension-image": "3.
|
|
67
|
-
"@tiptap/extension-link": "3.
|
|
68
|
-
"@tiptap/extension-list": "3.
|
|
69
|
-
"@tiptap/extension-mathematics": "3.
|
|
70
|
-
"@tiptap/extension-node-range": "3.
|
|
71
|
-
"@tiptap/extension-subscript": "3.
|
|
72
|
-
"@tiptap/extension-superscript": "3.
|
|
73
|
-
"@tiptap/extension-table": "3.
|
|
74
|
-
"@tiptap/extension-text-align": "3.
|
|
75
|
-
"@tiptap/extensions": "3.
|
|
76
|
-
"@tiptap/html": "3.
|
|
77
|
-
"@tiptap/pm": "3.
|
|
78
|
-
"@tiptap/react": "3.
|
|
79
|
-
"@tiptap/starter-kit": "3.
|
|
80
|
-
"@tiptap/static-renderer": "3.
|
|
81
|
-
"@tiptap/suggestion": "3.
|
|
82
|
-
"@tiptap/y-tiptap": "3.0.
|
|
53
|
+
"@tiptap/core": "3.31.3",
|
|
54
|
+
"@tiptap/extension-blockquote": "3.31.3",
|
|
55
|
+
"@tiptap/extension-bubble-menu": "3.31.3",
|
|
56
|
+
"@tiptap/extension-code": "3.31.3",
|
|
57
|
+
"@tiptap/extension-code-block-lowlight": "3.31.3",
|
|
58
|
+
"@tiptap/extension-collaboration": "3.31.3",
|
|
59
|
+
"@tiptap/extension-details": "3.31.3",
|
|
60
|
+
"@tiptap/extension-drag-handle": "3.31.3",
|
|
61
|
+
"@tiptap/extension-drag-handle-react": "3.31.3",
|
|
62
|
+
"@tiptap/extension-floating-menu": "3.31.3",
|
|
63
|
+
"@tiptap/extension-hard-break": "3.31.3",
|
|
64
|
+
"@tiptap/extension-highlight": "3.31.3",
|
|
65
|
+
"@tiptap/extension-horizontal-rule": "3.31.3",
|
|
66
|
+
"@tiptap/extension-image": "3.31.3",
|
|
67
|
+
"@tiptap/extension-link": "3.31.3",
|
|
68
|
+
"@tiptap/extension-list": "3.31.3",
|
|
69
|
+
"@tiptap/extension-mathematics": "3.31.3",
|
|
70
|
+
"@tiptap/extension-node-range": "3.31.3",
|
|
71
|
+
"@tiptap/extension-subscript": "3.31.3",
|
|
72
|
+
"@tiptap/extension-superscript": "3.31.3",
|
|
73
|
+
"@tiptap/extension-table": "3.31.3",
|
|
74
|
+
"@tiptap/extension-text-align": "3.31.3",
|
|
75
|
+
"@tiptap/extensions": "3.31.3",
|
|
76
|
+
"@tiptap/html": "3.31.3",
|
|
77
|
+
"@tiptap/pm": "3.31.3",
|
|
78
|
+
"@tiptap/react": "3.31.3",
|
|
79
|
+
"@tiptap/starter-kit": "3.31.3",
|
|
80
|
+
"@tiptap/static-renderer": "3.31.3",
|
|
81
|
+
"@tiptap/suggestion": "3.31.3",
|
|
82
|
+
"@tiptap/y-tiptap": "3.0.9",
|
|
83
83
|
"emoji-picker-react": "4.19.1",
|
|
84
|
-
"highlight.js": "11.
|
|
84
|
+
"highlight.js": "11.12.0",
|
|
85
85
|
"katex": "0.16.35",
|
|
86
86
|
"lowlight": "3.3.0",
|
|
87
87
|
"y-protocols": "1.0.7",
|
|
@@ -94,9 +94,10 @@
|
|
|
94
94
|
"eslint": "^10.0.2",
|
|
95
95
|
"tsup": "^8.5.1",
|
|
96
96
|
"typescript": "^5.9.3",
|
|
97
|
-
"vitest": "^
|
|
97
|
+
"vitest": "^5.0.0"
|
|
98
98
|
},
|
|
99
99
|
"overrides": {
|
|
100
|
-
"glob": "^13.0.6"
|
|
100
|
+
"glob": "^13.0.6",
|
|
101
|
+
"esbuild": "^0.28.1"
|
|
101
102
|
}
|
|
102
103
|
}
|
|
@@ -5,10 +5,7 @@ import {
|
|
|
5
5
|
TiptapMenuAddImage,
|
|
6
6
|
TiptapMenuBlockquote,
|
|
7
7
|
TiptapMenuBulletList,
|
|
8
|
-
TiptapMenuCalloutError,
|
|
9
|
-
TiptapMenuCalloutInfo,
|
|
10
8
|
TiptapMenuCalloutSuccess,
|
|
11
|
-
TiptapMenuCalloutWarning,
|
|
12
9
|
TiptapMenuCodeBlock,
|
|
13
10
|
TiptapMenuCustomQuote,
|
|
14
11
|
TiptapMenuDetails,
|
|
@@ -22,11 +19,9 @@ import {
|
|
|
22
19
|
TiptapMenuMythReality,
|
|
23
20
|
TiptapMenuOrderedList,
|
|
24
21
|
TiptapMenuPrompt,
|
|
25
|
-
TiptapMenuRedo,
|
|
26
22
|
TiptapMenuSubscript,
|
|
27
23
|
TiptapMenuSuperscript,
|
|
28
24
|
TiptapMenuTable,
|
|
29
|
-
TiptapMenuUndo,
|
|
30
25
|
TiptapMenuVideo,
|
|
31
26
|
} from "../../icons";
|
|
32
27
|
|
|
@@ -45,7 +40,7 @@ export interface CommandMenuItem {
|
|
|
45
40
|
|
|
46
41
|
export const getCommandItems = (
|
|
47
42
|
translations: TiptapTranslations,
|
|
48
|
-
hasImageHandlers: boolean
|
|
43
|
+
hasImageHandlers: boolean,
|
|
49
44
|
): CommandMenuItem[] => [
|
|
50
45
|
// Undo/Redo group
|
|
51
46
|
// {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { shouldHandleInsertedFile } from "./markup-insertion";
|
|
3
|
+
|
|
4
|
+
function fakeEvent() {
|
|
5
|
+
return {
|
|
6
|
+
preventDefault: vi.fn(),
|
|
7
|
+
stopPropagation: vi.fn(),
|
|
8
|
+
} as unknown as Event & {
|
|
9
|
+
preventDefault: ReturnType<typeof vi.fn>;
|
|
10
|
+
stopPropagation: ReturnType<typeof vi.fn>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("shouldHandleInsertedFile", () => {
|
|
15
|
+
it("leaves the event untouched when clipboard has no files (plain text / html paste)", () => {
|
|
16
|
+
const event = fakeEvent();
|
|
17
|
+
expect(shouldHandleInsertedFile(undefined, event)).toBe(false);
|
|
18
|
+
expect(event.preventDefault).not.toHaveBeenCalled();
|
|
19
|
+
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("blocks the event for a non-image file and does not handle it", () => {
|
|
23
|
+
const event = fakeEvent();
|
|
24
|
+
const file = new File(["data"], "doc.pdf", { type: "application/pdf" });
|
|
25
|
+
expect(shouldHandleInsertedFile(file, event)).toBe(false);
|
|
26
|
+
expect(event.preventDefault).toHaveBeenCalledOnce();
|
|
27
|
+
expect(event.stopPropagation).toHaveBeenCalledOnce();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("blocks the event for an image file and handles it", () => {
|
|
31
|
+
const event = fakeEvent();
|
|
32
|
+
const file = new File(["data"], "pic.png", { type: "image/png" });
|
|
33
|
+
expect(shouldHandleInsertedFile(file, event)).toBe(true);
|
|
34
|
+
expect(event.preventDefault).toHaveBeenCalledOnce();
|
|
35
|
+
expect(event.stopPropagation).toHaveBeenCalledOnce();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -3,7 +3,12 @@ import { useDialogCommand } from "../context";
|
|
|
3
3
|
import { EditorView } from "@tiptap/pm/view";
|
|
4
4
|
import { Slice } from "@tiptap/pm/model";
|
|
5
5
|
import { ImageHandlers } from "@/types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
applyUploadResultsToNodes,
|
|
8
|
+
collectRehostableImages,
|
|
9
|
+
removeImagesFromSlice,
|
|
10
|
+
shouldHandleInsertedFile,
|
|
11
|
+
} from "./markup-insertion";
|
|
7
12
|
|
|
8
13
|
export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
9
14
|
const [insertedImage, setInsertedImage] = useState<File | null>(null);
|
|
@@ -17,10 +22,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
|
17
22
|
}, [insertedImage, activeDialog]);
|
|
18
23
|
|
|
19
24
|
function genericInsertionHandler(file: File | undefined, event: Event) {
|
|
20
|
-
event
|
|
21
|
-
event.stopPropagation();
|
|
22
|
-
|
|
23
|
-
if (!file || !file.type.startsWith("image/")) return;
|
|
25
|
+
if (!file || !shouldHandleInsertedFile(file, event)) return;
|
|
24
26
|
if (!imageHandlers?.upload) return;
|
|
25
27
|
|
|
26
28
|
setInsertedImage(file);
|
|
@@ -48,7 +50,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
|
48
50
|
if (
|
|
49
51
|
"clipboardData" in event &&
|
|
50
52
|
Array.from(event.clipboardData?.items ?? []).some(
|
|
51
|
-
(item) => item.type === "text/html"
|
|
53
|
+
(item) => item.type === "text/html",
|
|
52
54
|
)
|
|
53
55
|
) {
|
|
54
56
|
hasMarkupInsertion = true;
|
|
@@ -57,7 +59,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
|
57
59
|
if (
|
|
58
60
|
"dataTransfer" in event &&
|
|
59
61
|
Array.from(event.dataTransfer?.items ?? []).some(
|
|
60
|
-
(item) => item.type === "text/html"
|
|
62
|
+
(item) => item.type === "text/html",
|
|
61
63
|
)
|
|
62
64
|
) {
|
|
63
65
|
hasMarkupInsertion = true;
|
|
@@ -69,7 +71,8 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
|
69
71
|
|
|
70
72
|
const imagesToNodes = collectRehostableImages(slice);
|
|
71
73
|
|
|
72
|
-
imageHandlers
|
|
74
|
+
imageHandlers
|
|
75
|
+
.uploadMultipleByURL(imagesToNodes.map(([url]) => url))
|
|
73
76
|
.then((results) => {
|
|
74
77
|
applyUploadResultsToNodes(imagesToNodes, results);
|
|
75
78
|
view.dispatch(view.state.tr.replaceSelection(slice));
|
|
@@ -78,7 +81,9 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
|
|
|
78
81
|
setDisabled(false);
|
|
79
82
|
});
|
|
80
83
|
} else {
|
|
81
|
-
view.dispatch(
|
|
84
|
+
view.dispatch(
|
|
85
|
+
view.state.tr.replaceSelection(removeImagesFromSlice(slice)),
|
|
86
|
+
);
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
return true;
|
|
@@ -70,3 +70,12 @@ export function applyUploadResultsToNodes(
|
|
|
70
70
|
node.attrs.storageKey = result.storageKey;
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
export function shouldHandleInsertedFile(file: File | undefined, event: Event): boolean {
|
|
75
|
+
if (!file) return false;
|
|
76
|
+
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
|
|
80
|
+
return file.type.startsWith("image/");
|
|
81
|
+
}
|
|
@@ -441,10 +441,12 @@ export const BaseLegacyFigure = Image.extend<FigureOptions>({
|
|
|
441
441
|
selectable: false,
|
|
442
442
|
|
|
443
443
|
addOptions() {
|
|
444
|
+
const parentOptions = this.parent?.() || {};
|
|
444
445
|
return {
|
|
446
|
+
...parentOptions,
|
|
445
447
|
HTMLAttributes: {},
|
|
446
448
|
getImageById: undefined,
|
|
447
|
-
};
|
|
449
|
+
} as FigureOptions;
|
|
448
450
|
},
|
|
449
451
|
|
|
450
452
|
addAttributes() {
|