WebberUI

AI 模型對戰(React)

競技場式雙欄並排盲測的 React 元件:兩個匿名回覆同步串流打字,投票 A/B/平手/都不好之後翻牌揭曉模型名與供應商色塊,勝方加冠並讓計分板數字滾動。

這是 WebberUI Pro 元件

上線活動期間免費:註冊或登入後,在上方預覽區按「複製安裝指令」就能直接安裝,不需付費、不用綁信用卡。下方那條指令未登入時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

把兩個模型對同一題的回覆並排放進「模型 A」「模型 B」兩張匿名卡片,同一個字元計數器同步逐字揭示、游標閃爍;串流結束才啟用底下四顆投票按鈕。投票後兩張卡以 rotateY 翻牌揭曉模型名與供應商色塊,被選的一方加上冠軍徽章並脈動一次,計分板數字逐位滾動;按「下一題」換入下一組題目就自動重新開始。揭曉狀態與計分都支援受控/非受控。

載入預覽⋯
npx shadcn@latest add "https://webberui.com/r/ai-model-arena.json?t=<安裝 token>"

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

2
240
<AiModelArena />

安裝

npx shadcn@latest add "https://webberui.com/r/ai-model-arena.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/ai-model-arena 安裝。

使用

import { AiModelArena, type ArenaVote } from "@/components/ui/ai-model-arena";

// 模型名與供應商皆為虛構示範
const round = {
  prompt: "用一句話解釋什麼是「複利」。",
  left: { model: "Aurora-7", provider: "北極光實驗室", response: "複利就是利息也會生利息……" },
  right: { model: "Sage-Coder", provider: "鼠尾草智能", response: "本期利息併入本金,下一期再計息……" },
};

<AiModelArena
  prompt={round.prompt}
  left={round.left}
  right={round.right}
  onVote={(choice: ArenaVote) => console.log(choice)} // "left" | "right" | "tie" | "both-bad"
  onNext={() => loadNextRound()} // 換入下一組題目與回覆,元件會自動從頭打字
/>

計分想自己管(例如平手各加 0.5 分)時,改成受控 scores

const [scores, setScores] = React.useState<Record<string, number>>({});

<AiModelArena
  {...round}
  scores={scores}
  onVote={(choice) =>
    setScores((prev) => {
      const next = { ...prev };
      if (choice === "left") next[round.left.model] = (next[round.left.model] ?? 0) + 1;
      if (choice === "right") next[round.right.model] = (next[round.right.model] ?? 0) + 1;
      if (choice === "tie") {
        next[round.left.model] = (next[round.left.model] ?? 0) + 0.5;
        next[round.right.model] = (next[round.right.model] ?? 0) + 0.5;
      }
      return next;
    })
  }
/>

Props

Prop型別預設值說明
promptstring題目文字,顯示在頂部的氣泡
leftArenaContestant左欄參賽者(模型名與回覆)
rightArenaContestant右欄參賽者(模型名與回覆)
streamingbooleantrue是否以打字效果逐字揭示回覆;false 或使用者偏好減少動態時直接完整顯示
charsPerTicknumber2每一拍(40ms)揭示的字元數,越大越快
onVote(choice: ArenaVote) => void投票時回呼
scoresRecord<string, number>受控計分:以模型名為 key 的累計分數,計分板只列出這裡有的模型;未提供時元件內部自行累計(投票後兩位參賽者都登記,勝方加 1 分)
showScoresbooleantrue是否顯示計分板
revealedboolean受控:是否已翻牌揭曉;不提供時由投票/下一題自動切換
defaultRevealedbooleanfalse非受控模式的初始揭曉狀態
onRevealedChange(revealed: boolean) => void揭曉狀態變動時回呼(投票後為 true、按下一題為 false
onNext() => void按「下一題」時回呼,通常在此換入下一組題目與回覆
maxHeightnumber | string240回覆區最大高度(px 或 CSS 長度),超出時欄內捲動
labelsArenaLabels介面文案,可覆寫任一項
classNamestring透傳到最外層容器

ArenaContestant

欄位型別預設值說明
modelstring模型名稱;投票前隱藏,翻牌後才揭曉
providerstring供應商名稱,翻牌後顯示於模型名下方並用於色塊配色;未提供時以模型名代替
huenumber供應商色塊的色相(0–360);未提供時依供應商名稱確定性產生
responsestring回覆全文;streaming 時由 0 逐字揭示

ArenaLabels

欄位型別預設值說明
leftAnonymousstring"模型 A"左欄匿名徽章文字
rightAnonymousstring"模型 B"右欄匿名徽章文字
leftBetterstring"A 較好"「左邊較好」按鈕文字
rightBetterstring"B 較好"「右邊較好」按鈕文字
tiestring"平手"「平手」按鈕與徽章文字
bothBadstring"都不好"「都不好」按鈕與徽章文字
nextstring"下一題"「下一題」按鈕文字
winnerstring"勝出"勝出徽章文字
scoreboardstring"計分板"計分板標題
noScoresstring"投票後開始計分"計分板尚無任何分數時的提示文字
promptstring"題目"題目氣泡的小標

另外具名匯出 ArenaVote 型別("left" \| "right" \| "tie" \| "both-bad")與 providerHue(name)——依供應商名稱產生 0–360 的確定性色相,想在別處畫同一組供應商色塊時可重複使用。

細節

  • 同步串流:兩欄共用同一個字元計數器,每 40ms 前進 charsPerTick 個字,短的那側先打完就停下游標、長的那側繼續;兩側都完成才啟用投票列。
  • 翻牌才揭曉:卡片背面的模型名與供應商色塊只在揭曉後才掛載——投票前 DOM 裡不存在模型名,搜尋、Ctrl+F 或輔助科技都提前讀不到。
  • 計分規則:非受控時投完票才把兩位參賽者登記進計分板(輸家 0 分),「A 較好」「B 較好」為對應模型加 1 分,平手與都不好不加分;要用不同規則(例如平手各加 0.5)就改傳受控 scores 並在 onVote 裡自己加。
  • 計分板不洩題:計分板只列出 scores 裡有的模型,本回合兩位參賽者要等投票揭曉後才出現——投票前計分板不會透露這一對是誰;受控 scores 若預先列出所有模型,就由你決定要不要保留這層匿名。
  • 換題不用重掛:只要 prompt 或任一 response 變了就視為新回合,自動從頭打字並清掉上一票;請不要對元件用 key 重掛,那會連內部計分一起清空。
  • 供應商色塊provider 名稱經確定性 hash 得到色相(SSR 與客戶端一致),同一供應商永遠同色;也可用 hue 自訂。

可及性

  • 根容器 role="group" 並以 aria-labelledby 指向題目文字;兩張卡片未揭曉時背面 aria-hidden,揭曉後正面 aria-hidden,螢幕閱讀器只會讀到當前那一面
  • 四顆投票鍵與「下一題」都是原生 buttontype="button"),Tab/Enter/Space 可操作,focus-visible 有明顯焦點環;串流未完成時投票鍵 disabled,投票後被選的那顆以 aria-pressed 標示,焦點自動移到「下一題」,鍵盤使用者不會掉到 body
  • aria-live="polite" 區域會播報「回覆完成」與揭曉結果(例如「模型 A:Aurora-7,模型 B:Sage-Coder。A 較好」);計分板數字另有 sr-only 的實際數值,滾動的視覺數字為 aria-hidden
  • 使用者系統開啟「減少動態效果」時:不逐字打字、直接完整顯示並立即啟用投票;翻牌改為兩面淡入淡出,游標閃爍、勝方脈動、數字滾動與名次平移全部關閉

本頁目錄