"use client"; import { useState, useEffect, useRef } from "react"; import { QUOTES } from "@/lib/quotes"; export default function TerminalBox({ children }: { children: React.ReactNode }) { // ---- BOOT SEQUENCE STATES ---- const [showFirstPrompt, setShowFirstPrompt] = useState(false); const [showEnterEcho, setShowEnterEcho] = useState(false); const [typedCommand1, setTypedCommand1] = useState(""); const [showQuote, setShowQuote] = useState(false); const [quote, setQuote] = useState(""); const [typedCommand2, setTypedCommand2] = useState(""); const [showLoader, setShowLoader] = useState(false); const [loaderProgress, setLoaderProgress] = useState(0); const [showTree, setShowTree] = useState(false); const [showIntroDone, setShowIntroDone] = useState(false); // ---- INTERACTIVE STATES ---- const [history, setHistory] = useState<{ type: "input" | "output"; text: any }[]>([]); const [currentInput, setCurrentInput] = useState(""); const inputRef = useRef(null); // --------------------------------------------------------- // BOOT SEQUENCE // --------------------------------------------------------- useEffect(() => { setQuote(QUOTES[Math.floor(Math.random() * QUOTES.length)]); setTimeout(() => setShowFirstPrompt(true), 400); setTimeout(() => setShowEnterEcho(true), 1500); setTimeout(() => typeCommand1(), 1800); }, []); // TYPE ONLY COMMAND, NOT PROMPT const typeCommand1 = () => { const cmd = "quoteOfTheDay"; let i = 0; const interval = setInterval(() => { setTypedCommand1(cmd.slice(0, i + 1)); i++; if (i >= cmd.length) { clearInterval(interval); setTimeout(() => setShowQuote(true), 300); setTimeout(() => typeCommand2(), 1200); } }, 50); }; const typeCommand2 = () => { const cmd = "tree ."; let i = 0; const interval = setInterval(() => { setTypedCommand2(cmd.slice(0, i + 1)); i++; if (i >= cmd.length) { clearInterval(interval); setTimeout(() => startLoader(), 400); } }, 50); }; const startLoader = () => { setShowLoader(true); let progress = 0; const interval = setInterval(() => { progress += Math.random() * 20; if (progress >= 100) { progress = 100; clearInterval(interval); setTimeout(() => { setShowTree(true); setShowLoader(false); setShowIntroDone(true); setTimeout(() => inputRef.current?.focus(), 200); }, 500); } setLoaderProgress(Math.floor(progress)); }, 200); }; // --------------------------------------------------------- // COMMAND REGISTRY // --------------------------------------------------------- const commands: Record any> = { quoteOfTheDay: () => QUOTES[Math.floor(Math.random() * QUOTES.length)], help: () => ` Available commands: quoteOfTheDay Show a random quote tree . Show the file structure ls, ls -la Minimal directory listing cd Playful message about not leaving pwd Print working directory whoami Identify yourself motivation Generate motivation (may crash) vibecheck Check the vibe clear I will not clear your history :) exit You can't exit, it's a website `, pwd: () => "/home/juntekim/site", whoami: () => "juntekim", clear: () => "no ❤️ you need to face your past commands.", motivation: () => [ "Compiling motivation…", setTimeout(() => { setHistory(h => [...h, { type: "output", text: "Segmentation fault (core dumped)" }]); }, 500), ], vibecheck: () => { const vibes = [ "vibes immaculate ✨", "vibes questionable 🧐", "vibes under maintenance 🚧", "vibes loading… 0%", "vibes not found", ]; return vibes[Math.floor(Math.random() * vibes.length)]; }, exit: () => "bro it's a website. chill.", ls: () => "nothing to see here 👀", "ls -la": () => "nothing to see here 👀", cd: () => "where you trying to go? everything you need is in tree .", tree: () => [ "Compiling…", setTimeout(() => { setHistory(h => [...h, { type: "output", text: "/" }]); setHistory(h => [...h, { type: "output", text: children }]); }, 400), ], }; // --------------------------------------------------------- // COMMAND EXECUTION // --------------------------------------------------------- const handleCommand = (cmd: string) => { const parts = cmd.trim().split(" "); const base = parts[0]; const args = parts.slice(1); setHistory(h => [...h, { type: "input", text: cmd }]); if (cmd === "tree .") return commands["tree"](args); if (commands[base]) { const output = commands[base](args); if (typeof output === "string") { setHistory(h => [...h, { type: "output", text: output }]); } return; } if (base === "cd") { setHistory(h => [...h, { type: "output", text: commands["cd"](args) }]); return; } setHistory(h => [...h, { type: "output", text: `command not found: ${cmd}` }]); }; const onKeyDown = (e: any) => { if (e.key === "Enter") { handleCommand(currentInput); setCurrentInput(""); } }; // --------------------------------------------------------- // RENDER // --------------------------------------------------------- return (
{/* BOOT SEQUENCE */} {showFirstPrompt &&
juntekim@site:~$
} {showEnterEcho &&
juntekim@site:~$
} {typedCommand1 && (
juntekim@site:~$  {typedCommand1}
)} {showQuote &&
{quote}
} {typedCommand2 && (
juntekim@site:~$  {typedCommand2}
)} {showLoader && (
compiling… {loaderProgress}%
)} {showTree && (
/ {children}
)} {/* INTERACTIVE MODE */} {showIntroDone && ( <> {history.map((item, idx) => (
{item.type === "input" ? ( <> juntekim@site:~$  {item.text} ) : ( {item.text} )}
))}
juntekim@site:~$  setCurrentInput(e.target.value)} onKeyDown={onKeyDown} className="bg-black text-zinc-300 outline-none w-full" autoFocus />
)}
); }