// Interactive terminal pane function Terminal({ onCommand, lines, setLines, focusSection, setTheme }) { const [value, setValue] = React.useState(""); const [history, setHistory] = React.useState([]); const [hIdx, setHIdx] = React.useState(-1); const bodyRef = React.useRef(null); const inputRef = React.useRef(null); React.useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [lines]); const prompt = ( zohaib@portfolio:~$ ); const print = (entry) => setLines((prev) => [...prev, entry]); const handleCmd = (raw) => { const cmd = raw.trim(); print({ kind: "cmd", text: raw }); if (!cmd) return; const [head, ...rest] = cmd.split(/\s+/); const arg = rest.join(" "); switch (head.toLowerCase()) { case "help": case "?": print({ kind: "out", text: `available commands: help show this menu whoami print profile summary about | experience open a section skills | projects open a section education | contact open a section ls list files cat print a file open
scroll editor to a section theme social show social links date show current date echo echo back clear clear terminal sudo you wish 😏 type a command and hit ↵` }); break; case "whoami": print({ kind: "out", text: `zohaib arshad — frontend engineer (5+ yrs) chemnitz, germany · open to senior frontend roles react · typescript · next.js · design systems · a11y · performance` }); break; case "ls": { const files = FILES.map((f) => f.name).join(" "); print({ kind: "out", text: files + "\nresume.pdf .gitconfig .zshrc" }); break; } case "cat": { const f = FILES.find((x) => x.name === arg || x.id === arg); if (!f) { print({ kind: "err", text: `cat: ${arg || ""}: No such file. try 'ls'.` }); break; } focusSection(f.id); print({ kind: "out", text: `opening ${f.name} in editor...` }); break; } case "open": case "goto": { const target = (arg || "").toLowerCase(); const map = { readme: "readme", about: "about", experience: "experience", exp: "experience", skills: "skills", projects: "projects", education: "education", contact: "contact" }; if (map[target]) { focusSection(map[target]); print({ kind: "out", text: `scrolling to ${target}...` }); } else print({ kind: "err", text: `open: unknown section '${arg}'. try: about | experience | skills | projects | education | contact` }); break; } case "about": focusSection("about"); print({ kind: "out", text: "opening about.ts..." }); break; case "experience": focusSection("experience"); print({ kind: "out", text: "opening experience.log..." }); break; case "skills": focusSection("skills"); print({ kind: "out", text: "opening skills.json..." }); break; case "projects": focusSection("projects"); print({ kind: "out", text: "opening projects.tsx..." }); break; case "education": focusSection("education"); print({ kind: "out", text: "opening education.yml..." }); break; case "contact": focusSection("contact"); print({ kind: "out", text: "opening contact.sh..." }); break; case "theme": { const t = (arg || "").toLowerCase(); if (!["deep", "paper", "dracula"].includes(t)) { print({ kind: "err", text: `theme: usage — theme ` }); } else { setTheme(t); print({ kind: "out", text: `theme set to '${t}'` }); } break; } case "social": print({ kind: "out", text: `github → https://github.com/zohaib-01 linkedin → https://linkedin.com/in/zohaib-arshad-dev email → zohaibarshaddev@gmail.com` }); break; case "date": print({ kind: "out", text: new Date().toString() }); break; case "echo": print({ kind: "out", text: arg }); break; case "sudo": print({ kind: "err", text: `user 'zohaib' is not in the sudoers file. this incident will be reported.` }); break; case "clear": case "cls": setLines([]); break; case "exit": case "quit": print({ kind: "out", text: "logout. (well, almost — refresh to come back)" }); break; default: print({ kind: "err", text: `command not found: ${head} — try 'help'` }); } }; const onKey = (e) => { if (e.key === "Enter") { handleCmd(value); if (value.trim()) setHistory((h) => [value, ...h]); setHIdx(-1); setValue(""); } else if (e.key === "ArrowUp") { e.preventDefault(); if (history.length === 0) return; const ni = Math.min(history.length - 1, hIdx + 1); setHIdx(ni); setValue(history[ni] || ""); } else if (e.key === "ArrowDown") { e.preventDefault(); const ni = Math.max(-1, hIdx - 1); setHIdx(ni); setValue(ni === -1 ? "" : history[ni] || ""); } else if (e.key === "l" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); setLines([]); } }; React.useEffect(() => { if (onCommand) onCommand.current = (c) => { handleCmd(c); }; }); return (
inputRef.current && inputRef.current.focus()}> {lines.map((ln, i) => { if (ln.kind === "cmd") { return (
{prompt} {ln.text}
); } if (ln.kind === "out") { return
{ln.text}
; } if (ln.kind === "err") { return
{ln.text}
; } if (ln.kind === "ascii") { return
{ln.text}
; } return null; })}
inputRef.current && inputRef.current.focus()}> {prompt} setValue(e.target.value)} onKeyDown={onKey} autoComplete="off" spellCheck="false" aria-label="terminal input" />
); } Object.assign(window, { Terminal });