Message turn Two turns, deliberately asymmetric. The user turn is a compact tinted container acting as a landmark; the assistant turn runs at full measure with no container and no avatar, because it is the thing being read. npx shadcn@latest add button Tokens this needs: --foreground, --card, --muted, --border, --chart-1 ──────────────────────────────────────────────────────────────────────── // MessageTurn.tsx ──────────────────────────────────────────────────────────────────────── import { useState } from "react"; import { Button } from "@/components/ui/button"; export type Turn = { role: "user" | "assistant"; text: string }; /** A user turn past this many words is a paste, and a paste collapses. Two * thousand pasted words otherwise make the landmark the biggest thing on * the page. */ const COLLAPSE_OVER = 120; /** * The transcript is a list and every turn is an item with a speaker in the * markup. Tint and alignment carry none of this to a screen reader, and * alignment inverts under right-to-left text. */ export function Transcript({ turns }: { turns: Turn[] }) { return (
    {turns.map((t, i) => (
  1. ))}
); } export function MessageTurn({ role, text }: Turn) { const [open, setOpen] = useState(false); // The assistant turn is the content, so nothing decorates it: no container, // no avatar, no timestamp, the full measure. In practice this is rendered // markdown; the point is what wraps it, which is nothing. if (role === "assistant") { return (
Assistant: {text}
); } const words = text.trim().split(/\s+/).length; const collapsed = words > COLLAPSE_OVER && !open; // The user turn is the label on the answer and the landmark people scan // for, so it is compact, tinted and narrow. return (
You: {text}
{collapsed && ( )}
); } ──────────────────────────────────────────────────────────────────────── // demo.tsx ──────────────────────────────────────────────────────────────────────── import { Transcript } from "./MessageTurn"; const PASTE = "Also check this against what you said. 12. BREAK CLAUSE 12.1 The Tenant may determine this Lease on the Break Date by giving the Landlord not less than six months' prior written notice. 12.2 The Lease will determine on the Break Date only if the Tenant has paid all Annual Rent due up to and including the Break Date, and gives vacant possession of the Premises on the Break Date. 12.3 If the Lease determines under this clause the Landlord shall refund to the Tenant, within 14 days, any Annual Rent paid in advance for the period after the Break Date. 13. RENT REVIEW 13.1 The Annual Rent shall be reviewed on the Review Date to the greater of the Annual Rent payable immediately before the Review Date and the Open Market Rent. 13.2 The Open Market Rent shall be agreed between the parties or, in default of agreement within two months before the Review Date, determined by an independent surveyor acting as an expert. 14. DEPOSIT 14.1 The Tenant shall on the date of this Lease pay to the Landlord the Deposit, being a sum equal to three months' Annual Rent, to be held in a separate interest-bearing account. 14.2 The Landlord may draw on the Deposit to make good any breach of the Tenant's covenants. 14.3 The Landlord shall return the balance of the Deposit to the Tenant within 30 days after the end of the Term, less any sums properly deducted under clause 14.2."; /** Three turns: a question, the answer at full measure, and a paste that collapses. */ export default function Demo() { return (
); }