import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { api } from '@/lib/api'; import { Save, Check, Loader2, TestTube2 } from 'lucide-react'; type SkillsConfig = Record; const SECTIONS = [ { title: 'CapCut / 剪映', fields: [ { key: 'jianyingDraftPath', label: '剪映草稿路径', placeholder: 'C:/Users/.../com.lveditor.draft' }, { key: 'capcutMateDir', label: 'CapCut Mate 目录', placeholder: 'C:/Users/.../capcut-mate' }, { key: 'capcutMateApiBase', label: 'CapCut Mate API', placeholder: 'http://localhost:30000' }, ], }, { title: 'Gemini 生图', fields: [ { key: 'geminiApiBaseUrl', label: 'API Base URL', placeholder: 'https://generativelanguage.googleapis.com' }, { key: 'geminiApiKey', label: 'API Key', type: 'password' as const }, { key: 'geminiModel', label: '模型', placeholder: 'gemini-3.1-flash-image-preview' }, ], }, { title: 'Midjourney', fields: [ { key: 'mjApiBaseUrl', label: 'API Base URL' }, { key: 'mjApiKey', label: 'API Key', type: 'password' as const }, ], }, { title: 'Veo 视频生成', fields: [ { key: 'veoApiBaseUrl', label: 'API Base URL' }, { key: 'veoApiKey', label: 'API Key', type: 'password' as const }, { key: 'veoModel', label: '模型', placeholder: 'veo3-fast-frames' }, { key: 'veoEnhancePrompt', label: '增强提示词', type: 'toggle' as const }, { key: 'veoEnableUpsample', label: '启用 Upsample', type: 'toggle' as const }, ], }, { title: 'Grok 视频生成', fields: [ { key: 'grokApiBaseUrl', label: 'API Base URL' }, { key: 'grokApiKey', label: 'API Key', type: 'password' as const }, { key: 'grokModel', label: '模型', placeholder: 'grok-video-3' }, ], }, { title: 'GPT Image', fields: [ { key: 'gptImageApiBaseUrl', label: 'API Base URL' }, { key: 'gptImageApiKey', label: 'API Key', type: 'password' as const }, { key: 'gptImageModel', label: '模型', placeholder: 'gpt-image-2' }, ], }, { title: 'Kling 可灵', fields: [ { key: 'kelingApiBaseUrl', label: 'API Base URL', placeholder: 'https://api-beijing.klingai.com' }, { key: 'kelingApiKey', label: 'API Key', type: 'password' as const }, { key: 'kelingSecretAccessKey', label: 'Secret Key', type: 'password' as const }, { key: 'kelingModel', label: '模型', placeholder: 'kling-v2-5-turbo' }, ], }, { title: 'OSS 存储', fields: [ { key: 'ossRegion', label: 'Region', placeholder: 'oss-cn-hangzhou' }, { key: 'ossAccessKeyId', label: 'Access Key ID' }, { key: 'ossAccessKeySecret', label: 'Access Key Secret', type: 'password' as const }, { key: 'ossBucket', label: 'Bucket', placeholder: 'my-bucket' }, { key: 'ossFolder', label: '目录前缀', placeholder: 'tmp/' }, { key: 'ossExpires', label: '链接有效期(秒)', placeholder: '31536000' }, ], }, { title: 'TTS 语音合成', fields: [ { key: 'ttsApiBaseUrl', label: 'API Base URL', placeholder: 'https://dashscope.aliyuncs.com/api/v1' }, { key: 'ttsApiKey', label: 'API Key', type: 'password' as const }, { key: 'ttsModel', label: '模型', placeholder: 'cosyvoice-v3.5-plus' }, { key: 'ttsVoice', label: '音色 ID', placeholder: 'cosyvoice-v3.5-plus-bailian-xxx' }, { key: 'ttsLanguage', label: '语言', placeholder: 'Chinese' }, ], }, ]; export function ConfigForm() { const [tab, setTab] = useState<'agent' | 'skills'>('agent'); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState<{ ok: boolean; error?: string; models?: string[] } | null>(null); // Agent config (DB-based) const [agentForm, setAgentForm] = useState({ protocol: 'anthropic' as 'anthropic' | 'openai', model: '', baseUrl: '', authToken: '', defaultImageModel: '', defaultVideoModel: '', defaultFormat: '', }); // Skills config (file-based) const [skillsForm, setSkillsForm] = useState({}); useEffect(() => { api.getConfigs().then((list) => { const next = { ...agentForm }; for (const item of list) { try { const v = typeof item.value === 'string' ? JSON.parse(item.value) : item.value; if (item.key === 'api_keys') { if (v.ANTHROPIC_MODEL) next.model = v.ANTHROPIC_MODEL as string; if (v.ANTHROPIC_BASE_URL) next.baseUrl = v.ANTHROPIC_BASE_URL as string; if (v.ANTHROPIC_AUTH_TOKEN) next.authToken = v.ANTHROPIC_AUTH_TOKEN as string; if (v.PROTOCOL) next.protocol = v.PROTOCOL as 'anthropic' | 'openai'; } if (item.key === 'defaults') { if (v.imageModel) next.defaultImageModel = v.imageModel; if (v.videoModel) next.defaultVideoModel = v.videoModel; if (v.format) next.defaultFormat = v.format; } } catch {} } setAgentForm(next); }); api.getSkillsConfig().then(setSkillsForm).catch(() => {}); }, []); const handleAgentChange = (key: string, value: string) => { setAgentForm((f) => ({ ...f, [key]: value })); }; const handleSkillsChange = (key: string, value: unknown) => { setSkillsForm((f) => ({ ...f, [key]: value })); }; const handleSave = async () => { setSaving(true); try { if (tab === 'agent') { await api.saveConfig('api_keys', { PROTOCOL: agentForm.protocol, ANTHROPIC_MODEL: agentForm.model, ANTHROPIC_BASE_URL: agentForm.baseUrl, ANTHROPIC_AUTH_TOKEN: agentForm.authToken, }); await api.saveConfig('defaults', { imageModel: agentForm.defaultImageModel, videoModel: agentForm.defaultVideoModel, format: agentForm.defaultFormat, }); } else { await api.saveSkillsConfig(skillsForm); } setSaved(true); setTimeout(() => setSaved(false), 2000); } catch {} setSaving(false); }; return (
{tab === 'agent' ? ( <>

API 配置

{(['anthropic', 'openai'] as const).map((p) => ( ))}

{agentForm.protocol === 'anthropic' ? 'Anthropic 兼容协议(支持 Claude、GLM、DeepSeek 代理等)' : 'OpenAI 兼容协议(支持 GPT、DeepSeek、Qwen、Ollama 等)'}

handleAgentChange('model', e.target.value)} placeholder={agentForm.protocol === 'anthropic' ? 'claude-sonnet-4-6 或 GLM-5.1' : 'gpt-4o 或 deepseek-chat'} className="mt-1 bg-zinc-50 border-zinc-200" />
handleAgentChange('baseUrl', e.target.value)} placeholder={agentForm.protocol === 'anthropic' ? 'https://api.anthropic.com' : 'https://api.openai.com/v1'} className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs" />
handleAgentChange('authToken', e.target.value)} type="password" placeholder="sk-..." className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs" />
{testResult && ( {testResult.ok ? `连接成功 ${testResult.models?.length ? `(${testResult.models.join(', ')})` : ''}` : testResult.error} )}

默认参数

) : ( <> {SECTIONS.map((section) => (

{section.title}

{section.fields.map((field) => { const val = skillsForm[field.key]; if (field.type === 'toggle') { return (
); } return (
handleSkillsChange(field.key, e.target.value)} type={field.type === 'password' ? 'password' : 'text'} placeholder={field.placeholder} className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs" />
); })}
))} )}
); }