'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { ArrowLeft, Download, Check, Loader2, Eye, EyeOff, RotateCcw, Moon, Sun, Sparkles, Trash2, AlertTriangle } from 'lucide-react'; import { Toggle } from '@/components/ui/Toggle'; import { ModelCardSelector } from '@/components/ui/ModelCardSelector'; import { FontSizePicker } from '@/components/ui/FontSizePicker'; import { ConfirmDialog } from '@/components/ui/ConfirmDialog'; import { cn } from '@/lib/utils'; import { useSettings, useModels, useTools } from '@/hooks/useSettings'; // 默认系统提示词 const DEFAULT_SYSTEM_PROMPT = `你是一个专业、友好的 AI 助手。请遵循以下规则来回复用户: ## 回复风格 - 使用中文回复,除非用户明确要求其他语言 - 回复要详细、有深度,不要过于简短 - 语气友好、专业,像一个耐心的老师 ## 格式规范 - 使用 Markdown 格式化回复 - 对于代码,使用代码块并标明语言,例如 \`\`\`python - 使用标题(##、###)来组织长回复 - 使用列表(有序或无序)来列举要点 - 使用粗体或斜体强调重要内容 ## 回答结构 当回答技术问题时,请按以下结构: 1. **简要概述**:先给出简洁的答案 2. **详细解释**:深入解释原理或概念 3. **代码示例**:如果适用,提供完整、可运行的代码 4. **注意事项**:指出常见错误或最佳实践 5. **延伸阅读**:如果有相关主题,可以简要提及 ## 代码规范 - 代码要完整、可运行,不要省略关键部分 - 添加适当的注释解释关键逻辑 - 使用有意义的变量名 - 如果代码较长,先展示完整代码,再逐段解释 ## 特别注意 - 如果问题不明确,先确认理解是否正确 - 如果有多种方案,说明各自的优缺点 - 承认不确定的地方,不要编造信息`; export default function SettingsPage() { const { settings, loading, saving, updateSettings } = useSettings(); const { models, loading: modelsLoading } = useModels(); const { tools, loading: toolsLoading } = useTools(); // CCH 配置状态 const [cchUrl, setCchUrl] = useState(''); const [cchApiKey, setCchApiKey] = useState(''); const [apiFormat, setApiFormat] = useState<'claude' | 'openai'>('claude'); const [showApiKey, setShowApiKey] = useState(false); const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); // AI 行为设置状态 const [systemPrompt, setSystemPrompt] = useState(''); const [temperature, setTemperature] = useState('0.7'); const [promptSaveStatus, setPromptSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); // 导出和清除状态 const [exportLoading, setExportLoading] = useState(false); const [clearDialogOpen, setClearDialogOpen] = useState(false); const [clearLoading, setClearLoading] = useState(false); const [chatStats, setChatStats] = useState<{ conversationCount: number; messageCount: number } | null>(null); // 当设置加载完成后,更新本地状态 useEffect(() => { if (settings) { setCchUrl(settings.cchUrl || ''); setApiFormat((settings.apiFormat as 'claude' | 'openai') || 'claude'); setSystemPrompt(settings.systemPrompt || ''); setTemperature(settings.temperature || '0.7'); } }, [settings]); // 保存 CCH 配置 const handleSaveCchConfig = async () => { setSaveStatus('saving'); try { const updates: Record = {}; if (cchUrl) updates.cchUrl = cchUrl; if (cchApiKey) updates.cchApiKey = cchApiKey; updates.apiFormat = apiFormat; if (Object.keys(updates).length > 0) { await updateSettings(updates); } setSaveStatus('saved'); setCchApiKey(''); // 清除输入的 API Key setTimeout(() => setSaveStatus('idle'), 2000); } catch { setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 2000); } }; // 清除 API Key const handleClearApiKey = async () => { try { await updateSettings({ cchApiKey: '' }); } catch (error) { console.error('Failed to clear API key:', error); } }; // 更新默认模型 const handleModelChange = async (modelId: string) => { try { await updateSettings({ defaultModel: modelId }); } catch (error) { console.error('Failed to update model:', error); } }; // 更新字体大小 const handleFontSizeChange = async (fontSize: number) => { try { await updateSettings({ fontSize }); // 实时应用到页面 document.documentElement.style.setProperty('--font-size-base', `${fontSize}px`); } catch (error) { console.error('Failed to update font size:', error); } }; // 切换工具 const handleToolToggle = async (toolId: string) => { const currentTools = settings?.defaultTools || []; const newTools = currentTools.includes(toolId) ? currentTools.filter((t) => t !== toolId) : [...currentTools, toolId]; try { await updateSettings({ defaultTools: newTools }); } catch (error) { console.error('Failed to update tools:', error); } }; // 切换思考模式 const handleThinkingToggle = async () => { try { await updateSettings({ enableThinking: !settings?.enableThinking }); } catch (error) { console.error('Failed to toggle thinking:', error); } }; // 切换聊天历史 const handleChatHistoryToggle = async () => { try { await updateSettings({ saveChatHistory: !settings?.saveChatHistory }); } catch (error) { console.error('Failed to toggle chat history:', error); } }; // 更新主题 const handleThemeChange = async (theme: string) => { try { await updateSettings({ theme }); } catch (error) { console.error('Failed to update theme:', error); } }; // 更新语言 const handleLanguageChange = async (language: string) => { try { await updateSettings({ language }); } catch (error) { console.error('Failed to update language:', error); } }; // 保存 System Prompt 和温度设置 const handleSavePromptSettings = async () => { setPromptSaveStatus('saving'); try { await updateSettings({ systemPrompt, temperature }); setPromptSaveStatus('saved'); setTimeout(() => setPromptSaveStatus('idle'), 2000); } catch { setPromptSaveStatus('error'); setTimeout(() => setPromptSaveStatus('idle'), 2000); } }; // 重置为默认 System Prompt const handleResetSystemPrompt = () => { setSystemPrompt(DEFAULT_SYSTEM_PROMPT); }; // 更新温度(实时显示但不立即保存) const handleTemperatureChange = (value: string) => { setTemperature(value); }; // 切换主题 const handleToggleTheme = async () => { const newTheme = settings?.theme === 'dark' ? 'light' : 'dark'; try { await updateSettings({ theme: newTheme }); document.documentElement.setAttribute('data-theme', newTheme); } catch (error) { console.error('Failed to update theme:', error); } }; // 导出聊天数据 const handleExportData = async () => { setExportLoading(true); try { const response = await fetch('/api/conversations/export'); if (!response.ok) { throw new Error('Export failed'); } const data = await response.json(); // 创建 Blob 并下载 const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const date = new Date().toISOString().split('T')[0]; a.download = `lioncode-chat-export-${date}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (error) { console.error('Failed to export data:', error); alert('导出失败,请稍后重试'); } finally { setExportLoading(false); } }; // 打开清除对话框(获取统计信息) const handleOpenClearDialog = async () => { try { const response = await fetch('/api/conversations/all'); if (response.ok) { const stats = await response.json(); setChatStats(stats); } } catch (error) { console.error('Failed to get stats:', error); } setClearDialogOpen(true); }; // 清除所有聊天 const handleClearAllChats = async () => { setClearLoading(true); try { const response = await fetch('/api/conversations/all', { method: 'DELETE', }); if (!response.ok) { throw new Error('Delete failed'); } setClearDialogOpen(false); // 跳转到首页 window.location.href = '/'; } catch (error) { console.error('Failed to clear chats:', error); alert('清除失败,请稍后重试'); } finally { setClearLoading(false); } }; if (loading) { return (
); } const isDarkMode = settings?.theme === 'dark'; return (
{/* 顶部导航栏 */}
{/* 左侧:返回按钮和标题 */}

Settings

{/* 右侧:主题切换 */}
{/* 主内容区 */}
{/* CCH 配置 */} {/* 服务地址配置 */} setCchUrl(e.target.value)} placeholder="https://claude.leocoder.cn/" /> {/* API 格式选择 */}
API 格式
选择中转站的 API 接口格式
{/* Claude 原生选项 */} {/* OpenAI 兼容选项 */}
{settings?.cchApiKeyConfigured ? ( <> 已配置 ) : (
setCchApiKey(e.target.value)} placeholder="输入 API Key" />
)}
{saveStatus === 'error' && ( 保存失败 )}
{/* 模型和工具设置 */} {/* 模型卡片选择 */}
默认模型
为新对话选择默认 AI 模型
{/* 字体大小设置 */}
字体大小
调整全局字体大小,实时预览效果
默认工具
选择新对话默认启用的工具
{toolsLoading ? ( 加载中... ) : ( tools.map((tool) => ( )) )}
{/* AI 行为设置 */} {/* 温度参数 */}
温度参数 (Temperature)
控制回复的创造性和随机性。较低值更保守精确,较高值更创新多样。
{temperature}
精确 handleTemperatureChange(e.target.value)} className="flex-1 h-2 bg-[var(--color-bg-tertiary)] rounded-lg appearance-none cursor-pointer accent-[var(--color-primary)]" /> 创意
{/* System Prompt 编辑器 */}
系统提示词 (System Prompt)
定义 AI 的角色、回复风格和行为规范。留空将使用默认提示词。