feat: launch scripts
ci-front / build (push) Successful in 2m19s

This commit is contained in:
nikita
2026-04-05 06:54:33 +03:00
parent d6512d6c97
commit 2bc3da21fd
11 changed files with 774 additions and 107 deletions
@@ -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>
);
};