"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 (
{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}
>
)}
)}
);
}