Stop generation One control slot holding two states. While generation runs the send button is a stop button, the partial text stays on screen labelled as stopped, and the tokens already written are still billed. npx shadcn@latest add button textarea npm i lucide-react Tokens this needs: --background, --card, --card-foreground, --accent, --accent-foreground, --muted, --muted-foreground, --border, --chart-1 ──────────────────────────────────────────────────────────────────────── // StopGeneration.tsx ──────────────────────────────────────────────────────────────────────── import { useEffect } from "react"; import { ArrowUp, Square } from "lucide-react"; import { Button } from "@/components/ui/button"; /** * One slot, two states. While a response streams the send button is the * stop button, because that is where the pointer already is. Esc stops too, * for the hand that is still on the keyboard. */ export function SendOrStop({ streaming, canSend, onSend, onStop }: { streaming: boolean; canSend: boolean; onSend: () => void; onStop: () => void; }) { useEffect(() => { if (!streaming) return; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onStop(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [streaming, onStop]); return streaming ? ( ) : ( ); } /** * The partial output stays. The first two paragraphs were often fine, and * truncating throws away the part that worked. It stays marked, because a * partial answer that looks complete is worse than none, and the mark is in * the text's own container so it travels with a copy. */ export function StoppedTurn({ paragraphs, expected, tokens }: { /** What was written before the stop. */ paragraphs: string[]; /** The model's own estimate of length, so the mark can say how far it got. */ expected: number; /** Already billed. Stopping does not refund the part that was written. */ tokens: { used: number; budget: number }; }) { return (
{p}
))}Stopped. {paragraphs.length} of about {expected} paragraphs.
{P1}
{p2} {streaming && }
> )}