function ScoreBar({ label, value }) { const safeValue = Math.max(0, Math.min(30, Number(value) || 0)); const pct = safeValue / 30 * 100; return (
{label}{safeValue}/30
); } function RoundStatus({ results = [] }) { const cells = [0,1,2].map((i) => results[i] ?? null); return (
{t("rounds")}
{cells.map((result, i) => { const mark = result === "player" ? "✓" : result === "ai" ? "×" : result === "parda" ? "=" : "·"; const cls = result === "player" ? "round-win" : result === "ai" ? "round-loss" : result === "parda" ? "round-draw" : "round-pending"; return
{i + 1} {mark}
; })}
); } function recommendedCardIdFor(match, playable) { const hand = match.hands?.player || []; if (!hand.length) return null; const current = match.currentTrick || []; const aiPlayed = current.find((p) => p.player === "ai")?.card; if (aiPlayed) { const winners = hand.filter((c) => playable.has(c.id) && trucoPower(c) > trucoPower(aiPlayed)); if (winners.length) return winners.slice().sort((a,b) => trucoPower(a) - trucoPower(b))[0].id; } const playableHand = hand.filter((c) => playable.has(c.id)); if (!playableHand.length) return null; const best = playableHand.slice().sort((a,b) => trucoPower(b) - trucoPower(a))[0]; return best?.id || null; } function TableView({ match, rival, sound, onAction, onHelp, onExit, onToggleSound }) { const wait = waitingOn(match); const legal = wait === "player" ? legalActions(match, "player") : []; const tip = coachTip(match); const calls = legal.filter((a) => a.type === "CALL"); const responds = legal.filter((a) => a.type === "RESPOND"); const canMazo = legal.some((a) => a.type === "MAZO"); const canAck = legal.some((a) => a.type === "ACK"); const playable = new Set(legal.filter((a) => a.type === "PLAY").map((a) => a.cardId)); const recommendedCardId = recommendedCardIdFor(match, playable); const playerTrick = match.currentTrick.find((p) => p.player === "player"); const aiTrick = match.currentTrick.find((p) => p.player === "ai"); return (
{rival.name} {trDifficulty(rival.difficultyLabel)} · {trRivalField(rival.id, "title", rival.title)}
{match.hands.ai.map((card, i) => ( ))}
{t("rival")} {aiTrick ? :
}
VS
{t("you")} {playerTrick ? :
}
{t("tricks")}: {match.bazas.player} – {match.bazas.ai} {t("handValue")} {match.trucoAccepted ? [1,2,3,4][match.trucoLevel] : 1} {match.mano === "player" ? t("youreHand") : t("rivalHand")}
{wait === "ai" ?

{t("thinking")}

: null}
{tip.title}
{tip.body}
{tip.hint}
{match.hands.player.map((card) => ( onAction({ type: "PLAY", cardId: card.id }) : undefined}/> ))}
{canAck ? : null} {responds.map((a) => ( ))} {calls.map((a) => )} {canMazo ? : null}
{t("handEnvido")}: {envidoPoints(match.hands.player)}
{match.toast && match.phase === "toast" ? (

{trToast(match.toast.title)}

{trToastBody(match.toast.body)}

) : null}
); }