Skip to content

Commit 4d565cf

Browse files
committed
site: add cli command component
1 parent 02c065b commit 4d565cf

File tree

3 files changed

+132
-1
lines changed

3 files changed

+132
-1
lines changed

site/components/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ReactNode } from "react";
22
import { useState } from "react";
3-
import { Star, Copy, Plug, Plug2, PlugZap, Download } from "lucide-react";
3+
import { Star, Plug, PlugZap, Download } from "lucide-react";
44
import Link from "next/link";
55
import { useRouter } from "next/router";
66
import { useWasm } from "@/context/wush";

site/components/ui/card.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
const Card = React.forwardRef<
6+
HTMLDivElement,
7+
React.HTMLAttributes<HTMLDivElement>
8+
>(({ className, ...props }, ref) => (
9+
<div
10+
ref={ref}
11+
className={cn(
12+
"rounded-xl border bg-card text-card-foreground shadow",
13+
className
14+
)}
15+
{...props}
16+
/>
17+
))
18+
Card.displayName = "Card"
19+
20+
const CardHeader = React.forwardRef<
21+
HTMLDivElement,
22+
React.HTMLAttributes<HTMLDivElement>
23+
>(({ className, ...props }, ref) => (
24+
<div
25+
ref={ref}
26+
className={cn("flex flex-col space-y-1.5 p-6", className)}
27+
{...props}
28+
/>
29+
))
30+
CardHeader.displayName = "CardHeader"
31+
32+
const CardTitle = React.forwardRef<
33+
HTMLDivElement,
34+
React.HTMLAttributes<HTMLDivElement>
35+
>(({ className, ...props }, ref) => (
36+
<div
37+
ref={ref}
38+
className={cn("font-semibold leading-none tracking-tight", className)}
39+
{...props}
40+
/>
41+
))
42+
CardTitle.displayName = "CardTitle"
43+
44+
const CardDescription = React.forwardRef<
45+
HTMLDivElement,
46+
React.HTMLAttributes<HTMLDivElement>
47+
>(({ className, ...props }, ref) => (
48+
<div
49+
ref={ref}
50+
className={cn("text-sm text-muted-foreground", className)}
51+
{...props}
52+
/>
53+
))
54+
CardDescription.displayName = "CardDescription"
55+
56+
const CardContent = React.forwardRef<
57+
HTMLDivElement,
58+
React.HTMLAttributes<HTMLDivElement>
59+
>(({ className, ...props }, ref) => (
60+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61+
))
62+
CardContent.displayName = "CardContent"
63+
64+
const CardFooter = React.forwardRef<
65+
HTMLDivElement,
66+
React.HTMLAttributes<HTMLDivElement>
67+
>(({ className, ...props }, ref) => (
68+
<div
69+
ref={ref}
70+
className={cn("flex items-center p-6 pt-0", className)}
71+
{...props}
72+
/>
73+
))
74+
CardFooter.displayName = "CardFooter"
75+
76+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

site/components/ui/cli-command.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useState } from "react";
2+
import { Card, CardContent } from "@/components/ui/card";
3+
import { Button } from "@/components/ui/button";
4+
import { toast } from "sonner";
5+
import { Check, Copy } from "lucide-react";
6+
7+
interface CliCommandCardProps {
8+
command: string;
9+
}
10+
11+
export function CliCommandCard({ command }: CliCommandCardProps) {
12+
const [isCopied, setIsCopied] = useState(false);
13+
14+
const copyToClipboard = async () => {
15+
try {
16+
await navigator.clipboard.writeText(command);
17+
setIsCopied(true);
18+
toast.success("Command copied to clipboard");
19+
setTimeout(() => setIsCopied(false), 2000);
20+
} catch (err) {
21+
toast.error("Failed to copy command");
22+
}
23+
};
24+
25+
return (
26+
<Card className="w-full max-w-2xl">
27+
<CardContent className="p-2 flex items-center justify-between border-gray-600 bg-gray-700 rounded">
28+
<code className="text-sm bg-muted px-[0.3rem] py-[0.2rem] rounded font-mono flex-grow mr-2 overflow-x-auto">
29+
{command}
30+
</code>
31+
<Button
32+
variant="outline"
33+
size="icon"
34+
onClick={copyToClipboard}
35+
className="flex-shrink-0 relative overflow-hidden"
36+
>
37+
<span
38+
className={`absolute inset-0 flex items-center justify-center transition-transform duration-300 ${
39+
isCopied ? "translate-y-full" : "translate-y-0"
40+
}`}
41+
>
42+
<Copy className="h-4 w-4" />
43+
</span>
44+
<span
45+
className={`absolute inset-0 flex items-center justify-center transition-transform duration-300 ${
46+
isCopied ? "translate-y-0" : "-translate-y-full"
47+
}`}
48+
>
49+
<Check className="h-4 w-4" />
50+
</span>
51+
</Button>
52+
</CardContent>
53+
</Card>
54+
);
55+
}

0 commit comments

Comments
 (0)