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
+41
View File
@@ -14,6 +14,7 @@ import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { renderToStaticMarkup } from "react-dom/server";
import { MessageResponse } from "../components/ai-elements/message.tsx";
import { ThinkingBlock, setAllUserThinkingOpen } from "@/components/thinking-block";
import { MermaidOverlay, SessionMermaidContext, chartSize } from "../components/session-overlay.tsx";
import { PlanPopup } from "../components/plan-block.tsx";
import { MarkdownCode, codeSource } from "../components/md-blocks.tsx";
@@ -229,3 +230,43 @@ describe("parseAlertMarker (pure)", () => {
assert.equal(parseAlertMarker(em), null);
});
});
describe("md-blocks — the restored plain blockquote (owner round, 2026-10-07)", () => {
it("a plain quote carries Streamdown's own styling (the override displaced it)", () => {
const html = renderToStaticMarkup(
<MessageResponse mode="static">{"Before.\n\n> An ordinary quote.\n\nAfter."}</MessageResponse>,
);
assert.ok(html.includes("border-l-4"), "the left border is back");
assert.ok(html.includes("text-muted-foreground italic"), "muted italic — the quote register");
assert.ok(!html.includes("md-callout"), "a plain quote is not a callout box");
});
});
describe("ThinkingBlock — side-based authorship + ⌘T sync (owner round, 2026-10-07)", () => {
it("the default row stays left with the my-3 rhythm", () => {
const html = renderToStaticMarkup(<ThinkingBlock text="left side" />);
assert.ok(html.includes("border-l-2"), "left rail");
assert.ok(html.includes("my-3"), "the 12px rhythm (was my-1)");
});
it("mirrored rows hug the right edge, rail flipped, arriving open", () => {
const html = renderToStaticMarkup(<ThinkingBlock text="right side" mirror defaultOpen />);
assert.ok(html.includes('data-side="right"'));
assert.ok(html.includes("ml-auto"), "pushed to the right edge");
assert.ok(html.includes("border-r-2") && !html.includes("border-l-2"), "rail on the right");
assert.match(html, /aria-expanded="true"/);
assert.ok(html.includes("right side"), "text visible on arrival");
});
it("⌘T sets every row — explicit sets stomp individual state, late mounts honor the last set", () => {
setAllUserThinkingOpen(false);
const collapsed = renderToStaticMarkup(<ThinkingBlock text="synced" defaultOpen />);
assert.match(collapsed, /aria-expanded="false"/);
assert.ok(!collapsed.includes(">synced<"), "the open default was stomped");
setAllUserThinkingOpen(true);
const reopened = renderToStaticMarkup(<ThinkingBlock text="synced" />);
assert.match(reopened, /aria-expanded="true"/);
// restore the null default for other tests in this file
setAllUserThinkingOpen(true);
});
});