import { RefreshCwIcon } from "lucide-react"; import type { ReactNode } from "react"; import { threadSyncLabel, type ThreadSyncPhase } from "../../threadSync"; import { cn } from "~/lib/utils"; import { NoticeText } from "./ComposerBannerStack"; /** A line of thread state for the end of the timeline. */ export interface ThreadNowItem { readonly id: string; readonly icon: ReactNode; readonly title: ReactNode; readonly description?: ReactNode; readonly actions?: ReactNode; } /** Matches ThreadStatusLine, so every line at the end of the timeline reads as one family. */ const lineClassName = cn( "mx-auto flex w-full min-w-0 max-w-(--chat-content-max-width) items-center gap-1.5 px-1 pb-2 text-xs text-muted-foreground [&_svg]:size-3.5 [&_svg]:shrink-0", // A details button must not make its line taller than the others. "[&_[data-slot=popover-trigger]]:-my-1.5", // Inline actions keep their text size but get a finger-sized hit area on touch. "pointer-coarse:[&_[data-slot=inline-button]]:relative pointer-coarse:[&_[data-slot=inline-button]]:after:absolute pointer-coarse:[&_[data-slot=inline-button]]:after:-inset-x-1.5 pointer-coarse:[&_[data-slot=inline-button]]:after:-inset-y-3.5", "transition-opacity duration-150 ease-out starting:opacity-0 motion-reduce:transition-none", ); /** One line of thread state at the end of the timeline: icon, title, detail, then actions. */ function ThreadNowLine({ item, announce = false, }: { readonly item: ThreadNowItem; /** Announces the line when it appears; only for something that needs the user. */ readonly announce?: boolean; }) { return (
{item.icon} {/* Title and detail read as one line that gives way before the actions, so Stop stays reachable; when it is cut, its details button shows all of it. */} {item.title} {item.description ? ( <> {" "} {item.description} ) : null} {item.actions ? ( {item.actions} ) : null}
); } function ThreadSyncLine({ phase }: { readonly phase: ThreadSyncPhase }) { return (
{threadSyncLabel(phase)}
); } /** * What the thread is doing now, at the end of the timeline: what needs you, background work, * the sync status while messages load, then the goal. */ export function ThreadNow({ attention, work, syncPhase, goal, }: { /** Something that needs you elsewhere, such as a subagent's question. */ readonly attention: ThreadNowItem | null; readonly work: ThreadNowItem | null; readonly syncPhase: ThreadSyncPhase | null; readonly goal: ThreadNowItem | null; }): ReactNode { return ( <> {attention ? : null} {work ? : null} {syncPhase ? : null} {goal ? : null} ); }