import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Button, ShortcutBadge } from "@tutti-os/ui-system"; import { getOptionalAgentHostApi, useOptionalAgentHostApi } from "../../../agentActivityHost"; import { approvalOptionDisplayLabel, approvalOptionVisualPresentation } from "../approvalOptionPresentation"; import type { AgentConversationPromptVM } from "../contracts/agentConversationVM"; import { PLAN_IMPLEMENTATION_ACTION_FEEDBACK, PLAN_IMPLEMENTATION_ACTION_IMPLEMENT, PLAN_IMPLEMENTATION_ACTION_SKIP } from "../planImplementationPresentation"; import type { AgentInteractivePromptSurfaceProps } from "./AgentInteractivePromptSurface"; import { approvalFeedbackOptionId, approvalOptionShortcutLabel, CommandTextWithTooltip, formatApprovalToolPresentation, InteractiveOptionSpinner, interactiveOptionLabel, interactivePromptCardClassName, interactivePromptClassName, isDenyApprovalOptionToken, isDarwinPlatform, isEditableKeyboardTarget, isEnterLikeKey, PromptDetailValue, SendFilledIcon, stripPromptTitlePunctuation } from "./interactivePromptPresentation"; import styles from "../../../agent-gui/agentGuiNode/AgentGUIConversation.styles"; export function ApprovalPromptSurface({ prompt, embedded = false, edgeGlow = false, keyboardShortcuts = true, isSubmitting, isInteractionDisabled = false, onSubmit, labels }: AgentInteractivePromptSurfaceProps & { prompt: Extract; embedded?: boolean; }) { "use memo"; const promptToolPresentation = useMemo( () => formatApprovalToolPresentation(prompt, labels), [labels, prompt] ); const [submittingOptionId, setSubmittingOptionId] = useState( null ); const [pendingFeedbackOptionId, setPendingFeedbackOptionId] = useState< string | null >(null); const [feedback, setFeedback] = useState(""); const feedbackTextareaRef = useRef(null); const agentHostApi = useOptionalAgentHostApi() ?? getOptionalAgentHostApi(); const isDarwin = isDarwinPlatform(agentHostApi?.meta?.platform); const controlsDisabled = isSubmitting || isInteractionDisabled; const feedbackOptionId = useMemo( () => approvalFeedbackOptionId(prompt.options), [prompt.options] ); const feedbackValue = feedback.trim(); const approveOptionId = useMemo( () => prompt.options.find( (option) => option.id !== feedbackOptionId && !isDenyApprovalOptionToken(option.id) && !isDenyApprovalOptionToken(option.kind) )?.id ?? null, [feedbackOptionId, prompt.options] ); const denyOptionId = useMemo( () => prompt.options.find( (option) => option.id !== feedbackOptionId && (isDenyApprovalOptionToken(option.id) || isDenyApprovalOptionToken(option.kind)) )?.id ?? null, [feedbackOptionId, prompt.options] ); useEffect(() => { setSubmittingOptionId(null); setPendingFeedbackOptionId(null); setFeedback(""); }, [prompt.requestId]); useEffect(() => { if (!isSubmitting) { setSubmittingOptionId(null); } }, [isSubmitting]); useEffect(() => { if (pendingFeedbackOptionId !== null) { feedbackTextareaRef.current?.focus(); } }, [pendingFeedbackOptionId]); const submitOption = useCallback( (optionId: string) => { if (controlsDisabled) { return; } const feedbackOption = feedbackOptionId === optionId; if (feedbackOption && pendingFeedbackOptionId !== optionId) { setFeedback(""); setPendingFeedbackOptionId(optionId); return; } setSubmittingOptionId(optionId); onSubmit({ requestId: prompt.requestId, ...(feedbackOption ? { action: "deny" } : {}), optionId, ...(feedbackOption && feedbackValue ? { payload: { denyMessage: feedbackValue } } : {}) }); }, [ feedbackOptionId, feedbackValue, controlsDisabled, onSubmit, pendingFeedbackOptionId, prompt.requestId ] ); useEffect(() => { if ( !keyboardShortcuts || controlsDisabled || submittingOptionId !== null || prompt.options.length === 0 ) { return undefined; } const handleKeyDown = (event: KeyboardEvent): void => { if ( !isEnterLikeKey(event) || isEditableKeyboardTarget(event.target) || event.isComposing || event.altKey || event.shiftKey ) { return; } const optionIndex = event.metaKey || event.ctrlKey ? 1 : 0; const option = prompt.options[optionIndex]; if (!option) { return; } event.preventDefault(); event.stopPropagation(); submitOption(option.id); }; window.addEventListener("keydown", handleKeyDown, true); return () => window.removeEventListener("keydown", handleKeyDown, true); }, [ controlsDisabled, keyboardShortcuts, prompt.options, submitOption, submittingOptionId ]); return (
{stripPromptTitlePunctuation(promptToolPresentation.lead)}
{promptToolPresentation.leadDetails.map((detail) => (
{detail.value} {detail.meta ? ` ${detail.meta}` : null}
))}
{promptToolPresentation.cardDetails.length > 0 ? (
{promptToolPresentation.cardDetails.map((detail) => (
{detail.label} {detail.meta ? ( {detail.meta} ) : null}
))}
) : null}
{prompt.options.map((option, optionIndex) => { const showSpinner = submittingOptionId === option.id; const optionSupportsFeedback = feedbackOptionId === option.id; const optionLabel = approvalOptionDisplayLabel(option, { feedback: optionSupportsFeedback }); const optionPresentation = approvalOptionVisualPresentation( option, { feedback: optionSupportsFeedback } ); const shortcutLabel = approvalOptionShortcutLabel( optionIndex, isDarwin ); const showFeedbackComposer = pendingFeedbackOptionId === option.id; const action = approvalActionTestId( option.id, approveOptionId, denyOptionId, feedbackOptionId ); if (showFeedbackComposer) { return (