Files
learnharness/components/thinking-block.tsx
T
amosblomqvist 3e402d3c35 ui: round 5 — AGENT'S THINKING buys the 3ch rail; the standard chord; dashed model names
- THE 3CH RAIL: 'AGENT’S THINKING' (16ch, flush) − 'YOUR THINKING' (13ch)
  = 3ch of mono — the rail widens to 3ch (border + padding), keeping the
  chevrons aligned while giving the quote room to breathe (the 1ch rail
  read cramped). YOU row follows.
- THE STANDARD CHORD everywhere: Enter submits, Shift+Enter is a
  newline, ⌘/Ctrl+Enter works too — the teaching thinking field drops
  the ⌘-only Gmail convention; the hint reads '↵ submits · ⇧↵ newline'
  in both docks, shown in text mode too (it Enter-submits as well).
- The transcript-end optimistic placeholder joins the register: small
  mono caps THINKING… (was text-sm sentence case).
- Model names keep their dashes: prettyModel renders 'glm-5.3-flash'
  exactly as written (no auto-caps, no dash→space join).
2026-10-05 04:55:59 +03:00

65 lines
2.6 KiB
TypeScript

"use client";
import { useState } from "react";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "@/lib/utils";
/** The unified user-thinking row (owner round, 2026-10-05): ONE visual
* language for every surface where the learner's own thinking renders —
* practice thinking traces, the reading channel lifted above the answered
* card, the quiz answer's thinking inside its container. It arrives
* COLLAPSED as a trigger row in the exact register of the agent's
* reasoning rows (muted label + chevron + hover affordance); expanding
* opens the quote block — rail, 50% opacity, italics. The italics + the
* half-opacity text are the inner-voice signature; the rail marks it as
* learner material. No shell, no border: document material, full width.
*
* The glance-level law (owner): user thinking = rail + 50% italic;
* agent thinking = no rail. */
export function ThinkingBlock({
label = "YOUR THINKING",
text,
testId,
dataCommit,
}: {
label?: string;
text: string;
testId?: string;
/** The practice marker's register flag (commit vs nudge). */
dataCommit?: boolean;
}) {
const [open, setOpen] = useState(false); // arrives collapsed, always
return (
/* The rail GROUPS and the rail IS THE MISSING CHARACTER (owner
rounds, 2026-10-05): the heading sits INSIDE the rail, and the
rail's full width (border + padding) is exactly THREE CHARACTERS of
the mono heading font — 'YOUR THINKING' is three chars shorter
than 'AGENT'S THINKING', so with the user row offset by the rail
and the agent row flush, the chevrons line up vertically. Three
(not one, owner round 5): the wider gap reads better than the
cramped single-char rail. The heading
register is the card eyebrows' small mono caps (text-xs = the
11px rung). */
<div
data-testid={testId}
data-commit={dataCommit === undefined ? undefined : dataCommit ? "true" : "false"}
className="lh-card-enter my-1 w-full border-l-2 border-muted-foreground/30 pl-[calc(3ch-2px)] font-ui text-xs"
>
<button
type="button"
data-testid={testId ? `${testId}-trigger` : undefined}
onClick={() => setOpen((v) => !v)}
aria-expanded={open}
className="flex w-full items-center gap-2 font-medium uppercase tracking-wide text-muted-foreground transition-colors hover:text-foreground"
>
{label}
<ChevronDownIcon className={cn("size-4 shrink-0 transition-transform", open ? "rotate-180" : "rotate-0")} />
</button>
{open && (
<span className="lh-prose mt-1.5 block whitespace-pre-wrap text-[15px] italic text-foreground/50">{text}</span>
)}
</div>
);
}