import { useCallback, useEffect, useState } from 'react' import { Puzzle, RefreshCw } from 'lucide-react' import { api } from '../lib/api' import { toastMessage } from '../lib/utils' import type { PluginInfo } from '../types' import { Badge } from './ui/Badge' import { Button } from './ui/Button' import { Modal } from './ui/Modal' interface PluginsResponse { ok?: boolean error?: string plugins?: PluginInfo[] } // 能力名(contract.CAPABILITIES 的键)→ 界面文案 const CAPABILITY_LABELS: Record = { login: '登录', session: '会话', renew: '续期', profile: '账号信息', courses: '课程列表', discover: '课程详情', study: '学习', quiz: '测验', capture: '抓包', push: '推送', } export function PluginsDialog({ open, onClose, notify, onChanged, }: { open: boolean onClose: () => void notify: (toast: { message: string; tone?: 'info' | 'success' | 'error' }) => void onChanged: () => void }) { const [plugins, setPlugins] = useState([]) const [busy, setBusy] = useState('') const [loading, setLoading] = useState(false) const pull = useCallback(async () => { setLoading(true) try { const response = await api('/api/plugins') setPlugins(response.plugins || []) } catch (error) { notify({ message: toastMessage(error), tone: 'error' }) } finally { setLoading(false) } }, [notify]) useEffect(() => { if (open) void pull() }, [open, pull]) async function run(name: string, path: string, okText: string) { setBusy(name) try { const response = await api(path, { method: 'POST' }) if (response.plugins) setPlugins(response.plugins) if (response.ok === false) { notify({ message: response.error || '插件操作失败', tone: 'error' }) } else { notify({ message: okText, tone: 'success' }) onChanged() } } catch (error) { notify({ message: toastMessage(error), tone: 'error' }) } finally { setBusy('') } } return ( 共 {plugins.length} 个 · 停用后平台会从「添加账号」下拉里消失,推送渠道也会停止推送 } >
{plugins.map((plugin) => (
{plugin.title || plugin.name} {plugin.name} {plugin.enabled ? '已启用' : '已停用'} {plugin.enabled && ( {plugin.loaded ? '已加载' : '未加载'} )}
适配网站 {plugin.sites?.length ? ( plugin.sites.map((site) => ( {site} )) ) : ( — )}
能力 {plugin.capabilities.length ? ( plugin.capabilities.map((cap) => ( {CAPABILITY_LABELS[cap] || cap} )) ) : ( 无 )}
平台 {plugin.label || '—'} id {plugin.id || '—'}
))} {!plugins.length && !loading && (

plugins/ 下还没有插件。建一个含 plugin.json + main.py 的目录丢进去,刷新即可看到。

)}
) }