import React, { useState } from "react"; export default function App() { const [expr, setExpr] = useState(""); // full expression shown to user const [entry, setEntry] = useState("0"); // current entry shown on screen const append = (s) => { // when appending digits or dot we update entry; for functions/operators we push entry into expr first if ("0123456789.".includes(s)) { setEntry((e) => (e === "0" && s !== "." ? s : (e.includes(".") && s === ".") ? e : e + s)); return; } if (s === "±") { setEntry((e) => (e.startsWith("-") ? e.slice(1) : "-" + e)); return; } if (s === "⌫") { setEntry((e) => (e.length <= 1 || (e.length === 2 && e.startsWith("-")) ? "0" : e.slice(0, -1))); return; } // for percent, functions or operators: move current entry into expr then append token const pushEntry = () => { if (entry !== "" && entry !== "0") { setExpr((x) => x + entry); } else if (entry === "0" && expr === "") { // do nothing } }; if (s === "%") { // convert current entry to (entry/100) setEntry((e) => { const v = parseFloat(e || "0"); return String(v / 100); }); return; } // functions: sin(, cos(, sqrt(, ln(, log( if (["sin(", "cos(", "tan(", "sqrt(", "ln(", "log("].includes(s)) { pushEntry(); setExpr((x) => x + s); setEntry("0"); return; } // parentheses or operators if (["(", ")"].includes(s)) { pushEntry(); setExpr((x) => x + s); setEntry("0"); return; } if (["+", "-", "×", "÷", "^"].includes(s)) { pushEntry(); // translate display operators to tokens we keep (use × ÷ in UI but convert later) setExpr((x) => x + s); setEntry("0"); return; } if (s === "C") { setExpr(""); setEntry("0"); return; } if (s === "CE") { setEntry("0"); return; } }; const buildJs = (raw) => { // convert tokens to JS expression safely-ish let js = raw; // replace UI operators js = js.replace(/×/g, "*").replace(/÷/g, "/").replace(/\^/g, "**"); // functions mapping js = js.replace(/sqrt\(/g, "Math.sqrt("); js = js.replace(/sin\(/g, "Math.sin("); js = js.replace(/cos\(/g, "Math.cos("); js = js.replace(/tan\(/g, "Math.tan("); js = js.replace(/ln\(/g, "Math.log("); // log -> Math.log10 if available, otherwise Math.log(x)/Math.LN10 if (typeof Math.log10 === "function") js = js.replace(/log\(/g, "Math.log10("); else js = js.replace(/log\(([^)]+)\)/g, "Math.log($1)/Math.LN10"); // handle percent written as trailing % on a number (e.g. 50% -> (50/100)) js = js.replace(/(\d+(\.\d+)?)%/g, "($1/100)"); return js; }; const evaluate = () => { // finalize expression: push current entry into expr const full = expr + (entry && entry !== "0" ? entry : ""); if (!full) return; const js = buildJs(full); try { // eslint-disable-next-line no-new-func const res = Function(`return (${js})`)(); const out = (typeof res === "number" && Number.isFinite(res)) ? String(+parseFloat(String(res)).toPrecision(12)) : "Error"; setExpr(""); setEntry(out); } catch (e) { setExpr(""); setEntry("Error"); } }; const press = (s) => { // convenience to handle "=" if (s === "=") { evaluate(); return; } append(s); }; const minimalStyle = ` .wrap{max-width:360px;margin:40px auto;font-family:system-ui,Segoe UI,Roboto} .screen{background:#071426;color:#7afcff;padding:12px;border-radius:8px;text-align:right;font-size:1.4rem;min-height:56px;overflow:auto} .expr{color:#9aa6b2;font-size:0.8rem;text-align:right;margin-bottom:6px;min-height:18px} .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-top:12px} button{padding:12px;border-radius:8px;border:0;background:#0b1220;color:#e6eef6;font-weight:600;cursor:pointer} .op{background:#f39c12;color:#111} .fn{background:#1b6df6} .clear{background:#e04b4b} .eq{background:#27ae60} .num{background:#233241} `; return (