| 1 | "use client"; |
| 2 | |
| 3 | import { useState } from "react"; |
| 4 | |
| 5 | type Runtime = "NodeJS" | "Swift" | "Ruby"; |
| 6 | |
| 7 | interface TerminalLine { |
| 8 | text: string; |
| 9 | type: "command" | "success" | "progress" | "muted"; |
| 10 | } |
| 11 | |
| 12 | const runnerSnippets: Record<Runtime, TerminalLine[]> = { |
| 13 | NodeJS: [ |
| 14 | { text: "$ smb", type: "command" }, |
| 15 | { text: "✔ Valid config", type: "success" }, |
| 16 | { text: "✔ NodeJS 🟩 runner detected", type: "success" }, |
| 17 | { text: "✔ Valid project", type: "success" }, |
| 18 | { text: "✔ Valid deployment setup", type: "success" }, |
| 19 | { text: "☱ Deploying › Use key path: ~/.ssh/id_11@smbcloud", type: "progress" }, |
| 20 | { text: "☱ Deploying › Building the app ✔", type: "progress" }, |
| 21 | { text: "☲ Deploying › App restart ✔", type: "progress" }, |
| 22 | { text: "☱ Deploying › App is running ✔", type: "progress" }, |
| 23 | { text: "✔ Deployment complete.", type: "success" }, |
| 24 | ], |
| 25 | Swift: [ |
| 26 | { text: "$ smb", type: "command" }, |
| 27 | { text: "✔ Valid config", type: "success" }, |
| 28 | { text: "✔ Swift 🟧 runner detected", type: "success" }, |
| 29 | { text: "✔ Valid project", type: "success" }, |
| 30 | { text: "✔ Valid deployment setup", type: "success" }, |
| 31 | { text: "☱ Deploying › Use key path: ~/.ssh/id_11@smbcloud", type: "progress" }, |
| 32 | { text: "☱ Deploying › Building the app ✔", type: "progress" }, |
| 33 | { text: "☲ Deploying › App restart ✔", type: "progress" }, |
| 34 | { text: "☱ Deploying › App is running ✔", type: "progress" }, |
| 35 | { text: "✔ Deployment complete.", type: "success" }, |
| 36 | ], |
| 37 | Ruby: [ |
| 38 | { text: "$ smb", type: "command" }, |
| 39 | { text: "✔ Valid config", type: "success" }, |
| 40 | { text: "✔ Ruby 🟥 runner detected", type: "success" }, |
| 41 | { text: "✔ Valid project", type: "success" }, |
| 42 | { text: "✔ Valid deployment setup", type: "success" }, |
| 43 | { text: "☱ Deploying › Use key path: ~/.ssh/id_11@smbcloud", type: "progress" }, |
| 44 | { text: "☱ Deploying › Building the app ✔", type: "progress" }, |
| 45 | { text: "☲ Deploying › App restart ✔", type: "progress" }, |
| 46 | { text: "☱ Deploying › App is running ✔", type: "progress" }, |
| 47 | { text: "✔ Deployment complete.", type: "success" }, |
| 48 | ], |
| 49 | }; |
| 50 | |
| 51 | const lineClass: Record<TerminalLine["type"], string> = { |
| 52 | command: "text-slate-200", |
| 53 | success: "text-emerald-400", |
| 54 | progress: "text-sky-400", |
| 55 | muted: "text-slate-500", |
| 56 | }; |
| 57 | |
| 58 | export default function TabbedRunner() { |
| 59 | const [tab, setTab] = useState<Runtime>("NodeJS"); |
| 60 | const tabs: Runtime[] = ["NodeJS", "Swift", "Ruby"]; |
| 61 | const lines = runnerSnippets[tab]; |
| 62 | |
| 63 | return ( |
| 64 | <div className="overflow-hidden rounded-xl border border-slate-200 dark:border-white/[0.08] bg-white dark:bg-[#0D1117] shadow-xl shadow-slate-900/5 dark:shadow-none"> |
| 65 | {/* Window chrome */} |
| 66 | <div className="flex items-center justify-between border-b border-slate-200 dark:border-white/[0.06] bg-slate-50 dark:bg-[#161B22] px-4 py-3"> |
| 67 | {/* Traffic lights */} |
| 68 | <div className="flex items-center gap-1.5"> |
| 69 | <div className="h-3 w-3 rounded-full bg-[#FF5F57]" /> |
| 70 | <div className="h-3 w-3 rounded-full bg-[#FEBC2E]" /> |
| 71 | <div className="h-3 w-3 rounded-full bg-[#28C840]" /> |
| 72 | </div> |
| 73 | |
| 74 | {/* Runtime tabs */} |
| 75 | <div className="flex gap-1"> |
| 76 | {tabs.map((t) => ( |
| 77 | <button |
| 78 | key={t} |
| 79 | onClick={() => setTab(t)} |
| 80 | className={`rounded px-3 py-1 text-xs font-medium transition-all ${ |
| 81 | tab === t |
| 82 | ? "bg-white dark:bg-slate-700 text-slate-900 dark:text-slate-100 shadow-sm" |
| 83 | : "text-slate-400 dark:text-slate-500 hover:text-slate-600 dark:hover:text-slate-400" |
| 84 | }`} |
| 85 | > |
| 86 | {t} |
| 87 | </button> |
| 88 | ))} |
| 89 | </div> |
| 90 | |
| 91 | {/* Spacer to balance traffic lights */} |
| 92 | <div className="w-16" /> |
| 93 | </div> |
| 94 | |
| 95 | {/* Terminal body */} |
| 96 | <div className="p-5 font-mono text-sm leading-relaxed"> |
| 97 | <div className="space-y-0.5"> |
| 98 | {lines.map((line, idx) => ( |
| 99 | <div key={idx} className={lineClass[line.type]}> |
| 100 | {line.text} |
| 101 | </div> |
| 102 | ))} |
| 103 | </div> |
| 104 | </div> |
| 105 | </div> |
| 106 | ); |
| 107 | } |