import { useState, useRef, useEffect } from 'react' import { createPortal } from 'react-dom' import { useSettingsStore } from '../../stores/settingsStore' import { useChatStore } from '../../stores/chatStore' import { useSessionStore } from '../../stores/sessionStore' import { useTabStore } from '../../stores/tabStore' import { useTranslation } from '../../i18n' import type { PermissionMode } from '../../types/settings' const MODE_ICONS: Record = { default: 'verified_user', acceptEdits: 'bolt', plan: 'architecture', bypassPermissions: 'gavel', dontAsk: 'gavel', } type Props = { workDir?: string /** Controlled mode: override current value */ value?: PermissionMode /** Controlled mode: called on change instead of updating global store */ onChange?: (mode: PermissionMode) => void } export function PermissionModeSelector({ workDir: workDirProp, value, onChange }: Props = {}) { const t = useTranslation() const { permissionMode: storeMode, setPermissionMode } = useSettingsStore() const setSessionPermissionMode = useChatStore((s) => s.setSessionPermissionMode) const activeTabId = useTabStore((s) => s.activeTabId) const sessions = useSessionStore((s) => s.sessions) const activeSessionId = useSessionStore((s) => s.activeSessionId) const [open, setOpen] = useState(false) const [confirmDialog, setConfirmDialog] = useState(false) const ref = useRef(null) const isControlled = value !== undefined const currentMode = isControlled ? value : storeMode const PERMISSION_ITEMS: Array<{ value: PermissionMode label: string description: string icon: string color?: string }> = [ { value: 'default', label: t('permMode.askPermissions'), description: t('permMode.askPermDesc'), icon: 'verified_user', }, { value: 'acceptEdits', label: t('permMode.autoAccept'), description: t('permMode.autoAcceptDesc'), icon: 'bolt', }, { value: 'plan', label: t('permMode.planMode'), description: t('permMode.planModeDesc'), icon: 'architecture', color: 'text-[var(--color-text-tertiary)]', }, { value: 'bypassPermissions', label: t('permMode.bypass'), description: t('permMode.bypassDesc'), icon: 'gavel', color: 'text-[var(--color-error)]', }, ] const MODE_LABELS: Record = { default: t('permMode.label.default'), acceptEdits: t('permMode.label.acceptEdits'), plan: t('permMode.label.plan'), bypassPermissions: t('permMode.label.bypassPermissions'), dontAsk: t('permMode.label.dontAsk'), } const activeSession = sessions.find((s) => s.id === activeSessionId) const workDir = workDirProp || activeSession?.workDir || '~' useEffect(() => { if (!open) return const handleClick = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) } const handleEsc = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false) } document.addEventListener('mousedown', handleClick) document.addEventListener('keydown', handleEsc) return () => { document.removeEventListener('mousedown', handleClick) document.removeEventListener('keydown', handleEsc) } }, [open]) return (
{open && (
{t('permMode.executionPermissions')}
{PERMISSION_ITEMS.map((item) => ( ))}
)} {/* Bypass confirmation dialog */} {confirmDialog && createPortal(
setConfirmDialog(false)}>
e.stopPropagation()} > {/* Header */}
warning
{t('permMode.enableBypassTitle')}
{t('permMode.enableBypassSubtitle')}
{/* Body */}

folder {workDir}
  • check {t('permMode.permReadWrite')}
  • check {t('permMode.permShell')}
  • check {t('permMode.permPackages')}
{/* Actions */}
, document.body, )}
) }