'use client'; import { useState, useRef } from 'react'; import { usePluginStore } from '@/stores/plugin-store'; import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section'; import { cn } from '@/lib/utils'; import { Upload, Trash2, AlertTriangle, Puzzle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { toast } from '@/stores/toast-store'; import type { InstalledPlugin, PluginStatus, SettingFieldSchema } from '@/lib/plugin-types'; const STATUS_COLORS: Record = { installed: 'bg-muted text-muted-foreground', enabled: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400', running: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', disabled: 'bg-muted text-muted-foreground', error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', }; export function PluginsSettings() { const { plugins, installPlugin, uninstallPlugin, enablePlugin, disablePlugin, updatePluginSettings } = usePluginStore(); const [isUploading, setIsUploading] = useState(false); const [expandedPlugin, setExpandedPlugin] = useState(null); const fileInputRef = useRef(null); const handleUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setIsUploading(true); try { const result = await installPlugin(file); if (result.success) { toast.success('Plugin installed'); if (result.warnings?.length) { toast.warning('Plugin warnings', { message: result.warnings.join('\n') }); } } else { toast.error('Plugin installation failed', { message: result.error }); } } catch (err) { toast.error('Plugin installation failed', { message: err instanceof Error ? err.message : 'Unknown error' }); } finally { setIsUploading(false); if (fileInputRef.current) fileInputRef.current.value = ''; } }; const handleToggle = async (plugin: InstalledPlugin) => { if (plugin.enabled) { disablePlugin(plugin.id); toast.info(`Plugin "${plugin.name}" disabled`); } else { await enablePlugin(plugin.id); toast.success(`Plugin "${plugin.name}" enabled`); } }; const handleUninstall = (plugin: InstalledPlugin) => { uninstallPlugin(plugin.id); toast.success(`Plugin "${plugin.name}" removed`); }; return ( {/* Plugin List */} {plugins.length === 0 ? (

No plugins installed

Upload a plugin .zip file to get started

) : (
{plugins.map(plugin => ( setExpandedPlugin(expandedPlugin === plugin.id ? null : plugin.id)} onToggle={() => handleToggle(plugin)} onUninstall={() => handleUninstall(plugin)} onUpdateSettings={(settings) => updatePluginSettings(plugin.id, settings)} /> ))}
)} {/* Upload */}
); } // ─── Plugin Card ───────────────────────────────────────────── interface PluginCardProps { plugin: InstalledPlugin; isExpanded: boolean; onToggleExpand: () => void; onToggle: () => void; onUninstall: () => void; onUpdateSettings: (settings: Record) => void; } function PluginCard({ plugin, isExpanded, onToggleExpand, onToggle, onUninstall, onUpdateSettings }: PluginCardProps) { return (
{/* Header */}
{plugin.name} {plugin.status}
{plugin.author} v{plugin.version} {plugin.type}
{/* Expanded Details */} {isExpanded && (
{/* Description */} {plugin.description && (

{plugin.description}

)} {/* Error */} {plugin.error && (
{plugin.error}
)} {/* Permissions */} {plugin.permissions.length > 0 && (
Permissions:
{plugin.permissions.map(perm => ( {perm} ))}
)} {/* Settings (auto-generated from schema) */} {plugin.settingsSchema && Object.keys(plugin.settingsSchema).length > 0 && (
Settings: {Object.entries(plugin.settingsSchema).map(([key, schema]) => ( onUpdateSettings({ [key]: value })} /> ))}
)} {/* Uninstall */}
)}
); } // ─── Auto-generated Setting Field ──────────────────────────── interface PluginSettingFieldProps { fieldKey: string; schema: SettingFieldSchema; value: unknown; onChange: (value: unknown) => void; } function PluginSettingField({ schema, value, onChange }: PluginSettingFieldProps) { switch (schema.type) { case 'boolean': return (
{schema.label} {schema.description &&

{schema.description}

}
onChange(v)} />
); case 'select': return (
{schema.label} {schema.description &&

{schema.description}

}
); case 'string': return (
{schema.label} {schema.description &&

{schema.description}

} onChange(e.target.value)} className="mt-1 w-full text-xs bg-background border border-border rounded px-2 py-1 text-foreground" />
); case 'number': return (
{schema.label} {schema.description &&

{schema.description}

}
onChange(Number(e.target.value))} className="w-20 text-xs bg-background border border-border rounded px-2 py-1 text-foreground" />
); default: return null; } }