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%
2.0 KB · 50 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import { BLIND_LETTERS } from "@/lib/arena/scoring";4import { cn } from "@/lib/utils";56/** Neutral letter avatar used while identities are hidden in Blind Arena. */7export function BlindAvatar({ index, size = 20, className }: { index: number; size?: number; className?: string }) {8  const letter = BLIND_LETTERS[index] ?? String(index + 1);9  return (10    <span className={cn("inline-flex shrink-0 items-center justify-center rounded-full bg-fg font-mono font-semibold text-bg", className)} style={{ width: size, height: size, fontSize: Math.max(9, Math.round(size * 0.55)) }} aria-label={`Model ${letter}`}>11      {letter}12    </span>13  );14}1516/**17 * Card-flip reveal: rotates the children 90° around Y, swaps content at the midpoint, rotates back.18 * `flipping` is driven by the parent; the content already reflects the new state when it flips back.19 */20export function Flip({ flipping, children, className }: { flipping: boolean; children: React.ReactNode; className?: string }) {21  return (22    <span className={cn("inline-flex min-w-0 items-center transition-transform duration-150 ease-out [backface-visibility:hidden] [transform-style:preserve-3d] motion-reduce:transition-none", flipping ? "[transform:rotateY(90deg)]" : "[transform:rotateY(0deg)]", className)}>23      {children}24    </span>25  );26}2728/** Drives a two-phase flip: returns `[flipping, trigger]`; `onMidpoint` fires when the card is edge-on. */29export function useFlip(durationMs = 150): [boolean, (onMidpoint: () => void) => void] {30  const [flipping, setFlipping] = React.useState(false);31  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);32  React.useEffect(33    () => () => {34      if (timer.current) clearTimeout(timer.current);35    },36    [],37  );38  const trigger = React.useCallback(39    (onMidpoint: () => void) => {40      setFlipping(true);41      timer.current = setTimeout(() => {42        onMidpoint();43        setFlipping(false);44      }, durationMs);45    },46    [durationMs],47  );48  return [flipping, trigger];49}50