import { useState, useEffect } from 'react'
import { X, Save } from 'lucide-react'
import CustomRulesTab from './CustomRulesTab'
interface SettingsModalProps {
isOpen: boolean
onClose: () => void
}
interface ConfigData {
Engine: {
tools_poisoning_engine: boolean
command_injection_engine: boolean
data_exfiltration_engine: boolean
file_system_exposure_engine: boolean
pii_leak_engine: boolean
}
}
function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
const [activeTab, setActiveTab] = useState<'engines' | 'apikeys' | 'customrules'>('engines')
const [config, setConfig] = useState<ConfigData | null>(null)
const [apiKey, setApiKey] = useState<string>('')
const [loading, setLoading] = useState(false)
const [saving, setSaving] = useState(false)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
if (isOpen) {
loadConfig()
}
}, [isOpen])
const loadConfig = async () => {
setLoading(true)
setError(null)
try {
const [configData, envData] = await Promise.all([
window.electronAPI.getConfig(),
window.electronAPI.getEnv()
])
setConfig(configData)
setApiKey(envData.MISTRAL_API_KEY || '')
} catch (err) {
setError('Failed to load configuration')
console.error('Error loading config:', err)
} finally {
setLoading(false)
}
}
const saveConfig = async () => {
if (!config) return
setSaving(true)
setError(null)
try {
await Promise.all([
window.electronAPI.saveConfig(config),
window.electronAPI.saveEnv({ MISTRAL_API_KEY: apiKey })
])
onClose()
alert('Please restart the program for the changes to take effect.')
} catch (err) {
setError('Failed to save configuration')
console.error('Error saving config:', err)
} finally {
setSaving(false)
}
}
const handleEngineToggle = (key: keyof ConfigData['Engine']) => {
if (!config) return
setConfig({
...config,
Engine: {
...config.Engine,
[key]: !config.Engine[key]
}
})
}
if (!isOpen) return null
return (
<div className="fixed inset-0 backdrop-blur-sm bg-white/30 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Settings</h2>
<button
onClick={onClose}
className="p-1 hover:bg-gray-100 rounded transition-colors"
>
<X size={20} className="text-gray-500" />
</button>
</div>
{/* Tabs */}
<div className="flex border-b border-gray-200">
<button
onClick={() => setActiveTab('engines')}
className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'engines'
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
Engines
</button>
<button
onClick={() => setActiveTab('apikeys')}
className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'apikeys'
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
API Keys
</button>
<button
onClick={() => setActiveTab('customrules')}
className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'customrules'
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
Custom Rules
</button>
</div>
{/* Content */}
<div className="p-4 max-h-[70vh] overflow-y-auto">
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>
) : error ? (
<div className="text-red-500 text-center py-4">{error}</div>
) : (
<>
{/* Engines Tab */}
{activeTab === 'engines' && config && (
<div>
<h3 className="text-sm font-medium text-gray-700 mb-3">Detection Engines</h3>
<div className="space-y-3">
<label className="flex items-center justify-between">
<span className="text-sm text-gray-600">Tools Poisoning Engine</span>
<input
type="checkbox"
checked={config.Engine.tools_poisoning_engine}
onChange={() => handleEngineToggle('tools_poisoning_engine')}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
</label>
<label className="flex items-center justify-between">
<span className="text-sm text-gray-600">Command Injection Engine</span>
<input
type="checkbox"
checked={config.Engine.command_injection_engine}
onChange={() => handleEngineToggle('command_injection_engine')}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
</label>
<label className="flex items-center justify-between">
<span className="text-sm text-gray-600">Data Exfiltration Engine</span>
<input
type="checkbox"
checked={config.Engine.data_exfiltration_engine}
onChange={() => handleEngineToggle('data_exfiltration_engine')}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
</label>
<label className="flex items-center justify-between">
<span className="text-sm text-gray-600">File System Exposure Engine</span>
<input
type="checkbox"
checked={config.Engine.file_system_exposure_engine}
onChange={() => handleEngineToggle('file_system_exposure_engine')}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
</label>
<label className="flex items-center justify-between">
<span className="text-sm text-gray-600">PII Leak Engine</span>
<input
type="checkbox"
checked={config.Engine.pii_leak_engine}
onChange={() => handleEngineToggle('pii_leak_engine')}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
</label>
</div>
</div>
)}
{/* API Keys Tab */}
{activeTab === 'apikeys' && (
<div>
<h3 className="text-sm font-medium text-gray-700 mb-3">API Keys</h3>
<div className="space-y-3">
<div>
<label className="block text-sm text-gray-600 mb-1">Mistral API Key</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="Enter your Mistral API key"
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
</div>
)}
{/* Custom Rules Tab */}
{activeTab === 'customrules' && <CustomRulesTab />}
</>
)}
</div>
{/* Footer */}
{activeTab !== 'customrules' && (
<div className="flex justify-end gap-2 p-4 border-t border-gray-200">
<button
onClick={onClose}
className="px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
>
Cancel
</button>
<button
onClick={saveConfig}
disabled={saving || !config}
className="flex items-center gap-2 px-4 py-2 text-sm text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<Save size={16} />
{saving ? 'Saving...' : 'Save'}
</button>
</div>
)}
</div>
</div>
)
}
export default SettingsModal