tetrons 2.2.1 → 2.2.3

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 (42) hide show
  1. package/dist/app/layout.d.ts +1 -1
  2. package/dist/app/{layout.jsx → layout.js} +2 -5
  3. package/dist/app/page.d.ts +2 -2
  4. package/dist/app/page.js +6 -0
  5. package/dist/components/tetrons/EditorContent.d.ts +1 -2
  6. package/dist/components/tetrons/{EditorContent.jsx → EditorContent.js} +2 -24
  7. package/dist/components/tetrons/{ResizableImageComponent.jsx → ResizableImageComponent.js} +10 -12
  8. package/dist/components/tetrons/{ResizableVideoComponent.jsx → ResizableVideoComponent.js} +7 -8
  9. package/dist/components/tetrons/toolbar/ActionGroup.d.ts +1 -2
  10. package/dist/components/tetrons/toolbar/{ActionGroup.jsx → ActionGroup.js} +18 -39
  11. package/dist/components/tetrons/toolbar/ClipboardGroup.d.ts +1 -2
  12. package/dist/components/tetrons/toolbar/ClipboardGroup.js +31 -0
  13. package/dist/components/tetrons/toolbar/FileGroup.d.ts +1 -2
  14. package/dist/components/tetrons/toolbar/{FileGroup.jsx → FileGroup.js} +3 -6
  15. package/dist/components/tetrons/toolbar/FontStyleGroup.d.ts +1 -2
  16. package/dist/components/tetrons/toolbar/FontStyleGroup.js +63 -0
  17. package/dist/components/tetrons/toolbar/InsertGroup.d.ts +1 -2
  18. package/dist/components/tetrons/toolbar/InsertGroup.js +138 -0
  19. package/dist/components/tetrons/toolbar/ListAlignGroup.d.ts +1 -2
  20. package/dist/components/tetrons/toolbar/ListAlignGroup.js +7 -0
  21. package/dist/components/tetrons/toolbar/MiscGroup.d.ts +1 -2
  22. package/dist/components/tetrons/toolbar/MiscGroup.js +25 -0
  23. package/dist/components/tetrons/toolbar/TableContextMenu.d.ts +1 -2
  24. package/dist/components/tetrons/toolbar/{TableContextMenu.jsx → TableContextMenu.js} +3 -21
  25. package/dist/components/tetrons/toolbar/TetronsToolbar.d.ts +1 -2
  26. package/dist/components/tetrons/toolbar/{TetronsToolbar.jsx → TetronsToolbar.js} +2 -16
  27. package/dist/components/tetrons/toolbar/ToolbarButton.js +7 -0
  28. package/dist/index.d.mts +2 -2
  29. package/dist/index.js +1 -0
  30. package/dist/index.mjs +731 -638
  31. package/dist/styles/styles/tetrons.css +371 -0
  32. package/dist/styles/tetrons.css +371 -0
  33. package/package.json +7 -4
  34. package/dist/app/page.jsx +0 -8
  35. package/dist/components/tetrons/toolbar/ClipboardGroup.jsx +0 -36
  36. package/dist/components/tetrons/toolbar/FontStyleGroup.jsx +0 -104
  37. package/dist/components/tetrons/toolbar/InsertGroup.jsx +0 -162
  38. package/dist/components/tetrons/toolbar/ListAlignGroup.jsx +0 -16
  39. package/dist/components/tetrons/toolbar/MiscGroup.jsx +0 -31
  40. package/dist/components/tetrons/toolbar/ToolbarButton.jsx +0 -8
  41. /package/dist/components/UI/{Button.jsx → Button.js} +0 -0
  42. /package/dist/components/UI/{Dropdown.jsx → Dropdown.js} +0 -0
@@ -4,4 +4,4 @@ import "./globals.css";
4
4
  export declare const metadata: Metadata;
5
5
  export default function RootLayout({ children, }: {
6
6
  children: React.ReactNode;
7
- }): import("react").JSX.Element;
7
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import { Geist, Geist_Mono } from "next/font/google";
2
3
  import "./globals.css";
