import { type ComponentType, type ReactElement, type ReactNode, use } from "react"; import { cn } from "../../lib/utils"; import { Button } from "../../ui/button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip"; import { SMALL_CODE_FONT, SYSTEM_HEIGHT } from "./constants"; import { useToolToggle } from "./tool-context"; import { ExecActionContext } from "./system-context"; import { type ExecAction, type ExecActionPayload, execStatusSlot, LABEL_INSET, OUTPUT_LINE, OUTPUT_PAD, PREVIEW_HEIGHT, TERMINAL_JOIN_PAD_Y, TERMINAL_PAD_X, TERMINAL_PAD_Y, } from "./system-layout"; import { IconCircleCheck, IconCircleX, IconCopy, IconExpand, IconMessageForward, IconStop, } from "../../icons"; type ExecActionItem = { action: ExecAction; label: string; tip: string; destructive?: boolean; icon: ComponentType<{ className?: string }>; }; const ACTIONS: Record = { view: { action: "view", label: "View output", tip: "View full output", icon: IconExpand, }, copy: { action: "copy", label: "Copy output", tip: "Copy", icon: IconCopy }, send: { action: "send", label: "Send to Ace", tip: "Send to Ace", icon: IconMessageForward, }, abort: { action: "abort", label: "Stop command", tip: "Stop", destructive: true, icon: IconStop, }, }; function SystemLine( { children, className, height = SYSTEM_HEIGHT, align = "center" }: { align?: "baseline" | "center"; children: ReactNode; className?: string; height?: number; }, ) { return (
{children}
); } function Text({ children, className }: { children: ReactNode; className?: string }) { return {children}; } function Link( { children, href, className }: { children: ReactNode; href?: string; className?: string }, ) { let classes = cn("shrink-0", className); return href ? {children} : {children}; } function ExecTooltip({ label, children }: { label: string; children: ReactElement }) { return ( {label} ); } function ExecStatus({ payload }: { payload: ExecActionPayload }) { let { id, input, cwd, stdout, stderr, exitCode, aborted } = payload; let onAction = use(ExecActionContext); let pending = exitCode === undefined; let status = aborted ? "aborted" : pending ? "running" : exitCode === 0 ? "passed" : "failed"; let slot = execStatusSlot({ aborted, exitCode }); let StateIcon = slot === "passed" ? IconCircleCheck : slot === "abort" ? IconStop : IconCircleX; return slot === "abort" ? onAction ? ( ) : null : ( ); } function ExecControls({ actions, payload }: { actions: ExecAction[]; payload: ExecActionPayload }) { let { id, input, cwd, stdout, stderr, exitCode, aborted } = payload; let onAction = use(ExecActionContext); if (!onAction && execStatusSlot(payload) === "abort") return null; let buttons = onAction && actions.length > 0 ? ( {actions.map(name => { let item = ACTIONS[name]; let Icon = item.icon; return ( ); })} ) : null; return ( {buttons} ); } function ExecView( { id, input, exitCode, aborted, waiting, expanded, preview, shown, hidden, height, lines, actions, cwd, stdout, stderr, }: { id: string; input: string; exitCode?: number | string; aborted?: boolean; pending: boolean; waiting: boolean; expanded: boolean; preview: string; shown: string[]; hidden: boolean; height: number; lines: number; actions: ExecAction[]; cwd?: string; stdout: string; stderr: string; }, ) { let toggle = useToolToggle(); let canExpand = !aborted && shown.length > 0 && toggle !== null; let command = ( {input} ); let controls = ( ); let lineStyle = { blockSize: SYSTEM_HEIGHT, paddingBlockEnd: TERMINAL_JOIN_PAD_Y, paddingBlockStart: TERMINAL_PAD_Y, paddingInline: TERMINAL_PAD_X, }; let line = canExpand ? (
{controls}
) : (
{command} {controls}
); return (
{line} {preview && (
{preview}
)} {expanded && shown.length > 0 && (
							{shown.join("\n")}
							{hidden ? "\n..." : ""}
						
)}
); } export { ExecView, Link, SystemLine, Text };