import { useState } from 'react' import { useChatStore } from '../../stores/chatStore' import { useTranslation } from '../../i18n' import { Button } from '../shared/Button' import { DiffViewer } from './DiffViewer' type Props = { requestId: string toolName: string input: unknown description?: string } /** * Icons for known tool types. * Uses Material Symbols Outlined names. */ const TOOL_META: Record = { Bash: { icon: 'terminal', label: 'Bash', color: '#CA8A04' }, Edit: { icon: 'edit_note', label: 'Edit File', color: '#8F482F' }, Write: { icon: 'edit_document', label: 'Write File', color: '#16A34A' }, Read: { icon: 'description', label: 'Read File', color: '#2D628F' }, Glob: { icon: 'search', label: 'Glob Search', color: '#2D628F' }, Grep: { icon: 'find_in_page', label: 'Grep Search', color: '#2D628F' }, Agent: { icon: 'smart_toy', label: 'Agent', color: '#7C3AED' }, WebSearch: { icon: 'travel_explore', label: 'Web Search', color: '#2D628F' }, WebFetch: { icon: 'cloud_download', label: 'Web Fetch', color: '#2D628F' }, NotebookEdit: { icon: 'note', label: 'Notebook Edit', color: '#8F482F' }, Skill: { icon: 'auto_awesome', label: 'Skill', color: '#7C3AED' }, } /** * Extract human-readable detail lines from tool input. */ function extractToolDetails(toolName: string, input: unknown, t: (key: any, params?: any) => string): { primary: string; secondary?: string } { const obj = (input && typeof input === 'object') ? input as Record : {} switch (toolName) { case 'Bash': { const cmd = typeof obj.command === 'string' ? obj.command : '' const desc = typeof obj.description === 'string' ? obj.description : undefined return { primary: cmd, secondary: desc } } case 'Edit': { const filePath = typeof obj.file_path === 'string' ? obj.file_path : '' return { primary: filePath, secondary: obj.old_string ? t('permission.replacingContent') : undefined } } case 'Write': { const filePath = typeof obj.file_path === 'string' ? obj.file_path : '' return { primary: filePath } } case 'Read': { const filePath = typeof obj.file_path === 'string' ? obj.file_path : '' return { primary: filePath } } case 'Glob': return { primary: typeof obj.pattern === 'string' ? obj.pattern : '' } case 'Grep': return { primary: typeof obj.pattern === 'string' ? obj.pattern : '' } case 'Agent': return { primary: typeof obj.description === 'string' ? obj.description : '' } case 'WebSearch': return { primary: typeof obj.query === 'string' ? obj.query : '' } case 'WebFetch': return { primary: typeof obj.url === 'string' ? obj.url : '' } default: return { primary: typeof input === 'string' ? input : JSON.stringify(input, null, 2) } } } function getPermissionTitle(toolName: string, input: unknown, t: (key: any, params?: any) => string) { const obj = (input && typeof input === 'object') ? input as Record : {} const filePath = typeof obj.file_path === 'string' ? obj.file_path : '' const fileName = filePath ? filePath.split('/').pop() || filePath : '' switch (toolName) { case 'Edit': case 'Write': return fileName ? t('permission.allowEditFile', { toolName, fileName }) : t('permission.allowEditFileGeneric', { toolName: toolName.toLowerCase() }) case 'Bash': return t('permission.allowBash') default: return t('permission.allowTool', { toolName }) } } function renderPermissionPreview(toolName: string, input: unknown) { const obj = (input && typeof input === 'object') ? input as Record : {} const filePath = typeof obj.file_path === 'string' ? obj.file_path : 'file' if (toolName === 'Edit' && typeof obj.old_string === 'string' && typeof obj.new_string === 'string') { return } if (toolName === 'Write' && typeof obj.content === 'string') { return } if (toolName === 'Bash' && typeof obj.command === 'string') { return (
          $ {obj.command}
        
) } return null } export function PermissionDialog({ requestId, toolName, input, description }: Props) { const { respondToPermission, pendingPermission } = useChatStore() const t = useTranslation() const isPending = pendingPermission?.requestId === requestId const [showRaw, setShowRaw] = useState(false) const meta = TOOL_META[toolName] || { icon: 'shield', label: toolName, color: '#87736D' } const details = extractToolDetails(toolName, input, t) const rawInput = typeof input === 'string' ? input : JSON.stringify(input, null, 2) const preview = renderPermissionPreview(toolName, input) const title = getPermissionTitle(toolName, input, t) const allowRawToggle = !preview return (
{/* Header */}
{meta.icon}
{title} {isPending && ( {t('permission.awaitingApproval')} )} {!isPending && ( {t('permission.responded')} )}
{description && (

{description}

)}
{/* Tool details */}
{preview ? (
{details.primary && toolName !== 'Bash' ? (
folder_open {details.primary}
) : null} {preview}
) : details.primary ? (
{toolName === 'Glob' || toolName === 'Grep' ? 'search' : 'folder_open'} {details.primary}
) : null} {/* Secondary detail */} {details.secondary && (

{details.secondary}

)} {allowRawToggle && ( )} {allowRawToggle && showRaw && (
            {rawInput}
          
)}
{/* Action buttons */} {isPending && (
)}
) }