SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.8 KB · 112 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import Link from "next/link";4import { ArrowRight } from "lucide-react";5import { ProviderIcon } from "@/components/brand/provider-icon";6import { Badge } from "@/components/ui/badge";7import { Skeleton } from "@/components/ui/misc";8import { useApi } from "@/lib/client/api";9import { usePrefersReducedMotion } from "@/lib/client/hooks";10import { formatContext, formatPricePair, NEW_WINDOW_DAYS, type PublicModelSummary } from "@/lib/marketing/public-models";11import { cn } from "@/lib/utils";12import { Container } from "./section";1314interface Resp {15  models: PublicModelSummary[];16  total: number;17  generatedAt: string;18}1920function isNew(iso: string, now: number) {21  return now - new Date(iso).getTime() < NEW_WINDOW_DAYS * 86_400_000;22}2324function ModelChip({ m, now }: { m: PublicModelSummary; now: number }) {25  const price = formatPricePair(m.inputPerMillion, m.outputPerMillion);26  return (27    <li className="flex h-10 shrink-0 items-center gap-2 rounded-lg border border-border bg-bg-elevated/80 px-3 text-[13px]">28      <ProviderIcon provider={m.provider} size={14} />29      <span className="max-w-[12rem] truncate font-medium">{m.displayName}</span>30      {isNew(m.firstSeenAt, now) ? <Badge variant="accent">New</Badge> : m.status === "preview" ? <Badge variant="warning">Preview</Badge> : null}31      <span className="font-mono text-[11px] tabular-nums text-fg-subtle">{formatContext(m.contextTokens)}</span>32      {price ? <span className="hidden font-mono text-[11px] tabular-nums text-fg-subtle sm:inline">{price}</span> : null}33    </li>34  );35}3637/**38 * Live model strip. Fetches the public registry summary (cached 10 min server-side), renders a39 * skeleton first, then a seamless marquee (content duplicated, paused on hover). Reduced motion40 * or no data → a static wrapped grid / nothing.41 */42export function ModelStrip({ className }: { className?: string }) {43  const { data, error, isLoading } = useApi<Resp>("/api/public/models", { revalidateOnFocus: false, dedupingInterval: 600_000 });44  const reduce = usePrefersReducedMotion();45  const [now, setNow] = React.useState(0);46  React.useEffect(() => {47    // Wall clock read once on the client (never in render) so "New" badges are stable.48    // eslint-disable-next-line react-hooks/set-state-in-effect49    setNow(Date.now());50  }, []);5152  const models = data?.models ?? [];53  if (!isLoading && (error || models.length === 0)) return null;5455  return (56    <section className={cn("border-y border-border bg-bg-subtle/60", className)} aria-labelledby="model-strip-title">57      <Container className="flex flex-col gap-3 py-5 sm:py-6">58        <div className="flex items-center justify-between gap-4">59          <p id="model-strip-title" className="inline-flex items-center gap-2 text-[13px] text-fg-muted">60            <span className="relative flex size-2">61              <span className="absolute inline-flex size-full animate-pulse-soft rounded-full bg-success" />62              <span className="relative inline-flex size-2 rounded-full bg-success" />63            </span>64            {data ? (65              <>66                <span className="font-medium text-fg tabular-nums">{data.total}</span> models in the registry · live from the catalog67              </>68            ) : (69              "Loading the live model catalog…"70            )}71          </p>72          <Link href="/models" className="inline-flex shrink-0 items-center gap-1 text-[13px] font-medium text-accent underline-offset-4 hover:underline">73            Browse all <ArrowRight className="size-3.5" aria-hidden />74          </Link>75        </div>76      </Container>77      <div className={cn("pb-5 sm:pb-6", !reduce && "mask-x overflow-hidden")}>78        {isLoading ? (79          <ul className="flex gap-2 px-5 sm:px-8" aria-hidden>80            {Array.from({ length: 10 }).map((_, i) => (81              <li key={i}>82                <Skeleton className="h-10 w-44 rounded-lg" />83              </li>84            ))}85          </ul>86        ) : reduce ? (87          <Container>88            <ul className="flex flex-wrap gap-2" aria-label="Models available in PolyLLM">89              {models.slice(0, 16).map((m) => (90                <ModelChip key={m.key} m={m} now={now} />91              ))}92            </ul>93          </Container>94        ) : (95          <div className="marquee gap-2 pr-2" aria-label="Models available in PolyLLM">96            <ul className="flex shrink-0 gap-2">97              {models.map((m) => (98                <ModelChip key={m.key} m={m} now={now} />99              ))}100            </ul>101            <ul className="flex shrink-0 gap-2" aria-hidden>102              {models.map((m) => (103                <ModelChip key={`dup-${m.key}`} m={m} now={now} />104              ))}105            </ul>106          </div>107        )}108      </div>109    </section>110  );111}112