tetrons 2.2.3 → 2.2.5

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 (49) hide show
  1. package/dist/app/api/register/route.d.ts +6 -0
  2. package/dist/app/api/register/route.js +26 -0
  3. package/dist/app/api/validate/route.d.ts +7 -0
  4. package/dist/app/api/validate/route.js +18 -0
  5. package/dist/app/layout.d.ts +1 -2
  6. package/dist/app/{layout.js → layout.jsx} +5 -2
  7. package/dist/app/page.d.ts +1 -1
  8. package/dist/app/page.jsx +62 -0
  9. package/dist/components/tetrons/EditorContent.d.ts +6 -1
  10. package/dist/components/tetrons/{EditorContent.js → EditorContent.jsx} +58 -3
  11. package/dist/components/tetrons/{ResizableImageComponent.js → ResizableImageComponent.jsx} +12 -10
  12. package/dist/components/tetrons/{ResizableVideoComponent.js → ResizableVideoComponent.jsx} +8 -7
  13. package/dist/components/tetrons/toolbar/ActionGroup.d.ts +2 -1
  14. package/dist/components/tetrons/toolbar/{ActionGroup.js → ActionGroup.jsx} +35 -12
  15. package/dist/components/tetrons/toolbar/ClipboardGroup.d.ts +2 -1
  16. package/dist/components/tetrons/toolbar/ClipboardGroup.jsx +36 -0
  17. package/dist/components/tetrons/toolbar/FileGroup.d.ts +2 -1
  18. package/dist/components/tetrons/toolbar/{FileGroup.js → FileGroup.jsx} +6 -3
  19. package/dist/components/tetrons/toolbar/FontStyleGroup.d.ts +2 -1
  20. package/dist/components/tetrons/toolbar/FontStyleGroup.jsx +104 -0
  21. package/dist/components/tetrons/toolbar/InsertGroup.d.ts +2 -1
  22. package/dist/components/tetrons/toolbar/InsertGroup.jsx +163 -0
  23. package/dist/components/tetrons/toolbar/ListAlignGroup.d.ts +2 -1
  24. package/dist/components/tetrons/toolbar/ListAlignGroup.jsx +16 -0
  25. package/dist/components/tetrons/toolbar/MiscGroup.d.ts +2 -1
  26. package/dist/components/tetrons/toolbar/MiscGroup.jsx +31 -0
  27. package/dist/components/tetrons/toolbar/TableContextMenu.d.ts +2 -1
  28. package/dist/components/tetrons/toolbar/{TableContextMenu.js → TableContextMenu.jsx} +21 -3
  29. package/dist/components/tetrons/toolbar/TetronsToolbar.d.ts +2 -1
  30. package/dist/components/tetrons/toolbar/{TetronsToolbar.js → TetronsToolbar.jsx} +14 -2
  31. package/dist/components/tetrons/toolbar/ToolbarButton.jsx +8 -0
  32. package/dist/index.d.mts +5 -2
  33. package/dist/index.mjs +658 -744
  34. package/dist/lib/db.d.ts +1 -0
  35. package/dist/lib/db.js +15 -0
  36. package/dist/models/ApiKey.d.ts +2 -0
  37. package/dist/models/ApiKey.js +14 -0
  38. package/dist/utils/apiKeyUtils.d.ts +11 -0
  39. package/dist/utils/apiKeyUtils.js +17 -0
  40. package/package.json +2 -1
  41. package/dist/app/page.js +0 -6
  42. package/dist/components/tetrons/toolbar/ClipboardGroup.js +0 -31
  43. package/dist/components/tetrons/toolbar/FontStyleGroup.js +0 -63
  44. package/dist/components/tetrons/toolbar/InsertGroup.js +0 -138
  45. package/dist/components/tetrons/toolbar/ListAlignGroup.js +0 -7
  46. package/dist/components/tetrons/toolbar/MiscGroup.js +0 -25
  47. package/dist/components/tetrons/toolbar/ToolbarButton.js +0 -7
  48. /package/dist/components/UI/{Button.js → Button.jsx} +0 -0
  49. /package/dist/components/UI/{Dropdown.js → Dropdown.jsx} +0 -0
@@ -0,0 +1,163 @@
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,4 +1,5 @@
1
+ import React from "react";
1
2
  import { Editor } from "@tiptap/react";
2
3
  export default function ListAlignGroup({ editor }: {
3
4
  editor: Editor;
4
- }): import("react/jsx-runtime").JSX.Element;
5
+ }): React.JSX.Element;
@@ -0,0 +1,16 @@
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,6 +1,7 @@
1
+ import React from "react";
1
2
  import { Editor } from "@tiptap/react";
2
3
  interface MiscGroupProps {
3
4
  editor: Editor;
4
5
  }
5
- export default function MiscGroup({ editor }: MiscGroupProps): import("react/jsx-runtime").JSX.Element;
6
+ export default function MiscGroup({ editor }: MiscGroupProps): React.JSX.Element;
6
7
  export {};
@@ -0,0 +1,31 @@
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,6 +1,7 @@
1
+ import React from "react";
1
2
  import { Editor } from "@tiptap/react";
2
3
  interface ContextMenuProps {
3
4
  editor: Editor;
4
5
  }
