import { useState } from "react"; import type { Question } from "../types"; import { esc, cn, truncate } from "../lib/utils"; import { TriangleAlert, Check } from "lucide-react"; // ============================================================ // PermissionPromptView — simple approve/reject for tool use // ============================================================ export function PermissionPromptView({ requestId, toolName, toolInput, description, onApprove, onReject, }: { requestId: string; toolName: string; toolInput: unknown; description: string; onApprove: () => void; onReject: () => void; }) { const inputStr = typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput, null, 2); return (
Permission Request
{description &&
{esc(description)}
}
{esc(toolName)}
{toolName !== "AskUserQuestion" && (
          {truncate(inputStr, 500)}
        
)}
); } // ============================================================ // AskUserPanelView — multi-question interactive panel // ============================================================ export function AskUserPanelView({ questions, description, onSubmit, onSkip, }: { requestId: string; questions: Question[]; description: string; onSubmit: (answers: Record) => void; onSkip: () => void; }) { const [answers, setAnswers] = useState>({}); const [otherTexts, setOtherTexts] = useState>({}); const [activeTab, setActiveTab] = useState(0); const handleSelect = (qIdx: number, oIdx: number, multiSelect: boolean) => { if (multiSelect) { const current = (answers[qIdx] as number[]) || []; const next = current.includes(oIdx) ? current.filter((i) => i !== oIdx) : [...current, oIdx]; setAnswers({ ...answers, [qIdx]: next }); } else { setAnswers({ ...answers, [qIdx]: oIdx }); } }; const handleOtherSubmit = (qIdx: number) => { const text = otherTexts[qIdx]?.trim(); if (!text) return; setAnswers({ ...answers, [qIdx]: text }); setOtherTexts({ ...otherTexts, [qIdx]: "" }); }; const handleSubmit = () => { const mapped: Record = {}; for (const [qIdx, val] of Object.entries(answers)) { const q = questions[parseInt(qIdx, 10)]; if (!q) continue; if (typeof val === "number") mapped[qIdx] = q.options?.[val]?.label || String(val); else if (Array.isArray(val)) mapped[qIdx] = val.map((i) => q.options?.[i]?.label || String(i)); else mapped[qIdx] = val; } onSubmit(mapped); }; // Single question — simple layout if (questions.length <= 1) { const q = questions[0] || { question: description, options: [], multiSelect: false }; const multiSelect = q.multiSelect || false; return (
{esc(description || q.question || "Question")}
{(q.options || []).map((opt, j) => { const isSelected = multiSelect ? ((answers[0] as number[]) || []).includes(j) : answers[0] === j; return ( ); })}
setOtherTexts({ ...otherTexts, [0]: e.target.value })} placeholder="Other..." className="flex-1 rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-brand focus:outline-none" onKeyDown={(e) => e.key === "Enter" && handleOtherSubmit(0)} />
); } // Multiple questions — tab layout const currentQ = questions[activeTab]; return (
{esc(description || "Questions")}
{questions.map((q, i) => ( ))}
{currentQ && ( setOtherTexts({ ...otherTexts, [qIdx]: text })} onOtherSubmit={handleOtherSubmit} /> )}
{activeTab + 1} / {questions.length}
); } function QuestionTab({ question, qIdx, answers, otherTexts, onSelect, onOtherTextChange, onOtherSubmit, }: { question: Question; qIdx: number; answers: Record; otherTexts: Record; onSelect: (qIdx: number, oIdx: number, multiSelect: boolean) => void; onOtherTextChange: (qIdx: number, text: string) => void; onOtherSubmit: (qIdx: number) => void; }) { const multiSelect = question.multiSelect || false; return (
{esc(question.question)}
{(question.options || []).map((opt, j) => { const isSelected = multiSelect ? ((answers[qIdx] as number[]) || []).includes(j) : answers[qIdx] === j; return ( ); })}
onOtherTextChange(qIdx, e.target.value)} placeholder="Other..." className="flex-1 rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-brand focus:outline-none" onKeyDown={(e) => e.key === "Enter" && onOtherSubmit(qIdx)} />
); } // ============================================================ // PlanPanelView — plan approval with feedback // ============================================================ export function PlanPanelView({ planContent, description, onSubmit, }: { requestId: string; planContent: string; description: string; onSubmit: (value: string, feedback?: string) => void; }) { const [selected, setSelected] = useState(null); const [feedback, setFeedback] = useState(""); const isEmpty = !planContent || !planContent.trim(); const handleSubmit = () => { if (!selected) return; onSubmit(selected, selected === "no" ? feedback : undefined); }; return (
{isEmpty ? "Exit plan mode?" : "Ready to code?"}
{!isEmpty && (
)}
{isEmpty ? ( <> setSelected("yes-default")} label="Yes" /> setSelected("no")} label="No" /> ) : ( <> setSelected("yes-accept-edits")} label="Yes, auto-accept edits" desc="Approve plan and auto-accept file edits" /> setSelected("yes-default")} label="Yes, manually approve edits" desc="Approve plan but confirm each edit" /> setSelected("no")} label="No, keep planning" desc="Provide feedback to refine the plan" /> )}
{selected === "no" && (