"use client";
import * as React from "react";
import { Brain, Eye, FileText, Globe, Wrench, Braces, Zap, Maximize2 } from "lucide-react";
import type { PolyModel } from "@/lib/client/types";
import { Badge } from "@/components/ui/badge";
import { Tooltip } from "@/components/ui/tooltip";
import { useApp } from "@/components/app/store";
import { BadgeChips, LifecycleChip } from "@/components/models/badge-chips";
import { buildBadgeContext, deriveBadges, isFastModel as isFastModelBase, lifecycleStatus, retiresSoon as retiresSoonBase, shutdownDateOf, LONG_CONTEXT_TOKENS, type BadgeContext } from "@/lib/models/badges";
import { formatPrice } from "@/lib/models/format";
import { formatTokens } from "@/lib/utils";
import { cn } from "@/lib/utils";
/** Capability glyph definitions shared by rows, profile and catalog (order = display order). */
export const CAPABILITY_GLYPHS: { key: keyof PolyModel["capabilities"]; label: string; icon: React.ReactNode; title: string }[] = [
{ key: "vision", label: "Vision", icon: , title: "Understands images" },
{ key: "reasoning", label: "Reasoning", icon: , title: "Extended reasoning / thinking" },
{ key: "tools", label: "Tools", icon: , title: "Function / tool calling" },
{ key: "webSearch", label: "Web", icon: , title: "Provider-native web search" },
{ key: "structuredOutput", label: "JSON", icon: , title: "Structured output (JSON schema)" },
{ key: "files", label: "PDF", icon: , title: "PDF / file input" },
];
export function capabilityList(m: PolyModel) {
const out: { key: string; label: string; icon: React.ReactNode; title: string }[] = CAPABILITY_GLYPHS.filter((g) => m.capabilities[g.key]).map((g) => ({ key: g.key, label: g.label, icon: g.icon, title: g.title }));
if ((m.limits?.contextTokens ?? 0) >= LONG_CONTEXT_TOKENS) out.push({ key: "long", label: formatTokens(m.limits!.contextTokens!), icon: , title: "Long context window" });
return out;
}
/** Kept for existing callers (arena auto-pick). Prefer `speedTier()` from `lib/models/badges` for new code. */
export function isFastModel(m: PolyModel): boolean {
return isFastModelBase(m);
}
export const retiresSoon = retiresSoonBase;
/** Registry-wide badge context (pricing quantiles, "new" window) memoized on the store's model list. */
export function useBadgeContext(): BadgeContext {
const { models } = useApp();
const [now] = React.useState(() => Date.now());
return React.useMemo(() => buildBadgeContext(models, now), [models, now]);
}
/** Compact icon-only capability row: Vision · Reasoning · Tools · Web · JSON · PDF. */
export function CapabilityGlyphs({ model, max = 6, className, size = "sm" }: { model: PolyModel; max?: number; className?: string; size?: "sm" | "md" }) {
const on = CAPABILITY_GLYPHS.filter((g) => model.capabilities[g.key]).slice(0, max);
if (!on.length) return text only;
return (
g.label).join(", ")}>
{on.map((g) => (
{g.icon}
))}
);
}
export function CapabilityBadges({ model, compact, max = 6, className }: { model: PolyModel; compact?: boolean; max?: number; className?: string }) {
const caps = capabilityList(model).slice(0, max);
return (
{isFastModel(model) ? (
{compact ? null : "Fast"}
) : null}
{caps.map((c) => (
{c.icon} {compact ? null : c.label}
))}
);
}
/** NEW / FAST / CHEAP / REASONING / VISION / CODING / LONG CONTEXT from real registry data. */
export function ModelBadges({ model, ctx, max = 3, className, size }: { model: PolyModel; ctx: BadgeContext; max?: number; className?: string; size?: "xs" | "sm" }) {
const badges = React.useMemo(() => deriveBadges(model, ctx), [model, ctx]);
return ;
}
export function PriceLabel({ model, className }: { model: PolyModel; className?: string }) {
const p = model.pricing;
if (!p || (p.inputPerMillion === undefined && p.outputPerMillion === undefined)) return price n/a;
return (
{formatPrice(p.inputPerMillion)} / {formatPrice(p.outputPerMillion)} per 1M
);
}
/**
* Lifecycle badge: New / Active / Preview / Deprecated / Retiring / Unavailable.
* `showActive` renders the green "Active" chip too (catalog); otherwise Active/New-less models render nothing.
*/
export function StatusBadge({ model, connected, ctx, showActive = false, className }: { model: PolyModel; connected?: boolean; ctx?: BadgeContext; showActive?: boolean; className?: string }) {
const [now] = React.useState(() => Date.now());
const lc = lifecycleStatus(model, { connected, ctx, now });
if (lc.status === "active" && !showActive) return null;
const sd = shutdownDateOf(model);
return ;
}