'use client'; import { useState, useRef, useCallback, useEffect } from 'react'; import { Plus, Trash2, X, ChevronDown, ChevronUp } from 'lucide-react'; import { useCalculateStore } from '@/lib/calculate/store'; import { evaluateExpression } from '@/lib/calculate/math-engine'; import { cn } from '@/lib/utils'; const QUICK_KEYS = [ // Constants { label: 'π', insert: 'pi', group: 'const' }, { label: 'e', insert: 'e', group: 'const' }, { label: 'φ', insert: '(1+sqrt(5))/2', group: 'const' }, { label: '∞', insert: 'Infinity', group: 'const' }, { label: 'i', insert: 'i', group: 'const' }, // Ops { label: '^', insert: '^', group: 'op' }, { label: '(', insert: '(', group: 'op' }, { label: ')', insert: ')', group: 'op' }, { label: '%', insert: ' % ', group: 'op' }, { label: 'mod', insert: ' mod ', group: 'op' }, // Functions { label: '√', insert: 'sqrt(', group: 'fn' }, { label: '∛', insert: 'cbrt(', group: 'fn' }, { label: '|x|', insert: 'abs(', group: 'fn' }, { label: 'n!', insert: '!', group: 'fn' }, { label: 'sin', insert: 'sin(', group: 'trig' }, { label: 'cos', insert: 'cos(', group: 'trig' }, { label: 'tan', insert: 'tan(', group: 'trig' }, { label: 'asin', insert: 'asin(', group: 'trig' }, { label: 'acos', insert: 'acos(', group: 'trig' }, { label: 'atan', insert: 'atan(', group: 'trig' }, { label: 'sinh', insert: 'sinh(', group: 'trig' }, { label: 'cosh', insert: 'cosh(', group: 'trig' }, { label: 'log', insert: 'log10(', group: 'log' }, { label: 'ln', insert: 'log(', group: 'log' }, { label: 'log₂', insert: 'log2(', group: 'log' }, { label: 'exp', insert: 'exp(', group: 'log' }, { label: 'floor', insert: 'floor(', group: 'round' }, { label: 'ceil', insert: 'ceil(', group: 'round' }, { label: 'round', insert: 'round(', group: 'round' }, { label: 'gcd', insert: 'gcd(', group: 'misc' }, { label: 'lcm', insert: 'lcm(', group: 'misc' }, { label: 'nCr', insert: 'combinations(', group: 'misc' }, { label: 'nPr', insert: 'permutations(', group: 'misc' }, ] as const; export function ExpressionPanel() { const { expression, setExpression, history, addToHistory, clearHistory, variables, setVariable, removeVariable, } = useCalculateStore(); const [liveResult, setLiveResult] = useState<{ result: string; error: boolean } | null>(null); const [newVarName, setNewVarName] = useState(''); const [newVarValue, setNewVarValue] = useState(''); const [showAddVar, setShowAddVar] = useState(false); const [showAllKeys, setShowAllKeys] = useState(false); const textareaRef = useRef(null); // Real-time evaluation useEffect(() => { if (!expression.trim()) { setLiveResult(null); return; } const r = evaluateExpression(expression, variables); setLiveResult(r.result ? { result: r.result, error: r.error } : null); }, [expression, variables]); const handleSubmit = useCallback(() => { if (!expression.trim()) return; const r = evaluateExpression(expression, variables); if (!r.result) return; addToHistory({ expression: expression.trim(), result: r.result, error: r.error }); if (!r.error) { if (r.assignedName && r.assignedValue) { setVariable(r.assignedName, r.assignedValue); } setExpression(''); } }, [expression, variables, addToHistory, setExpression, setVariable]); const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }, [handleSubmit]); const insertAtCursor = useCallback((text: string) => { const ta = textareaRef.current; if (!ta) { setExpression(expression + text); return; } const start = ta.selectionStart; const end = ta.selectionEnd; const next = expression.slice(0, start) + text + expression.slice(end); setExpression(next); requestAnimationFrame(() => { ta.focus(); const pos = start + text.length; ta.selectionStart = ta.selectionEnd = pos; }); }, [expression, setExpression]); const addVar = useCallback(() => { if (!newVarName.trim() || !newVarValue.trim()) return; setVariable(newVarName.trim(), newVarValue.trim()); setNewVarName(''); setNewVarValue(''); setShowAddVar(false); }, [newVarName, newVarValue, setVariable]); const visibleKeys = showAllKeys ? QUICK_KEYS : QUICK_KEYS.slice(0, 16); return (
{/* ── Expression input ──────────────────────────────────── */}
Expression Enter to evaluate · Shift+Enter for newline