UNPKG

react-latex-editor

Version:

A professional React rich text editor with mathematical equation support, built on TipTap with MathLive integration. Production-ready with TypeScript support, accessibility features, and industrial-grade error handling.

1,653 lines (1,652 loc) 103 kB
import { jsx as t, jsxs as r, Fragment as q } from "react/jsx-runtime"; import { NodeViewWrapper as I, ReactNodeViewRenderer as D, InputRule as $, useEditor as Y, EditorContent as J } from "@tiptap/react"; import H, { Component as W, useRef as E, useCallback as C, useState as f, memo as X, forwardRef as z, useEffect as L, useMemo as _, useImperativeHandle as O, useLayoutEffect as Z } from "react"; import Q from "@tiptap/starter-kit"; import ee from "@tiptap/extension-table"; import te from "@tiptap/extension-table-row"; import ie from "@tiptap/extension-table-cell"; import le from "@tiptap/extension-table-header"; import ne from "@tiptap/extension-link"; import re from "@tiptap/extension-text-align"; import oe from "@tiptap/extension-highlight"; import ae from "@tiptap/extension-underline"; import se from "@tiptap/extension-subscript"; import ce from "@tiptap/extension-superscript"; import { CodeBlockLowlight as de } from "@tiptap/extension-code-block-lowlight"; import { createLowlight as ue, common as he } from "lowlight"; import pe from "@tiptap/extension-strike"; import me from "@tiptap/extension-color"; import ge from "@tiptap/extension-blockquote"; import ye from "@tiptap/extension-horizontal-rule"; import be from "@tiptap/extension-hard-break"; import fe from "@tiptap/extension-task-list"; import xe from "@tiptap/extension-task-item"; import ve from "@tiptap/extension-placeholder"; import ke from "@tiptap/extension-character-count"; import we from "@tiptap/extension-floating-menu"; import { Node as j, Extension as Ce, mergeAttributes as R, Mark as G } from "@tiptap/core"; import Ae from "@tiptap/extension-image"; import Me from "@tiptap/extension-youtube"; import Le from "@tiptap/extension-text-style"; import "mathlive"; class Te extends W { mathFieldRef; cleanup; constructor(i) { super(i), this.mathFieldRef = H.createRef(); } async componentDidMount() { if (typeof window < "u") try { const i = await import("mathlive"); customElements.get("math-field") || customElements.define("math-field", i.MathfieldElement), i.MathfieldElement && (i.MathfieldElement.fontsDirectory = null), this.setupMathField(); } catch (i) { console.error("Failed to load MathLive:", i); } } componentDidUpdate(i) { i.node.attrs.latex !== this.props.node.attrs.latex && this.updateMathField(); } componentWillUnmount() { this.cleanupMathField(); } setupMathField() { const i = this.mathFieldRef.current; if (!i) return; const l = () => { try { const n = i.value; n !== this.props.node.attrs.latex && this.props.updateAttributes({ latex: n }); } catch (n) { console.error("Error updating math field:", n); } }; setTimeout(() => { if (i && i.isConnected) try { i.addEventListener("input", l), i.value = this.props.node.attrs.latex || ""; } catch (n) { console.error("Error setting up math field:", n); } }, 0), this.cleanup = () => { if (i && i.isConnected) try { i.removeEventListener("input", l), i.menuItems = []; } catch (n) { console.error("Error cleaning up math field:", n); } }; } updateMathField() { this.mathFieldRef.current && (this.mathFieldRef.current.value = this.props.node.attrs.latex || ""); } cleanupMathField() { this.cleanup && this.cleanup(); } render() { const { node: i } = this.props, { displayMode: l } = i.attrs; return /* @__PURE__ */ t( I, { className: l ? "math-node-wrapper-block" : "math-node-wrapper-inline", children: H.createElement("math-field", { ref: this.mathFieldRef, className: "test-math-background", readonly: !0 }) } ); } } const Ee = j.create({ name: "math", group: "inline", inline: !0, atom: !0, addAttributes() { return { latex: { default: "", parseHTML: (e) => e.getAttribute("data-latex"), renderHTML: (e) => ({ "data-latex": e.latex }) }, displayMode: { default: !1, parseHTML: (e) => e.hasAttribute("data-display-mode"), renderHTML: (e) => e.displayMode ? { "data-display-mode": "" } : {} } }; }, parseHTML() { return [ { tag: 'span[data-type="math"]' } ]; }, renderHTML({ HTMLAttributes: e, node: i }) { return [ "span", { ...e, "data-type": "math", "data-latex": i?.attrs?.latex || "" }, i?.attrs?.latex || "" ]; }, addNodeView() { return D(Te); } }), Ne = Ce.create({ name: "inlineMath", addOptions() { return { inlineDelimiter: "$", blockDelimiter: "$$" }; }, addProseMirrorPlugins() { return []; }, addCommands() { return { insertInlineMath: (e) => ({ commands: i }) => i.insertContent({ type: "math", attrs: { latex: e } }) }; }, addInputRules() { const e = this.options.inlineDelimiter.replace( /[-\/\\^$*+?.()|\[\]{}]/g, "\\$&" ), i = this.options.blockDelimiter.replace( /[-\/\\^$*+?.()|\[\]{}]/g, "\\$&" ); return [ // Inline math: $latex$ new $({ find: new RegExp( `${e}(.*?)${e}$` ), handler: ({ state: l, range: n, match: o }) => { const [, c] = o, { from: u, to: s } = n; l.tr.replaceWith( u, s, l.schema.nodes.math.create({ latex: c }) ); } }), // Block math: $$latex$$ new $({ find: new RegExp( `${i}(.*?)${i}$` ), handler: ({ state: l, range: n, match: o }) => { const [, c] = o, { from: u, to: s } = n; l.tr.replaceWith( u, s, l.schema.nodes.math.create({ latex: c, displayMode: !0 }) ); } }) ]; } }), Se = ({ node: e, updateAttributes: i }) => { const l = E(null), { width: n, align: o } = e.attrs, c = C( (u) => { u.preventDefault(); const s = u.pageX, v = l.current.clientWidth, g = (m) => { const A = v + (m.pageX - s); i({ width: `${Math.max(50, A)}px` }); }, h = () => { document.removeEventListener("mousemove", g), document.removeEventListener("mouseup", h); }; document.addEventListener("mousemove", g), document.addEventListener("mouseup", h); }, [i] ); return /* @__PURE__ */ r( I, { className: `resizable-image-container align-${o}`, style: { width: n }, ref: l, children: [ /* @__PURE__ */ t( "img", { src: e.attrs.src, alt: e.attrs.alt, title: e.attrs.title, className: "editor-image", draggable: "true" } ), /* @__PURE__ */ t("div", { className: "resize-handle-left", onMouseDown: c }), /* @__PURE__ */ t("div", { className: "resize-handle-right", onMouseDown: c }) ] } ); }, Be = Ae.extend({ addAttributes() { return { ...this.parent?.(), width: { default: "500px", renderHTML: (e) => ({ width: e.width }) }, height: { default: "auto", renderHTML: (e) => ({ height: e.height }) }, align: { default: "left", renderHTML: (e) => ({ "data-align": e.align }), parseHTML: (e) => e.getAttribute("data-align") || "left" } }; }, renderHTML({ node: e, HTMLAttributes: i }) { const l = e.attrs.align || "left", o = { left: "left", center: "center", right: "right" }[l] || "left"; return [ "div", { class: "resizable-image-wrapper resizable-image-wrapper-align-" + l, style: `text-align: ${o}; width: 100%; display: block;`, "data-align": l }, [ "div", { class: "resizable-image-container align-" + l, style: `display: inline-block; width: ${e.attrs.width || "500px"}; height: ${e.attrs.height || "auto"};` }, [ "img", R(i, { src: e.attrs.src, alt: e.attrs.alt || "", title: e.attrs.title || "", width: e.attrs.width, height: e.attrs.height, "data-align": l }) ] ] ]; }, parseHTML() { return [ { tag: "img[src]", getAttrs: (e) => { if (typeof e == "string") return !1; const i = e, l = i.closest(".resizable-image-wrapper") || i.parentElement; let n = "left"; if (l) { const o = l.getAttribute("data-align") || i.getAttribute("data-align"); if (o) n = o; else { const u = window.getComputedStyle(l).textAlign; u === "center" ? n = "center" : u === "right" && (n = "right"); } } else { const o = i.getAttribute("data-align"); o && (n = o); } return { src: i.getAttribute("src"), alt: i.getAttribute("alt"), title: i.getAttribute("title"), width: i.getAttribute("width") || "500px", height: i.getAttribute("height") || "auto", align: n }; } }, { tag: "div.resizable-image-wrapper img", getAttrs: (e) => { if (typeof e == "string") return !1; const i = e, l = i.closest(".resizable-image-wrapper"); let n = "left"; if (l) { const o = l.getAttribute("data-align"); o && (n = o); } return { src: i.getAttribute("src"), alt: i.getAttribute("alt"), title: i.getAttribute("title"), width: i.getAttribute("width") || "500px", height: i.getAttribute("height") || "auto", align: n }; } } ]; }, addNodeView() { return D(Se); } }), Re = j.create({ name: "imageGroup", group: "block", content: "image+", addAttributes() { return { align: { default: "left" } }; }, renderHTML({ node: e, HTMLAttributes: i }) { const l = e.attrs.align || "left", o = { left: "flex-start", center: "center", right: "flex-end" }[l] || "flex-start"; return [ "div", R(i, { "data-type": "image-group", style: `justify-content: ${o}` }), 0 ]; }, parseHTML() { return [ { tag: 'div[data-type="image-group"]', getAttrs: (e) => { const i = e.style.justifyContent; return i === "center" || i === "flex-end" ? { align: i === "center" ? "center" : "right" } : { align: "left" }; } } ]; } }), He = G.create({ name: "customTextStyle", addAttributes() { return { fontSize: { default: null, parseHTML: (e) => e.style.fontSize?.replace(" !important", "") || null, renderHTML: (e) => e.fontSize ? { style: `font-size: ${e.fontSize} !important;` } : {} }, color: { default: null, parseHTML: (e) => e.style.color || null, renderHTML: (e) => e.color ? { style: `color: ${e.color}` } : {} }, fontFamily: { default: null, parseHTML: (e) => e.style.fontFamily || null, renderHTML: (e) => e.fontFamily ? { style: `font-family: ${e.fontFamily} !important;` } : {} } }; }, parseHTML() { return [ { tag: "span", getAttrs: (e) => { const i = e.style.fontSize, l = e.style.color, n = e.style.fontFamily; return i || l || n ? { fontSize: i ? i.replace(" !important", "") : null, color: l || null, fontFamily: n ? n.replace(" !important", "") : null } : !1; } } ]; }, renderHTML({ HTMLAttributes: e }) { return ["span", R(e), 0]; } }), Ie = G.create({ name: "backgroundColor", addOptions() { return { HTMLAttributes: {} }; }, addAttributes() { return { backgroundColor: { default: null, parseHTML: (e) => e.style.backgroundColor || null, renderHTML: (e) => e.backgroundColor ? { style: `background-color: ${e.backgroundColor}` } : {} } }; }, parseHTML() { return [ { style: "background-color" } ]; }, renderHTML({ HTMLAttributes: e }) { return [ "span", R(this.options.HTMLAttributes, e), 0 ]; }, addCommands() { return { setBackgroundColor: (e) => ({ commands: i }) => i.setMark(this.name, { backgroundColor: e }), unsetBackgroundColor: () => ({ commands: e }) => e.unsetMark(this.name) }; } }), De = ({ node: e, updateAttributes: i, selected: l }) => { const [n, o] = f(!1), [c, u] = f(0), [s, v] = f(0), [g, h] = f(0), [m, A] = f(0), w = C( (p, x) => { p.preventDefault(), o(!0), u(p.clientX), v(p.clientY), h(parseInt(e.attrs.width)), A(parseInt(e.attrs.height)); const y = (M) => { if (!n) return; const b = M.clientX - c, T = M.clientY - s; let S = g, B = m; x.includes("right") && (S = Math.max(200, g + b)), x.includes("left") && (S = Math.max(200, g - b)), x.includes("bottom") && (B = Math.max(150, m + T)), x.includes("top") && (B = Math.max(150, m - T)), i({ width: `${S}px`, height: `${B}px` }); }, k = () => { o(!1), document.removeEventListener("mousemove", y), document.removeEventListener("mouseup", k); }; document.addEventListener("mousemove", y), document.addEventListener("mouseup", k); }, [ n, c, s, g, m, e.attrs.width, e.attrs.height, i ] ), a = (p) => { i({ align: p }); }; return /* @__PURE__ */ t( I, { className: `resizable-youtube-wrapper ${l ? "ProseMirror-selectednode" : ""}`, style: { textAlign: e.attrs.align, position: "relative", display: "inline-block" }, children: /* @__PURE__ */ r( "div", { className: "resizable-youtube-container", style: { position: "relative", display: "inline-block", width: e.attrs.width, height: e.attrs.height }, children: [ /* @__PURE__ */ t( "iframe", { src: e.attrs.src, width: e.attrs.width, height: e.attrs.height, frameBorder: "0", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: !0, style: { display: "block", width: "100%", height: "100%" } } ), l && /* @__PURE__ */ r(q, { children: [ /* @__PURE__ */ t( "div", { className: "resize-handle resize-handle-bottom-right", onMouseDown: (p) => w(p, "bottom-right"), style: { position: "absolute", bottom: "-5px", right: "-5px", width: "10px", height: "10px", backgroundColor: "#007acc", cursor: "nw-resize", borderRadius: "2px" } } ), /* @__PURE__ */ t( "div", { className: "resize-handle resize-handle-bottom-left", onMouseDown: (p) => w(p, "bottom-left"), style: { position: "absolute", bottom: "-5px", left: "-5px", width: "10px", height: "10px", backgroundColor: "#007acc", cursor: "ne-resize", borderRadius: "2px" } } ), /* @__PURE__ */ t( "div", { className: "resize-handle resize-handle-top-right", onMouseDown: (p) => w(p, "top-right"), style: { position: "absolute", top: "-5px", right: "-5px", width: "10px", height: "10px", backgroundColor: "#007acc", cursor: "ne-resize", borderRadius: "2px" } } ), /* @__PURE__ */ t( "div", { className: "resize-handle resize-handle-top-left", onMouseDown: (p) => w(p, "top-left"), style: { position: "absolute", top: "-5px", left: "-5px", width: "10px", height: "10px", backgroundColor: "#007acc", cursor: "nw-resize", borderRadius: "2px" } } ), /* @__PURE__ */ r( "div", { className: "alignment-controls", style: { position: "absolute", top: "-30px", left: "0", display: "flex", gap: "5px", backgroundColor: "white", border: "1px solid #ccc", borderRadius: "4px", padding: "2px" }, children: [ /* @__PURE__ */ t( "button", { onClick: () => a("left"), style: { padding: "2px 6px", border: "none", backgroundColor: e.attrs.align === "left" ? "#007acc" : "transparent", color: e.attrs.align === "left" ? "white" : "black", cursor: "pointer", borderRadius: "2px" }, type: "button", children: "←" } ), /* @__PURE__ */ t( "button", { onClick: () => a("center"), style: { padding: "2px 6px", border: "none", backgroundColor: e.attrs.align === "center" ? "#007acc" : "transparent", color: e.attrs.align === "center" ? "white" : "black", cursor: "pointer", borderRadius: "2px" }, type: "button", children: "⟷" } ), /* @__PURE__ */ t( "button", { onClick: () => a("right"), style: { padding: "2px 6px", border: "none", backgroundColor: e.attrs.align === "right" ? "#007acc" : "transparent", color: e.attrs.align === "right" ? "white" : "black", cursor: "pointer", borderRadius: "2px" }, type: "button", children: "→" } ) ] } ) ] }) ] } ) } ); }, ze = Me.extend({ addAttributes() { return { ...this.parent?.(), width: { default: "640px", renderHTML: (e) => ({ width: e.width }) }, height: { default: "360px", renderHTML: (e) => ({ height: e.height }) }, align: { default: "center", renderHTML: (e) => ({ "data-align": e.align }), parseHTML: (e) => e.getAttribute("data-align") || "center" } }; }, addNodeView() { return D(De); } }), $e = ue(he); function Fe() { return [ Q.configure({ heading: { levels: [1, 2, 3, 4, 5, 6] }, codeBlock: !1, blockquote: !1, horizontalRule: !1, hardBreak: !1, strike: !1 }), Ee, Ne, Be.configure({ allowBase64: !0 }), Re, Le, me, Ie, fe, xe, ee.configure({ resizable: !0, HTMLAttributes: { class: "editor-table" } }), te, ie, le, ne.configure({ openOnClick: !1, HTMLAttributes: { class: "editor-link", rel: "noopener noreferrer" } }), re.configure({ types: ["heading", "paragraph"] }), oe, ae, se, ce, de.configure({ lowlight: $e, HTMLAttributes: { class: "editor-code-block" } }), ze.configure({ controls: !0, nocookie: !1 }), ve.configure({ placeholder: "Start typing..." }), ke, we, ge.configure({ HTMLAttributes: { class: "editor-blockquote" } }), ye, be, pe, He.configure({ HTMLAttributes: { class: "editor-text-style" } }) ]; } function Pe({ className: e = "", placeholder: i = "Start typing..." }) { return { attributes: { class: `prose focus:outline-none ${e} ${i ? "has-placeholder" : ""}`, "aria-label": "Rich text editor", "aria-placeholder": i } }; } function qe(e) { if (!e || e.trim() === "") return { isValid: !1, error: "LaTeX expression cannot be empty" }; let i = 0; for (const l of e) if (l === "{" && i++, l === "}" && i--, i < 0) return { isValid: !1, error: "Unbalanced braces" }; return i !== 0 ? { isValid: !1, error: "Unbalanced braces" } : { isValid: !0 }; } function Pt(e) { const i = document.createElement("div"); return i.textContent = e, i.innerHTML; } function We(e) { const i = document.createElement("div"); return i.innerHTML = e, i.textContent || i.innerText || ""; } function qt(e) { return e.trim().split(/\s+/).filter(Boolean).length; } function Wt(e) { if (e === 0) return "0 Bytes"; const i = 1024, l = ["Bytes", "KB", "MB", "GB"], n = Math.floor(Math.log(e) / Math.log(i)); return Math.round(e / Math.pow(i, n) * 100) / 100 + " " + l[n]; } function _t(e, i) { let l = null; return function(...o) { const c = () => { l = null, e(...o); }; l && clearTimeout(l), l = setTimeout(c, i); }; } function jt(e, i) { let l; return function(...o) { l || (e(...o), l = !0, setTimeout(() => l = !1, i)); }; } function Gt() { const e = []; return typeof window > "u" ? { supported: !1, missingFeatures: ["window object"] } : (window.customElements || e.push("Custom Elements"), window.localStorage || e.push("Local Storage"), { supported: e.length === 0, missingFeatures: e }); } function Vt(e) { return JSON.parse(JSON.stringify(e)); } function Ut(e) { return We(e).trim().length === 0; } function Kt() { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } function _e(e, i) { e.chain().focus().setBackgroundColor(i).run(); } function je(e) { e && e.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: !0 }).run(); } function F(e, i) { if (Array.isArray(i)) if (i.length > 1) { const n = [{ type: "imageGroup", content: i.map((o) => ({ type: "image", attrs: { src: o, width: "250px" } })) }]; e.chain().focus().insertContent(n).run(); } else i.length === 1 && e.chain().focus().setImage({ src: i[0], width: "500px" }).run(); else typeof i == "string" && e.chain().focus().setImage({ src: i, width: "500px" }).run(); } function Ge(e, i) { try { if (!e) throw new Error("Editor instance is not available"); const l = i.trim(), n = qe(l); if (!n.isValid) throw new Error(n.error || "Invalid LaTeX expression"); e.chain().focus().insertContent({ type: "math", attrs: { latex: l } }).run(); } catch (l) { throw console.error("Math insertion error:", l), l instanceof Error ? l : new Error("Error inserting math equation"); } } const d = X( z( ({ onClick: e, isActive: i, title: l, children: n, shortcut: o, disabled: c }, u) => { const s = E(null), v = E(null), g = C(() => { if (!s.current || !v.current) return; const h = v.current.getBoundingClientRect(), m = s.current; m.style.left = `${h.left + h.width / 2}px`, m.style.top = `${h.top - 8}px`; const A = m.getBoundingClientRect(); A.left < 0 && (m.style.left = "0px", m.style.transform = "none"), A.right > window.innerWidth && (m.style.left = `${window.innerWidth}px`, m.style.transform = "translateX(-100%)"); }, []); return L(() => { const h = () => { g(); }, m = () => { g(); }; return window.addEventListener("scroll", h, !0), window.addEventListener("resize", m), () => { window.removeEventListener("scroll", h, !0), window.removeEventListener("resize", m); }; }, [g]), /* @__PURE__ */ r("div", { className: "tooltip-container", onMouseEnter: g, children: [ /* @__PURE__ */ t( "button", { ref: (h) => { v.current = h, typeof u == "function" ? u(h) : u && (u.current = h); }, onClick: e, className: `toolbar-button ${i ? "is-active" : ""} ${c ? "is-disabled" : ""}`, "aria-label": l, "aria-pressed": i, disabled: c, type: "button", children: n } ), /* @__PURE__ */ r("div", { ref: s, className: "tooltip", children: [ l, o && /* @__PURE__ */ t("span", { className: "tooltip-shortcut", children: o }) ] }) ] }); } ) ); d.displayName = "ToolbarButton"; const Ve = ({ editor: e, readOnly: i }) => /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Text alignment", children: [ /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().setTextAlign("left").run(), isActive: e?.isActive({ textAlign: "left" }), title: "Align Left", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "4", y1: "6", x2: "20", y2: "6" }), /* @__PURE__ */ t("line", { x1: "4", y1: "12", x2: "14", y2: "12" }), /* @__PURE__ */ t("line", { x1: "4", y1: "18", x2: "18", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().setTextAlign("center").run(), isActive: e?.isActive({ textAlign: "center" }), title: "Align Center", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "6", y1: "6", x2: "18", y2: "6" }), /* @__PURE__ */ t("line", { x1: "4", y1: "12", x2: "20", y2: "12" }), /* @__PURE__ */ t("line", { x1: "8", y1: "18", x2: "16", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().setTextAlign("right").run(), isActive: e?.isActive({ textAlign: "right" }), title: "Align Right", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "4", y1: "6", x2: "20", y2: "6" }), /* @__PURE__ */ t("line", { x1: "10", y1: "12", x2: "20", y2: "12" }), /* @__PURE__ */ t("line", { x1: "6", y1: "18", x2: "20", y2: "18" }) ] } ) } ) ] }), Ue = ({ editor: e, readOnly: i }) => /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Blocks", children: [ /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleBlockquote().run(), isActive: e?.isActive("blockquote"), title: "Blockquote", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("path", { d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" }), /* @__PURE__ */ t("path", { d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().setHorizontalRule().run(), title: "Horizontal Rule", disabled: !e || i, children: /* @__PURE__ */ t( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ t("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().setHardBreak().run(), title: "Line Break", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("path", { d: "M21 8H3M21 8L17 4M21 8L17 12" }), /* @__PURE__ */ t("path", { d: "M3 16H15M3 16L7 12M3 16L7 20" }) ] } ) } ) ] }), Ke = [1, 2, 3, 4, 5, 6], P = 768, Ye = ({ editor: e, readOnly: i }) => /* @__PURE__ */ t("div", { className: "toolbar-group", role: "group", "aria-label": "Headings", children: Ke.map((l) => /* @__PURE__ */ r( d, { onClick: () => e?.chain().focus().toggleHeading({ level: l }).run(), isActive: e?.isActive("heading", { level: l }), title: `Heading ${l}`, disabled: !e || i, children: [ /* @__PURE__ */ r( "svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className: "icon icon-tabler icons-tabler-outline icon-tabler-heading", children: [ /* @__PURE__ */ t("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), /* @__PURE__ */ t("path", { d: "M7 12h10" }), /* @__PURE__ */ t("path", { d: "M7 5v14" }), /* @__PURE__ */ t("path", { d: "M17 5v14" }), /* @__PURE__ */ t("path", { d: "M15 19h4" }), /* @__PURE__ */ t("path", { d: "M15 5h4" }), /* @__PURE__ */ t("path", { d: "M5 19h4" }), /* @__PURE__ */ t("path", { d: "M5 5h4" }) ] } ), l ] }, l )) }), Je = ({ editor: e, readOnly: i }) => !e?.isActive("image") || e?.isActive("imageGroup") ? null : /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Image alignment", children: [ /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("image", { align: "left" }).run(), isActive: e?.isActive("image", { align: "left" }), title: "Align Image Left", disabled: !e?.isActive("image") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "3", y1: "12", x2: "15", y2: "12" }), /* @__PURE__ */ t("line", { x1: "3", y1: "18", x2: "18", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("image", { align: "center" }).run(), isActive: e?.isActive("image", { align: "center" }), title: "Align Image Center", disabled: !e?.isActive("image") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "6", y1: "12", x2: "18", y2: "12" }), /* @__PURE__ */ t("line", { x1: "4", y1: "18", x2: "20", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("image", { align: "right" }).run(), isActive: e?.isActive("image", { align: "right" }), title: "Align Image Right", disabled: !e?.isActive("image") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "9", y1: "12", x2: "21", y2: "12" }), /* @__PURE__ */ t("line", { x1: "6", y1: "18", x2: "21", y2: "18" }) ] } ) } ) ] }), Xe = ({ editor: e, readOnly: i }) => e?.isActive("imageGroup") ? /* @__PURE__ */ r( "div", { className: "toolbar-group", role: "group", "aria-label": "Image group alignment", children: [ /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("imageGroup", { align: "left" }).run(), isActive: e?.isActive("imageGroup", { align: "left" }), title: "Align Group Left", disabled: !e?.isActive("imageGroup") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "3", y1: "12", x2: "15", y2: "12" }), /* @__PURE__ */ t("line", { x1: "3", y1: "18", x2: "18", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("imageGroup", { align: "center" }).run(), isActive: e?.isActive("imageGroup", { align: "center" }), title: "Align Group Center", disabled: !e?.isActive("imageGroup") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "6", y1: "12", x2: "18", y2: "12" }), /* @__PURE__ */ t("line", { x1: "4", y1: "18", x2: "20", y2: "18" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().updateAttributes("imageGroup", { align: "right" }).run(), isActive: e?.isActive("imageGroup", { align: "right" }), title: "Align Group Right", disabled: !e?.isActive("imageGroup") || i, children: /* @__PURE__ */ r( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [ /* @__PURE__ */ t("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ t("line", { x1: "9", y1: "12", x2: "21", y2: "12" }), /* @__PURE__ */ t("line", { x1: "6", y1: "18", x2: "21", y2: "18" }) ] } ) } ) ] } ) : null, Oe = ({ editor: e, readOnly: i }) => /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Lists", children: [ /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleBulletList().run(), isActive: e?.isActive("bulletList"), title: "Bullet List", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "9", y1: "6", x2: "20", y2: "6" }), /* @__PURE__ */ t("line", { x1: "9", y1: "12", x2: "20", y2: "12" }), /* @__PURE__ */ t("line", { x1: "9", y1: "18", x2: "20", y2: "18" }), /* @__PURE__ */ t("circle", { cx: "5", cy: "6", r: "1" }), /* @__PURE__ */ t("circle", { cx: "5", cy: "12", r: "1" }), /* @__PURE__ */ t("circle", { cx: "5", cy: "18", r: "1" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleOrderedList().run(), isActive: e?.isActive("orderedList"), title: "Numbered List", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "11", y1: "6", x2: "20", y2: "6" }), /* @__PURE__ */ t("line", { x1: "11", y1: "12", x2: "20", y2: "12" }), /* @__PURE__ */ t("line", { x1: "11", y1: "18", x2: "20", y2: "18" }), /* @__PURE__ */ t("text", { x: "5", y: "8", fontSize: "4", children: "1" }), /* @__PURE__ */ t("text", { x: "5", y: "14", fontSize: "4", children: "2" }), /* @__PURE__ */ t("text", { x: "5", y: "20", fontSize: "4", children: "3" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleTaskList().run(), isActive: e?.isActive("taskList"), title: "Task List", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2" }), /* @__PURE__ */ t("path", { d: "M9 12l2 2l4 -4" }) ] } ) } ) ] }), Ze = ({ editor: e, readOnly: i, onMathDialogOpen: l, onImagePicker: n }) => /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Special features", children: [ /* @__PURE__ */ t( d, { onClick: l, title: "Insert Math Equation (Ctrl/Cmd + M)", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "icon icon-tabler icons-tabler-outline icon-tabler-math", children: [ /* @__PURE__ */ t("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), /* @__PURE__ */ t("path", { d: "M19 5h-7l-4 14l-3 -6h-2" }), /* @__PURE__ */ t("path", { d: "M14 13l6 6" }), /* @__PURE__ */ t("path", { d: "M14 19l6 -6" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: n, title: "Insert Image", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }), /* @__PURE__ */ t("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), /* @__PURE__ */ t("polyline", { points: "21 15 16 10 5 21" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => je(e), title: "Insert Table", disabled: !e || i, children: /* @__PURE__ */ t( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ t("path", { d: "M3 3h18v18H3zM12 3v18M3 12h18M3 8h18M3 16h18" }) } ) } ), /* @__PURE__ */ t( d, { onClick: () => { const o = window.prompt("Enter YouTube video URL:"); o && e.chain().focus().setYoutubeVideo({ src: o }).run(); }, title: "Insert YouTube Video", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("rect", { x: "2", y: "4", width: "20", height: "16", rx: "3" }), /* @__PURE__ */ t("polygon", { points: "10,9 16,12 10,15", fill: "currentColor" }) ] } ) } ) ] }), Qe = ({ editor: e, readOnly: i }) => /* @__PURE__ */ r("div", { className: "toolbar-group", role: "group", "aria-label": "Text formatting", children: [ /* @__PURE__ */ t( d, { onClick: () => { if (!e) return; const l = e.getAttributes("link").href || "", n = window.prompt("Enter URL:", l); if (n !== null) { if (n === "") { e.chain().focus().unsetLink().run(); return; } e.chain().focus().setLink({ href: n }).run(); } }, isActive: e?.isActive("link"), title: "Insert/Edit Link", shortcut: "Ctrl+K", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("path", { d: "M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3" }), /* @__PURE__ */ t("line", { x1: "8", y1: "12", x2: "16", y2: "12" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().unsetLink().run(), isActive: !1, title: "Remove Link", disabled: !e || i || !e?.isActive("link"), children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("path", { d: "M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3" }), /* @__PURE__ */ t("line", { x1: "8", y1: "12", x2: "16", y2: "12" }), /* @__PURE__ */ t("line", { x1: "17", y1: "7", x2: "7", y2: "17" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleBold().run(), isActive: e?.isActive("bold"), title: "Bold", shortcut: "Ctrl+B", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("path", { d: "M7 5h6a3 3 0 0 1 0 6H7z" }), /* @__PURE__ */ t("path", { d: "M7 11h8a3 3 0 0 1 0 6H7z" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleItalic().run(), isActive: e?.isActive("italic"), title: "Italic", shortcut: "Ctrl+I", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("line", { x1: "19", y1: "4", x2: "10", y2: "4" }), /* @__PURE__ */ t("line", { x1: "14", y1: "20", x2: "5", y2: "20" }), /* @__PURE__ */ t("line", { x1: "15", y1: "4", x2: "9", y2: "20" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleUnderline().run(), isActive: e?.isActive("underline"), title: "Underline", shortcut: "Ctrl+U", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("path", { d: "M6 4v6a6 6 0 0 0 12 0V4" }), /* @__PURE__ */ t("line", { x1: "4", y1: "20", x2: "20", y2: "20" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleStrike().run(), isActive: e?.isActive("strike"), title: "Strike-through", shortcut: "Ctrl+Shift+S", disabled: !e || i, children: /* @__PURE__ */ r( "svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "icon icon-tabler icons-tabler-outline icon-tabler-strikethrough", children: [ /* @__PURE__ */ t("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), /* @__PURE__ */ t("path", { d: "M5 12l14 0" }), /* @__PURE__ */ t("path", { d: "M16 6.5a4 2 0 0 0 -4 -1.5h-1a3.5 3.5 0 0 0 0 7h2a3.5 3.5 0 0 1 0 7h-1.5a4 2 0 0 1 -4 -1.5" }) ] } ) } ), /* @__PURE__ */ t( d, { onClick: () => e?.chain().focus().toggleHighlight().run(), isActive: e?.isActive("highlight"), title: "Highlight",