5
- export default function TableContextMenu({ editor }: ContextMenuProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export default function TableContextMenu({ editor }: ContextMenuProps): React.JSX.Element | null;
6
7
  export {};
@@ -1,6 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from "react";
2
+ import React, { useEffect, useState } from "react";
4
3
  export default function TableContextMenu({ editor }) {
5
4
  const [menuPosition, setMenuPosition] = useState(null);
6
5
  useEffect(() => {
@@ -30,5 +29,24 @@ export default function TableContextMenu({ editor }) {
30
29
  const deleteCol = () => editor.chain().focus().deleteColumn().run();
31
30
  if (!menuPosition)
32
31
  return null;
33
- return (_jsxs("ul", { className: "absolute bg-white shadow border rounded text-sm z-50", style: { top: menuPosition.y, left: menuPosition.x }, children: [_jsx("li", { className: "px-3 py-1 hover:bg-gray-100 cursor-pointer", onClick: insertRowAbove, children: "Insert Row Above" }), _jsx("li", { className: "px-3 py-1 hover:bg-gray-100 cursor-pointer", onClick: insertRowBelow, children: "Insert Row Below" }), _jsx("li", { className: "px-3 py-1 hover:bg-gray-100 cursor-pointer", onClick: insertColLeft, children: "Insert Column Left" }), _jsx("li", { className: "px-3 py-1 hover:bg-gray-100 cursor-pointer", onClick: insertColRight, children: "Insert Column Right" }), _jsx("li", { className: "px-3 py-1 hover:bg-red-100 cursor-pointer", onClick: deleteRow, children: "Delete Row" }), _jsx("li", { className: "px-3 py-1 hover:bg-red-100 cursor-pointer", onClick: deleteCol, children: "Delete Column" })] }));
32
+ return (<ul className="absolute bg-white shadow border rounded text-sm z-50" style={{ top: menuPosition.y, left: menuPosition.x }}>
33
+ <li className="px-3 py-1 hover:bg-gray-100 cursor-pointer" onClick={insertRowAbove}>
34
+ Insert Row Above
35
+ </li>
36
+ <li className="px-3 py-1 hover:bg-gray-100 cursor-pointer" onClick={insertRowBelow}>
37
+ Insert Row Below
38
+ </li>
39
+ <li className="px-3 py-1 hover:bg-gray-100 cursor-pointer" onClick={insertColLeft}>
40
+ Insert Column Left
41
+ </li>
42
+ <li className="px-3 py-1 hover:bg-gray-100 cursor-pointer" onClick={insertColRight}>
43
+ Insert Column Right
44
+ </li>
45
+ <li className="px-3 py-1 hover:bg-red-100 cursor-pointer" onClick={deleteRow}>
46
+ Delete Row
47
+ </li>
48
+ <li className="px-3 py-1 hover:bg-red-100 cursor-pointer" onClick={deleteCol}>
49
+ Delete Column
50
+ </li>
51
+ </ul>);
34
52
  }
@@ -1,4 +1,5 @@
1
+ import React from "react";
1
2
  import type { Editor } from "@tiptap/react";
2
3
  export default function TetronsToolbar({ editor }: {
3
4
  editor: Editor;
4
- }): import("react/jsx-runtime").JSX.Element;
5
+ }): React.JSX.Element;
@@ -1,5 +1,4 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
2
  import React, { useEffect } from "react";
4
3
  import ActionGroup from "./ActionGroup";
5
4
  import ClipboardGroup from "./ClipboardGroup";
@@ -28,5 +27,18 @@ export default function TetronsToolbar({ editor }) {
28
27
  editor.off("update", handleUpdate);
29
28
  };
30
29
  }, [autoSave, editor]);
31
- return (_jsxs("div", { className: "tetrons-toolbar", children: [_jsxs("div", { className: "group", children: [_jsx("input", { type: "checkbox", id: "autoSave", checked: autoSave, onChange: (e) => setAutoSave(e.target.checked) }), _jsx("label", { htmlFor: "autoSave", children: "Auto Save" })] }), _jsx(FileGroup, { editor: editor }), _jsx(ClipboardGroup, { editor: editor }), _jsx(FontStyleGroup, { editor: editor }), _jsx(ListAlignGroup, { editor: editor }), _jsx(InsertGroup, { editor: editor }), _jsx(MiscGroup, { editor: editor }), _jsx(ActionGroup, { editor: editor })] }));
30
+ return (<div className="tetrons-toolbar">
31
+ <div className="group">
32
+ <input type="checkbox" id="autoSave" checked={autoSave} onChange={(e) => setAutoSave(e.target.checked)}/>
33
+ <label htmlFor="autoSave">Auto Save</label>
34
+ </div>
35
+
36
+ <FileGroup editor={editor}/>
37
+ <ClipboardGroup editor={editor}/>
38
+ <FontStyleGroup editor={editor}/>
39
+ <ListAlignGroup editor={editor}/>
40
+ <InsertGroup editor={editor}/>
41
+ <MiscGroup editor={editor}/>
42
+ <ActionGroup editor={editor}/>
43
+ </div>);
32
44
  }
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ const ToolbarButton = React.forwardRef(({ icon: Icon, onClick, disabled = false, title, label, isActive = false }, ref) => {
3
+ return (<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" : ""}`}>
4
+ <Icon size={20}/>
5
+ </button>);
6
+ });
7
+ ToolbarButton.displayName = "ToolbarButton";
8
+ export default ToolbarButton;
package/dist/index.d.mts CHANGED
@@ -1,5 +1,8 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import React from 'react';
2
2
 
3
- declare function EditorContent(): react_jsx_runtime.JSX.Element;
3
+ type EditorContentProps = {
4
+ apiKey: string;
5
+ };
6
+ declare function EditorContent({ apiKey }: EditorContentProps): React.JSX.Element;
4
7
 
5
8
  export { EditorContent, EditorContent as default };