tetrons 2.3.1 → 2.3.21

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.
Files changed (83) hide show
  1. package/dist/components/components/tetrons/EditorContent.tsx +282 -0
  2. package/dist/components/components/tetrons/ResizableImageComponent.tsx +77 -0
  3. package/dist/components/components/tetrons/ResizableVideoComponent.tsx +56 -0
  4. package/dist/index.d.mts +5 -1
  5. package/dist/index.d.ts +12 -7
  6. package/dist/index.js +17254 -21
  7. package/dist/index.mjs +54 -11
  8. package/dist/tetrons-UCHWNATC.css +372 -0
  9. package/package.json +3 -3
  10. package/dist/app/api/export/route.d.ts +0 -1
  11. package/dist/app/api/export/route.js +0 -4
  12. package/dist/app/api/register/route.d.ts +0 -7
  13. package/dist/app/api/register/route.js +0 -32
  14. package/dist/app/api/save/route.d.ts +0 -6
  15. package/dist/app/api/save/route.js +0 -15
  16. package/dist/app/api/validate/route.d.ts +0 -10
  17. package/dist/app/api/validate/route.js +0 -58
  18. package/dist/app/layout.d.ts +0 -6
  19. package/dist/app/layout.jsx +0 -30
  20. package/dist/app/page.d.ts +0 -2
  21. package/dist/app/page.jsx +0 -51
  22. package/dist/components/UI/Button.jsx +0 -1
  23. package/dist/components/UI/Dropdown.jsx +0 -1
  24. package/dist/components/tetrons/EditorContent.d.ts +0 -6
  25. package/dist/components/tetrons/EditorContent.jsx +0 -209
  26. package/dist/components/tetrons/ResizableImage.d.ts +0 -1
  27. package/dist/components/tetrons/ResizableImage.js +0 -40
  28. package/dist/components/tetrons/ResizableImageComponent.d.ts +0 -11
  29. package/dist/components/tetrons/ResizableImageComponent.jsx +0 -37
  30. package/dist/components/tetrons/ResizableVideo.d.ts +0 -12
  31. package/dist/components/tetrons/ResizableVideo.js +0 -61
  32. package/dist/components/tetrons/ResizableVideoComponent.d.ts +0 -4
  33. package/dist/components/tetrons/ResizableVideoComponent.jsx +0 -32
  34. package/dist/components/tetrons/helpers.d.ts +0 -0
  35. package/dist/components/tetrons/helpers.js +0 -1
  36. package/dist/components/tetrons/toolbar/ActionGroup.d.ts +0 -7
  37. package/dist/components/tetrons/toolbar/ActionGroup.jsx +0 -167
  38. package/dist/components/tetrons/toolbar/ClipboardGroup.d.ts +0 -5
  39. package/dist/components/tetrons/toolbar/ClipboardGroup.jsx +0 -36
  40. package/dist/components/tetrons/toolbar/FileGroup.d.ts +0 -7
  41. package/dist/components/tetrons/toolbar/FileGroup.jsx +0 -40
  42. package/dist/components/tetrons/toolbar/FontStyleGroup.d.ts +0 -7
  43. package/dist/components/tetrons/toolbar/FontStyleGroup.jsx +0 -104
  44. package/dist/components/tetrons/toolbar/InsertGroup.d.ts +0 -5
  45. package/dist/components/tetrons/toolbar/InsertGroup.jsx +0 -163
  46. package/dist/components/tetrons/toolbar/ListAlignGroup.d.ts +0 -5
  47. package/dist/components/tetrons/toolbar/ListAlignGroup.jsx +0 -16
  48. package/dist/components/tetrons/toolbar/MiscGroup.d.ts +0 -7
  49. package/dist/components/tetrons/toolbar/MiscGroup.jsx +0 -31
  50. package/dist/components/tetrons/toolbar/TableContextMenu.d.ts +0 -7
  51. package/dist/components/tetrons/toolbar/TableContextMenu.jsx +0 -52
  52. package/dist/components/tetrons/toolbar/TetronsToolbar.d.ts +0 -6
  53. package/dist/components/tetrons/toolbar/TetronsToolbar.jsx +0 -46
  54. package/dist/components/tetrons/toolbar/ToolbarButton.d.ts +0 -12
  55. package/dist/components/tetrons/toolbar/ToolbarButton.jsx +0 -8
  56. package/dist/components/tetrons/toolbar/extensions/Comment.d.ts +0 -17
  57. package/dist/components/tetrons/toolbar/extensions/Comment.js +0 -45
  58. package/dist/components/tetrons/toolbar/extensions/Embed.d.ts +0 -2
  59. package/dist/components/tetrons/toolbar/extensions/Embed.js +0 -90
  60. package/dist/components/tetrons/toolbar/extensions/FontFamily.d.ts +0 -9
  61. package/dist/components/tetrons/toolbar/extensions/FontFamily.js +0 -28
  62. package/dist/components/tetrons/toolbar/extensions/FontSize.d.ts +0 -9
  63. package/dist/components/tetrons/toolbar/extensions/FontSize.js +0 -28
  64. package/dist/components/tetrons/toolbar/extensions/ResizableTable.d.ts +0 -1
  65. package/dist/components/tetrons/toolbar/extensions/ResizableTable.js +0 -11
  66. package/dist/components/tetrons/toolbar/marks/Subscript.d.ts +0 -2
  67. package/dist/components/tetrons/toolbar/marks/Subscript.js +0 -35
  68. package/dist/components/tetrons/toolbar/marks/Superscript.d.ts +0 -2
  69. package/dist/components/tetrons/toolbar/marks/Superscript.js +0 -35
  70. package/dist/lib/db.d.ts +0 -1
  71. package/dist/lib/db.js +0 -15
  72. package/dist/lib/export.d.ts +0 -0
  73. package/dist/lib/export.js +0 -1
  74. package/dist/lib/tiptap-extensions.d.ts +0 -0
  75. package/dist/lib/tiptap-extensions.js +0 -1
  76. package/dist/models/ApiKey.d.ts +0 -2
  77. package/dist/models/ApiKey.js +0 -14
  78. package/dist/utils/apiKeyUtils.d.ts +0 -11
  79. package/dist/utils/apiKeyUtils.js +0 -33
  80. package/dist/utils/loadEmojiPicker.d.ts +0 -1
  81. package/dist/utils/loadEmojiPicker.js +0 -12
  82. /package/dist/components/{UI/Button.d.ts → components/UI/Button.tsx} +0 -0
  83. /package/dist/components/{UI/Dropdown.d.ts → components/UI/Dropdown.tsx} +0 -0
