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
JavaScript
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",