TypeScript · 2671 bytes Raw Blame History
1 "use client";
2
3 import { useState } from "react";
4 import ShellHighlighter from "./ShellHighlighter";
5
6 type TabKey = "curl" | "homebrew" | "source";
7
8 const installCommands: Record<TabKey, { label: string; commands: string[] }> = {
9 curl: {
10 label: "curl",
11 commands: ["curl -fsSL https://tarmac.musicsian.com/install.sh | bash"],
12 },
13 homebrew: {
14 label: "Homebrew",
15 commands: [
16 "brew tap gardesk/tap",
17 "brew install ers",
18 "brew install tarmac",
19 ],
20 },
21 source: {
22 label: "Source",
23 commands: [
24 "git clone https://github.com/gardesk/tarmac.git",
25 "cd tarmac && cargo build --release",
26 "# binary at ./target/release/tarmac",
27 ],
28 },
29 };
30
31 export default function Terminal() {
32 const [activeTab, setActiveTab] = useState<TabKey>("curl");
33 const [copied, setCopied] = useState(false);
34
35 const copyToClipboard = () => {
36 const commands = installCommands[activeTab].commands
37 .filter((cmd) => !cmd.startsWith("#"))
38 .join("\n");
39 navigator.clipboard.writeText(commands);
40 setCopied(true);
41 setTimeout(() => setCopied(false), 2000);
42 };
43
44 return (
45 <div className="terminal text-left max-w-2xl mx-auto">
46 <div className="terminal-header">
47 <div className="flex gap-1.5">
48 <span className="terminal-dot bg-red-500" />
49 <span className="terminal-dot bg-yellow-500" />
50 <span className="terminal-dot bg-green-500" />
51 </div>
52 <div className="flex-1 flex gap-1 ml-4 overflow-x-auto">
53 {(Object.keys(installCommands) as TabKey[]).map((key) => (
54 <button
55 key={key}
56 onClick={() => setActiveTab(key)}
57 className={`px-3 py-1 text-xs rounded transition-colors ${
58 activeTab === key
59 ? "bg-surface-700 text-surface-100"
60 : "text-surface-400 hover:text-surface-200 hover:bg-surface-800"
61 }`}
62 >
63 {installCommands[key].label}
64 </button>
65 ))}
66 </div>
67 <button
68 onClick={copyToClipboard}
69 className="text-surface-400 hover:text-surface-200 text-xs px-2"
70 title="Copy to clipboard"
71 >
72 {copied ? "Copied" : "Copy"}
73 </button>
74 </div>
75
76 <div className="terminal-content">
77 {installCommands[activeTab].commands.map((cmd, i) => (
78 <div key={i} className="flex">
79 {!cmd.startsWith("#") && (
80 <span className="text-green-500 mr-2 select-none">$</span>
81 )}
82 <ShellHighlighter code={cmd} />
83 </div>
84 ))}
85 </div>
86 </div>
87 );
88 }