Import source: src/components/common/terminal-panel
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Download, Maximize2, RefreshCcw, Search, Trash2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
export function TerminalPanel() {
|
||||
const targetRef = useRef<HTMLDivElement | null>(null);
|
||||
const [connected, setConnected] = useState(true);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let disposed = false;
|
||||
async function boot() {
|
||||
const [{ Terminal }, { FitAddon }] = await Promise.all([
|
||||
import("xterm"),
|
||||
import("xterm-addon-fit"),
|
||||
]);
|
||||
if (disposed || !targetRef.current) return;
|
||||
const term = new Terminal({
|
||||
cursorBlink: true,
|
||||
rows: 14,
|
||||
convertEol: true,
|
||||
disableStdin: true,
|
||||
theme: {
|
||||
background: "#0A0F1E",
|
||||
foreground: "#E4E7EC",
|
||||
cursor: "#FF8A00",
|
||||
},
|
||||
});
|
||||
const fit = new FitAddon();
|
||||
term.loadAddon(fit);
|
||||
term.open(targetRef.current);
|
||||
fit.fit();
|
||||
term.writeln("CoaxysPanel mock terminal");
|
||||
term.writeln("Connected to srv-dal-01.coaxys.net as coaxys");
|
||||
term.writeln("");
|
||||
term.writeln("$ php -v");
|
||||
term.writeln("PHP 8.4.7 (mock output)");
|
||||
term.writeln("$ systemctl status nginx");
|
||||
term.writeln("active (running) - no command was executed on the host");
|
||||
setLoaded(true);
|
||||
}
|
||||
void boot();
|
||||
return () => {
|
||||
disposed = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex-row flex-wrap items-center justify-between gap-2 space-y-0">
|
||||
<CardTitle>Terminal</CardTitle>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<span
|
||||
className={
|
||||
connected
|
||||
? "h-1.5 w-1.5 animate-pulse-soft rounded-full bg-success"
|
||||
: "h-1.5 w-1.5 rounded-full bg-destructive"
|
||||
}
|
||||
/>
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setConnected(true)}
|
||||
aria-label="Reconnect"
|
||||
>
|
||||
<RefreshCcw className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="outline" aria-label="Search terminal">
|
||||
<Search className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="outline" aria-label="Download transcript">
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="outline" aria-label="Clear terminal">
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="outline" aria-label="Fullscreen">
|
||||
<Maximize2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div
|
||||
ref={targetRef}
|
||||
className="min-h-[320px] overflow-hidden rounded-md border border-border/60 bg-[#0A0F1E] p-2"
|
||||
aria-label="Mock terminal session"
|
||||
/>
|
||||
{!loaded ? (
|
||||
<p className="mt-2 text-xs text-muted-foreground">Loading terminal emulator...</p>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user