ui: the chat comeback — side-based authorship, restored quotes, tucked whys

Blockquotes: the callout override had displaced Streamdown's own styled
blockquote component, so plain quotes rendered as bare unstyled text
since that commit. The pass-through branch now restores Streamdown's
verbatim classes (border-l-4, muted italic).

Explanations tucked for ALL questions (owner): the prediction's Why /
Hide-why toggle is universal — every quiz answered card sits its
explanation behind the same collapsed button. Same pattern, one law.

The thinking row's rhythm: my-1 -> my-3. The 4px margin hugged the
options on non-graded answered cards (owner report); 12px matches the
card's mt-2/mt-3 spacing grammar everywhere the row renders.

Side-based authorship (owner round, 2026-10-07): the document shape
blurred user into agent — three left-rail idioms (user text, user
thinking, quoted prose) collided once quotes rendered. User rows go
RIGHT: 'You' messages and practice thinking cards hug the right edge at
85% width with the rail mirrored (border-r), text still reading
left-to-right. In-card thinking (inside the answered card, the agent's
context) stays left. Transcript thinking rows arrive EXPANDED — the
thinking was clearly sent. Cmd+T (toggle-user-thinking, chat scope,
cheat-sheet row) sets every user-thinking row to one state via a module
store: explicit sets stomp individual toggles, late mounts honor the
last set (the state initializer reads the store, so static render
reflects it too).
This commit is contained in:
amosblomqvist
2026-10-08 02:29:05 +03:00
parent cdf9305eea
commit d5c4c3bf9f
10 changed files with 192 additions and 41 deletions
+9 -6
View File
@@ -241,7 +241,10 @@ describe("render smoke tests", () => {
const answer = buildAnswer(quizQD(), { values: ["Venus"] });
const html = renderToStaticMarkup(<AnsweredCard answer={answer} />);
assert.match(html, /incorrect/);
assert.match(html, /innermost/, "explanation renders post-answer");
// Tucked for ALL questions (owner, 2026-10-07): the explanation sits
// behind the Why toggle — never center stage.
assert.ok(html.includes("tucked-explanation"), "the Why toggle mounts");
assert.ok(!html.includes("innermost"), "explanation hidden until opened");
// Glyph-shaped SVG marks (owner preference) — pinned via data-mark.
assert.ok(html.includes('data-mark="check"') && html.includes('data-mark="x"'));
});
@@ -302,8 +305,8 @@ describe("render smoke tests", () => {
// The thinking sits ABOVE the verdict's explanation — the dialogue of
// thinking, not an afterthought.
assert.ok(
html.indexOf("your-thinking") < html.indexOf("innermost"),
"thinking before the explanation",
html.indexOf("your-thinking") < html.indexOf("tucked-explanation"),
"thinking before the explanation toggle",
);
});
@@ -574,11 +577,11 @@ describe("predict vs check rendering", () => {
assert.ok(html.includes("tucked-explanation"), "it sits behind the toggle");
});
it("a check answer keeps the inline explanation", () => {
it("a check answer tucks the explanation like every question (owner, 2026-10-07)", () => {
const answer = buildAnswer(quizQD(), { values: ["Venus"] });
const html = renderToStaticMarkup(<AnsweredCard answer={answer} />);
assert.ok(html.includes("Why:"), "checks explain inline (current behavior)");
assert.ok(!html.includes("tucked-explanation"));
assert.ok(html.includes("tucked-explanation"), "the prediction tuck is now universal");
assert.ok(!html.includes("Why:"), "no inline explanation — behind the toggle");
});
it("the reading-thinking channel is LIFTED above the card as its own user-side block", () => {