@@ -0,0 +1,262 @@
|
||||
import React from "react";
|
||||
import { useTerminalStore } from "../store/useTerminalStore";
|
||||
import { MdClose, MdClearAll } from "react-icons/md";
|
||||
import { FiTerminal } from "react-icons/fi";
|
||||
|
||||
export const TerminalOutput: React.FC = () => {
|
||||
const {
|
||||
jobs,
|
||||
isOpen,
|
||||
activeJobId,
|
||||
closeTerminal,
|
||||
setActiveJob,
|
||||
clearJobs,
|
||||
removeJob,
|
||||
} = useTerminalStore();
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const activeJob = jobs.find((j) => j.id === activeJobId) || jobs[jobs.length - 1];
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
backgroundColor: "#1e1e1e",
|
||||
borderTop: "1px solid #3e3e42",
|
||||
}}
|
||||
>
|
||||
{/* Terminal header */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "0 12px",
|
||||
height: "35px",
|
||||
borderBottom: "1px solid #3e3e42",
|
||||
backgroundColor: "#252526",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FiTerminal size={14} color="#bbbbbb" />
|
||||
<span
|
||||
style={{
|
||||
color: "#bbbbbb",
|
||||
fontWeight: 500,
|
||||
fontSize: "11px",
|
||||
letterSpacing: "0.8px",
|
||||
}}
|
||||
>
|
||||
TERMINAL
|
||||
</span>
|
||||
{jobs.length > 0 && (
|
||||
<span
|
||||
style={{
|
||||
color: "#858585",
|
||||
fontSize: "11px",
|
||||
backgroundColor: "#3c3c3c",
|
||||
padding: "2px 8px",
|
||||
borderRadius: "10px",
|
||||
}}
|
||||
>
|
||||
{jobs.length}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: "4px", alignItems: "center" }}>
|
||||
{jobs.length > 0 && (
|
||||
<button
|
||||
onClick={clearJobs}
|
||||
style={{
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
color: "#858585",
|
||||
cursor: "pointer",
|
||||
padding: "4px",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
title="Clear all"
|
||||
>
|
||||
<MdClearAll size={14} />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={closeTerminal}
|
||||
style={{
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
color: "#858585",
|
||||
cursor: "pointer",
|
||||
padding: "4px",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
title="Close"
|
||||
>
|
||||
<MdClose size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Job tabs */}
|
||||
{jobs.length > 1 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
backgroundColor: "#2d2d2d",
|
||||
borderBottom: "1px solid #3e3e42",
|
||||
overflowX: "auto",
|
||||
}}
|
||||
>
|
||||
{jobs.map((job) => (
|
||||
<button
|
||||
key={job.id}
|
||||
onClick={() => setActiveJob(job.id)}
|
||||
style={{
|
||||
padding: "6px 16px",
|
||||
backgroundColor:
|
||||
job.id === activeJobId ? "#1e1e1e" : "transparent",
|
||||
border: "none",
|
||||
borderBottom:
|
||||
job.id === activeJobId
|
||||
? "2px solid #0e639c"
|
||||
: "2px solid transparent",
|
||||
color: job.isRunning ? "#cccccc" : "#858585",
|
||||
fontSize: "12px",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "6px",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: "8px",
|
||||
height: "8px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: job.isRunning ? "#4ec9b0" : "#858585",
|
||||
display: "inline-block",
|
||||
}}
|
||||
/>
|
||||
{job.scriptPath.split("/").pop()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Terminal output */}
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
overflowY: "auto",
|
||||
padding: "12px",
|
||||
fontFamily: "'Consolas', 'Courier New', monospace",
|
||||
fontSize: "13px",
|
||||
lineHeight: "1.5",
|
||||
}}
|
||||
>
|
||||
{activeJob ? (
|
||||
<>
|
||||
{/* Command header */}
|
||||
<div style={{ marginBottom: "8px" }}>
|
||||
<span style={{ color: "#6a9955" }}>$ </span>
|
||||
<span style={{ color: "#cccccc" }}>
|
||||
{activeJob.command.join(" ")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Stdin if provided */}
|
||||
{activeJob.stdin && (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "8px",
|
||||
padding: "8px",
|
||||
backgroundColor: "#2d2d2d",
|
||||
borderRadius: "4px",
|
||||
borderLeft: "3px solid #0e639c",
|
||||
}}
|
||||
>
|
||||
<span style={{ color: "#858585" }}>stdin: </span>
|
||||
<pre
|
||||
style={{
|
||||
margin: 0,
|
||||
color: "#cccccc",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{activeJob.stdin}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stdout */}
|
||||
{activeJob.stdout && (
|
||||
<pre
|
||||
style={{
|
||||
margin: "0 0 8px 0",
|
||||
color: "#cccccc",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{activeJob.stdout}
|
||||
</pre>
|
||||
)}
|
||||
|
||||
{/* Stderr */}
|
||||
{activeJob.stderr && (
|
||||
<pre
|
||||
style={{
|
||||
margin: "0 0 8px 0",
|
||||
color: "#f44747",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{activeJob.stderr}
|
||||
</pre>
|
||||
)}
|
||||
|
||||
{/* Status */}
|
||||
{activeJob.isRunning ? (
|
||||
<div style={{ color: "#4ec9b0" }}>⏳ Running...</div>
|
||||
) : activeJob.status !== null ? (
|
||||
<div
|
||||
style={{
|
||||
color: activeJob.status === 0 ? "#4ec9b0" : "#f44747",
|
||||
}}
|
||||
>
|
||||
{activeJob.status === 0
|
||||
? "✓ Process exited with code 0"
|
||||
: `✗ Process exited with code ${activeJob.status}`}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "#858585",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "100%",
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<FiTerminal size={32} />
|
||||
<span>No active jobs</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user