From 0d41179b305a0ecfa88a2dcf08f632bb932a1d18 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 12 Sep 2026 20:18:05 +0200 Subject: [PATCH] feat: the topic behind the right answer, as a chip where the eye already is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reading links existed and sat at the foot of the whole explanation block, under everything else, as a headed list. A learner who has just been shown the right answer is looking at the right answer — so the link now sits directly under it, as a chip, the way a reference site does it. Outside the option's button rather than inside it: a link nested in a button is neither, and the click would have toggled the explanation instead of opening the article. Also a real bug beside it. Key points were gated on `option_explanations` being non-empty — the wrong field entirely — so a question with key points and no per-option reasoning showed none of them. It happens not to bite today because exactly one question in the bank has either, which is its own finding. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN --- .../src/components/QuestionReadingLinks.jsx | 21 ++++++++++++++++++- frontend/src/pages/QuizPage.jsx | 16 ++++++++++++-- frontend/src/pages/QuizPlayer.css | 18 ++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/QuestionReadingLinks.jsx b/frontend/src/components/QuestionReadingLinks.jsx index 775721b..1ae7ec9 100644 --- a/frontend/src/components/QuestionReadingLinks.jsx +++ b/frontend/src/components/QuestionReadingLinks.jsx @@ -9,7 +9,7 @@ import api from '../api/client' * the article's own sections for it — this used to ask for every linked * article in full, prose and all, to print a title and a heading. */ -export default function QuestionReadingLinks({ questionId }) { +export default function QuestionReadingLinks({ questionId, variant = 'list' }) { const [links, setLinks] = useState(null) useEffect(() => { if (!questionId) { setLinks([]); return } @@ -17,6 +17,25 @@ export default function QuestionReadingLinks({ questionId }) { .then(res => setLinks(res.data)).catch(() => setLinks([])) }, [questionId]) if (!links || links.length === 0) return null + + // Under the right answer in the player, the same links are a row of chips — + // one control, plainly a door to an article — rather than a headed list, + // which at that point in the page reads as another section of explanation. + if (variant === 'chips') { + return ( +
+ {links.map(link => ( + + + {link.section_title || link.title} + + ))} +
+ ) + } + return (
Topic reading diff --git a/frontend/src/pages/QuizPage.jsx b/frontend/src/pages/QuizPage.jsx index 5db20f8..dcdcfce 100644 --- a/frontend/src/pages/QuizPage.jsx +++ b/frontend/src/pages/QuizPage.jsx @@ -2104,6 +2104,14 @@ const timerStarted = timeLeft !== null )} + {/* The topic behind the right answer, offered where the + learner is already looking. Outside the option's + button rather than inside it, because a link nested + in a button is neither: the click would toggle the + explanation instead of opening the article. */} + {showCorrect && ( + + )} {/* Outside the option, so ruling one out is never mistaken for choosing it. Gone once the question is marked — there is nothing left to narrow down. */} @@ -2183,7 +2191,12 @@ const timerStarted = timeLeft !== null )}
)} - {current.option_explanations && Object.keys(current.option_explanations).length > 0 && ( + {/* Gated on key points, not on option explanations. It asked + whether the *other* field was populated, so a question + with key points and no per-option reasoning showed none of + them — which is most of the bank, once anybody writes + them. */} + {(current.key_points || []).length > 0 && (
Key points
    @@ -2198,7 +2211,6 @@ const timerStarted = timeLeft !== null
)} - {current.question_type === 'fill_blank' && (
Correct Answer: {current.correct_answer} diff --git a/frontend/src/pages/QuizPlayer.css b/frontend/src/pages/QuizPlayer.css index 8a1c559..7dde923 100644 --- a/frontend/src/pages/QuizPlayer.css +++ b/frontend/src/pages/QuizPlayer.css @@ -932,3 +932,21 @@ body:has(.quiz-player.is-exam-chrome) .site-footer { display: none; } /* The clocks, at the foot of the rail with everything else about the session rather than about the question in front of you. */ .quiz-rail-foot { display: flex; align-items: center; } + +/* The topic behind the right answer, in the player. A chip rather than a + headed list: at that point on the page a heading reads as another section of + explanation, and what this is is a door. */ +.question-reading-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 4px 42px; } +.question-reading-chip { + display: inline-flex; align-items: center; gap: 8px; + padding: 7px 13px; border: 1px solid var(--border); border-radius: 8px; + background: var(--card-bg); color: var(--text); text-decoration: none; + font-size: 0.85rem; font-weight: 600; +} +.question-reading-chip:hover { border-color: var(--primary); color: var(--primary); } +.question-reading-chip span { color: var(--text-muted); font-size: 0.9rem; } +.question-reading-chip:hover span { color: var(--primary); } + +@media (max-width: 640px) { + .question-reading-chips { margin-left: 0; } +}