import { useEffect, useMemo } from 'react' import { usePluginStore } from '../../stores/pluginStore' import { useSessionStore } from '../../stores/sessionStore' import { useTranslation } from '../../i18n' import { useUIStore } from '../../stores/uiStore' import { Button } from '../shared/Button' import type { PluginSummary } from '../../types/plugin' type PluginBucket = 'attention' | 'enabled' | 'disabled' export function PluginList() { const { plugins, marketplaces, summary, lastReloadSummary, isLoading, isApplying, error, fetchPlugins, fetchPluginDetail, reloadPlugins, } = usePluginStore() const sessions = useSessionStore((s) => s.sessions) const activeSessionId = useSessionStore((s) => s.activeSessionId) const addToast = useUIStore((s) => s.addToast) const t = useTranslation() const activeSession = sessions.find((session) => session.id === activeSessionId) const currentWorkDir = activeSession?.workDir || undefined useEffect(() => { void fetchPlugins(currentWorkDir) }, [fetchPlugins, currentWorkDir]) const grouped = useMemo(() => { const buckets: Record = { attention: [], enabled: [], disabled: [], } for (const plugin of plugins) { if (plugin.hasErrors) { buckets.attention.push(plugin) } else if (plugin.enabled) { buckets.enabled.push(plugin) } else { buckets.disabled.push(plugin) } } return buckets }, [plugins]) const handleReload = async () => { try { const reloadSummary = await reloadPlugins(currentWorkDir, activeSessionId || undefined) addToast({ type: reloadSummary.errors > 0 ? 'warning' : 'success', message: t('settings.plugins.reloadToast', { enabled: String(reloadSummary.enabled), skills: String(reloadSummary.skills), errors: String(reloadSummary.errors), }), }) } catch (err) { addToast({ type: 'error', message: err instanceof Error ? err.message : String(err), }) } } if (isLoading) { return (
) } if (error) { return
{error}
} if (plugins.length === 0) { return (
extension

{t('settings.plugins.empty')}

{t('settings.plugins.emptyHint')}

) } return (
{t('settings.plugins.browserEyebrow')}
extension

{t('settings.plugins.browserTitle')}

{t('settings.plugins.browserDescription')}

plugin.enabled).length)} icon="check_circle" />
{lastReloadSummary && (

{t('settings.plugins.lastReload', { enabled: String(lastReloadSummary.enabled), skills: String(lastReloadSummary.skills), errors: String(lastReloadSummary.errors), })}

)}
{marketplaces.length > 0 && (

{t('settings.plugins.marketplacesTitle')}

{t('settings.plugins.marketplacesHint')}

{marketplaces.map((marketplace) => (
{marketplace.name} {marketplace.autoUpdate ? t('settings.plugins.marketplaceAutoUpdateOn') : t('settings.plugins.marketplaceAutoUpdateOff')}
{marketplace.source}
{t('settings.plugins.marketplaceInstalledCount', { count: String(marketplace.installedCount) })} {marketplace.lastUpdated && ( {t('settings.plugins.marketplaceUpdatedAt', { value: new Date(marketplace.lastUpdated).toLocaleString() })} )}
))}
)} {renderGroup('attention', grouped.attention, fetchPluginDetail, currentWorkDir, t)} {renderGroup('enabled', grouped.enabled, fetchPluginDetail, currentWorkDir, t)} {renderGroup('disabled', grouped.disabled, fetchPluginDetail, currentWorkDir, t)}
) } function renderGroup( bucket: PluginBucket, items: PluginSummary[], fetchPluginDetail: (id: string, cwd?: string) => Promise, cwd: string | undefined, t: ReturnType, ) { if (items.length === 0) return null const titleKey = bucket === 'attention' ? 'settings.plugins.group.attention' : bucket === 'enabled' ? 'settings.plugins.group.enabled' : 'settings.plugins.group.disabled' return (

{t(titleKey)}

{t('settings.plugins.groupHint', { count: String(items.length) })}

{items.length}
{items.map((plugin) => ( ))}
) } function SummaryCard({ label, value, icon, }: { label: string value: string icon: string }) { return (
{icon} {label}
{value}
) } function StatusPill({ plugin }: { plugin: PluginSummary }) { const t = useTranslation() if (plugin.hasErrors) { return ( {t('settings.plugins.status.attention')} ) } return ( {plugin.enabled ? t('settings.plugins.status.enabled') : t('settings.plugins.status.disabled')} ) } function ScopePill({ scope }: { scope: PluginSummary['scope'] }) { const t = useTranslation() return ( {t(`settings.plugins.scope.${scope}`)} ) }