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' && (