nextjs-cmd / app /page.tsx
diamond-in's picture
Update app/page.tsx
ad6f6d2 verified
"use client";
import { useState, useRef, useEffect } from "react";
type TerminalLine = {
type: "user" | "system" | "error";
text: string;
};
export default function Terminal() {
const [history, setHistory] = useState<TerminalLine[]>([
{ type: "system", text: "Next.js Terminal [v1.0.0]" },
{ type: "system", text: "Connected to Hugging Face Docker Space..." },
{ type: "system", text: "Type 'ls' to see files or 'python3 scripts/logic.py' to run logic." }
]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
// Auto-scroll to bottom when history changes
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [history]);
const executeCommand = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) return;
const cmd = input;
setInput("");
setLoading(true);
// Add user command to history
setHistory((prev) => [...prev, { type: "user", text: `> ${cmd}` }]);
try {
const res = await fetch("/api/run", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ command: cmd }),
});
const data = await res.json();
setHistory((prev) => [
...prev,
{ type: data.type === "error" ? "error" : "system", text: data.output }
]);
} catch (err) {
setHistory((prev) => [...prev, { type: "error", text: "Failed to connect to API" }]);
}
setLoading(false);
};
return (
<div style={{
backgroundColor: "#0d1117",
color: "#00ff00",
height: "100vh",
padding: "20px",
fontFamily: "Courier New, monospace",
display: "flex",
flexDirection: "column"
}}>
{/* Output Area */}
<div
ref={scrollRef}
style={{ flex: 1, overflowY: "auto", whiteSpace: "pre-wrap", marginBottom: "10px" }}
>
{history.map((line, i) => (
<div key={i} style={{
color: line.type === "user" ? "white" : line.type === "error" ? "red" : "#00ff00",
marginBottom: "4px"
}}>
{line.text}
</div>
))}
{loading && <div style={{ color: "yellow" }}>Running...</div>}
</div>
{/* Input Area */}
<form onSubmit={executeCommand} style={{ display: "flex", borderTop: "1px solid #333", paddingTop: "10px" }}>
<span style={{ color: "#00ff00", marginRight: "10px" }}>$</span>
<input
autoFocus
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter shell command..."
style={{
flex: 1,
background: "transparent",
border: "none",
color: "white",
outline: "none",
fontFamily: "inherit"
}}
/>
</form>
</div>
);
}