import { NodeViewWrapper } from '@tiptap/react' import React from 'react' import styled from 'styled-components' import 'katex/dist/katex.min.css' import { BlockMath } from 'react-katex' import { Save, Sigma, ExternalLink, ChevronDown, BookOpen, Lightbulb } from 'lucide-react' import Link from 'next/link' import { useEditorProvider } from '@components/Contexts/Editor/EditorContext' import { motion } from 'framer-motion' // Predefined LaTeX templates const mathTemplates = [ { name: 'Fraction', latex: '\\frac{a}{b}', description: 'Simple fraction' }, { name: 'Square Root', latex: '\\sqrt{x}', description: 'Square root' }, { name: 'Summation', latex: '\\sum_{i=1}^{n} x_i', description: 'Sum with limits' }, { name: 'Integral', latex: '\\int_{a}^{b} f(x) \\, dx', description: 'Definite integral' }, { name: 'Limit', latex: '\\lim_{x \\to \\infty} f(x)', description: 'Limit expression' }, { name: 'Matrix 2×2', latex: '\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}', description: '2×2 matrix with parentheses' }, { name: 'Binomial', latex: '\\binom{n}{k}', description: 'Binomial coefficient' }, { name: 'Quadratic Formula', latex: 'x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}', description: 'Solution to quadratic equation' }, { name: 'Vector', latex: '\\vec{v} = \\begin{pmatrix} x \\\\ y \\\\ z \\end{pmatrix}', description: '3D vector' }, { name: 'System of Equations', latex: '\\begin{cases} a_1x + b_1y = c_1 \\\\ a_2x + b_2y = c_2 \\end{cases}', description: 'System of linear equations' } ]; // Common LaTeX symbols const mathSymbols = [ { symbol: '\\alpha', display: 'α' }, { symbol: '\\beta', display: 'β' }, { symbol: '\\gamma', display: 'γ' }, { symbol: '\\delta', display: 'δ' }, { symbol: '\\theta', display: 'θ' }, { symbol: '\\pi', display: 'π' }, { symbol: '\\sigma', display: 'σ' }, { symbol: '\\infty', display: '∞' }, { symbol: '\\pm', display: '±' }, { symbol: '\\div', display: '÷' }, { symbol: '\\cdot', display: '·' }, { symbol: '\\leq', display: '≤' }, { symbol: '\\geq', display: '≥' }, { symbol: '\\neq', display: '≠' }, { symbol: '\\approx', display: '≈' }, ]; // Styled components const MathEqWrapper = styled.div` transition: all 0.2s ease; background-color: #f9f9f9; border: 1px solid #eaeaea; ` const EditBar = styled.div` display: flex; justify-content: space-between; border-radius: 8px; padding: 0 5px 0 12px; background-color: white; color: #5252528d; align-items: center; height: 45px; border: solid 1px #e2e2e2; transition: all 0.2s ease; &:focus-within { border-color: #d1d1d1; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.03); } input { border: none; background: none; font-size: 14px; color: #494949; width: 100%; font-family: 'DM Sans', sans-serif; &:focus { outline: none; } &::placeholder { color: #49494980; } } ` const SaveButton = styled(motion.button)` display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 6px; border: none; background: rgba(217, 217, 217, 0.5); color: #494949; cursor: pointer; ` const InfoLink = styled.div` padding-left: 2px; ` const TemplateButton = styled.button` display: flex; align-items: center; padding: 6px 10px; background: rgba(217, 217, 217, 0.4); border-radius: 6px; border: none; font-size: 13px; color: #494949; cursor: pointer; ` const TemplateDropdown = styled.div` background: white; border-radius: 8px; border: 1px solid #e2e2e2; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; ` const TemplateItem = styled.div` padding: 8px 12px; cursor: pointer; transition: background 0.15s; &:hover { background: rgba(217, 217, 217, 0.24); } ` const SymbolsDropdown = styled.div` background: white; border-radius: 8px; border: 1px solid #e2e2e2; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; ` const SymbolButton = styled.button` display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; margin: 2px; background: rgba(217, 217, 217, 0.3); border-radius: 4px; border: none; font-size: 16px; color: #494949; cursor: pointer; ` const HelpDropdown = styled.div` background: white; border-radius: 8px; border: 1px solid #e2e2e2; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; ` function MathEquationBlockComponent(props: any) { const [equation, setEquation] = React.useState(props.node.attrs.math_equation) const [isEditing, setIsEditing] = React.useState(true) const [showTemplates, setShowTemplates] = React.useState(false) const [showSymbols, setShowSymbols] = React.useState(false) const [showHelp, setShowHelp] = React.useState(false) const editorState = useEditorProvider() as any const isEditable = editorState.isEditable const inputRef = React.useRef(null) const templatesRef = React.useRef(null) const symbolsRef = React.useRef(null) const helpRef = React.useRef(null) // Close dropdowns when clicking outside React.useEffect(() => { function handleClickOutside(event: MouseEvent) { if (templatesRef.current && !templatesRef.current.contains(event.target as Node)) { setShowTemplates(false) } if (symbolsRef.current && !symbolsRef.current.contains(event.target as Node)) { setShowSymbols(false) } if (helpRef.current && !helpRef.current.contains(event.target as Node)) { setShowHelp(false) } } document.addEventListener('mousedown', handleClickOutside) return () => { document.removeEventListener('mousedown', handleClickOutside) } }, []) const handleEquationChange = (event: React.ChangeEvent) => { setEquation(event.target.value) props.updateAttributes({ math_equation: event.target.value, }) } const saveEquation = () => { props.updateAttributes({ math_equation: equation, }) //setIsEditing(false); } const insertTemplate = (template: string) => { setEquation(template) props.updateAttributes({ math_equation: template, }) setShowTemplates(false) // Focus the input and place cursor at the end if (inputRef.current) { inputRef.current.focus() inputRef.current.setSelectionRange(template.length, template.length) } } const insertSymbol = (symbol: string) => { const cursorPosition = inputRef.current?.selectionStart || equation.length const newEquation = equation.substring(0, cursorPosition) + symbol + equation.substring(cursorPosition) setEquation(newEquation) props.updateAttributes({ math_equation: newEquation, }) // Focus the input and place cursor after the inserted symbol setTimeout(() => { if (inputRef.current) { inputRef.current.focus() inputRef.current.setSelectionRange(cursorPosition + symbol.length, cursorPosition + symbol.length) } }, 0) } return (
Math Equation
{equation}
{isEditing && isEditable && (
setShowTemplates(!showTemplates)} className="flex items-center space-x-1" > Templates {showTemplates && (
Select a template to insert
{mathTemplates.map((template, index) => ( insertTemplate(template.latex)} >
{template.name} {template.description}
))}
)}
setShowSymbols(!showSymbols)} className="flex items-center space-x-1" > Symbols {showSymbols && (
Click a symbol to insert
{mathSymbols.map((symbol, index) => ( insertSymbol(symbol.symbol)} title={symbol.symbol} > {symbol.display} ))}
)}
setShowHelp(!showHelp)} className="flex items-center space-x-1" > Help {showHelp && (
LaTeX Math Quick Reference
Fractions: \frac{'{'}'numerator'{'}'}{'{'}denominator{'}'}
Exponents: x^{'{'}'power'{'}'}
Subscripts: x_{'{'}'subscript'{'}'}
Square root: \sqrt{'{'}'x'{'}'}
Summation: \sum_{'{'}'lower'{'}'}^{'{'}'upper'{'}'}
Integral: \int_{'{'}'lower'{'}'}^{'{'}'upper'{'}'}
View complete reference
)}
saveEquation()} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > Please refer to this guide for supported TeX functions
)}
) } export default MathEquationBlockComponent