import React, { useCallback, useState } from "react"; import { Plus, X } from "lucide-react"; import type { AIPermissionMode } from "../../../../infrastructure/ai/types"; import { DEFAULT_COMMAND_BLOCKLIST, MAX_COMMAND_TIMEOUT_SECONDS, MAX_RESPONSE_IDLE_TIMEOUT_SECONDS, } from "../../../../infrastructure/ai/types"; import { useI18n } from "../../../../application/i18n/I18nProvider"; import { Button } from "../../../ui/button"; import { Select, SettingCard, SettingRow, SettingsAnchor, SettingsSection } from "../../settings-ui"; export const SafetySettings: React.FC<{ globalPermissionMode: AIPermissionMode; setGlobalPermissionMode: (mode: AIPermissionMode) => void; commandBlocklist: string[]; setCommandBlocklist: (value: string[]) => void; commandTimeout: number; setCommandTimeout: (value: number) => void; responseIdleTimeout: number; setResponseIdleTimeout: (value: number) => void; maxIterations: number; setMaxIterations: (value: number) => void; }> = ({ globalPermissionMode, setGlobalPermissionMode, commandBlocklist, setCommandBlocklist, commandTimeout, setCommandTimeout, responseIdleTimeout, setResponseIdleTimeout, maxIterations, setMaxIterations, }) => { const { t } = useI18n(); const [regexErrors, setRegexErrors] = useState>({}); const validatePattern = useCallback((pattern: string, idx: number): boolean => { if (!pattern) { setRegexErrors((prev) => { const next = { ...prev }; delete next[idx]; return next; }); return true; } try { new RegExp(pattern); setRegexErrors((prev) => { const next = { ...prev }; delete next[idx]; return next; }); return true; } catch (e) { setRegexErrors((prev) => ({ ...prev, [idx]: e instanceof Error ? e.message : String(e), })); return false; } }, []); const handlePatternChange = useCallback((value: string, idx: number) => { const next = [...commandBlocklist]; next[idx] = value; validatePattern(value, idx); setCommandBlocklist(next); }, [commandBlocklist, setCommandBlocklist, validatePattern]); const permissionModeOptions = [ { value: "observer", label: t('ai.safety.permissionMode.observer') }, { value: "confirm", label: t('ai.safety.permissionMode.confirm') }, { value: "auto", label: t('ai.safety.permissionMode.auto') }, ]; return (
{ const val = parseInt(e.target.value, 10); if (!isNaN(val)) setResponseIdleTimeout(val); }} min={1} max={MAX_RESPONSE_IDLE_TIMEOUT_SECONDS} className="w-20 h-9 rounded-md border border-input bg-background px-3 text-sm text-right focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" /> {t('ai.safety.responseIdleTimeout.unit')}
{ const val = parseInt(e.target.value, 10); if (!isNaN(val)) setCommandTimeout(val); }} min={1} max={MAX_COMMAND_TIMEOUT_SECONDS} className="w-20 h-9 rounded-md border border-input bg-background px-3 text-sm text-right focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" /> {t('ai.safety.commandTimeout.unit')}
{ const val = parseInt(e.target.value, 10); if (!isNaN(val) && val > 0) setMaxIterations(val); }} min={1} max={100} className="w-20 h-9 rounded-md border border-input bg-background px-3 text-sm text-right focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" /> {/* Command Blocklist */}

{t('ai.safety.blocklist')}

{t('ai.safety.blocklist.description')}

{commandBlocklist.map((pattern, idx) => (
handlePatternChange(e.target.value, idx)} className={`flex-1 h-8 rounded-md border bg-background px-3 text-xs font-mono focus-visible:outline-none focus-visible:ring-1 ${ regexErrors[idx] ? 'border-destructive focus-visible:ring-destructive' : 'border-input focus-visible:ring-ring' }`} placeholder={t('ai.safety.blocklist.placeholder')} />
{regexErrors[idx] && (

{regexErrors[idx]}

)}
))}

{t('ai.safety.note')}

); };