modelsdev 0.11.4

A fast TUI and CLI for browsing AI models, benchmarks, and coding agents
---
import { cn } from "@/utils/cn";
import type { HTMLAttributes } from "astro/types";

export interface Props extends HTMLAttributes<"button"> {
  variant?:
    | "default"
    | "destructive"
    | "outline"
    | "secondary"
    | "ghost"
    | "link"
    | "success"
    | "warning"
    | "info";
  size?: "xs" | "sm" | "default" | "lg" | "xl";
  iconOnly?: boolean;
  pill?: boolean;
  href?: string;
  disabled?: boolean;
}

const {
  variant = "default",
  size = "default",
  iconOnly = false,
  pill = false,
  href,
  disabled = false,
  class: className,
  ...props
} = Astro.props;

const baseStyles =
  "inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 cursor-pointer";

const variantStyles = {
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
  destructive:
    "bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:text-destructive-foreground dark:hover:bg-destructive/30",
  outline:
    "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
  ghost: "hover:bg-accent hover:text-accent-foreground",
  link: "text-primary underline-offset-4 hover:underline",
  success:
    "bg-success/10 text-success hover:bg-success/20 dark:bg-success/20 dark:text-success-foreground dark:hover:bg-success/30",
  warning:
    "bg-warning/10 text-warning hover:bg-warning/20 dark:bg-warning/20 dark:text-warning-foreground dark:hover:bg-warning/30",
  info: "bg-info/10 text-info hover:bg-info/20 dark:bg-info/20 dark:text-info-foreground dark:hover:bg-info/30",
};

const sizeStyles = {
  xs: "h-7 px-2.5 gap-1.5 text-xs [&_svg]:size-3",
  sm: "h-8 px-3 gap-1.5 text-xs [&_svg]:size-3.5",
  default: "h-9 px-4 gap-2 text-sm [&_svg]:size-4",
  lg: "h-10 px-6 gap-2.5 text-base [&_svg]:size-5",
  xl: "h-11 px-8 gap-3 text-lg [&_svg]:size-5",
};

const iconOnlySizeStyles = {
  xs: "size-7 text-xs [&_svg]:size-3",
  sm: "size-8 text-xs [&_svg]:size-3.5",
  default: "size-9 text-sm [&_svg]:size-4",
  lg: "size-10 text-base [&_svg]:size-5",
  xl: "size-11 text-lg [&_svg]:size-5",
};

const radiusStyles = {
  xs: "rounded-sm",
  sm: "rounded-md",
  default: "rounded-md",
  lg: "rounded-md",
  xl: "rounded-lg",
};

const classes = cn(
  baseStyles,
  variantStyles[variant],
  iconOnly ? iconOnlySizeStyles[size] : sizeStyles[size],
  pill ? "rounded-full" : radiusStyles[size],
  className,
);
---

{
  href ? (
    <a href={href} class={classes} {...props}>
      <slot name="left-icon" />
      <slot />
      <slot name="right-icon" />
    </a>
  ) : (
    <button class={classes} disabled={disabled} {...props}>
      <slot name="left-icon" />
      <slot />
      <slot name="right-icon" />
    </button>
  )
}