@@ -1,167 +0,0 @@
1
- "use client";
2
- import React, { useEffect, useRef, useState } from "react";
3
- import { MdZoomIn, MdZoomOut, MdPrint, MdSave, MdDownload, } from "react-icons/md";
4
- import ToolbarButton from "./ToolbarButton";
5
- export default function ActionGroup({ editor }) {
6
- const [dropdownOpen, setDropdownOpen] = useState(false);
7
- const dropdownRef = useRef(null);
8
- useEffect(() => {
9
- const handleClickOutside = (event) => {
10
- if (dropdownRef.current &&
11
- !dropdownRef.current.contains(event.target)) {
12
- setDropdownOpen(false);
13
- }
14
- };
15
- if (dropdownOpen) {
16
- document.addEventListener("mousedown", handleClickOutside);
17
- }
18
- else {
19
- document.removeEventListener("mousedown", handleClickOutside);
20
- }
21
- return () => {
22
- document.removeEventListener("mousedown", handleClickOutside);
23
- };
24
- }, [dropdownOpen]);
25
- const zoomIn = () => {
26
- const element = document.querySelector(".ProseMirror");
27
- if (element) {
28
- const style = element.style;
29
- const currentZoom = parseFloat(style.zoom || "1");
30
- const next = Math.min(currentZoom + 0.1, 2);
31
- style.zoom = next.toString();
32
- }
33
- };
34
- const zoomOut = () => {
35
- const element = document.querySelector(".ProseMirror");
36
- if (element) {
37
- const style = element.style;
38
- const currentZoom = parseFloat(style.zoom || "1");
39
- const next = Math.max(currentZoom - 0.1, 0.5);
40
- style.zoom = next.toString();
41
- }
42
- };
43
- const handlePrint = () => {
44
- const html = editor.getHTML();
45
- const printWindow = window.open("", "_blank");
46
- if (printWindow) {
47
- printWindow.document.write(`
48
- <html>
49
- <head>
50
- <title>Print</title>
51
- </head>
52
- <body>${html}</body>
53
- </html>
54
- `);
55
- printWindow.document.close();
56
- printWindow.focus();
57
- printWindow.print();
58
- }
59
- };
60
- const handleSave = async () => {
61
- const content = editor.getJSON();
62
- try {
63
- const response = await fetch("/api/save", {
64
- method: "POST",
65
- headers: { "Content-Type": "application/json" },
66
- body: JSON.stringify(content),
67
- });
68
- if (!response.ok)
69
- throw new Error("Failed to save file");
70
- const result = await response.json();
71
- console.log(result.message);
72
- alert("Content saved successfully!");
73
- }
74
- catch (error) {
75
- console.error("Error saving content:", error);
76
- alert("Failed to save content.");
77
- }
78
- };
79
- const handleDownloadPDF = async () => {
80
- const html = editor.getHTML();
81
- const container = document.createElement("div");
82
- container.innerHTML = html;
83
- container.classList.add("p-4", "prose");
84
- document.body.appendChild(container);
85
- try {
86
- const domToPdf = (await import("dom-to-pdf")).default;
87
- const options = {
88
- filename: "document.pdf",
89
- overrideWidth: 800,
90
- overrideHeight: 1120,
91
- };
92
- domToPdf(container, options, () => {
93
- console.log("PDF downloaded!");
94
- });
95
- }
96
- catch (error) {
97
- console.error("PDF download failed", error);
98
- alert("Failed to download PDF.");
99
- }
100
- finally {
101
- document.body.removeChild(container);
102
- }
103
- };
104
- const handleDownloadHTML = () => {
105
- const html = editor.getHTML();
106
- const blob = new Blob([html], { type: "text/html" });
107
- const link = document.createElement("a");
108
- link.href = URL.createObjectURL(blob);
109
- link.download = "document.html";
110
- document.body.appendChild(link);
111
- link.click();
112
- document.body.removeChild(link);
113
- };
114
- const handleDownloadDOCX = async () => {
115
- const { Document, Packer, Paragraph } = await import("docx");
116
- const text = editor.getText();
117
- const doc = new Document({
118
- sections: [
119
- {
120
- properties: {},
121
- children: [new Paragraph(text)],
122
- },
123
- ],
124
- });
125
- const blob = await Packer.toBlob(doc);
126
- const link = document.createElement("a");
127
- link.href = URL.createObjectURL(blob);
128
- link.download = "document.docx";
129
- document.body.appendChild(link);
130
- link.click();
131
- document.body.removeChild(link);
132
- };
133
- return (<div className="action-group" role="group" aria-label="Editor actions">
134
- <ToolbarButton icon={MdZoomIn} onClick={zoomIn} title="Zoom In"/>
135
- <ToolbarButton icon={MdZoomOut} onClick={zoomOut} title="Zoom Out"/>
136
- <ToolbarButton icon={MdPrint} onClick={handlePrint} title="Print"/>
137
- <ToolbarButton icon={MdSave} onClick={handleSave} title="Save"/>
138
-
139
- <div className="relative" ref={dropdownRef}>
140
- <button type="button" onClick={() => setDropdownOpen((open) => !open)} aria-haspopup="menu" aria-expanded={dropdownOpen ? "true" : "false"} className="export-button" title="Export">
141
- <MdDownload />
142
- <span className="text-sm"></span>
143
- </button>
144
-
145
- {dropdownOpen && (<div className="export-dropdown">
146
- <button type="button" onClick={() => {
147
- setDropdownOpen(false);
148
- handleDownloadPDF();
149
- }}>
150
- Export as PDF
151
- </button>
152
- <button type="button" onClick={() => {
153
- setDropdownOpen(false);
154
- handleDownloadHTML();
155
- }}>
156
- Export as HTML
157
- </button>
158
- <button type="button" onClick={() => {
159
- setDropdownOpen(false);
160
- handleDownloadDOCX();
161
- }}>
162
- Export as DOCX
163
- </button>
164
- </div>)}
165
- </div>
166
- </div>);
167
- }
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- import { Editor } from "@tiptap/react";
3
- export default function ClipboardGroup({ editor }: {
4
- editor: Editor;
5
- }): React.JSX.Element;
@@ -1,36 +0,0 @@
1
- import { MdContentPaste, MdContentCut, MdContentCopy, MdFormatPaint, } from "react-icons/md";
2
- import React from "react";
3
- import ToolbarButton from "./ToolbarButton";
4
- export default function ClipboardGroup({ editor }) {
5
- return (<div className="clipboard-group">
6
- <ToolbarButton icon={MdContentPaste} title="Paste" onClick={async () => {
7
- try {
8
- const text = await navigator.clipboard.readText();
9
- editor.chain().focus().insertContent(text).run();
10
- }
11
- catch (error) {
12
- console.error("Failed to read clipboard contents:", error);
13
- }
14
- }}/>
15
- <ToolbarButton icon={MdContentCut} title="Cut" onClick={() => {
16
- const { from, to } = editor.state.selection;
17
- if (from === to)
18
- return;
19
- const selectedText = editor.state.doc.textBetween(from, to);
20
- navigator.clipboard.writeText(selectedText).then(() => {
21
- editor.chain().focus().deleteRange({ from, to }).run();
22
- });
23
- }}/>
24
- <ToolbarButton icon={MdContentCopy} title="Copy" onClick={() => {
25
- const { from, to } = editor.state.selection;
26
- if (from === to)
27
- return;
28
- const selectedText = editor.state.doc.textBetween(from, to);
29
- navigator.clipboard.writeText(selectedText);
30
- }}/>
31
- <ToolbarButton icon={MdFormatPaint} title="Format Painter" onClick={() => {
32
- const currentMarks = editor.getAttributes("textStyle");
33
- localStorage.setItem("formatPainter", JSON.stringify(currentMarks));
34
- }}/>
35
- </div>);
36
- }
@@ -1,7 +0,0 @@
1
- import { Editor } from "@tiptap/react";
2
- import React from "react";
3
- type FileGroupProps = {
4
- editor: Editor;
5
- };
6
- export default function FileGroup({ editor }: FileGroupProps): React.JSX.Element;
7
- export {};
@@ -1,40 +0,0 @@
1
- "use client";
2
- import { FaRegFolderOpen } from "react-icons/fa";
3
- import { VscNewFile } from "react-icons/vsc";
4
- import ToolbarButton from "./ToolbarButton";
5
- import React, { useRef } from "react";
6
- export default function FileGroup({ editor }) {
7
- const fileInputRef = useRef(null);
8
- const handleNew = () => {
9
- if (confirm("Are you sure you want to create a new document? Unsaved changes will be lost.")) {
10
- editor.commands.clearContent();
11
- }
12
- };
13
- const handleOpen = () => {
14
- var _a;
15
- (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
16
- };
17
- const handleFileChange = async (e) => {
18
- var _a;
19
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
20
- if (!file)
21
- return;
22
- try {
23
- const text = await file.text();
24
- const json = JSON.parse(text);
25
- editor.commands.setContent(json);
26
- }
27
- catch (error) {
28
- console.error("Error reading file:", error);
29
- alert("Failed to open or parse the file. Make sure it's a valid JSON file.");
30
- }
31
- finally {
32
- e.target.value = "";
33
- }
34
- };
35
- return (<div className="file-group" role="group" aria-label="File actions">
36
- <input type="file" accept=".json" ref={fileInputRef} onChange={handleFileChange} className="hidden" aria-label="Open JSON file"/>
37
- <ToolbarButton icon={VscNewFile} onClick={handleNew} title="New"/>
38
- <ToolbarButton icon={FaRegFolderOpen} onClick={handleOpen} title="Open File"/>
39
- </div>);
40
- }
@@ -1,7 +0,0 @@
1
- import { Editor } from "@tiptap/react";
2
- import React from "react";
3
- interface FontStyleGroupProps {
4
- editor: Editor;
5
- }
6
- export default function FontStyleGroup({ editor }: FontStyleGroupProps): React.JSX.Element;
7
- export {};
@@ -1,104 +0,0 @@
1
- "use client";
2
- import { MdFormatBold, MdFormatItalic, MdFormatUnderlined, MdStrikethroughS, MdSubscript, MdSuperscript, MdFormatClear, MdFormatPaint, } from "react-icons/md";
3
- import { ImTextColor } from "react-icons/im";
4
- import { BiSolidColorFill } from "react-icons/bi";
5
- import ToolbarButton from "./ToolbarButton";
6
- import React, { useEffect, useState } from "react";
7
- export default function FontStyleGroup({ editor }) {
8
- const [textColor, setTextColor] = useState("#000000");
9
- const [highlightColor, setHighlightColor] = useState("#ffff00");
10
- const [fontFamily, setFontFamily] = useState("Arial");
11
- const [fontSize, setFontSize] = useState("16px");
12
- useEffect(() => {
13
- if (!editor)
14
- return;
15
- const updateStates = () => {
16
- var _a, _b, _c;
17
- const highlight = editor.getAttributes("highlight");
18
- setHighlightColor((highlight === null || highlight === void 0 ? void 0 : highlight.color) || "#ffff00");
19
- const color = (_a = editor.getAttributes("textStyle")) === null || _a === void 0 ? void 0 : _a.color;
20
- setTextColor(color || "#000000");
21
- const fontAttr = ((_b = editor.getAttributes("fontFamily")) === null || _b === void 0 ? void 0 : _b.font) || "Arial";
22
- setFontFamily(fontAttr);
23
- const sizeAttr = ((_c = editor.getAttributes("fontSize")) === null || _c === void 0 ? void 0 : _c.size) || "16px";
24
- setFontSize(sizeAttr);
25
- };
26
- updateStates();
27
- editor.on("selectionUpdate", updateStates);
28
- editor.on("transaction", updateStates);
29
- return () => {
30
- editor.off("selectionUpdate", updateStates);
31
- editor.off("transaction", updateStates);
32
- };
33
- }, [editor]);
34
- return (<div className="font-style-group">
35
- <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
- }}>
40
- <option value="Arial">Arial</option>
41
- <option value="Georgia">Georgia</option>
42
- <option value="Times New Roman">Times New Roman</option>
43
- <option value="Courier New">Courier New</option>
44
- <option value="Verdana">Verdana</option>
45
- </select>
46
-
47
- <select title="Font Size" value={fontSize} onChange={(e) => {
48
- const value = e.target.value;
49
- setFontSize(value);
50
- editor.chain().focus().setFontSize(value).run();
51
- }}>
52
- <option value="12px">12</option>
53
- <option value="14px">14</option>
54
- <option value="16px">16</option>
55
- <option value="18px">18</option>
56
- <option value="24px">24</option>
57
- <option value="36px">36</option>
58
- <option value="48px">48</option>
59
- <option value="64px">64</option>
60
- <option value="72px">72</option>
61
- </select>
62
-
63
- <ToolbarButton icon={MdFormatBold} label="Bold" onClick={() => editor.chain().focus().toggleBold().run()} isActive={editor.isActive("bold")}/>
64
- <ToolbarButton icon={MdFormatItalic} label="Italic" onClick={() => editor.chain().focus().toggleItalic().run()} isActive={editor.isActive("italic")}/>
65
- <ToolbarButton icon={MdFormatUnderlined} label="Underline" onClick={() => editor.chain().focus().toggleUnderline().run()} isActive={editor.isActive("underline")}/>
66
- <ToolbarButton icon={MdStrikethroughS} label="Strikethrough" onClick={() => editor.chain().focus().toggleStrike().run()} isActive={editor.isActive("strike")}/>
67
- <ToolbarButton icon={MdSubscript} label="Subscript" onClick={() => editor.chain().focus().toggleSubscript().run()} isActive={editor.isActive("subscript")}/>
68
- <ToolbarButton icon={MdSuperscript} label="Superscript" onClick={() => editor.chain().focus().toggleSuperscript().run()} isActive={editor.isActive("superscript")}/>
69
-
70
- <label title="Font Color" aria-label="Font Color" className="color-label" style={{ "--indicator-color": textColor }}>
71
- <ImTextColor size={20}/>
72
- <div className="color-indicator"/>
73
- <input type="color" value={textColor} onChange={(e) => {
74
- const color = e.target.value;
75
- setTextColor(color);
76
- editor.chain().focus().setColor(color).run();
77
- }}/>
78
- </label>
79
-
80
- <label title="Highlight Color" aria-label="Highlight Color" className="color-label" style={{ "--indicator-color": highlightColor }}>
81
- <BiSolidColorFill size={20}/>
82
- <div className="color-indicator"/>
83
- <input type="color" value={highlightColor} onChange={(e) => {
84
- const color = e.target.value;
85
- setHighlightColor(color);
86
- editor.chain().focus().setHighlight({ color }).run();
87
- }}/>
88
- </label>
89
-
90
- <ToolbarButton icon={MdFormatClear} label="Clear Formatting" onClick={() => editor.chain().focus().unsetAllMarks().run()}/>
91
- <ToolbarButton icon={MdFormatPaint} label="Apply Painter Format" onClick={() => {
92
- const format = JSON.parse(localStorage.getItem("formatPainter") || "{}");
93
- if (format.color)
94
- editor.chain().focus().setColor(format.color).run();
95
- if (format.backgroundColor) {
96
- editor
97
- .chain()
98
- .focus()
99
- .setHighlight({ color: format.backgroundColor })
100
- .run();
101
- }
102
- }}/>
103
- </div>);
104
- }
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- import { Editor } from "@tiptap/react";
3
- export default function InsertGroup({ editor }: {
4
- editor: Editor;
5
- }): React.JSX.Element;
@@ -1,163 +0,0 @@
1
- "use client";
2
- import React, { useRef, useState } from "react";
3
- import { MdTableChart, MdInsertPhoto, MdInsertLink, MdInsertComment, MdInsertEmoticon, MdHorizontalRule, MdVideoLibrary, MdOutlineOndemandVideo, } from "react-icons/md";
4
- import ToolbarButton from "./ToolbarButton";
5
- import Picker from "@emoji-mart/react";
6
- export default function InsertGroup({ editor }) {
7
- const [showTableGrid, setShowTableGrid] = useState(false);
8
- const [selectedRows, setSelectedRows] = useState(1);
9
- const [selectedCols, setSelectedCols] = useState(1);
10
- const imageInputRef = useRef(null);
11
- const videoInputRef = useRef(null);
12
- const [showPicker, setShowPicker] = useState(false);
13
- const addEmoji = (emoji) => {
14
- editor.chain().focus().insertContent(emoji.native).run();
15
- setShowPicker(false);
16
- };
17
- const handleTableCellHover = (row, col) => {
18
- setSelectedRows(row);
19
- setSelectedCols(col);
20
- };
21
- const handleTableInsert = (rows, cols) => {
22
- editor
23
- .chain()
24
- .focus()
25
- .insertTable({
26
- rows: rows || 1,
27
- cols: cols || 1,
28
- withHeaderRow: true,
29
- })
30
- .run();
31
- setShowTableGrid(false);
32
- setSelectedRows(1);
33
- setSelectedCols(1);
34
- };
35
- const handleImageUpload = (e) => {
36
- var _a;
37
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
38
- if (file) {
39
- const reader = new FileReader();
40
- reader.onload = () => {
41
- editor
42
- .chain()
43
- .focus()
44
- .setImage({ src: reader.result })
45
- .run();
46
- };
47
- reader.readAsDataURL(file);
48
- }
49
- };
50
- const handleVideoUpload = (e) => {
51
- var _a;
52
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
53
- if (file) {
54
- const reader = new FileReader();
55
- reader.onload = () => {
56
- editor
57
- .chain()
58
- .focus()
59
- .setVideo({ src: reader.result, controls: true })
60
- .run();
61
- };
62
- reader.readAsDataURL(file);
63
- }
64
- };
65
- function normalizeEmbedUrl(url) {
66
- try {
67
- const u = new URL(url);
68
- const hostname = u.hostname.replace("www.", "").toLowerCase();
69
- if (hostname === "youtube.com" || hostname === "youtu.be") {
70
- let videoId = u.searchParams.get("v");
71
- if (!videoId && hostname === "youtu.be") {
72
- videoId = u.pathname.slice(1);
73
- }
74
- if (videoId) {
75
- return `https://www.youtube.com/embed/${videoId}`;
76
- }
77
- }
78
- if (hostname === "vimeo.com") {
79
- const videoId = u.pathname.split("/")[1];
80
- if (videoId) {
81
- return `https://player.vimeo.com/video/${videoId}`;
82
- }
83
- }
84
- if (hostname === "google.com" ||
85
- hostname === "maps.google.com" ||
86
- hostname === "goo.gl") {
87
- if (url.includes("/maps/embed")) {
88
- return url;
89
- }
90
- return url.replace("/maps/", "/maps/embed/");
91
- }
92
- return url;
93
- }
94
- catch (_a) {
95
- return url;
96
- }
97
- }
98
- return (<div className="insert-group">
99
- <input type="file" accept="image/*" ref={imageInputRef} className="hidden-input" onChange={handleImageUpload} aria-label="Upload Image" title="Upload Image"/>
100
- <input type="file" accept="video/*" ref={videoInputRef} className="hidden-input" onChange={handleVideoUpload} aria-label="Upload Video" title="Upload Video"/>
101
-
102
- <ToolbarButton icon={MdTableChart} label="Insert Table" onClick={() => setShowTableGrid(!showTableGrid)}/>
103
- {showTableGrid && (<div className="table-grid-popup" onMouseLeave={() => setShowTableGrid(false)}>
104
- <div className="table-grid">
105
- {[...Array(10)].map((_, row) => [...Array(10)].map((_, col) => {
106
- const isSelected = row < selectedRows && col < selectedCols;
107
- return (<div key={`${row}-${col}`} className={`table-grid-cell ${isSelected ? "selected" : ""}`} onMouseEnter={() => handleTableCellHover(row + 1, col + 1)} onClick={() => handleTableInsert(row + 1, col + 1)}/>);
108
- }))}
109
- </div>
110
- <div className="table-grid-label">
111
- {selectedRows} x {selectedCols}
112
- </div>
113
- </div>)}
114
-
115
- <ToolbarButton icon={MdInsertPhoto} label="Insert Image" onClick={() => { var _a; return (_a = imageInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }}/>
116
- <ToolbarButton icon={MdVideoLibrary} label="Insert Video" onClick={() => { var _a; return (_a = videoInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }}/>
117
- <ToolbarButton icon={MdInsertLink} label="Insert Link" onClick={() => {
118
- const url = prompt("Enter URL");
119
- if (url) {
120
- editor
121
- .chain()
122
- .focus()
123
- .extendMarkRange("link")
124
- .setLink({ href: url })
125
- .run();
126
- }
127
- }}/>
128
- <ToolbarButton icon={MdInsertComment} label="Insert Comment" onClick={() => {
129
- const comment = prompt("Enter your comment");
130
- if (comment &&
131
- editor.can().chain().focus().setComment(comment).run()) {
132
- editor.chain().focus().setComment(comment).run();
133
- }
134
- else {
135
- console.warn("Cannot apply comment — maybe no selection?");
136
- }
137
- }}/>
138
- <div className="relative">
139
- <ToolbarButton icon={MdInsertEmoticon} label="Emoji" onClick={() => setShowPicker(!showPicker)}/>
140
- {showPicker && (<div className="emoji-picker">
141
- <Picker onEmojiSelect={addEmoji} theme="auto" emoji="point_up" showPreview={false} showSkinTones={true} emojiTooltip={true}/>
142
- </div>)}
143
- </div>
144
- <ToolbarButton icon={MdHorizontalRule} label="Horizontal Line" onClick={() => editor.chain().focus().setHorizontalRule().run()}/>
145
- <ToolbarButton icon={MdOutlineOndemandVideo} label="Embed" onClick={() => {
146
- const url = prompt("Enter embed URL (YouTube, Vimeo, Google Maps, etc.)");
147
- if (!url)
148
- return;
149
- const embedUrl = normalizeEmbedUrl(url);
150
- const width = prompt("Enter width in pixels", "560");
151
- const height = prompt("Enter height in pixels", "315");
152
- editor
153
- .chain()
154
- .focus()
155
- .setEmbed({
156
- src: embedUrl,
157
- width: width ? parseInt(width) : 560,
158
- height: height ? parseInt(height) : 315,
159
- })
160
- .run();
161
- }}/>
162
- </div>);
163
- }
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- import { Editor } from "@tiptap/react";
3
- export default function ListAlignGroup({ editor }: {
4
- editor: Editor;
5
- }): React.JSX.Element;
@@ -1,16 +0,0 @@
1
- "use client";
2
- import React from "react";
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 (<div className="list-align-group">
7
- <ToolbarButton icon={MdFormatListBulleted} title="Bulleted List" onClick={() => editor.chain().focus().toggleBulletList().run()} disabled={!editor.can().toggleBulletList()}/>
8
- <ToolbarButton icon={MdFormatListNumbered} title="Numbered List" onClick={() => editor.chain().focus().toggleOrderedList().run()} disabled={!editor.can().toggleOrderedList()}/>
9
- <ToolbarButton icon={MdFormatIndentIncrease} title="Increase Indent" onClick={() => editor.chain().focus().sinkListItem("listItem").run()} disabled={!editor.can().sinkListItem("listItem")}/>
10
- <ToolbarButton icon={MdFormatIndentDecrease} title="Decrease Indent" onClick={() => editor.chain().focus().liftListItem("listItem").run()} disabled={!editor.can().liftListItem("listItem")}/>
11
- <ToolbarButton icon={MdFormatAlignLeft} title="Align Left" onClick={() => editor.chain().focus().setTextAlign("left").run()} disabled={!editor.can().setTextAlign("left")}/>
12
- <ToolbarButton icon={MdFormatAlignCenter} title="Align Center" onClick={() => editor.chain().focus().setTextAlign("center").run()} disabled={!editor.can().setTextAlign("center")}/>
13
- <ToolbarButton icon={MdFormatAlignRight} title="Align Right" onClick={() => editor.chain().focus().setTextAlign("right").run()} disabled={!editor.can().setTextAlign("right")}/>
14
- <ToolbarButton icon={MdFormatAlignJustify} title="Justify" onClick={() => editor.chain().focus().setTextAlign("justify").run()} disabled={!editor.can().setTextAlign("justify")}/>
15
- </div>);
16
- }
@@ -1,7 +0,0 @@
1
- import React from "react";
2
- import { Editor } from "@tiptap/react";
3
- interface MiscGroupProps {
4
- editor: Editor;
5
- }
6
- export default function MiscGroup({ editor }: MiscGroupProps): React.JSX.Element;
7
- export {};
@@ -1,31 +0,0 @@
1
- import React from "react";
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 (<div className="misc-group">
25
- <ToolbarButton icon={MdUndo} label="Undo" onClick={() => editor.chain().focus().undo().run()} disabled={!editor.can().undo()}/>
26
- <ToolbarButton icon={MdRedo} label="Redo" onClick={() => editor.chain().focus().redo().run()} disabled={!editor.can().redo()}/>
27
- <ToolbarButton icon={MdRefresh} label="Reset Formatting" onClick={() => editor.chain().focus().unsetAllMarks().clearNodes().run()}/>
28
- <ToolbarButton icon={MdCode} label="Toggle Code Block" onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={editor.isActive("codeBlock")}/>
29
- <ToolbarButton icon={MdVisibility} label="Preview" onClick={handlePreview}/>
30
- </div>);
31
- }
@@ -1,7 +0,0 @@
1
- import React from "react";
2
- import { Editor } from "@tiptap/react";
3
- interface ContextMenuProps {
4
- editor: Editor;
5
- }
6
- export default function TableContextMenu({ editor }: ContextMenuProps): React.JSX.Element | null;
7
- export {};