"use client"; import { useCallback, useRef, useState } from "react"; import { IconArrowUp, IconFileTextSpark, IconPaperclip, IconPlayerPauseFilled, IconPlugConnected, IconPlugConnectedX, } from "@tabler/icons-react"; import { GridSpinner } from "@/components/grid-spinner"; import { KeyboardShortcutTooltip } from "@/components/keyboard-shortcut-tooltip"; import { useAppStore } from "@/components/state-provider"; import { Button } from "@kandev/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { getShortcut } from "@/lib/keyboard/shortcut-overrides"; import { SHORTCUTS } from "@/lib/keyboard/constants"; import { formatShortcut } from "@/lib/keyboard/utils"; import { cn } from "@/lib/utils"; type SubmitButtonProps = { isAgentBusy: boolean; hasContent: boolean; isDisabled: boolean; submitDisabledReason?: string; isSending: boolean; planModeEnabled: boolean; onCancel: () => void | Promise; onSubmit: () => void; submitShortcut: (typeof SHORTCUTS)[keyof typeof SHORTCUTS]; }; type SendSubmitButtonProps = Pick< SubmitButtonProps, "isDisabled" | "isSending" | "planModeEnabled" | "onSubmit" | "submitShortcut" > & { tooltipDescription?: string; }; function submitTooltipDescription( isAgentBusy: boolean, planModeEnabled: boolean, submitDisabledReason?: string, ) { if (submitDisabledReason) return submitDisabledReason; if (isAgentBusy) return "Queue message"; if (planModeEnabled) return "Request plan changes"; return undefined; } function SendSubmitButton({ isDisabled, isSending, planModeEnabled, onSubmit, submitShortcut, tooltipDescription, }: SendSubmitButtonProps) { return ( ); } export function SubmitButton({ isAgentBusy, hasContent, isDisabled, submitDisabledReason, isSending, planModeEnabled, onCancel, onSubmit, submitShortcut, }: SubmitButtonProps) { const showSendButton = !isAgentBusy || hasContent; const [isCancelling, setIsCancelling] = useState(false); const tooltipDescription = submitTooltipDescription( isAgentBusy, planModeEnabled, submitDisabledReason, ); const cancellingRef = useRef(false); const handleCancelClick = useCallback(async () => { if (cancellingRef.current) return; cancellingRef.current = true; setIsCancelling(true); try { await onCancel(); } catch (error) { console.error("Failed to cancel agent turn:", error); } finally { cancellingRef.current = false; setIsCancelling(false); } }, [onCancel]); return (
{isAgentBusy && ( {isCancelling ? "Cancelling..." : "Cancel agent"} )} {showSendButton && ( )}
); } export function PlanToggleButton({ planModeEnabled, planModeAvailable, onPlanModeChange, }: { planModeEnabled: boolean; planModeAvailable: boolean; onPlanModeChange: (enabled: boolean) => void; }) { const keyboardShortcuts = useAppStore((s) => s.userSettings.keyboardShortcuts); const planModeShortcutLabel = formatShortcut(getShortcut("TOGGLE_PLAN_MODE", keyboardShortcuts)); const tooltip = planModeAvailable ? `Toggle plan mode (${planModeShortcutLabel}) — Agent collaborates on the plan without implementing changes` : `Toggle plan layout (${planModeShortcutLabel}) — View and edit the plan (agent cannot read/write it without MCP)`; return ( {tooltip} ); } export function McpIndicator({ mcpServers }: { mcpServers: string[] }) { const hasMcp = mcpServers.length > 0; const tooltipText = hasMcp ? `MCP Servers: ${mcpServers.join(", ")}` : "Agent does not support MCP"; const Icon = hasMcp ? IconPlugConnected : IconPlugConnectedX; return (
{tooltipText}
); } export function AttachFilesButton({ onClick }: { onClick: () => void }) { return ( Attach files ); }