"use client"; import { forwardRef } from "react"; import { SnowflakeIcon } from "@/components/icons"; import { cn } from "@/lib/utils"; type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "success"; type ButtonSize = "sm" | "md" | "lg"; /** * ChatGPT-flavoured buttons: pill-shaped, near-black primary in light mode and * white in dark mode, quiet secondaries with hairline borders. */ const VARIANTS: Record = { primary: "btn-primary", secondary: "btn-secondary", ghost: "btn-ghost", danger: "btn-primary !bg-rose-600 hover:!bg-rose-700 dark:!bg-rose-500 dark:!text-white dark:hover:!bg-rose-600", success: "btn-primary !bg-emerald-600 hover:!bg-emerald-700 dark:!bg-emerald-500 dark:!text-white dark:hover:!bg-emerald-600", }; const SIZES: Record = { sm: "h-9 px-3.5 text-[13px] rounded-full", md: "h-10 px-4 text-sm rounded-full", lg: "h-11 px-5 text-[15px] rounded-full", }; export type ButtonProps = React.ButtonHTMLAttributes & { variant?: ButtonVariant; size?: ButtonSize; loading?: boolean; }; export const Button = forwardRef(function Button( { className, variant = "primary", size = "md", loading, children, disabled, ...props }, ref, ) { return ( ); }); export function Spinner({ className }: { className?: string }) { return ( ); } /** Small neutral pill. Colour is reserved for meaning, not decoration. */ export function Chip({ children, className, tone = "neutral", }: { children: React.ReactNode; className?: string; tone?: "neutral" | "accent" | "success" | "warning" | "danger"; }) { const tones: Record = { neutral: "bg-[var(--surface-sunken)] text-[var(--text-muted)]", accent: "bg-accent-500/12 text-accent-700 dark:text-accent-200", success: "bg-emerald-500/12 text-emerald-700 dark:text-emerald-300", warning: "bg-amber-500/14 text-amber-800 dark:text-amber-300", danger: "bg-rose-500/12 text-rose-700 dark:text-rose-300", }; return ( {children} ); } export function ProgressBar({ value, max, className, label, }: { value: number; max: number; className?: string; label?: string; }) { const percent = max <= 0 ? 0 : Math.min(100, Math.max(0, (value / max) * 100)); return (
); } export function EmptyState({ title, children, icon, action, }: { title: string; children?: React.ReactNode; icon?: React.ReactNode; action?: React.ReactNode; }) { return (
{icon ?? }

{title}

{children ? (

{children}

) : null} {action ?
{action}
: null}
); }