3
4
  const geistSans = Geist({
@@ -22,9 +23,5 @@ export const metadata = {
22
23
  manifest: "/site.webmanifest",
23
24
  };
24
25
  export default function RootLayout({ children, }) {
25
- return (<html lang="en">
26
- <body suppressHydrationWarning className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased bg-gray-50 text-gray-900`}>
27
- <main className="max-w-screen-xl mx-auto p-4">{children}</main>
28
- </body>
29
- </html>);
26
+ return (_jsx("html", { lang: "en", children: _jsx("body", { suppressHydrationWarning: true, className: `${geistSans.variable} ${geistMono.variable} font-sans antialiased bg-gray-50 text-gray-900`, children: _jsx("main", { className: "max-w-screen-xl mx-auto p-4", children: children }) }) }));
30
27
  }
@@ -1,2 +1,2 @@
1
- /// <reference types="react" />
2
- export default function Home(): import("react").JSX.Element;
1
+ import "../styles/tetrons.css";
2
+ export default function Home(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
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,2 +1 @@
1
- import React from "react";
2
- export default function EditorContent(): React.JSX.Element;
1
+ export default function EditorContent(): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import { Comment } from "./toolbar/extensions/Comment";
4
5
  import { useEffect, useRef } from "react";
@@ -133,28 +134,5 @@ export default function EditorContent() {
133
134
  setCurrentVersionIndex(index);
134
135
  }
135
136
  };
136
- return (<div className="flex flex-col h-full">
137
- <div className="p-2 border-b border-gray-300 bg-gray-50 flex flex-wrap items-center gap-3">
138
- <button onClick={saveVersion} disabled={!editor} className="px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50">
139
- Save Version
140
- </button>
141
-
142
- <div className="flex items-center gap-2 overflow-x-auto max-w-full">
143
- {versions.length === 0 && (<span className="text-gray-500 text-sm">No saved versions</span>)}
144
- {versions.map((_, idx) => (<button key={idx} onClick={() => restoreVersion(idx)} className={`px-2 py-1 rounded border ${idx === currentVersionIndex
145
- ? "border-blue-600 font-semibold text-blue-600"
146
- : "border-gray-300 text-gray-700 hover:border-gray-600"}`} title={`Restore Version ${idx + 1}`}>
147
- {`V${idx + 1}`}
148
- </button>))}
149
- </div>
150
- </div>
151
-
152
- {editor && <TetronsToolbar editor={editor}/>}
153
- <div ref={wrapperRef} className="flex-grow p-4 md:p-6 bg-white border border-gray-300 rounded shadow-sm overflow-auto min-h-0 prose relative" onClick={handleEditorClick}>
154
- {editor ? (<>
155
- <TiptapEditorContent editor={editor}/>
156
- {editor && <TableContextMenu editor={editor}/>}
157
- </>) : (<div className="text-gray-500">Loading editor...</div>)}
158
- </div>
159
- </div>);
137
+ return (_jsxs("div", { className: "editor-container", children: [_jsxs("div", { className: "editor-toolbar", children: [_jsx("button", { type: "button", onClick: saveVersion, disabled: !editor, className: "editor-save-btn", children: "Save Version" }), _jsxs("div", { className: "editor-versions-wrapper", children: [versions.length === 0 && (_jsx("span", { className: "editor-no-versions", children: "No saved versions" })), versions.map((_, idx) => (_jsx("button", { type: "button", onClick: () => restoreVersion(idx), className: `editor-version-btn ${idx === currentVersionIndex ? "active" : ""}`, title: `Restore Version ${idx + 1}`, children: `V${idx + 1}` }, idx)))] })] }), editor && _jsx(TetronsToolbar, { editor: editor }), _jsx("div", { ref: wrapperRef, className: "editor-content-wrapper", onClick: handleEditorClick, children: editor ? (_jsxs(_Fragment, { children: [_jsx(TiptapEditorContent, { editor: editor }), editor && _jsx(TableContextMenu, { editor: editor })] })) : (_jsx("div", { className: "editor-loading", children: "Loading editor..." })) })] }));
160
138
  }
@@ -1,4 +1,5 @@
1
- import React, { useRef, useEffect } from "react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef, useEffect } from "react";
2
3
  import { NodeViewWrapper } from "@tiptap/react";
3
4
  const ResizableImageComponent = ({ node, updateAttributes, selected, }) => {
4
5
  const { src, alt, title, width, height } = node.attrs;
@@ -16,22 +17,19 @@ const ResizableImageComponent = ({ node, updateAttributes, selected, }) => {
16
17
  observer.observe(img);
17
18
  return () => observer.disconnect();
18
19
  }, [updateAttributes]);
19
- return (<NodeViewWrapper ref={wrapperRef} contentEditable={false} className={`resizable-image-wrapper ${selected ? "ProseMirror-selectednode" : ""}`} style={{
20
+ return (_jsx(NodeViewWrapper, { ref: wrapperRef, contentEditable: false, className: `resizable-image-wrapper ${selected ? "ProseMirror-selectednode" : ""}`, style: {
20
21
  resize: "both",
21
22
  overflow: "auto",
22
23
  border: "1px solid #ccc",
23
24
  padding: 2,
24
25
  display: "inline-block",
25
26
  maxWidth: "100%",
26
- }}>
27
- {/* eslint-disable-next-line @next/next/no-img-element */}
28
- <img ref={imgRef} src={src} alt={alt !== null && alt !== void 0 ? alt : ""} title={title !== null && title !== void 0 ? title : ""} loading="lazy" style={{
29
- width: width ? `${width}px` : "auto",
30
- height: height ? `${height}px` : "auto",
31
- display: "block",
32
- userSelect: "none",
33
- pointerEvents: "auto",
34
- }} draggable={false}/>
35
- </NodeViewWrapper>);
27
+ }, children: _jsx("img", { ref: imgRef, src: src, alt: alt !== null && alt !== void 0 ? alt : "", title: title !== null && title !== void 0 ? title : "", loading: "lazy", style: {
28
+ width: width ? `${width}px` : "auto",
29
+ height: height ? `${height}px` : "auto",
30
+ display: "block",
31
+ userSelect: "none",
32
+ pointerEvents: "auto",
33
+ }, draggable: false }) }));
36
34
  };
37
35
  export default ResizableImageComponent;
@@ -1,4 +1,5 @@
1
- import React, { useRef, useEffect } from "react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef, useEffect } from "react";
2
3
  import { NodeViewWrapper } from "@tiptap/react";
3
4
  const ResizableVideoComponent = ({ node, updateAttributes, selected, }) => {
4
5
  const { src, controls, width, height } = node.attrs;
@@ -16,17 +17,15 @@ const ResizableVideoComponent = ({ node, updateAttributes, selected, }) => {
16
17
  observer.observe(video);
17
18
  return () => observer.disconnect();
18
19
  }, [updateAttributes]);
19
- return (<NodeViewWrapper ref={wrapperRef} contentEditable={false} className={`resizable-video-wrapper ${selected ? "ProseMirror-selectednode" : ""}`} style={{
20
+ return (_jsx(NodeViewWrapper, { ref: wrapperRef, contentEditable: false, className: `resizable-video-wrapper ${selected ? "ProseMirror-selectednode" : ""}`, style: {
20
21
  resize: "both",
21
22
  overflow: "auto",
22
23
  border: "1px solid #ccc",
23
24
  padding: "2px",
24
25
  display: "inline-block",
25
- }}>
26
- <video ref={videoRef} src={src} controls={controls} style={{
27
- width: width ? `${width}px` : "auto",
28
- height: height ? `${height}px` : "auto",
29
- }}/>
30
- </NodeViewWrapper>);
26
+ }, children: _jsx("video", { ref: videoRef, src: src, controls: controls, style: {
27
+ width: width ? `${width}px` : "auto",
28
+ height: height ? `${height}px` : "auto",
29
+ } }) }));
31
30
  };
32
31
  export default ResizableVideoComponent;
@@ -1,7 +1,6 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  type ActionGroupProps = {
4
3
  editor: Editor;
5
4
  };
6
- export default function ActionGroup({ editor }: ActionGroupProps): React.JSX.Element;
5
+ export default function ActionGroup({ editor }: ActionGroupProps): import("react/jsx-runtime").JSX.Element;
7
6
  export {};
@@ -1,5 +1,6 @@
1
1
  "use client";
2
- import React, { useEffect, useRef, useState } from "react";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
3
4
  import { MdZoomIn, MdZoomOut, MdPrint, MdSave, MdDownload, } from "react-icons/md";
4
5
  import ToolbarButton from "./ToolbarButton";
5
6
  export default function ActionGroup({ editor }) {
@@ -25,17 +26,19 @@ export default function ActionGroup({ editor }) {
25
26
  const zoomIn = () => {
26
27
  const element = document.querySelector(".ProseMirror");
27
28
  if (element) {
28
- const currentZoom = parseFloat(element.style.zoom || "1");
29
+ const style = element.style;
30
+ const currentZoom = parseFloat(style.zoom || "1");
29
31
  const next = Math.min(currentZoom + 0.1, 2);
30
- element.style.zoom = next.toString();
32
+ style.zoom = next.toString();
31
33
  }
32
34
  };
33
35
  const zoomOut = () => {
34
36
  const element = document.querySelector(".ProseMirror");
35
37
  if (element) {
36
- const currentZoom = parseFloat(element.style.zoom || "1");
38
+ const style = element.style;
39
+ const currentZoom = parseFloat(style.zoom || "1");
37
40
  const next = Math.max(currentZoom - 0.1, 0.5);
38
- element.style.zoom = next.toString();
41
+ style.zoom = next.toString();
39
42
  }
40
43
  };
41
44
  const handlePrint = () => {
@@ -128,38 +131,14 @@ export default function ActionGroup({ editor }) {
128
131
  link.click();
129
132
  document.body.removeChild(link);
130
133
  };
131
- return (<div className="relative flex items-center gap-1" role="group" aria-label="Editor actions">
132
- <ToolbarButton icon={MdZoomIn} onClick={zoomIn} title="Zoom In"/>
133
- <ToolbarButton icon={MdZoomOut} onClick={zoomOut} title="Zoom Out"/>
134
- <ToolbarButton icon={MdPrint} onClick={handlePrint} title="Print"/>
135
- <ToolbarButton icon={MdSave} onClick={handleSave} title="Save"/>
136
-
137
- <div className="relative" ref={dropdownRef}>
138
- <button type="button" onClick={() => setDropdownOpen((open) => !open)} aria-haspopup="menu" aria-expanded={dropdownOpen ? "true" : "false"} className="flex items-center gap-1 px-2 py-1 rounded hover:bg-gray-100 focus:outline-none" title="Export">
139
- <MdDownload />
140
- <span className="text-sm"></span>
141
- </button>
142
-
143
- {dropdownOpen && (<div className="absolute z-10 mt-2 w-40 bg-white border rounded shadow-md">
144
- <button type="button" onClick={() => {
145
- setDropdownOpen(false);
146
- handleDownloadPDF();
147
- }} className="w-full text-left px-4 py-2 hover:bg-gray-100">
148
- Export as PDF
149
- </button>
150
- <button type="button" onClick={() => {
151
- setDropdownOpen(false);
152
- handleDownloadHTML();
153
- }} className="w-full text-left px-4 py-2 hover:bg-gray-100">
154
- Export as HTML
155
- </button>
156
- <button type="button" onClick={() => {
157
- setDropdownOpen(false);
158
- handleDownloadDOCX();
159
- }} className="w-full text-left px-4 py-2 hover:bg-gray-100">
160
- Export as DOCX
161
- </button>
162
- </div>)}
163
- </div>
164
- </div>);
134
+ return (_jsxs("div", { className: "action-group", role: "group", "aria-label": "Editor actions", children: [_jsx(ToolbarButton, { icon: MdZoomIn, onClick: zoomIn, title: "Zoom In" }), _jsx(ToolbarButton, { icon: MdZoomOut, onClick: zoomOut, title: "Zoom Out" }), _jsx(ToolbarButton, { icon: MdPrint, onClick: handlePrint, title: "Print" }), _jsx(ToolbarButton, { icon: MdSave, onClick: handleSave, title: "Save" }), _jsxs("div", { className: "relative", ref: dropdownRef, children: [_jsxs("button", { type: "button", onClick: () => setDropdownOpen((open) => !open), "aria-haspopup": "menu", "aria-expanded": dropdownOpen ? "true" : "false", className: "export-button", title: "Export", children: [_jsx(MdDownload, {}), _jsx("span", { className: "text-sm" })] }), dropdownOpen && (_jsxs("div", { className: "export-dropdown", children: [_jsx("button", { type: "button", onClick: () => {
135
+ setDropdownOpen(false);
136
+ handleDownloadPDF();
137
+ }, children: "Export as PDF" }), _jsx("button", { type: "button", onClick: () => {
138
+ setDropdownOpen(false);
139
+ handleDownloadHTML();
140
+ }, children: "Export as HTML" }), _jsx("button", { type: "button", onClick: () => {
141
+ setDropdownOpen(false);
142
+ handleDownloadDOCX();
143
+ }, children: "Export as DOCX" })] }))] })] }));
165
144
  }
@@ -1,5 +1,4 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  export default function ClipboardGroup({ editor }: {
4
3
  editor: Editor;
5
- }): React.JSX.Element;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
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,7 +1,6 @@
1
1
  import { Editor } from "@tiptap/react";
2
- import React from "react";
3
2
  type FileGroupProps = {
4
3
  editor: Editor;
5
4
  };
6
- export default function FileGroup({ editor }: FileGroupProps): React.JSX.Element;
5
+ export default function FileGroup({ editor }: FileGroupProps): import("react/jsx-runtime").JSX.Element;
7
6
  export {};
@@ -1,8 +1,9 @@
1
1
  "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { FaRegFolderOpen } from "react-icons/fa";
3
4
  import { VscNewFile } from "react-icons/vsc";
4
5
  import ToolbarButton from "./ToolbarButton";
5
- import React, { useRef } from "react";
6
+ import { useRef } from "react";
6
7
  export default function FileGroup({ editor }) {
7
8
  const fileInputRef = useRef(null);
8
9
  const handleNew = () => {
@@ -32,9 +33,5 @@ export default function FileGroup({ editor }) {
32
33
  e.target.value = "";
33
34
  }
34
35
  };
35
- return (<div className="flex items-center gap-1 border-r pr-3" 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>);
36
+ return (_jsxs("div", { className: "file-group", role: "group", "aria-label": "File actions", children: [_jsx("input", { type: "file", accept: ".json", ref: fileInputRef, onChange: handleFileChange, className: "hidden", "aria-label": "Open JSON file" }), _jsx(ToolbarButton, { icon: VscNewFile, onClick: handleNew, title: "New" }), _jsx(ToolbarButton, { icon: FaRegFolderOpen, onClick: handleOpen, title: "Open File" })] }));
40
37
  }
@@ -1,7 +1,6 @@
1
1
  import { Editor } from "@tiptap/react";
2
- import React from "react";
3
2
  interface FontStyleGroupProps {
4
3
  editor: Editor;
5
4
  }
6
- export default function FontStyleGroup({ editor }: FontStyleGroupProps): React.JSX.Element;
5
+ export default function FontStyleGroup({ editor }: FontStyleGroupProps): import("react/jsx-runtime").JSX.Element;
7
6
  export {};
@@ -0,0 +1,63 @@
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,5 +1,4 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  export default function InsertGroup({ editor }: {
4
3
  editor: Editor;
5
- }): React.JSX.Element;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,138 @@
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,5 +1,4 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  export default function ListAlignGroup({ editor }: {
4
3
  editor: Editor;
5
- }): React.JSX.Element;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
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,7 +1,6 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  interface MiscGroupProps {
4
3
  editor: Editor;
5
4
  }
6
- export default function MiscGroup({ editor }: MiscGroupProps): React.JSX.Element;
5
+ export default function MiscGroup({ editor }: MiscGroupProps): import("react/jsx-runtime").JSX.Element;
7
6
  export {};
@@ -0,0 +1,25 @@
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 +1,6 @@
1
- import React from "react";
2
1
  import { Editor } from "@tiptap/react";
3
2
  interface ContextMenuProps {
4
3
  editor: Editor;
5
4
  }
6
- export default function TableContextMenu({ editor }: ContextMenuProps): React.JSX.Element | null;
5
+ export default function TableContextMenu({ editor }: ContextMenuProps): import("react/jsx-runtime").JSX.Element | null;
7
6
  export {};