This commit is contained in:
2026-07-29 11:42:41 +08:00
commit 48b7391365
71 changed files with 28910 additions and 0 deletions

View File

@ -0,0 +1,100 @@
"use client";
import {
CheckCircle2,
CircleMinus,
GripVertical,
LoaderCircle,
Sparkles,
X,
} from "lucide-react";
import { cn } from "@/lib/utils";
export function SkillTag({
name,
index,
onRemove,
active = false,
muted = false,
draggable = false,
status = "selected",
showStatusLabel = true,
className,
}: {
name: string;
index?: number;
onRemove?: () => void;
active?: boolean;
muted?: boolean;
draggable?: boolean;
status?: "selected" | "checking" | "used" | "unused";
showStatusLabel?: boolean;
className?: string;
}) {
return (
<span
className={cn(
"inline-flex h-7 max-w-full items-center gap-1.5 rounded-full border px-2.5 text-[11px] font-medium transition-colors",
active
? "border-foreground/15 bg-foreground text-background"
: "border-border bg-background text-foreground/75",
status === "checking" &&
"border-foreground/15 bg-background text-foreground",
status === "used" &&
"border-emerald-200 bg-emerald-50 text-emerald-700",
status === "unused" &&
"border-border bg-background text-muted-foreground opacity-65",
muted && "opacity-55 grayscale-[0.2]",
className,
)}
>
{draggable ? (
<GripVertical className="size-3.5 shrink-0 text-muted-foreground" />
) : active ? (
<Sparkles className="size-3.5 shrink-0" />
) : null}
{typeof index === "number" && (
<span
className={cn(
"font-mono text-[10px]",
active ? "text-background/75" : "text-muted-foreground",
)}
>
{String(index + 1).padStart(2, "0")}
</span>
)}
<span className="truncate">{name}</span>
{showStatusLabel && status === "checking" && (
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
<LoaderCircle className="size-3 animate-spin" />
</span>
)}
{showStatusLabel && status === "used" && (
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
<CheckCircle2 className="size-3" />
</span>
)}
{showStatusLabel && status === "unused" && (
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
<CircleMinus className="size-3" />
</span>
)}
{onRemove && (
<button
type="button"
aria-label={`移除 ${name}`}
className="ml-0.5 grid size-5 shrink-0 place-items-center rounded-full text-current/55 hover:bg-black/5 hover:text-current focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"
onClick={(event) => {
event.stopPropagation();
onRemove();
}}
>
<X className="size-3.5" />
</button>
)}
</span>
);
}