tetrons 2.2.3 → 2.2.4
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/app/api/register/route.d.ts +6 -0
- package/dist/app/api/register/route.js +26 -0
- package/dist/app/api/validate/route.d.ts +7 -0
- package/dist/app/api/validate/route.js +18 -0
- package/dist/app/layout.d.ts +1 -2
- package/dist/app/{layout.js → layout.jsx} +5 -2
- package/dist/app/page.d.ts +1 -1
- package/dist/app/page.jsx +55 -0
- package/dist/components/tetrons/EditorContent.d.ts +6 -1
- package/dist/components/tetrons/{EditorContent.js → EditorContent.jsx} +58 -3
- package/dist/components/tetrons/{ResizableImageComponent.js → ResizableImageComponent.jsx} +12 -10
- package/dist/components/tetrons/{ResizableVideoComponent.js → ResizableVideoComponent.jsx} +8 -7
- package/dist/components/tetrons/toolbar/ActionGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/{ActionGroup.js → ActionGroup.jsx} +35 -12
- package/dist/components/tetrons/toolbar/ClipboardGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/ClipboardGroup.jsx +36 -0
- package/dist/components/tetrons/toolbar/FileGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/{FileGroup.js → FileGroup.jsx} +6 -3
- package/dist/components/tetrons/toolbar/FontStyleGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/FontStyleGroup.jsx +104 -0
- package/dist/components/tetrons/toolbar/InsertGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/InsertGroup.jsx +163 -0
- package/dist/components/tetrons/toolbar/ListAlignGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/ListAlignGroup.jsx +16 -0
- package/dist/components/tetrons/toolbar/MiscGroup.d.ts +2 -1
- package/dist/components/tetrons/toolbar/MiscGroup.jsx +31 -0
- package/dist/components/tetrons/toolbar/TableContextMenu.d.ts +2 -1
- package/dist/components/tetrons/toolbar/{TableContextMenu.js → TableContextMenu.jsx} +21 -3
- package/dist/components/tetrons/toolbar/TetronsToolbar.d.ts +2 -1
- package/dist/components/tetrons/toolbar/{TetronsToolbar.js → TetronsToolbar.jsx} +14 -2
- package/dist/components/tetrons/toolbar/ToolbarButton.jsx +8 -0
- package/dist/index.d.mts +5 -2
- package/dist/index.mjs +658 -744
- package/dist/lib/db.d.ts +1 -0
- package/dist/lib/db.js +15 -0
- package/dist/models/ApiKey.d.ts +2 -0
- package/dist/models/ApiKey.js +14 -0
- package/dist/utils/apiKeyUtils.d.ts +11 -0
- package/dist/utils/apiKeyUtils.js +17 -0
- package/package.json +2 -1
- package/dist/app/page.js +0 -6
- package/dist/components/tetrons/toolbar/ClipboardGroup.js +0 -31
- package/dist/components/tetrons/toolbar/FontStyleGroup.js +0 -63
- package/dist/components/tetrons/toolbar/InsertGroup.js +0 -138
- package/dist/components/tetrons/toolbar/ListAlignGroup.js +0 -7
- package/dist/components/tetrons/toolbar/MiscGroup.js +0 -25
- package/dist/components/tetrons/toolbar/ToolbarButton.js +0 -7
- /package/dist/components/UI/{Button.js → Button.jsx} +0 -0
- /package/dist/components/UI/{Dropdown.js → Dropdown.jsx} +0 -0
package/dist/lib/db.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function connectDB(): Promise<void>;
|
package/dist/lib/db.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const MONGO_URL = process.env.MONGO_URL;
|
|
3
|
+
export const connectDB = async () => {
|
|
4
|
+
if (mongoose.connection.readyState >= 1) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
await mongoose.connect(MONGO_URL);
|
|
9
|
+
console.log("Connected to MongoDB 👍");
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.error("MongoDB connection failed ❌", error);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const ApiKeySchema = new mongoose.Schema({
|
|
3
|
+
email: { type: String, required: true },
|
|
4
|
+
organization: { type: String, required: true },
|
|
5
|
+
version: {
|
|
6
|
+
type: String,
|
|
7
|
+
enum: ["free", "pro", "premium", "platinum"],
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
apiKey: { type: String, required: true, unique: true },
|
|
11
|
+
createdAt: { type: Date, default: Date.now },
|
|
12
|
+
expiresAt: { type: Date },
|
|
13
|
+
});
|
|
14
|
+
export const ApiKey = mongoose.models.ApiKey || mongoose.model("ApiKey", ApiKeySchema);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a secure random API key
|
|
3
|
+
* @param length Length of the key (default 48 characters)
|
|
4
|
+
* @returns Hex string API key
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateApiKey(length?: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* Generates a unique key with optional prefix (e.g., for versioning or branding)
|
|
9
|
+
* @param prefix Optional prefix like 'PRO-', 'PREMIUM-', etc.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateVersionedKey(prefix?: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a secure random API key
|
|
4
|
+
* @param length Length of the key (default 48 characters)
|
|
5
|
+
* @returns Hex string API key
|
|
6
|
+
*/
|
|
7
|
+
export function generateApiKey(length = 48) {
|
|
8
|
+
return crypto.randomBytes(length / 2).toString("hex"); // length must be even
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generates a unique key with optional prefix (e.g., for versioning or branding)
|
|
12
|
+
* @param prefix Optional prefix like 'PRO-', 'PREMIUM-', etc.
|
|
13
|
+
*/
|
|
14
|
+
export function generateVersionedKey(prefix = "") {
|
|
15
|
+
const key = generateApiKey(48);
|
|
16
|
+
return `${prefix}${key}`;
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tetrons",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "A Next.js project written in TypeScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"dom-to-pdf": "^0.3.2",
|
|
30
30
|
"html2pdf.js": "^0.10.3",
|
|
31
31
|
"lowlight": "^3.3.0",
|
|
32
|
+
"mongoose": "^8.16.0",
|
|
32
33
|
"react-icons": "^5.5.0"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
package/dist/app/page.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import EditorContent from "../components/tetrons/EditorContent";
|
|
3
|
-
import "../styles/tetrons.css";
|
|
4
|
-
export default function Home() {
|
|
5
|
-
return (_jsx("main", { className: "flex flex-col h-screen overflow-hidden", children: _jsx("div", { className: "flex-1 overflow-auto flex flex-col", children: _jsx(EditorContent, {}) }) }));
|
|
6
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { MdContentPaste, MdContentCut, MdContentCopy, MdFormatPaint, } from "react-icons/md";
|
|
3
|
-
import ToolbarButton from "./ToolbarButton";
|
|
4
|
-
export default function ClipboardGroup({ editor }) {
|
|
5
|
-
return (_jsxs("div", { className: "clipboard-group", children: [_jsx(ToolbarButton, { icon: MdContentPaste, title: "Paste", onClick: async () => {
|
|
6
|
-
try {
|
|
7
|
-
const text = await navigator.clipboard.readText();
|
|
8
|
-
editor.chain().focus().insertContent(text).run();
|
|
9
|
-
}
|
|
10
|
-
catch (error) {
|
|
11
|
-
console.error("Failed to read clipboard contents:", error);
|
|
12
|
-
}
|
|
13
|
-
} }), _jsx(ToolbarButton, { icon: MdContentCut, title: "Cut", onClick: () => {
|
|
14
|
-
const { from, to } = editor.state.selection;
|
|
15
|
-
if (from === to)
|
|
16
|
-
return;
|
|
17
|
-
const selectedText = editor.state.doc.textBetween(from, to);
|
|
18
|
-
navigator.clipboard.writeText(selectedText).then(() => {
|
|
19
|
-
editor.chain().focus().deleteRange({ from, to }).run();
|
|
20
|
-
});
|
|
21
|
-
} }), _jsx(ToolbarButton, { icon: MdContentCopy, title: "Copy", onClick: () => {
|
|
22
|
-
const { from, to } = editor.state.selection;
|
|
23
|
-
if (from === to)
|
|
24
|
-
return;
|
|
25
|
-
const selectedText = editor.state.doc.textBetween(from, to);
|
|
26
|
-
navigator.clipboard.writeText(selectedText);
|
|
27
|
-
} }), _jsx(ToolbarButton, { icon: MdFormatPaint, title: "Format Painter", onClick: () => {
|
|
28
|
-
const currentMarks = editor.getAttributes("textStyle");
|
|
29
|
-
localStorage.setItem("formatPainter", JSON.stringify(currentMarks));
|
|
30
|
-
} })] }));
|
|
31
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { MdFormatBold, MdFormatItalic, MdFormatUnderlined, MdStrikethroughS, MdSubscript, MdSuperscript, MdFormatClear, MdFormatPaint, } from "react-icons/md";
|
|
4
|
-
import { ImTextColor } from "react-icons/im";
|
|
5
|
-
import { BiSolidColorFill } from "react-icons/bi";
|
|
6
|
-
import ToolbarButton from "./ToolbarButton";
|
|
7
|
-
import { useEffect, useState } from "react";
|
|
8
|
-
export default function FontStyleGroup({ editor }) {
|
|
9
|
-
const [textColor, setTextColor] = useState("#000000");
|
|
10
|
-
const [highlightColor, setHighlightColor] = useState("#ffff00");
|
|
11
|
-
const [fontFamily, setFontFamily] = useState("Arial");
|
|
12
|
-
const [fontSize, setFontSize] = useState("16px");
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
if (!editor)
|
|
15
|
-
return;
|
|
16
|
-
const updateStates = () => {
|
|
17
|
-
var _a, _b, _c;
|
|
18
|
-
const highlight = editor.getAttributes("highlight");
|
|
19
|
-
setHighlightColor((highlight === null || highlight === void 0 ? void 0 : highlight.color) || "#ffff00");
|
|
20
|
-
const color = (_a = editor.getAttributes("textStyle")) === null || _a === void 0 ? void 0 : _a.color;
|
|
21
|
-
setTextColor(color || "#000000");
|
|
22
|
-
const fontAttr = ((_b = editor.getAttributes("fontFamily")) === null || _b === void 0 ? void 0 : _b.font) || "Arial";
|
|
23
|
-
setFontFamily(fontAttr);
|
|
24
|
-
const sizeAttr = ((_c = editor.getAttributes("fontSize")) === null || _c === void 0 ? void 0 : _c.size) || "16px";
|
|
25
|
-
setFontSize(sizeAttr);
|
|
26
|
-
};
|
|
27
|
-
updateStates();
|
|
28
|
-
editor.on("selectionUpdate", updateStates);
|
|
29
|
-
editor.on("transaction", updateStates);
|
|
30
|
-
return () => {
|
|
31
|
-
editor.off("selectionUpdate", updateStates);
|
|
32
|
-
editor.off("transaction", updateStates);
|
|
33
|
-
};
|
|
34
|
-
}, [editor]);
|
|
35
|
-
return (_jsxs("div", { className: "font-style-group", children: [_jsxs("select", { title: "Font Family", value: fontFamily, onChange: (e) => {
|
|
36
|
-
const value = e.target.value;
|
|
37
|
-
setFontFamily(value);
|
|
38
|
-
editor.chain().focus().setFontFamily(value).run();
|
|
39
|
-
}, children: [_jsx("option", { value: "Arial", children: "Arial" }), _jsx("option", { value: "Georgia", children: "Georgia" }), _jsx("option", { value: "Times New Roman", children: "Times New Roman" }), _jsx("option", { value: "Courier New", children: "Courier New" }), _jsx("option", { value: "Verdana", children: "Verdana" })] }), _jsxs("select", { title: "Font Size", value: fontSize, onChange: (e) => {
|
|
40
|
-
const value = e.target.value;
|
|
41
|
-
setFontSize(value);
|
|
42
|
-
editor.chain().focus().setFontSize(value).run();
|
|
43
|
-
}, children: [_jsx("option", { value: "12px", children: "12" }), _jsx("option", { value: "14px", children: "14" }), _jsx("option", { value: "16px", children: "16" }), _jsx("option", { value: "18px", children: "18" }), _jsx("option", { value: "24px", children: "24" }), _jsx("option", { value: "36px", children: "36" }), _jsx("option", { value: "48px", children: "48" }), _jsx("option", { value: "64px", children: "64" }), _jsx("option", { value: "72px", children: "72" })] }), _jsx(ToolbarButton, { icon: MdFormatBold, label: "Bold", onClick: () => editor.chain().focus().toggleBold().run(), isActive: editor.isActive("bold") }), _jsx(ToolbarButton, { icon: MdFormatItalic, label: "Italic", onClick: () => editor.chain().focus().toggleItalic().run(), isActive: editor.isActive("italic") }), _jsx(ToolbarButton, { icon: MdFormatUnderlined, label: "Underline", onClick: () => editor.chain().focus().toggleUnderline().run(), isActive: editor.isActive("underline") }), _jsx(ToolbarButton, { icon: MdStrikethroughS, label: "Strikethrough", onClick: () => editor.chain().focus().toggleStrike().run(), isActive: editor.isActive("strike") }), _jsx(ToolbarButton, { icon: MdSubscript, label: "Subscript", onClick: () => editor.chain().focus().toggleSubscript().run(), isActive: editor.isActive("subscript") }), _jsx(ToolbarButton, { icon: MdSuperscript, label: "Superscript", onClick: () => editor.chain().focus().toggleSuperscript().run(), isActive: editor.isActive("superscript") }), _jsxs("label", { title: "Font Color", "aria-label": "Font Color", className: "color-label", style: { "--indicator-color": textColor }, children: [_jsx(ImTextColor, { size: 20 }), _jsx("div", { className: "color-indicator" }), _jsx("input", { type: "color", value: textColor, onChange: (e) => {
|
|
44
|
-
const color = e.target.value;
|
|
45
|
-
setTextColor(color);
|
|
46
|
-
editor.chain().focus().setColor(color).run();
|
|
47
|
-
} })] }), _jsxs("label", { title: "Highlight Color", "aria-label": "Highlight Color", className: "color-label", style: { "--indicator-color": highlightColor }, children: [_jsx(BiSolidColorFill, { size: 20 }), _jsx("div", { className: "color-indicator" }), _jsx("input", { type: "color", value: highlightColor, onChange: (e) => {
|
|
48
|
-
const color = e.target.value;
|
|
49
|
-
setHighlightColor(color);
|
|
50
|
-
editor.chain().focus().setHighlight({ color }).run();
|
|
51
|
-
} })] }), _jsx(ToolbarButton, { icon: MdFormatClear, label: "Clear Formatting", onClick: () => editor.chain().focus().unsetAllMarks().run() }), _jsx(ToolbarButton, { icon: MdFormatPaint, label: "Apply Painter Format", onClick: () => {
|
|
52
|
-
const format = JSON.parse(localStorage.getItem("formatPainter") || "{}");
|
|
53
|
-
if (format.color)
|
|
54
|
-
editor.chain().focus().setColor(format.color).run();
|
|
55
|
-
if (format.backgroundColor) {
|
|
56
|
-
editor
|
|
57
|
-
.chain()
|
|
58
|
-
.focus()
|
|
59
|
-
.setHighlight({ color: format.backgroundColor })
|
|
60
|
-
.run();
|
|
61
|
-
}
|
|
62
|
-
} })] }));
|
|
63
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useState } from "react";
|
|
4
|
-
import { MdTableChart, MdInsertPhoto, MdInsertLink, MdInsertComment, MdInsertEmoticon, MdHorizontalRule, MdVideoLibrary, MdOutlineOndemandVideo, } from "react-icons/md";
|
|
5
|
-
import ToolbarButton from "./ToolbarButton";
|
|
6
|
-
import Picker from "@emoji-mart/react";
|
|
7
|
-
export default function InsertGroup({ editor }) {
|
|
8
|
-
const [showTableGrid, setShowTableGrid] = useState(false);
|
|
9
|
-
const [selectedRows, setSelectedRows] = useState(1);
|
|
10
|
-
const [selectedCols, setSelectedCols] = useState(1);
|
|
11
|
-
const imageInputRef = useRef(null);
|
|
12
|
-
const videoInputRef = useRef(null);
|
|
13
|
-
const [showPicker, setShowPicker] = useState(false);
|
|
14
|
-
const addEmoji = (emoji) => {
|
|
15
|
-
editor.chain().focus().insertContent(emoji.native).run();
|
|
16
|
-
setShowPicker(false);
|
|
17
|
-
};
|
|
18
|
-
const handleTableCellHover = (row, col) => {
|
|
19
|
-
setSelectedRows(row);
|
|
20
|
-
setSelectedCols(col);
|
|
21
|
-
};
|
|
22
|
-
const handleTableInsert = (rows, cols) => {
|
|
23
|
-
editor
|
|
24
|
-
.chain()
|
|
25
|
-
.focus()
|
|
26
|
-
.insertTable({
|
|
27
|
-
rows: rows || 1,
|
|
28
|
-
cols: cols || 1,
|
|
29
|
-
withHeaderRow: true,
|
|
30
|
-
})
|
|
31
|
-
.run();
|
|
32
|
-
setShowTableGrid(false);
|
|
33
|
-
setSelectedRows(1);
|
|
34
|
-
setSelectedCols(1);
|
|
35
|
-
};
|
|
36
|
-
const handleImageUpload = (e) => {
|
|
37
|
-
var _a;
|
|
38
|
-
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
39
|
-
if (file) {
|
|
40
|
-
const reader = new FileReader();
|
|
41
|
-
reader.onload = () => {
|
|
42
|
-
editor
|
|
43
|
-
.chain()
|
|
44
|
-
.focus()
|
|
45
|
-
.setImage({ src: reader.result })
|
|
46
|
-
.run();
|
|
47
|
-
};
|
|
48
|
-
reader.readAsDataURL(file);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const handleVideoUpload = (e) => {
|
|
52
|
-
var _a;
|
|
53
|
-
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
54
|
-
if (file) {
|
|
55
|
-
const reader = new FileReader();
|
|
56
|
-
reader.onload = () => {
|
|
57
|
-
editor
|
|
58
|
-
.chain()
|
|
59
|
-
.focus()
|
|
60
|
-
.setVideo({ src: reader.result, controls: true })
|
|
61
|
-
.run();
|
|
62
|
-
};
|
|
63
|
-
reader.readAsDataURL(file);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
function normalizeEmbedUrl(url) {
|
|
67
|
-
try {
|
|
68
|
-
const u = new URL(url);
|
|
69
|
-
const hostname = u.hostname.replace("www.", "").toLowerCase();
|
|
70
|
-
if (hostname === "youtube.com" || hostname === "youtu.be") {
|
|
71
|
-
let videoId = u.searchParams.get("v");
|
|
72
|
-
if (!videoId && hostname === "youtu.be") {
|
|
73
|
-
videoId = u.pathname.slice(1);
|
|
74
|
-
}
|
|
75
|
-
if (videoId) {
|
|
76
|
-
return `https://www.youtube.com/embed/${videoId}`;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (hostname === "vimeo.com") {
|
|
80
|
-
const videoId = u.pathname.split("/")[1];
|
|
81
|
-
if (videoId) {
|
|
82
|
-
return `https://player.vimeo.com/video/${videoId}`;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (hostname === "google.com" ||
|
|
86
|
-
hostname === "maps.google.com" ||
|
|
87
|
-
hostname === "goo.gl") {
|
|
88
|
-
if (url.includes("/maps/embed")) {
|
|
89
|
-
return url;
|
|
90
|
-
}
|
|
91
|
-
return url.replace("/maps/", "/maps/embed/");
|
|
92
|
-
}
|
|
93
|
-
return url;
|
|
94
|
-
}
|
|
95
|
-
catch (_a) {
|
|
96
|
-
return url;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return (_jsxs("div", { className: "insert-group", children: [_jsx("input", { type: "file", accept: "image/*", ref: imageInputRef, className: "hidden-input", onChange: handleImageUpload, "aria-label": "Upload Image", title: "Upload Image" }), _jsx("input", { type: "file", accept: "video/*", ref: videoInputRef, className: "hidden-input", onChange: handleVideoUpload, "aria-label": "Upload Video", title: "Upload Video" }), _jsx(ToolbarButton, { icon: MdTableChart, label: "Insert Table", onClick: () => setShowTableGrid(!showTableGrid) }), showTableGrid && (_jsxs("div", { className: "table-grid-popup", onMouseLeave: () => setShowTableGrid(false), children: [_jsx("div", { className: "table-grid", children: [...Array(10)].map((_, row) => [...Array(10)].map((_, col) => {
|
|
100
|
-
const isSelected = row < selectedRows && col < selectedCols;
|
|
101
|
-
return (_jsx("div", { className: `table-grid-cell ${isSelected ? "selected" : ""}`, onMouseEnter: () => handleTableCellHover(row + 1, col + 1), onClick: () => handleTableInsert(row + 1, col + 1) }, `${row}-${col}`));
|
|
102
|
-
})) }), _jsxs("div", { className: "table-grid-label", children: [selectedRows, " x ", selectedCols] })] })), _jsx(ToolbarButton, { icon: MdInsertPhoto, label: "Insert Image", onClick: () => { var _a; return (_a = imageInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }), _jsx(ToolbarButton, { icon: MdVideoLibrary, label: "Insert Video", onClick: () => { var _a; return (_a = videoInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }), _jsx(ToolbarButton, { icon: MdInsertLink, label: "Insert Link", onClick: () => {
|
|
103
|
-
const url = prompt("Enter URL");
|
|
104
|
-
if (url) {
|
|
105
|
-
editor
|
|
106
|
-
.chain()
|
|
107
|
-
.focus()
|
|
108
|
-
.extendMarkRange("link")
|
|
109
|
-
.setLink({ href: url })
|
|
110
|
-
.run();
|
|
111
|
-
}
|
|
112
|
-
} }), _jsx(ToolbarButton, { icon: MdInsertComment, label: "Insert Comment", onClick: () => {
|
|
113
|
-
const comment = prompt("Enter your comment");
|
|
114
|
-
if (comment &&
|
|
115
|
-
editor.can().chain().focus().setComment(comment).run()) {
|
|
116
|
-
editor.chain().focus().setComment(comment).run();
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
console.warn("Cannot apply comment — maybe no selection?");
|
|
120
|
-
}
|
|
121
|
-
} }), _jsxs("div", { className: "relative", children: [_jsx(ToolbarButton, { icon: MdInsertEmoticon, label: "Emoji", onClick: () => setShowPicker(!showPicker) }), showPicker && (_jsx("div", { className: "emoji-picker", children: _jsx(Picker, { onEmojiSelect: addEmoji, theme: "auto", emoji: "point_up", showPreview: false, showSkinTones: true, emojiTooltip: true }) }))] }), _jsx(ToolbarButton, { icon: MdHorizontalRule, label: "Horizontal Line", onClick: () => editor.chain().focus().setHorizontalRule().run() }), _jsx(ToolbarButton, { icon: MdOutlineOndemandVideo, label: "Embed", onClick: () => {
|
|
122
|
-
const url = prompt("Enter embed URL (YouTube, Vimeo, Google Maps, etc.)");
|
|
123
|
-
if (!url)
|
|
124
|
-
return;
|
|
125
|
-
const embedUrl = normalizeEmbedUrl(url);
|
|
126
|
-
const width = prompt("Enter width in pixels", "560");
|
|
127
|
-
const height = prompt("Enter height in pixels", "315");
|
|
128
|
-
editor
|
|
129
|
-
.chain()
|
|
130
|
-
.focus()
|
|
131
|
-
.setEmbed({
|
|
132
|
-
src: embedUrl,
|
|
133
|
-
width: width ? parseInt(width) : 560,
|
|
134
|
-
height: height ? parseInt(height) : 315,
|
|
135
|
-
})
|
|
136
|
-
.run();
|
|
137
|
-
} })] }));
|
|
138
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { MdFormatListBulleted, MdFormatListNumbered, MdFormatIndentDecrease, MdFormatIndentIncrease, MdFormatAlignLeft, MdFormatAlignCenter, MdFormatAlignRight, MdFormatAlignJustify, } from "react-icons/md";
|
|
4
|
-
import ToolbarButton from "./ToolbarButton";
|
|
5
|
-
export default function ListAlignGroup({ editor }) {
|
|
6
|
-
return (_jsxs("div", { className: "list-align-group", children: [_jsx(ToolbarButton, { icon: MdFormatListBulleted, title: "Bulleted List", onClick: () => editor.chain().focus().toggleBulletList().run(), disabled: !editor.can().toggleBulletList() }), _jsx(ToolbarButton, { icon: MdFormatListNumbered, title: "Numbered List", onClick: () => editor.chain().focus().toggleOrderedList().run(), disabled: !editor.can().toggleOrderedList() }), _jsx(ToolbarButton, { icon: MdFormatIndentIncrease, title: "Increase Indent", onClick: () => editor.chain().focus().sinkListItem("listItem").run(), disabled: !editor.can().sinkListItem("listItem") }), _jsx(ToolbarButton, { icon: MdFormatIndentDecrease, title: "Decrease Indent", onClick: () => editor.chain().focus().liftListItem("listItem").run(), disabled: !editor.can().liftListItem("listItem") }), _jsx(ToolbarButton, { icon: MdFormatAlignLeft, title: "Align Left", onClick: () => editor.chain().focus().setTextAlign("left").run(), disabled: !editor.can().setTextAlign("left") }), _jsx(ToolbarButton, { icon: MdFormatAlignCenter, title: "Align Center", onClick: () => editor.chain().focus().setTextAlign("center").run(), disabled: !editor.can().setTextAlign("center") }), _jsx(ToolbarButton, { icon: MdFormatAlignRight, title: "Align Right", onClick: () => editor.chain().focus().setTextAlign("right").run(), disabled: !editor.can().setTextAlign("right") }), _jsx(ToolbarButton, { icon: MdFormatAlignJustify, title: "Justify", onClick: () => editor.chain().focus().setTextAlign("justify").run(), disabled: !editor.can().setTextAlign("justify") })] }));
|
|
7
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { MdUndo, MdRedo, MdRefresh, MdVisibility, MdCode, } from "react-icons/md";
|
|
3
|
-
import ToolbarButton from "./ToolbarButton";
|
|
4
|
-
export default function MiscGroup({ editor }) {
|
|
5
|
-
const handlePreview = () => {
|
|
6
|
-
const html = editor.getHTML();
|
|
7
|
-
const previewWindow = window.open("", "_blank");
|
|
8
|
-
if (previewWindow) {
|
|
9
|
-
previewWindow.document.open();
|
|
10
|
-
previewWindow.document.write(`
|
|
11
|
-
<html>
|
|
12
|
-
<head>
|
|
13
|
-
<title>Preview</title>
|
|
14
|
-
<style>
|
|
15
|
-
body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
|
|
16
|
-
</style>
|
|
17
|
-
</head>
|
|
18
|
-
<body>${html}</body>
|
|
19
|
-
</html>
|
|
20
|
-
`);
|
|
21
|
-
previewWindow.document.close();
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
return (_jsxs("div", { className: "misc-group", children: [_jsx(ToolbarButton, { icon: MdUndo, label: "Undo", onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo() }), _jsx(ToolbarButton, { icon: MdRedo, label: "Redo", onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo() }), _jsx(ToolbarButton, { icon: MdRefresh, label: "Reset Formatting", onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run() }), _jsx(ToolbarButton, { icon: MdCode, label: "Toggle Code Block", onClick: () => editor.chain().focus().toggleCodeBlock().run(), isActive: editor.isActive("codeBlock") }), _jsx(ToolbarButton, { icon: MdVisibility, label: "Preview", onClick: handlePreview })] }));
|
|
25
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React from "react";
|
|
3
|
-
const ToolbarButton = React.forwardRef(({ icon: Icon, onClick, disabled = false, title, label, isActive = false }, ref) => {
|
|
4
|
-
return (_jsx("button", { type: "button", ref: ref, onClick: onClick, disabled: disabled, title: title !== null && title !== void 0 ? title : label, "aria-label": title !== null && title !== void 0 ? title : label, className: `toolbar-button ${isActive ? "active" : ""}`, children: _jsx(Icon, { size: 20 }) }));
|
|
5
|
-
});
|
|
6
|
-
ToolbarButton.displayName = "ToolbarButton";
|
|
7
|
-
export default ToolbarButton;
|
|
File without changes
|
|
File without changes
|