"use client"; import { GlyphMark } from "@/components/ui/glyph-mark"; import { cn } from "@/lib/utils"; import { MessageResponse } from "@/components/ai-elements/message"; import type { QuestionAnswerData } from "@/src/core"; /** * Static rendering of an answered question (tool part in `output-available`). * * Quiz answers are GRADED: ✓/✗ marks, the correct answers, and the * explanation. Ask answers are NOT graded — a plain record of what was said. * (The v1 bug this pins: ask answers must never get quiz grading UI.) * Explain answers are graded by the MODEL in its next reply, not by the * client — the card reveals his answer, the committed correct answer, and * the explanation, with NO verdict chips (owner ruling 2026-09-27). */ export function AnsweredCard({ answer }: { answer: QuestionAnswerData }) { if (answer.dismissed) { return (
{answer.questionText} — skipped
); } const isQuiz = answer.kind === "quiz"; const isExplain = answer.kind === "explain"; const selectedIdx = new Set(answer.answers.map((a) => a.index)); const correctIdx = new Set(answer.correctIndices ?? []); return (
{isQuiz ? "Quiz" : isExplain ? "Explain" : "Question"} {isQuiz && (answer.dontKnow ? ( didn't know ) : answer.correct ? ( correct ) : ( incorrect ))}
{answer.questionText}
{((answer.options?.length ?? 0) > 0 || answer.other) && (
{answer.options?.map((opt) => { const sel = selectedIdx.has(opt.index); const key = isQuiz && correctIdx.has(opt.index); return (
{isQuiz && ( // Zapf-Dingbats-derived SVG outlines — the exact LaTeX \ding{51}/ // \ding{55} hand-drawn shapes (owner, round 6); see ui/glyph-mark. {/* Owner round 8: a touch smaller than the text (size-4→size-3.5, ≈14px vs the 16px row) while centered. */} {(key || sel) && } )} {opt.label}
); })} {/* The learner's own typed option is an OPTION — same container, same selected styling as its siblings, not a loose quote floating outside the list. Quotes keep the learner's words. */} {answer.other && (
“{answer.other}”
)}
)} {answer.text &&
“{answer.text}”
} {answer.dontKnow && (isQuiz || isExplain) && (
Answered “I don't know”.
)} {answer.note && (
Your thinking {answer.note}
)} {/* px-3→removed: card p-4 + box px-3 double-inset sat the "Why" label deeper than the option box edges (owner, round 3). Sides flush with everything else; py-2 stays for vertical air. */} {isQuiz && answer.explanation && (
Why: {/* The markdown wrapper must go truly inline — a block

after the "Why:" label created the oversized leading gap (owner's fix list). */} {answer.explanation}

)} {/* The explain reveal (owner ruling 2026-09-27): the committed answer and its explanation, shown on submit — no verdict, the model's next reply IS the grading. */} {isExplain && (answer.committed || answer.explanation) && (
{answer.committed && ( <> The answer: {answer.committed} )} {answer.committed && answer.explanation &&
} {answer.explanation && ( <> Why: {answer.explanation} )}
)}
); }