ui: the unified ThinkingBlock — one visual language for the learner's thinking

Owner round (2026-10-05): the same act (a human thinking in text) had two
visual languages — the quiz cards' quote block vs the practice container
with a collapsing chevron and raw pre-wrap text that read oversized.

- components/thinking-block.tsx: the quiz quote-block recipe verbatim
  (rail + uppercase label + lh-prose at foreground/90) carried by a
  faint user-side shell — ml-auto, max-w-[95%] mirroring the user
  bubble's geometry, px-4/py-3 rhythm. Never collapsible. Rails reuse
  the existing vocabulary: primary = committed register, muted =
  working register.
- ThinkingCard becomes the practice adapter: 'Your solution' (primary)
  / 'Your thinking' (muted) — one component, two registers.
- The reading channel LIFTS out of the answered card: chronologically
  prior to the question, it renders above it as its own user-side
  block under the plain 'Your thinking' label; the in-card inset and
  the 'while reading' phrasing are gone.
- lh-card-enter (globals.css): the one-shot 150ms fade+rise mount,
  the app's motion register; honors reduced-motion.
This commit is contained in:
amosblomqvist
2026-10-05 02:25:13 +03:00
parent 09480979c4
commit d19df86b13
5 changed files with 110 additions and 34 deletions
+12 -3
View File
@@ -572,15 +572,24 @@ describe("predict vs check rendering", () => {
assert.ok(!html.includes("tucked-explanation"));
});
it("the reading-thinking channel renders as its own block; legacy `note` still renders", () => {
it("the reading-thinking channel is LIFTED above the card as its own user-side block", () => {
const withReading = buildAnswer(quizQD(), {
values: ["Mercury"],
thinking: "answering thought",
readingThinking: "was unsure mid-paragraph",
});
const html = renderToStaticMarkup(<AnsweredCard answer={withReading} />);
assert.ok(html.includes("Your thinking while reading"), "the reading channel is labeled distinctly");
assert.ok(html.includes("answering thought"));
// The unified ThinkingBlock (owner round, 2026-10-05): the reading
// thoughts render BEFORE the card, user-side (right shell), under the
// plain "Your thinking" label — no longer an inset inside the answer.
const blockAt = html.indexOf("reading-thinking-block");
const cardAt = html.indexOf("answered-card");
assert.ok(blockAt !== -1, "the lifted block renders");
assert.ok(blockAt < cardAt, "it sits ABOVE the answered card");
assert.ok(html.includes("Your thinking"), "the plain label");
assert.ok(!html.includes("Your thinking while reading"), "the old verbose label is gone");
assert.ok(!html.includes("your-reading-thinking"), "the in-card inset is gone");
assert.ok(html.includes("answering thought"), "the answer thinking stays inside the card");
assert.ok(html.includes("was unsure mid-paragraph"));
const legacy = { ...buildAnswer(quizQD(), { values: ["Mercury"] }), note: "a 2026-09 answer" };
const legacyHtml = renderToStaticMarkup(<AnsweredCard answer={legacy} />);